diff --git a/README.textile b/README.textile index 7d99217a..5e04f316 100644 --- a/README.textile +++ b/README.textile @@ -177,7 +177,7 @@ leaf directory resulting in URLs like 2008/11/17/blogging-like-a-hacker/. h2. Configuration File All of the options listed above can be specified on a site-by-site basis in -a '_config.yaml' file at the root of the site's source. As the filename +a '_config.yml' file at the root of the site's source. As the filename suggests, the configuration is given in "YAML":http://www.yaml.org/. As well as all of the options discussed in the last section, there are a few additional options: diff --git a/bin/jekyll b/bin/jekyll index 48264f99..1f3cc567 100755 --- a/bin/jekyll +++ b/bin/jekyll @@ -9,10 +9,10 @@ Basic Command Line Usage: jekyll # . -> ./_site jekyll # . -> jekyll # -> - + Configuration is read from '/_config.yaml' but can be overriden using the following options: - + HELP require 'optparse' @@ -30,28 +30,28 @@ opts = OptionParser.new do |opts| opts.on("--no-auto", "No auto-regenerate") do options['auto'] = false end - + opts.on("--server [PORT]", "Start web server (default port 4000)") do |port| options['server'] = true options['server_port'] = port unless port.nil? end - + opts.on("--lsi", "Use LSI for better related posts") do options['lsi'] = true end - + opts.on("--pygments", "Use pygments to highlight code") do options['pygments'] = true end - + opts.on("--rdiscount", "Use rdiscount gem for Markdown") do options['markdown'] = 'rdiscount' end - + opts.on("--permalink [TYPE]", "Use 'date' (default) for YYYY/MM/DD") do |style| options['permalink'] = style unless style.nil? end - + opts.on("--version", "Display current version") do puts "Jekyll " + Jekyll.version exit 0 @@ -61,36 +61,20 @@ end # Read command line options into `options` hash opts.parse! -# Temporarily set source and destination options to read in config file -source = Jekyll::DEFAULTS['source'] -destination = Jekyll::DEFAULTS['destination'] - # Get source and destintation from command line case ARGV.size when 0 when 1 - destination = options['destination'] = ARGV[0] + options['destination'] = ARGV[0] when 2 - source = options['source'] = ARGV[0] - destination = options['destination'] = ARGV[1] + options['source'] = ARGV[0] + options['destination'] = ARGV[1] else puts "Invalid options. Run `jekyll --help` for assistance." exit(1) end -# Get configuration from /_config.yaml -config = {} -config_file = File.join(source, '_config.yaml') -begin - config = YAML.load_file( config_file ) - puts "Configuration from #{config_file}" -rescue => err - puts "WARNING: Could not read configuration. Using defaults (and options)." - puts "\t" + err -end - -# Merge DEFAULTS < config file < command line options -options = Jekyll::DEFAULTS.deep_merge(config).deep_merge(options) +options = Jekyll.configuration(options) # Get source and destination directories (possibly set by config file) source = options['source'] @@ -106,30 +90,33 @@ def globs(source) end end +# Create the Site +site = Jekyll::Site.new(options) + # Run the directory watcher for auto-generation, if required if options['auto'] require 'directory_watcher' puts "Auto-regenerating enabled: #{source} -> #{destination}" - + dw = DirectoryWatcher.new(source) dw.interval = 1 dw.glob = globs(source) - + dw.add_observer do |*args| t = Time.now.strftime("%Y-%m-%d %H:%M:%S") puts "[#{t}] regeneration: #{args.size} files changed" - Jekyll.process(options) + site.process end - + dw.start - + unless options['server'] loop { sleep 1000 } end else puts "Building site: #{source} -> #{destination}" - Jekyll.process(options) + site.process puts "Successfully generated site: #{source} -> #{destination}" end @@ -137,7 +124,7 @@ end if options['server'] require 'webrick' include WEBrick - + FileUtils.mkdir_p(destination) s = HTTPServer.new( @@ -147,7 +134,7 @@ if options['server'] t = Thread.new { s.start } - + trap("INT") { s.shutdown } t.join() end \ No newline at end of file diff --git a/lib/jekyll.rb b/lib/jekyll.rb index 2721d6b4..e7d4fe53 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -27,8 +27,6 @@ require 'jekyll/tags/include' require 'jekyll/albino' module Jekyll - VERSION = '0.3.0' - # Default options. Overriden by values in _config.yaml or command-line opts. # (Strings rather symbols used for compatability with YAML) DEFAULTS = { @@ -52,83 +50,32 @@ module Jekyll 'png_url' => '/images/latex' } } - - class << self - attr_accessor :source,:dest,:lsi,:pygments,:permalink_style - end - # Initializes some global Jekyll parameters - def self.configure(options) - # Interpret the simple options and configure Jekyll appropriately - Jekyll.source = options['source'] - Jekyll.dest = options['destination'] - Jekyll.lsi = options['lsi'] - Jekyll.pygments = options['pygments'] - Jekyll.permalink_style = options['permalink'].to_sym + # Generate a Jekyll configuration Hash by merging the default options + # with anything in _config.yml, and adding the given options on top + # +override+ is a Hash of config directives + # + # Returns Hash + def self.configuration(override) + # _config.yml may override default source location, but until + # then, we need to know where to look for _config.yml + source = override['source'] || Jekyll::DEFAULTS['source'] - # Check to see if LSI is enabled. - require 'classifier' if Jekyll.lsi - - # Set the Markdown interpreter (and Maruku options, if necessary) - case options['markdown'] - - when 'rdiscount' - begin - require 'rdiscount' - - def self.markdown(content) - RDiscount.new(content).to_html - end - - puts 'Using rdiscount for Markdown' - rescue LoadError - puts 'You must have the rdiscount gem installed first' - end - - when 'maruku' - begin - require 'maruku' - - def self.markdown(content) - Maruku.new(content).to_html - end - - if options['maruku']['use_divs'] - require 'maruku/ext/div' - puts 'Maruku: Using extended syntax for div elements.' - end - - if options['maruku']['use_tex'] - require 'maruku/ext/math' - puts "Maruku: Using LaTeX extension. Images in `#{options['maruku']['png_dir']}`." - - # Switch off MathML output - MaRuKu::Globals[:html_math_output_mathml] = false - MaRuKu::Globals[:html_math_engine] = 'none' - - # Turn on math to PNG support with blahtex - # Resulting PNGs stored in `images/latex` - MaRuKu::Globals[:html_math_output_png] = true - MaRuKu::Globals[:html_png_engine] = options['maruku']['png_engine'] - MaRuKu::Globals[:html_png_dir] = options['maruku']['png_dir'] - MaRuKu::Globals[:html_png_url] = options['maruku']['png_url'] - end - rescue LoadError - puts "The maruku gem is required for markdown support!" - end + # Get configuration from /_config.yaml + config = {} + config_file = File.join(source, '_config.yml') + begin + config = YAML.load_file(config_file) + puts "Configuration from #{config_file}" + rescue => err + puts "WARNING: Could not read configuration. Using defaults (and options)." + puts "\t" + err end + # Merge DEFAULTS < _config.yml < override + Jekyll::DEFAULTS.deep_merge(config).deep_merge(override) end - def self.textile(content) - RedCloth.new(content).to_html - end - - def self.process(config) - Jekyll.configure(config) - Jekyll::Site.new(config).process - end - def self.version yml = YAML.load(File.read(File.join(File.dirname(__FILE__), *%w[.. VERSION.yml]))) "#{yml[:major]}.#{yml[:minor]}.#{yml[:patch]}" diff --git a/lib/jekyll/albino.rb b/lib/jekyll/albino.rb index f452b329..900cf2c9 100644 --- a/lib/jekyll/albino.rb +++ b/lib/jekyll/albino.rb @@ -38,7 +38,7 @@ # # To see all lexers and formatters available, run `pygmentize -L`. # -# Chris Wanstrath // chris@ozmm.org +# Chris Wanstrath // chris@ozmm.org # GitHub // http://github.com # require 'open4' diff --git a/lib/jekyll/converters/mephisto.rb b/lib/jekyll/converters/mephisto.rb index 6d6c6565..aa985b5e 100644 --- a/lib/jekyll/converters/mephisto.rb +++ b/lib/jekyll/converters/mephisto.rb @@ -13,7 +13,7 @@ require File.join(File.dirname(__FILE__),"csv.rb") # installed, running the following commands should work: # $ sudo gem install sequel # $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config - + module Jekyll module Mephisto #Accepts a hash with database config variables, exports mephisto posts into a csv @@ -38,24 +38,24 @@ module Jekyll # through the created posts to make sure nothing is accidently published. QUERY = "SELECT id, permalink, body, published_at, title FROM contents WHERE user_id = 1 AND type = 'Article' AND published_at IS NOT NULL ORDER BY published_at" - + def self.process(dbname, user, pass, host = 'localhost') db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host) - + FileUtils.mkdir_p "_posts" - + db[QUERY].each do |post| title = post[:title] slug = post[:permalink] date = post[:published_at] content = post[:body] # more_content = '' - + # Be sure to include the body and extended body. # if more_content != nil # content = content + " \n" + more_content # end - + # Ideally, this script would determine the post format (markdown, html # , etc) and create files with proper extensions. At this point it # just assumes that markdown will be acceptable. @@ -66,14 +66,14 @@ module Jekyll 'title' => title.to_s, 'mt_id' => post[:entry_id], }.delete_if { |k,v| v.nil? || v == ''}.to_yaml - + File.open("_posts/#{name}", "w") do |f| f.puts data f.puts "---" f.puts content end end - + end end end diff --git a/lib/jekyll/converters/mt.rb b/lib/jekyll/converters/mt.rb index 3b98b139..27b12226 100644 --- a/lib/jekyll/converters/mt.rb +++ b/lib/jekyll/converters/mt.rb @@ -11,31 +11,31 @@ require 'fileutils' # installed, running the following commands should work: # $ sudo gem install sequel # $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config - + module Jekyll module MT # This query will pull blog posts from all entries across all blogs. If # you've got unpublished, deleted or otherwise hidden posts please sift # through the created posts to make sure nothing is accidently published. QUERY = "SELECT entry_id, entry_basename, entry_text, entry_text_more, entry_created_on, entry_title FROM mt_entry" - + def self.process(dbname, user, pass, host = 'localhost') db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host) - + FileUtils.mkdir_p "_posts" - + db[QUERY].each do |post| title = post[:entry_title] slug = post[:entry_basename] date = post[:entry_created_on] content = post[:entry_text] more_content = post[:entry_text_more] - + # Be sure to include the body and extended body. if more_content != nil content = content + " \n" + more_content end - + # Ideally, this script would determine the post format (markdown, html # , etc) and create files with proper extensions. At this point it # just assumes that markdown will be acceptable. @@ -46,14 +46,14 @@ module Jekyll 'title' => title.to_s, 'mt_id' => post[:entry_id], }.delete_if { |k,v| v.nil? || v == ''}.to_yaml - + File.open("_posts/#{name}", "w") do |f| f.puts data f.puts "---" f.puts content end end - + end end end diff --git a/lib/jekyll/converters/textpattern.rb b/lib/jekyll/converters/textpattern.rb index 98e15fdb..a9a972db 100644 --- a/lib/jekyll/converters/textpattern.rb +++ b/lib/jekyll/converters/textpattern.rb @@ -18,20 +18,20 @@ module Jekyll def self.process(dbname, user, pass, host = 'localhost') db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host) - + FileUtils.mkdir_p "_posts" - + db[QUERY].each do |post| # Get required fields and construct Jekyll compatible name title = post[:Title] slug = post[:url_title] date = post[:Posted] content = post[:Body] - + name = [date.strftime("%Y-%m-%d"), slug].join('-') + ".textile" # Get the relevant fields as a hash, delete empty fields and convert - # to YAML for the header + # to YAML for the header data = { 'layout' => 'post', 'title' => title.to_s, diff --git a/lib/jekyll/converters/typo.rb b/lib/jekyll/converters/typo.rb index febac65d..23775483 100644 --- a/lib/jekyll/converters/typo.rb +++ b/lib/jekyll/converters/typo.rb @@ -2,22 +2,22 @@ require 'fileutils' require 'rubygems' require 'sequel' - + module Jekyll module Typo - # this SQL *should* work for both MySQL and PostgreSQL, but I haven't + # this SQL *should* work for both MySQL and PostgreSQL, but I haven't # tested PostgreSQL yet (as of 2008-12-16) SQL = <<-EOS SELECT c.id id, - c.title title, - c.permalink slug, + c.title title, + c.permalink slug, c.body body, - c.published_at date, + c.published_at date, c.state state, COALESCE(tf.name, 'html') filter - FROM contents c + FROM contents c LEFT OUTER JOIN text_filters tf - ON c.text_filter_id = tf.id + ON c.text_filter_id = tf.id EOS def self.process dbname, user, pass, host='localhost' @@ -30,7 +30,7 @@ module Jekyll sprintf("%.02d", post[:date].month), sprintf("%.02d", post[:date].day), post[:slug].strip ].join('-') - # Can have more than one text filter in this field, but we just want + # Can have more than one text filter in this field, but we just want # the first one for this name += '.' + post[:filter].split(' ')[0] diff --git a/lib/jekyll/convertible.rb b/lib/jekyll/convertible.rb index 8ea9d6c4..4d2ec17f 100644 --- a/lib/jekyll/convertible.rb +++ b/lib/jekyll/convertible.rb @@ -1,10 +1,15 @@ +# Convertible provides methods for converting a pagelike item +# from a certain type of markup into actual content +# +# Requires +# self.site -> Jekyll::Site module Jekyll module Convertible # Return the contents as a string def to_s self.content || '' end - + # Read the YAML frontmatter # +base+ is the String path to the dir containing the file # +name+ is the String filename of the file @@ -12,14 +17,14 @@ module Jekyll # Returns nothing def read_yaml(base, name) self.content = File.read(File.join(base, name)) - + if self.content =~ /^(---\s*\n.*?)\n---\s*\n/m self.content = self.content[($1.size + 5)..-1] - + self.data = YAML.load($1) end end - + # Transform the contents based on the file extension. # # Returns nothing @@ -27,13 +32,13 @@ module Jekyll case self.content_type when 'textile' self.ext = ".html" - self.content = Jekyll.textile(self.content) + self.content = self.site.textile(self.content) when 'markdown' self.ext = ".html" - self.content = Jekyll.markdown(self.content) + self.content = self.site.markdown(self.content) end end - + # Determine which formatting engine to use based on this convertible's # extension # @@ -44,30 +49,32 @@ module Jekyll return 'textile' when /markdown/i, /mkdn/i, /md/i return 'markdown' - end + end return 'unknown' end - + # Add any necessary layouts to this convertible document # +layouts+ is a Hash of {"name" => "layout"} # +site_payload+ is the site payload hash # # Returns nothing def do_layout(payload, layouts) + info = { :filters => [Jekyll::Filters], :registers => { :site => self.site } } + # render and transform content (this becomes the final content of the object) payload["content_type"] = self.content_type - self.content = Liquid::Template.parse(self.content).render(payload, [Jekyll::Filters]) + self.content = Liquid::Template.parse(self.content).render(payload, info) self.transform - + # output keeps track of what will finally be written self.output = self.content - + # recursively render layouts layout = layouts[self.data["layout"]] while layout payload = payload.deep_merge({"content" => self.output, "page" => layout.data}) - self.output = Liquid::Template.parse(layout.content).render(payload, [Jekyll::Filters]) - + self.output = Liquid::Template.parse(layout.content).render(payload, info) + layout = layouts[layout.data["layout"]] end end diff --git a/lib/jekyll/core_ext.rb b/lib/jekyll/core_ext.rb index de584711..18159fbf 100644 --- a/lib/jekyll/core_ext.rb +++ b/lib/jekyll/core_ext.rb @@ -1,22 +1,22 @@ class Hash # Merges self with another hash, recursively. - # + # # This code was lovingly stolen from some random gem: # http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html - # + # # Thanks to whoever made it. def deep_merge(hash) target = dup - + hash.keys.each do |key| if hash[key].is_a? Hash and self[key].is_a? Hash target[key] = target[key].deep_merge(hash[key]) next end - + target[key] = hash[key] end - + target end end \ No newline at end of file diff --git a/lib/jekyll/filters.rb b/lib/jekyll/filters.rb index 6bb08096..1e369175 100644 --- a/lib/jekyll/filters.rb +++ b/lib/jekyll/filters.rb @@ -1,10 +1,10 @@ module Jekyll - + module Filters def textilize(input) RedCloth.new(input).to_html end - + def date_to_string(date) date.strftime("%d %b %Y") end @@ -12,19 +12,19 @@ module Jekyll def date_to_long_string(date) date.strftime("%d %B %Y") end - + def date_to_xmlschema(date) date.xmlschema end - + def xml_escape(input) input.gsub("&", "&").gsub("<", "<").gsub(">", ">") end - + def number_of_words(input) input.split.length end - + def array_to_sentence_string(array) connector = "and" case array.length @@ -39,5 +39,5 @@ module Jekyll end end - end + end end diff --git a/lib/jekyll/layout.rb b/lib/jekyll/layout.rb index 391cf69c..bc048a5d 100644 --- a/lib/jekyll/layout.rb +++ b/lib/jekyll/layout.rb @@ -2,25 +2,28 @@ module Jekyll class Layout include Convertible - + + attr_accessor :site attr_accessor :ext attr_accessor :data, :content - + # Initialize a new Layout. + # +site+ is the Site # +base+ is the String path to the # +name+ is the String filename of the post file # # Returns - def initialize(base, name) + def initialize(site, base, name) + @site = site @base = base @name = name - + self.data = {} - + self.process(name) self.read_yaml(base, name) end - + # Extract information from the layout filename # +name+ is the String filename of the layout file # diff --git a/lib/jekyll/page.rb b/lib/jekyll/page.rb index 140258fc..306389a7 100644 --- a/lib/jekyll/page.rb +++ b/lib/jekyll/page.rb @@ -2,28 +2,31 @@ module Jekyll class Page include Convertible - + + attr_accessor :site attr_accessor :ext attr_accessor :data, :content, :output - + # Initialize a new Page. + # +site+ is the Site # +base+ is the String path to the # +dir+ is the String path between and the file # +name+ is the String filename of the file # # Returns - def initialize(base, dir, name) + def initialize(site, base, dir, name) + @site = site @base = base @dir = dir @name = name - + self.data = {} - + self.process(name) self.read_yaml(File.join(base, dir), name) #self.transform end - + # Extract information from the page filename # +name+ is the String filename of the page file # @@ -31,7 +34,7 @@ module Jekyll def process(name) self.ext = File.extname(name) end - + # Add any necessary layouts to this post # +layouts+ is a Hash of {"name" => "layout"} # +site_payload+ is the site payload hash @@ -41,19 +44,19 @@ module Jekyll payload = {"page" => self.data}.deep_merge(site_payload) do_layout(payload, layouts) end - + # Write the generated page file to the destination directory. # +dest+ is the String path to the destination dir # # Returns nothing def write(dest) FileUtils.mkdir_p(File.join(dest, @dir)) - + name = @name if self.ext != "" name = @name.split(".")[0..-2].join('.') + self.ext end - + path = File.join(dest, @dir, name) File.open(path, 'w') do |f| f.write(self.output) diff --git a/lib/jekyll/post.rb b/lib/jekyll/post.rb index 0ba50f36..5bde3b13 100644 --- a/lib/jekyll/post.rb +++ b/lib/jekyll/post.rb @@ -3,13 +3,13 @@ module Jekyll class Post include Comparable include Convertible - + class << self attr_accessor :lsi end - + MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/ - + # Post name validator. Post filenames must be like: # 2008-11-05-my-awesome-post.textile # @@ -17,34 +17,37 @@ module Jekyll def self.valid?(name) name =~ MATCHER end - + + attr_accessor :site attr_accessor :date, :slug, :ext, :categories, :topics, :published attr_accessor :data, :content, :output - + # 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 - def initialize(source, dir, name) + def initialize(site, source, dir, name) + @site = site @base = File.join(source, dir, '_posts') @name = name - + self.categories = dir.split('/').reject { |x| x.empty? } - + parts = name.split('/') self.topics = parts.size > 1 ? parts[0..-2] : [] - + self.process(name) self.read_yaml(@base, name) - if self.data.has_key?('published') && self.data['published'] == false - self.published = false - else - self.published = true - end - + if self.data.has_key?('published') && self.data['published'] == false + self.published = false + else + self.published = true + end + if self.categories.empty? if self.data.has_key?('category') self.categories << self.data['category'] @@ -59,14 +62,14 @@ module Jekyll end end end - + # Spaceship is based on Post#date # # Returns -1, 0, 1 def <=>(other) self.date <=> other.date end - + # Extract information from the post filename # +name+ is the String filename of the post file # @@ -77,7 +80,7 @@ module Jekyll self.slug = slug self.ext = ext end - + # 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 @@ -89,14 +92,14 @@ module Jekyll permalink.to_s.split("/")[0..-2].join("/") + '/' else prefix = self.categories.empty? ? '' : '/' + self.categories.join('/') - if [:date, :pretty].include?(Jekyll.permalink_style) + if [:date, :pretty].include?(self.site.permalink_style) prefix + date.strftime("/%Y/%m/%d/") else prefix + '/' end end end - + # The full path and filename of the post. # Defined in the YAML of the post body # (Optional) @@ -105,16 +108,16 @@ module Jekyll def permalink self.data && self.data['permalink'] end - + # The generated relative url of this post # e.g. /2008/11/05/my-awesome-post.html # # Returns def url - ext = Jekyll.permalink_style == :pretty ? '' : '.html' + ext = self.site.permalink_style == :pretty ? '' : '.html' permalink || self.id + ext end - + # The UID for this post (useful in feeds) # e.g. /2008/11/05/my-awesome-post # @@ -122,14 +125,14 @@ module Jekyll def id self.dir + self.slug end - + # Calculate related posts. # # Returns [] def related_posts(posts) return [] unless posts.size > 1 - - if Jekyll.lsi + + if self.site.lsi self.class.lsi ||= begin puts "Running the classifier... this could take a while." lsi = Classifier::LSI.new @@ -144,7 +147,7 @@ module Jekyll (posts - [self])[0..9] end end - + # Add any necessary layouts to this post # +layouts+ is a Hash of {"name" => "layout"} # +site_payload+ is the site payload hash @@ -158,20 +161,20 @@ module Jekyll "page" => self.to_liquid } payload = payload.deep_merge(site_payload) - + do_layout(payload, layouts) end - + # Write the generated post file to the destination directory. # +dest+ is the String path to the destination dir # # Returns nothing def write(dest) FileUtils.mkdir_p(File.join(dest, dir)) - + path = File.join(dest, self.url) - if Jekyll.permalink_style == :pretty + if self.site.permalink_style == :pretty FileUtils.mkdir_p(path) path = File.join(path, "index.html") end @@ -180,7 +183,7 @@ module Jekyll f.write(self.output) end end - + # Convert this post into a Hash for use in Liquid templates. # # Returns @@ -192,7 +195,7 @@ module Jekyll "topics" => self.topics, "content" => self.content }.deep_merge(self.data) end - + def inspect "" end diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb index a52a5f45..b80e2226 100644 --- a/lib/jekyll/site.rb +++ b/lib/jekyll/site.rb @@ -1,25 +1,85 @@ module Jekyll - + class Site attr_accessor :config, :layouts, :posts, :categories - + attr_accessor :source, :dest, :lsi, :pygments, :permalink_style + # Initialize the site # +config+ is a Hash containing site configurations details # # Returns def initialize(config) - self.config = config.clone - self.layouts = {} - self.posts = [] - self.categories = Hash.new { |hash, key| hash[key] = Array.new } + self.config = config.clone + + self.source = config['source'] + self.dest = config['destination'] + self.lsi = config['lsi'] + self.pygments = config['pygments'] + self.permalink_style = config['permalink'].to_sym + + self.layouts = {} + self.posts = [] + self.categories = Hash.new { |hash, key| hash[key] = Array.new } + + self.setup end - - # The directory containing the proto-site. - def source; self.config['source']; end - - # Where the completed site should be written. - def dest; self.config['destination']; end - + + def setup + # Check to see if LSI is enabled. + require 'classifier' if self.lsi + + # Set the Markdown interpreter (and Maruku self.config, if necessary) + case self.config['markdown'] + when 'rdiscount' + begin + require 'rdiscount' + + def markdown(content) + RDiscount.new(content).to_html + end + + puts 'Using rdiscount for Markdown' + rescue LoadError + puts 'You must have the rdiscount gem installed first' + end + when 'maruku' + begin + require 'maruku' + + def markdown(content) + Maruku.new(content).to_html + end + + if self.config['maruku']['use_divs'] + require 'maruku/ext/div' + puts 'Maruku: Using extended syntax for div elements.' + end + + if self.config['maruku']['use_tex'] + require 'maruku/ext/math' + puts "Maruku: Using LaTeX extension. Images in `#{self.config['maruku']['png_dir']}`." + + # Switch off MathML output + MaRuKu::Globals[:html_math_output_mathml] = false + MaRuKu::Globals[:html_math_engine] = 'none' + + # Turn on math to PNG support with blahtex + # Resulting PNGs stored in `images/latex` + MaRuKu::Globals[:html_math_output_png] = true + MaRuKu::Globals[:html_png_engine] = self.config['maruku']['png_engine'] + MaRuKu::Globals[:html_png_dir] = self.config['maruku']['png_dir'] + MaRuKu::Globals[:html_png_url] = self.config['maruku']['png_url'] + end + rescue LoadError + puts "The maruku gem is required for markdown support!" + end + end + end + + def textile(content) + RedCloth.new(content).to_html + end + # Do the actual work of processing the site and generating the # real deal. # @@ -37,15 +97,15 @@ module Jekyll base = File.join(self.source, "_layouts") entries = [] Dir.chdir(base) { entries = filter_entries(Dir['*.*']) } - + entries.each do |f| name = f.split(".")[0..-2].join(".") - self.layouts[name] = Layout.new(base, f) + self.layouts[name] = Layout.new(self, base, f) end rescue Errno::ENOENT => e # ignore missing layout dir end - + # Read all the files in /_posts and create a new Post object with each one. # # Returns nothing @@ -57,7 +117,7 @@ module Jekyll # first pass processes, but does not yet render post content entries.each do |f| if Post.valid?(f) - post = Post.new(self.source, dir, f) + post = Post.new(self, self.source, dir, f) if post.published self.posts << post @@ -65,18 +125,18 @@ module Jekyll end end end - + # second pass renders each post now that full site payload is available self.posts.each do |post| post.render(self.layouts, site_payload) end - + self.posts.sort! self.categories.values.map { |cats| cats.sort! { |a, b| b <=> a} } rescue Errno::ENOENT => e # ignore missing layout dir end - + # Write each post to //// # # Returns nothing @@ -85,7 +145,7 @@ module Jekyll post.write(self.dest) end end - + # Copy all regular files from to / ignoring # any files/directories that are hidden or backup files (start # with "." or "#" or end with "~") or contain site content (start with "_") @@ -101,7 +161,7 @@ module Jekyll directories = entries.select { |e| File.directory?(File.join(base, e)) } files = entries.reject { |e| File.directory?(File.join(base, e)) } - # we need to make sure to process _posts *first* otherwise they + # we need to make sure to process _posts *first* otherwise they # might not be available yet to other templates as {{ site.posts }} if directories.include?('_posts') directories.delete('_posts') @@ -114,10 +174,10 @@ module Jekyll transform_pages(File.join(dir, f)) else first3 = File.open(File.join(self.source, dir, f)) { |fd| fd.read(3) } - + if first3 == "---" # file appears to have a YAML header so process it as a page - page = Page.new(self.source, dir, f) + page = Page.new(self, self.source, dir, f) page.render(self.layouts, site_payload) page.write(self.dest) else @@ -150,7 +210,7 @@ module Jekyll # "topics" => [] }} def site_payload {"site" => { - "time" => Time.now, + "time" => Time.now, "posts" => self.posts.sort { |a,b| b <=> a }, "categories" => post_attr_hash('categories'), "topics" => post_attr_hash('topics') diff --git a/lib/jekyll/tags/highlight.rb b/lib/jekyll/tags/highlight.rb index 07069923..ca087958 100644 --- a/lib/jekyll/tags/highlight.rb +++ b/lib/jekyll/tags/highlight.rb @@ -1,10 +1,11 @@ module Jekyll - + class HighlightBlock < Liquid::Block include Liquid::StandardFilters + # we need a language, but the linenos argument is optional. SYNTAX = /(\w+)\s?(:?linenos)?\s?/ - + def initialize(tag_name, markup, tokens) super if markup =~ SYNTAX @@ -19,25 +20,25 @@ module Jekyll raise SyntaxError.new("Syntax Error in 'highlight' - Valid syntax: highlight [linenos]") end end - + def render(context) - if Jekyll.pygments + if context.registers[:site].pygments render_pygments(context, super.to_s) else render_codehighlighter(context, super.to_s) end end - + def render_pygments(context, code) if context["content_type"] == :markdown return "\n" + Albino.new(code, @lang).to_s(@options) + "\n" - elsif content["content_type"] == :textile + elsif context["content_type"] == :textile return "" + Albino.new(code, @lang).to_s(@options) + "" else return Albino.new(code, @lang).to_s(@options) end end - + def render_codehighlighter(context, code) #The div is required because RDiscount blows ass <<-HTML @@ -49,7 +50,7 @@ module Jekyll HTML end end - + end Liquid::Template.register_tag('highlight', Jekyll::HighlightBlock) diff --git a/lib/jekyll/tags/include.rb b/lib/jekyll/tags/include.rb index 840f36bd..fc39c08f 100644 --- a/lib/jekyll/tags/include.rb +++ b/lib/jekyll/tags/include.rb @@ -1,17 +1,17 @@ module Jekyll - + class IncludeTag < Liquid::Tag def initialize(tag_name, file, tokens) super @file = file.strip end - + def render(context) if @file !~ /^[a-zA-Z0-9_\/\.-]+$/ || @file =~ /\.\// || @file =~ /\/\./ return "Include file '#{@file}' contains invalid characters or sequences" end - - Dir.chdir(File.join(Jekyll.source, '_includes')) do + + Dir.chdir(File.join('.', '_includes')) do choices = Dir['**/*'].reject { |x| File.symlink?(x) } if choices.include?(@file) source = File.read(@file) @@ -25,7 +25,7 @@ module Jekyll end end end - + end Liquid::Template.register_tag('include', Jekyll::IncludeTag)