diff --git a/lib/jekyll.rb b/lib/jekyll.rb index 65bdd7fe..6393d4eb 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -94,8 +94,8 @@ module Jekyll } } - # Generate a Jekyll configuration Hash by merging the default options - # with anything in _config.yml, and adding the given options on top. + # Public: Generate a Jekyll configuration Hash by merging the default + # options with anything in _config.yml, and adding the given options on top. # # override - A Hash of config directives that override any options in both # the defaults and the config file. See Jekyll::DEFAULTS for a diff --git a/lib/jekyll/generators/pagination.rb b/lib/jekyll/generators/pagination.rb index 59d71093..a9571046 100644 --- a/lib/jekyll/generators/pagination.rb +++ b/lib/jekyll/generators/pagination.rb @@ -1,8 +1,14 @@ module Jekyll class Pagination < Generator + # This generator is safe from arbitrary code execution. safe true + # Generate paginated pages if necessary. + # + # site - The Site. + # + # Returns nothing. def generate(site) site.pages.dup.each do |page| paginate(site, page) if Pager.pagination_enabled?(site.config, page.name) @@ -10,9 +16,11 @@ module Jekyll end # Paginates the blog's posts. Renders the index.html file into paginated - # directories, ie: page2/index.html, page3/index.html, etc and adds more + # directories, e.g.: page2/index.html, page3/index.html, etc and adds more # site-wide data. - # +page+ is the index.html Page that requires pagination + # + # site - The Site. + # page - The index.html Page that requires pagination. # # {"paginator" => { "page" => , # "per_page" => , @@ -36,22 +44,40 @@ module Jekyll end end end - end class Pager attr_reader :page, :per_page, :posts, :total_posts, :total_pages, :previous_page, :next_page + # Calculate the number of pages. + # + # all_posts - The Array of all Posts. + # per_page - The Integer of entries per page. + # + # Returns the Integer number of pages. def self.calculate_pages(all_posts, per_page) num_pages = all_posts.size / per_page.to_i num_pages = num_pages + 1 if all_posts.size % per_page.to_i != 0 num_pages end + # Determine if pagination is enabled for a given file. + # + # config - The configuration Hash. + # file - The String filename of the file. + # + # Returns true if pagination is enabled, false otherwise. def self.pagination_enabled?(config, file) file == 'index.html' && !config['paginate'].nil? end + # Initialize a new Pager. + # + # config - The Hash configuration of the site. + # page - The Integer page number. + # all_posts - The Array of all the site's Posts. + # num_pages - The Integer number of pages or nil if you'd like the number + # of pages calculated. def initialize(config, page, all_posts, num_pages = nil) @page = page @per_page = config['paginate'].to_i @@ -70,6 +96,9 @@ module Jekyll @next_page = @page != @total_pages ? @page + 1 : nil end + # Convert this Pager's data to a Hash suitable for use by Liquid. + # + # Returns the Hash representation of this Pager. def to_liquid { 'page' => page, @@ -83,5 +112,4 @@ module Jekyll end end - end diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb index 1ee62a54..41905d65 100644 --- a/lib/jekyll/site.rb +++ b/lib/jekyll/site.rb @@ -9,10 +9,9 @@ module Jekyll attr_accessor :converters, :generators - # Initialize the site - # +config+ is a Hash containing site configurations details + # Public: Initialize a new Site. # - # Returns + # config - A Hash containing site configuration details. def initialize(config) self.config = config.clone @@ -31,6 +30,21 @@ module Jekyll self.setup end + # Public: Read, process, and write this Site to output. + # + # Returns nothing. + def process + self.reset + self.read + self.generate + self.render + self.cleanup + self.write + end + + # Reset Site details. + # + # Returns nothing def reset self.time = if self.config['time'] Time.parse(self.config['time'].to_s) @@ -44,13 +58,18 @@ module Jekyll self.categories = Hash.new { |hash, key| hash[key] = [] } self.tags = Hash.new { |hash, key| hash[key] = [] } - raise ArgumentError, "Limit posts must be nil or >= 1" if !self.limit_posts.nil? && self.limit_posts < 1 + if !self.limit_posts.nil? && self.limit_posts < 1 + raise ArgumentError, "Limit posts must be nil or >= 1" + end end + # Load necessary libraries, plugins, converters, and generators. + # + # Returns nothing. def setup require 'classifier' if self.lsi - # If safe mode is off, load in any ruby files under the plugins + # If safe mode is off, load in any Ruby files under the plugins # directory. unless self.safe Dir[File.join(self.plugins, "**/*.rb")].each do |f| @@ -71,29 +90,18 @@ module Jekyll end end - # Do the actual work of processing the site and generating the - # real deal. 5 phases; reset, read, generate, render, write. This allows - # rendering to have full site payload available. + # Read Site data from disk and load it into internal data structures. # - # Returns nothing - def process - self.reset - self.read - self.generate - self.render - self.cleanup - self.write - end - + # Returns nothing. def read - self.read_layouts # existing implementation did this at top level only so preserved that + self.read_layouts self.read_directories end # Read all the files in //_layouts and create a new Layout # object with each one. # - # Returns nothing + # Returns nothing. def read_layouts(dir = '') base = File.join(self.source, dir, "_layouts") return unless File.exists?(base) @@ -106,10 +114,44 @@ module Jekyll end end + # Recursively traverse directories to find posts, pages and static files + # that will become part of the site according to the rules in + # filter_entries. + # + # dir - The String relative path of the directory to read. + # + # Returns nothing. + def read_directories(dir = '') + base = File.join(self.source, dir) + entries = Dir.chdir(base) { filter_entries(Dir['*']) } + + self.read_posts(dir) + + entries.each do |f| + f_abs = File.join(base, f) + f_rel = File.join(dir, f) + if File.directory?(f_abs) + next if self.dest.sub(/\/$/, '') == f_abs + read_directories(f_rel) + elsif !File.symlink?(f_abs) + first3 = File.open(f_abs) { |fd| fd.read(3) } + if first3 == "---" + # file appears to have a YAML header so process it as a page + pages << Page.new(self, self.source, dir, f) + else + # otherwise treat it as a static file + static_files << StaticFile.new(self, self.source, dir, f) + end + end + end + end + # Read all the files in //_posts and create a new Post # object with each one. # - # Returns nothing + # dir - The String relative path of the directory to read. + # + # Returns nothing. def read_posts(dir) base = File.join(self.source, dir, '_posts') return unless File.exists?(base) @@ -134,12 +176,18 @@ module Jekyll self.posts = self.posts[-limit_posts, limit_posts] if limit_posts end + # Run each of the Generators. + # + # Returns nothing. def generate self.generators.each do |generator| generator.generate(self) end end + # Render the site to the destination. + # + # Returns nothing. def render self.posts.each do |post| post.render(self.layouts, site_payload) @@ -149,15 +197,15 @@ module Jekyll page.render(self.layouts, site_payload) end - self.categories.values.map { |ps| ps.sort! { |a, b| b <=> a} } - self.tags.values.map { |ps| ps.sort! { |a, b| b <=> a} } + self.categories.values.map { |ps| ps.sort! { |a, b| b <=> a } } + self.tags.values.map { |ps| ps.sort! { |a, b| b <=> a } } rescue Errno::ENOENT => e # ignore missing layout dir end - - # Remove orphaned files and empty directories in destination + + # Remove orphaned files and empty directories in destination. # - # Returns nothing + # Returns nothing. def cleanup # all files and directories in destination, including hidden ones dest_files = Set.new @@ -176,20 +224,20 @@ module Jekyll self.static_files.each do |sf| files << sf.destination(self.dest) end - + # adding files' parent directories dirs = Set.new files.each { |file| dirs << File.dirname(file) } files.merge(dirs) - + obsolete_files = dest_files - files - + FileUtils.rm_rf(obsolete_files.to_a) end - # Write static files, pages and posts + # Write static files, pages, and posts. # - # Returns nothing + # Returns nothing. def write self.posts.each do |post| post.write(self.dest) @@ -202,59 +250,45 @@ module Jekyll end end - # Reads the directories and finds posts, pages and static files that will - # become part of the valid site according to the rules in +filter_entries+. - # The +dir+ String is a relative path used to call this method - # recursively as it descends through directories + # Constructs a Hash of Posts indexed by the specified Post attribute. # - # Returns nothing - def read_directories(dir = '') - base = File.join(self.source, dir) - entries = Dir.chdir(base){ filter_entries(Dir['*']) } - - self.read_posts(dir) - - entries.each do |f| - f_abs = File.join(base, f) - f_rel = File.join(dir, f) - if File.directory?(f_abs) - next if self.dest.sub(/\/$/, '') == f_abs - read_directories(f_rel) - elsif !File.symlink?(f_abs) - first3 = File.open(f_abs) { |fd| fd.read(3) } - if first3 == "---" - # file appears to have a YAML header so process it as a page - pages << Page.new(self, self.source, dir, f) - else - # otherwise treat it as a static file - static_files << StaticFile.new(self, self.source, dir, f) - end - end - end - end - - # Constructs a hash map of Posts indexed by the specified Post attribute + # post_attr - The String name of the Post attribute. # - # Returns {post_attr => []} + # Examples + # + # post_attr_hash('categories') + # # => { 'tech' => [, ], + # # 'ruby' => [] } + # + # Returns the Hash: { attr => posts } where + # attr - One of the values for the requested attribute. + # posts - The Array of Posts with the given attr value. def post_attr_hash(post_attr) - # Build a hash map based on the specified post attribute ( post attr => array of posts ) - # then sort each array in reverse order + # Build a hash map based on the specified post attribute ( post attr => + # array of posts ) then sort each array in reverse order. hash = Hash.new { |hash, key| hash[key] = Array.new } self.posts.each { |p| p.send(post_attr.to_sym).each { |t| hash[t] << p } } - hash.values.map { |sortme| sortme.sort! { |a, b| b <=> a} } - return hash + hash.values.map { |sortme| sortme.sort! { |a, b| b <=> a } } + hash end - # The Hash payload containing site-wide data + # The Hash payload containing site-wide data. # - # Returns {"site" => {"time" =>