merge lastest from mojombo/jekyll master

This commit is contained in:
edeustace
2013-01-11 12:23:53 +01:00
71 changed files with 4538 additions and 94 deletions

View File

@@ -46,47 +46,50 @@ require_all 'jekyll/generators'
require_all 'jekyll/tags'
module Jekyll
VERSION = '0.11.2'
VERSION = '0.12.0'
# Default options. Overriden by values in _config.yml or command-line opts.
# (Strings rather symbols used for compatability with YAML).
# Strings rather than symbols are used for compatability with YAML.
DEFAULTS = {
'safe' => false,
'auto' => false,
'server' => false,
'server_port' => 4000,
'source' => Dir.pwd,
'destination' => File.join(Dir.pwd, '_site'),
'plugins' => File.join(Dir.pwd, '_plugins'),
'source' => Dir.pwd,
'destination' => File.join(Dir.pwd, '_site'),
'plugins' => File.join(Dir.pwd, '_plugins'),
'layouts' => '_layouts',
'keep_files' => ['.git','.svn'],
'layouts' => '_layouts',
'future' => true,
'lsi' => false,
'pygments' => false,
'markdown' => 'maruku',
'permalink' => 'date',
'include' => ['.htaccess'],
'future' => true,
'lsi' => false,
'pygments' => false,
'markdown' => 'maruku',
'permalink' => 'date',
'include' => ['.htaccess'],
'paginate_path' => 'page:num',
'markdown_ext' => 'markdown,mkd,mkdn,md',
'textile_ext' => 'textile',
'markdown_ext' => 'markdown,mkd,mkdn,md',
'textile_ext' => 'textile',
'maruku' => {
'maruku' => {
'use_tex' => false,
'use_divs' => false,
'png_engine' => 'blahtex',
'png_dir' => 'images/latex',
'png_url' => '/images/latex'
},
'rdiscount' => {
'rdiscount' => {
'extensions' => []
},
'redcarpet' => {
'redcarpet' => {
'extensions' => []
},
'kramdown' => {
'kramdown' => {
'auto_ids' => true,
'footnote_nr' => 1,
'entity_output' => 'as_char',
@@ -103,8 +106,9 @@ module Jekyll
'coderay_css' => 'style'
}
},
'redcloth' => {
'hard_breaks' => true
'redcloth' => {
'hard_breaks' => true
}
}

View File

@@ -8,12 +8,28 @@ module Jekyll
def setup
return if @setup
# Set the Markdown interpreter (and Maruku self.config, if necessary)
case @config['markdown']
when 'redcarpet'
begin
require 'redcarpet'
@redcarpet_extensions = @config['redcarpet']['extensions'].map { |e| e.to_sym }
@renderer ||= Class.new(Redcarpet::Render::HTML) do
def block_code(code, lang)
lang = lang && lang.split.first || "text"
output = add_code_tags(
Pygments.highlight(code, :lexer => lang, :options => { :encoding => 'utf-8' }),
lang
)
end
def add_code_tags(code, lang)
code = code.sub(/<pre>/,'<pre><code class="' + lang + '">')
code = code.sub(/<\/pre>/,"</code></pre>")
end
end
@redcarpet_extensions = {}
@config['redcarpet']['extensions'].each { |e| @redcarpet_extensions[e.to_sym] = true }
rescue LoadError
STDERR.puts 'You are missing a library required for Markdown. Please run:'
STDERR.puts ' $ [sudo] gem install redcarpet'
@@ -30,8 +46,6 @@ module Jekyll
when 'rdiscount'
begin
require 'rdiscount'
# Load rdiscount extensions
@rdiscount_extensions = @config['rdiscount']['extensions'].map { |e| e.to_sym }
rescue LoadError
STDERR.puts 'You are missing a library required for Markdown. Please run:'
@@ -88,7 +102,10 @@ module Jekyll
setup
case @config['markdown']
when 'redcarpet'
Redcarpet.new(content, *@redcarpet_extensions).to_html
@redcarpet_extensions[:fenced_code_blocks] = !@redcarpet_extensions[:no_fenced_code_blocks]
@renderer.send :include, Redcarpet::Render::SmartyPants if @redcarpet_extensions[:smart]
markdown = Redcarpet::Markdown.new(@renderer.new(@redcarpet_extensions), @redcarpet_extensions)
markdown.render(content)
when 'kramdown'
# Check for use of coderay
if @config['kramdown']['use_coderay']

View File

@@ -25,15 +25,17 @@ module Jekyll
#
# Returns nothing.
def read_yaml(base, name)
self.content = File.read(File.join(base, name))
begin
if self.content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
self.content = File.read(File.join(base, name))
if self.content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
self.content = $POSTMATCH
self.data = YAML.load($1)
end
rescue => e
puts "YAML Exception reading #{name}: #{e.message}"
puts "Error reading file #{File.join(base, name)}: #{e.message}"
rescue SyntaxError => e
puts "YAML Exception reading #{File.join(base, name)}: #{e.message}"
end
self.data ||= {}
@@ -76,9 +78,13 @@ module Jekyll
payload["pygments_suffix"] = converter.pygments_suffix
begin
self.content = Liquid::Template.parse(self.content).render(payload, info)
self.content = Liquid::Template.parse(self.content).render!(payload, info)
rescue => e
puts "Liquid Exception: #{e.message} in #{self.name}"
e.backtrace.each do |backtrace|
puts backtrace
end
abort("Build Failed")
end
self.transform
@@ -94,9 +100,13 @@ module Jekyll
payload = payload.deep_merge({"content" => self.output, "page" => layout.data})
begin
self.output = Liquid::Template.parse(layout.content).render(payload, info)
self.output = Liquid::Template.parse(layout.content).render!(payload, info)
rescue => e
puts "Liquid Exception: #{e.message} in #{self.data["layout"]}"
e.backtrace.each do |backtrace|
puts backtrace
end
abort("Build Failed")
end
if layout = layouts[layout.data["layout"]]

View File

@@ -57,6 +57,17 @@ module Jekyll
date.xmlschema
end
# XML escape a string for use. Replaces any special characters with
# appropriate HTML entity replacements.
#
# input - The String to escape.
#
# Examples
#
# xml_escape('foo "bar" <baz>')
# # => "foo &quot;bar&quot; &lt;baz&gt;"
#
# Returns the escaped String.
def xml_escape(input)
CGI.escapeHTML(input)
end

View File

@@ -1,11 +1,14 @@
require 'rubygems'
require 'jekyll'
require 'fileutils'
require 'net/http'
require 'net/https'
require 'open-uri'
require 'uri'
require "json"
# ruby -r './lib/jekyll/migrators/posterous.rb' -e 'Jekyll::Posterous.process(email, pass, api_key, blog)'
# ruby -r './lib/jekyll/migrators/posterous.rb' -e 'Jekyll::Posterous.process(email, pass, api_token, blog, tags_key)'
# You can find your api token in posterous api page - http://posterous.com/api . Click on any of the 'view token' links to see your token.
# blog is optional, by default it is the primary one
module Jekyll
module Posterous
@@ -14,6 +17,9 @@ module Jekyll
raise ArgumentError, 'Stuck in a redirect loop. Please double check your email and password' if limit == 0
response = nil
puts uri_str
puts '-------'
Net::HTTP.start('posterous.com') do |http|
req = Net::HTTP::Get.new(uri_str)
req.basic_auth @email, @pass
@@ -23,36 +29,50 @@ module Jekyll
case response
when Net::HTTPSuccess then response
when Net::HTTPRedirection then fetch(response['location'], limit - 1)
when Net::HTTPForbidden then
retry_after = response.to_hash['retry-after'][0]
puts "We have been told to try again after #{retry_after} seconds"
sleep(retry_after.to_i + 1)
fetch(uri_str, limit - 1)
else response.error!
end
end
def self.process(email, pass, api_token, blog = 'primary')
def self.process(email, pass, api_token, blog = 'primary', tags_key = 'categories')
@email, @pass, @api_token = email, pass, api_token
FileUtils.mkdir_p "_posts"
posts = JSON.parse(self.fetch("/api/v2/users/me/sites/#{blog}/posts?api_token=#{@api_token}").body)
posts = JSON.parse(self.fetch("/api/2/sites/#{blog}/posts?api_token=#{@api_token}").body)
page = 1
while posts.any?
posts.each do |post|
title = post["title"]
slug = title.gsub(/[^[:alnum:]]+/, '-').downcase
slug = title.gsub(/[^[:alnum:]]+/, '-').gsub(/^-+|-+$/, '').downcase
posterous_slug = post["slug"]
date = Date.parse(post["display_date"])
content = post["body_html"]
published = !post["is_private"]
name = "%02d-%02d-%02d-%s.html" % [date.year, date.month, date.day, slug]
tags = []
post["tags"].each do |tag|
tags.push(tag["name"])
end
# Get the relevant fields as a hash, delete empty fields and convert
# to YAML for the header
data = {
'layout' => 'post',
'title' => title.to_s,
'published' => published
'published' => published,
tags_key => tags,
'posterous_url' => post["full_url"],
'posterous_slug' => posterous_slug
}.delete_if { |k,v| v.nil? || v == ''}.to_yaml
# Write out the data and content to file
File.open("_posts/#{name}", "w") do |f|
puts name
f.puts data
f.puts "---"
f.puts content
@@ -60,7 +80,7 @@ module Jekyll
end
page += 1
posts = JSON.parse(self.fetch("/api/v2/users/me/sites/#{blog}/posts?api_token=#{@api_token}&page=#{page}").body)
posts = JSON.parse(self.fetch("/api/2/sites/#{blog}/posts?api_token=#{@api_token}&page=#{page}").body)
end
end
end

View File

@@ -8,12 +8,13 @@ module Jekyll
attr_accessor :lsi
end
# Valid post name regex.
MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
# Post name validator. Post filenames must be like:
# 2008-11-05-my-awesome-post.textile
# 2008-11-05-my-awesome-post.textile
#
# Returns <Bool>
# Returns true if valid, false if not.
def self.valid?(name)
name =~ MATCHER
end
@@ -25,12 +26,13 @@ module Jekyll
attr_reader :name
# Initialize this Post instance.
# +site+ is the Site
# +base+ is the String path to the dir containing the post file
# +name+ is the String filename of the post file
# +categories+ is an Array of Strings for the categories for this post
#
# Returns <Post>
# site - The Site.
# base - The String path to the dir containing the post file.
# name - The String filename of the post file.
# categories - An Array of Strings for the categories for this post.
#
# Returns the new Post.
def initialize(site, source, dir, name)
@site = site
@base = File.join(source, dir, '_posts')
@@ -38,10 +40,14 @@ module Jekyll
self.categories = dir.split('/').reject { |x| x.empty? }
self.process(name)
self.read_yaml(@base, name)
begin
self.read_yaml(@base, name)
rescue Exception => msg
raise FatalException.new("#{msg} in #{@base}/#{name}")
end
#If we've added a date and time to the yaml, use that instead of the filename date
#Means we'll sort correctly.
# If we've added a date and time to the YAML, use that instead of the
# filename date. Means we'll sort correctly.
if self.data.has_key?('date')
# ensure Time via to_s and reparse
self.date = Time.parse(self.data["date"].to_s)
@@ -60,7 +66,10 @@ module Jekyll
end
end
# Spaceship is based on Post#date, slug
# Compares Post objects. First compares the Post date. If the dates are
# equal, it compares the Post slugs.
#
# other - The other Post we are comparing to.
#
# Returns -1, 0, 1
def <=>(other)
@@ -71,10 +80,11 @@ module Jekyll
return cmp
end
# Extract information from the post filename
# +name+ is the String filename of the post file
# Extract information from the post filename.
#
# Returns nothing
# name - The String filename of the post file.
#
# Returns nothing.
def process(name)
m, cats, date, slug, ext = *name.match(MATCHER)
self.date = Time.parse(date)
@@ -87,18 +97,17 @@ module Jekyll
# The generated directory into which the post will be placed
# upon generation. This is derived from the permalink or, if
# permalink is absent, set to the default date
# e.g. "/2008/11/05/" if the permalink style is :date, otherwise nothing
# e.g. "/2008/11/05/" if the permalink style is :date, otherwise nothing.
#
# Returns <String>
# Returns the String directory.
def dir
File.dirname(url)
end
# The full path and filename of the post.
# Defined in the YAML of the post body
# (Optional)
# The full path and filename of the post. Defined in the YAML of the post
# body (optional).
#
# Returns <String>
# Returns the String permalink.
def permalink
self.data && self.data['permalink']
end
@@ -116,10 +125,10 @@ module Jekyll
end
end
# The generated relative url of this post
# The generated relative url of this post.
# e.g. /2008/11/05/my-awesome-post.html
#
# Returns <String>
# Returns the String URL.
def url
return @url if @url
@@ -146,17 +155,17 @@ module Jekyll
@url
end
# The UID for this post (useful in feeds)
# The UID for this post (useful in feeds).
# e.g. /2008/11/05/my-awesome-post
#
# Returns <String>
# Returns the String UID.
def id
File.join(self.dir, self.slug)
end
# Calculate related posts.
#
# Returns [<Post>]
# Returns an Array of related Posts.
def related_posts(posts)
return [] unless posts.size > 1
@@ -176,11 +185,12 @@ module Jekyll
end
end
# Add any necessary layouts to this post
# +layouts+ is a Hash of {"name" => "layout"}
# +site_payload+ is the site payload hash
# Add any necessary layouts to this post.
#
# Returns nothing
# layouts - A Hash of {"name" => "layout"}.
# site_payload - The site payload hash.
#
# Returns nothing.
def render(layouts, site_payload)
# construct payload
payload = {
@@ -192,9 +202,10 @@ module Jekyll
end
# Obtain destination path.
# +dest+ is the String path to the destination dir
#
# Returns destination file path.
# dest - The String path to the destination dir.
#
# Returns destination file path String.
def destination(dest)
# The url needs to be unescaped in order to preserve the correct filename
path = File.join(dest, CGI.unescape(self.url))
@@ -203,9 +214,10 @@ module Jekyll
end
# Write the generated post file to the destination directory.
# +dest+ is the String path to the destination dir
#
# Returns nothing
# dest - The String path to the destination dir.
#
# Returns nothing.
def write(dest)
path = destination(dest)
FileUtils.mkdir_p(File.dirname(path))
@@ -216,7 +228,7 @@ module Jekyll
# Convert this post into a Hash for use in Liquid templates.
#
# Returns <Hash>
# Returns the representative Hash.
def to_liquid
self.data.deep_merge({
"title" => self.data["title"] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' '),
@@ -230,6 +242,7 @@ module Jekyll
"content" => self.content })
end
# Returns the shorthand String identifier of this Post.
def inspect
"<Post: #{self.id}>"
end

View File

@@ -72,6 +72,12 @@ module Jekyll
def setup
require 'classifier' if self.lsi
# Check that the destination dir isn't the source dir or a directory
# parent to the source dir.
if self.source =~ /^#{self.dest}/
raise FatalException.new "Destination directory cannot be or contain the Source directory."
end
# If safe mode is off, load in any Ruby files under the plugins
# directory.
unless self.safe
@@ -244,7 +250,6 @@ module Jekyll
files.merge(dirs)
obsolete_files = dest_files - files
FileUtils.rm_rf(obsolete_files.to_a)
end
@@ -329,7 +334,7 @@ module Jekyll
#
# Returns the Array of filtered entries.
def filter_entries(entries)
entries = entries.reject do |e|
entries.reject do |e|
unless self.include.include?(e)
['.', '_', '#'].include?(e[0..0]) ||
e[-1..-1] == '~' ||