diff --git a/features/hooks.feature b/features/hooks.feature index b0846bab..489f256b 100644 --- a/features/hooks.feature +++ b/features/hooks.feature @@ -74,7 +74,7 @@ Feature: Hooks Given I have a _plugins directory And I have a "_plugins/ext.rb" file with content: """ - Jekyll::Hooks.register :page, :post_init do |page| + Jekyll::Hooks.register :pages, :post_init do |page| page.name = 'renamed.html' page.process(page.name) end @@ -88,7 +88,7 @@ Feature: Hooks Given I have a _plugins directory And I have a "_plugins/ext.rb" file with content: """ - Jekyll::Hooks.register :page, :pre_render do |page, payload| + Jekyll::Hooks.register :pages, :pre_render do |page, payload| payload['myparam'] = 'special' if page.name == 'page1.html' end """ @@ -103,7 +103,7 @@ Feature: Hooks And I have a "index.html" page that contains "WRAP ME" And I have a "_plugins/ext.rb" file with content: """ - Jekyll::Hooks.register :page, :post_render do |page| + Jekyll::Hooks.register :pages, :post_render do |page| page.output = "{{{{{ #{page.output.chomp} }}}}}" end """ @@ -115,7 +115,7 @@ Feature: Hooks And I have a "index.html" page that contains "HELLO FROM A PAGE" And I have a "_plugins/ext.rb" file with content: """ - Jekyll::Hooks.register :page, :post_write do |page| + Jekyll::Hooks.register :pages, :post_write do |page| require 'fileutils' filename = page.destination(page.site.dest) FileUtils.mv(filename, "#{filename}.moved") @@ -128,16 +128,15 @@ Feature: Hooks Given I have a _plugins directory And I have a "_plugins/ext.rb" file with content: """ - # rot13 translate - Jekyll::Hooks.register :post, :post_init do |post| - post.content.tr!('abcdefghijklmnopqrstuvwxyz', - 'nopqrstuvwxyzabcdefghijklm') + Jekyll::Hooks.register :posts, :post_init do |post| + post.data['harold'] = "content for entry1.".tr!('abcdefghijklmnopqrstuvwxyz', + 'nopqrstuvwxyzabcdefghijklm') end """ And I have a _posts directory And I have the following posts: - | title | date | layout | content | - | entry1 | 2015-03-14 | nil | content for entry1. | + | title | date | layout | content | + | entry1 | 2015-03-14 | nil | {{ page.harold }} | When I run jekyll build Then the _site directory should exist And I should see "pbagrag sbe ragel1." in "_site/2015/03/14/entry1.html" @@ -148,7 +147,7 @@ Feature: Hooks """ # Add myvar = 'old' to posts before 2015-03-15, and myvar = 'new' for # others - Jekyll::Hooks.register :post, :pre_render do |post, payload| + Jekyll::Hooks.register :posts, :pre_render do |post, payload| if post.date < Time.new(2015, 3, 15) payload['myvar'] = 'old' else @@ -170,7 +169,7 @@ Feature: Hooks And I have a "_plugins/ext.rb" file with content: """ # Replace content after rendering - Jekyll::Hooks.register :post, :post_render do |post| + Jekyll::Hooks.register :posts, :post_render do |post| post.output.gsub! /42/, 'the answer to life, the universe and everything' end """ @@ -188,7 +187,7 @@ Feature: Hooks And I have a "_plugins/ext.rb" file with content: """ # Log all post filesystem writes - Jekyll::Hooks.register :post, :post_write do |post| + Jekyll::Hooks.register :posts, :post_write do |post| filename = post.destination(post.site.dest) open('_site/post-build.log', 'a') do |f| f.puts "Wrote #{filename} at #{Time.now}" @@ -208,7 +207,7 @@ Feature: Hooks Given I have a _plugins directory And I have a "_plugins/ext.rb" file with content: """ - Jekyll::Hooks.register [:page, :post], :post_render do |owner| + Jekyll::Hooks.register [:pages, :posts], :post_render do |owner| owner.output = "{{{{{ #{owner.output.chomp} }}}}}" end """ @@ -225,19 +224,19 @@ Feature: Hooks Given I have a _plugins directory And I have a "_plugins/ext.rb" file with content: """ - Jekyll::Hooks.register :page, :post_render, priority: :normal do |owner| + Jekyll::Hooks.register :pages, :post_render, priority: :normal do |owner| # first normal runs second owner.output = "1 #{owner.output.chomp}" end - Jekyll::Hooks.register :page, :post_render, priority: :high do |owner| + Jekyll::Hooks.register :pages, :post_render, priority: :high do |owner| # high runs last owner.output = "2 #{owner.output.chomp}" end - Jekyll::Hooks.register :page, :post_render do |owner| + Jekyll::Hooks.register :pages, :post_render do |owner| # second normal runs third (normal is default) owner.output = "3 #{owner.output.chomp}" end - Jekyll::Hooks.register :page, :post_render, priority: :low do |owner| + Jekyll::Hooks.register :pages, :post_render, priority: :low do |owner| # low runs first owner.output = "4 #{owner.output.chomp}" end @@ -250,7 +249,7 @@ Feature: Hooks Given I have a _plugins directory And I have a "_plugins/ext.rb" file with content: """ - Jekyll::Hooks.register :document, :pre_render do |doc, payload| + Jekyll::Hooks.register :documents, :pre_render do |doc, payload| doc.data['text'] = doc.data['text'] << ' are belong to us' end """ @@ -276,7 +275,7 @@ Feature: Hooks Given I have a _plugins directory And I have a "_plugins/ext.rb" file with content: """ - Jekyll::Hooks.register :document, :post_render do |doc| + Jekyll::Hooks.register :documents, :post_render do |doc| doc.output.gsub! /

/, '

' end """ @@ -302,7 +301,7 @@ Feature: Hooks Given I have a _plugins directory And I have a "_plugins/ext.rb" file with content: """ - Jekyll::Hooks.register :document, :post_write do |doc| + Jekyll::Hooks.register :documents, :post_write do |doc| open('_site/document-build.log', 'a') do |f| f.puts "Wrote document #{doc.collection.docs.index doc} at #{Time.now}" end diff --git a/lib/jekyll.rb b/lib/jekyll.rb index 9d6b5b63..2ac3b3fa 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -30,7 +30,6 @@ require 'kramdown' require 'colorator' SafeYAML::OPTIONS[:suppress_warnings] = true -Liquid::Template.error_mode = :strict module Jekyll @@ -53,7 +52,6 @@ module Jekyll autoload :CollectionReader, 'jekyll/readers/collection_reader' autoload :DataReader, 'jekyll/readers/data_reader' autoload :LayoutReader, 'jekyll/readers/layout_reader' - autoload :DraftReader, 'jekyll/readers/draft_reader' autoload :PostReader, 'jekyll/readers/post_reader' autoload :PageReader, 'jekyll/readers/page_reader' autoload :StaticFileReader, 'jekyll/readers/static_file_reader' @@ -136,7 +134,7 @@ module Jekyll # # Returns the new logger. def logger=(writer) - @logger = LogAdapter.new(writer) + @logger = LogAdapter.new(writer, (ENV["JEKYLL_LOG_LEVEL"] || :info).to_sym) end # Public: An array of sites diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb index e8740cf6..9faffaab 100644 --- a/lib/jekyll/collection.rb +++ b/lib/jekyll/collection.rb @@ -1,6 +1,7 @@ module Jekyll class Collection attr_reader :site, :label, :metadata + attr_writer :docs # Create a new Collection. # @@ -22,6 +23,14 @@ module Jekyll @docs ||= [] end + [:sort, :sort!, :each, :[], :reject, :first, :last, :size, :length].each do |method| + class_eval %Q" + def #{method}(*args, &blk) + docs.#{method}(*args, &blk) + end + " + end + # Fetch the static files in this collection. # Defaults to an empty array if no static files have been read in. # @@ -40,7 +49,7 @@ module Jekyll if Utils.has_yaml_header? full_path doc = Jekyll::Document.new(full_path, { site: site, collection: self }) doc.read - docs << doc if site.publisher.publish?(doc) + docs << doc if site.publisher.publish?(doc) || !write? else relative_dir = Jekyll.sanitized_path(relative_directory, File.dirname(file_path)).chomp("/.") files << StaticFile.new(site, site.source, relative_dir, File.basename(full_path), self) @@ -163,7 +172,7 @@ module Jekyll # # Returns true if the 'write' metadata is true, false otherwise. def write? - !!metadata['output'] + !!metadata.fetch('output', false) end # The URL template to render collection's documents at. diff --git a/lib/jekyll/configuration.rb b/lib/jekyll/configuration.rb index b7562d30..ee05e8bc 100644 --- a/lib/jekyll/configuration.rb +++ b/lib/jekyll/configuration.rb @@ -5,7 +5,7 @@ module Jekyll # Default options. Overridden by values in _config.yml. # Strings rather than symbols are used for compatibility with YAML. - DEFAULTS = { + DEFAULTS = Configuration[{ # Where things are 'source' => Dir.pwd, 'destination' => File.join(Dir.pwd, '_site'), @@ -13,7 +13,7 @@ module Jekyll 'layouts_dir' => '_layouts', 'data_dir' => '_data', 'includes_dir' => '_includes', - 'collections' => nil, + 'collections' => {}, # Handling Reading 'safe' => false, @@ -80,7 +80,7 @@ module Jekyll 'coderay_css' => 'style' } } - } + }] # Public: Turn all keys into string # @@ -186,7 +186,7 @@ module Jekyll $stderr.puts "#{err}" end - configuration.fix_common_issues.backwards_compatibilize + configuration.fix_common_issues.backwards_compatibilize.add_default_collections end # Public: Split a CSV string into an array containing its values @@ -275,6 +275,21 @@ module Jekyll config end + def add_default_collections + config = clone + + return config if config['collections'].nil? + + if config['collections'].is_a?(Array) + config['collections'] = Hash[config['collections'].map{|c| [c, {}]}] + end + config['collections']['posts'] ||= {} + config['collections']['posts']['output'] = true + config['collections']['posts']['permalink'] = style_to_permalink(config['permalink']) + + config + end + def renamed_key(old, new, config, allowed_values = nil) if config.key?(old) Jekyll::Deprecator.deprecation_message "The '#{old}' configuration" + @@ -283,5 +298,21 @@ module Jekyll config[new] = config.delete(old) end end + + private + def style_to_permalink(permalink_style) + case permalink_style.to_sym + when :pretty + "/:categories/:year/:month/:day/:title/" + when :none + "/:categories/:title.html" + when :date + "/:categories/:year/:month/:day/:title.html" + when :ordinal + "/:categories/:year/:y_day/:title.html" + else + permalink_style.to_s + end + end end end diff --git a/lib/jekyll/convertible.rb b/lib/jekyll/convertible.rb index d91a31e8..57abb7d1 100644 --- a/lib/jekyll/convertible.rb +++ b/lib/jekyll/convertible.rb @@ -135,21 +135,15 @@ module Jekyll # # Returns the type of self. def type - if is_a?(Draft) - :drafts - elsif is_a?(Post) - :posts - elsif is_a?(Page) + if is_a?(Page) :pages end end # returns the owner symbol for hook triggering def hook_owner - if is_a?(Post) - :post - elsif is_a?(Page) - :page + if is_a?(Page) + :pages end end @@ -215,6 +209,7 @@ module Jekyll used = Set.new([layout]) while layout + Jekyll.logger.debug "Rendering Layout:", path payload = Utils.deep_merge_hashes(payload, {"content" => output, "page" => layout.data}) self.output = render_liquid(layout.content, @@ -245,6 +240,9 @@ module Jekyll # # Returns nothing. def do_layout(payload, layouts) + Jekyll.logger.debug "Rendering:", self.relative_path + + Jekyll.logger.debug "Pre-Render Hooks:", self.relative_path Jekyll::Hooks.trigger hook_owner, :pre_render, self, payload info = { :filters => [Jekyll::Filters], :registers => { :site => site, :page => payload['page'] } } @@ -252,13 +250,18 @@ module Jekyll payload["highlighter_prefix"] = converters.first.highlighter_prefix payload["highlighter_suffix"] = converters.first.highlighter_suffix - self.content = render_liquid(content, payload, info, path) if render_with_liquid? + if render_with_liquid? + Jekyll.logger.debug "Rendering Liquid:", self.relative_path + self.content = render_liquid(content, payload, info, path) + end + Jekyll.logger.debug "Rendering Markup:", self.relative_path self.content = transform # output keeps track of what will finally be written self.output = content render_all_layouts(layouts, payload, info) if place_in_layout? + Jekyll.logger.debug "Post-Render Hooks:", self.relative_path Jekyll::Hooks.trigger hook_owner, :post_render, self end diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb index da07a995..9dbc4cee 100644 --- a/lib/jekyll/document.rb +++ b/lib/jekyll/document.rb @@ -7,6 +7,8 @@ module Jekyll attr_reader :path, :site, :extname, :output_ext, :content, :output, :collection YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m + DATELESS_FILENAME_MATCHER = /^(.*)(\.[^.]+)$/ + DATE_FILENAME_MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/ # Create a new Document. # @@ -21,6 +23,17 @@ module Jekyll @output_ext = Jekyll::Renderer.new(site, self).output_ext @collection = relations[:collection] @has_yaml_header = nil + + subdirs = relative_path.split(File::SEPARATOR).reject do |c| + c.empty? || c.eql?(collection.relative_directory) || c.eql?("_drafts") || c.eql?(basename) + end + merge_data!({'categories' => subdirs }) + + data.default_proc = proc do |hash, key| + site.frontmatter_defaults.find(relative_path, collection.label, key) + end + + trigger_hooks(:post_init) end def output=(output) @@ -41,6 +54,27 @@ module Jekyll @data ||= Hash.new end + # Merge some data in with this document's data. + # + # Returns the merged data. + def merge_data!(other) + if other.key?('categories') && !other['categories'].nil? + if other['categories'].is_a?(String) + other['categories'] = other['categories'].split(" ").map(&:strip) + end + other['categories'] = (data['categories'] || []) | other['categories'] + end + Utils.deep_merge_hashes!(data, other) + if data.key?('date') && !data['date'].is_a?(Time) + data['date'] = Utils.parse_date(data['date'].to_s, "Document '#{relative_path}' does not have a valid date in the YAML front matter.") + end + data + end + + def date + data['date'] ||= site.time + end + # The path to the document, relative to the site source. # # Returns a String path which represents the relative path @@ -138,11 +172,23 @@ module Jekyll # Returns the Hash of key-value pairs for replacement in the URL. def url_placeholders { - collection: collection.label, - path: cleaned_relative_path, - output_ext: output_ext, - name: Utils.slugify(basename_without_ext), - title: Utils.slugify(data['slug']) || Utils.slugify(basename_without_ext) + collection: collection.label, + path: cleaned_relative_path, + output_ext: output_ext, + name: Utils.slugify(basename_without_ext), + title: Utils.slugify(data['slug']) || Utils.slugify(basename_without_ext), + year: date.strftime("%Y"), + month: date.strftime("%m"), + day: date.strftime("%d"), + hour: date.strftime("%H"), + minute: date.strftime("%M"), + second: date.strftime("%S"), + i_day: date.strftime("%-d"), + i_month: date.strftime("%-m"), + categories: (data['categories'] || []).map { |c| c.to_s.downcase }.uniq.join('/'), + short_month: date.strftime("%b"), + short_year: date.strftime("%y"), + y_day: date.strftime("%j"), } end @@ -165,6 +211,10 @@ module Jekyll }).to_s end + def [](key) + data[key] + end + # The full path to the output file. # # base_directory - the base path of the output directory @@ -190,7 +240,7 @@ module Jekyll f.write(output) end - Jekyll::Hooks.trigger :document, :post_write, self + trigger_hooks(:post_write) end # Returns merged option hash for File.read of self.site (if exists) @@ -218,22 +268,23 @@ module Jekyll def read(opts = {}) @to_liquid = nil + Jekyll.logger.debug "Reading:", relative_path + if yaml_file? @data = SafeYAML.load_file(path) else begin defaults = @site.frontmatter_defaults.all(url, collection.label.to_sym) - unless defaults.empty? - @data = defaults - end + merge_data!(defaults) unless defaults.empty? + self.content = File.read(path, merged_file_read_opts(opts)) if content =~ YAML_FRONT_MATTER_REGEXP self.content = $POSTMATCH data_file = SafeYAML.load($1) - unless data_file.nil? - @data = Utils.deep_merge_hashes(defaults, data_file) - end + merge_data!(data_file) if data_file end + + post_read rescue SyntaxError => e puts "YAML Exception reading #{path}: #{e.message}" rescue Exception => e @@ -242,19 +293,54 @@ module Jekyll end end + def post_read + if DATE_FILENAME_MATCHER =~ relative_path + m, cats, date, slug, ext = *relative_path.match(DATE_FILENAME_MATCHER) + merge_data!({ + "slug" => slug, + "ext" => ext + }) + merge_data!({"date" => date}) if data['date'].nil? || data['date'].to_i == site.time.to_i + data['title'] ||= slug.split('-').select {|w| w.capitalize! || w }.join(' ') + end + populate_categories + populate_tags + + if generate_excerpt? + data['excerpt'] = Jekyll::Excerpt.new(self) + end + end + + def populate_categories + merge_data!({ + "categories" => ( + Array(data['categories']) + Utils.pluralized_array_from_hash(data, 'category', 'categories') + ).map { |c| c.to_s }.flatten.uniq + }) + end + + def populate_tags + merge_data!({ + "tags" => Utils.pluralized_array_from_hash(data, "tag", "tags").flatten + }) + end + # Create a Liquid-understandable version of this Document. # # Returns a Hash representing this Document's data. def to_liquid @to_liquid ||= if data.is_a?(Hash) - Utils.deep_merge_hashes data, { + Utils.deep_merge_hashes Utils.deep_merge_hashes({ "output" => output, "content" => content, "relative_path" => relative_path, "path" => relative_path, "url" => url, - "collection" => collection.label - } + "collection" => collection.label, + "next" => next_doc, + "previous" => previous_doc, + "id" => id, + }, data), { 'excerpt' => data['excerpt'].to_s } else data end @@ -272,7 +358,7 @@ module Jekyll # # Returns the content of the document def to_s - content || '' + output || content || 'NO CONTENT' end # Compare this document against another document. @@ -281,7 +367,11 @@ module Jekyll # Returns -1, 0, +1 or nil depending on whether this doc's path is less than, # equal or greater than the other doc's path. See String#<=> for more details. def <=>(anotherDocument) - path <=> anotherDocument.path + cmp = data['date'] <=> anotherDocument.data['date'] + if 0 == cmp + cmp = path <=> anotherDocument.path + end + cmp end # Determine whether this document should be written. @@ -292,5 +382,54 @@ module Jekyll def write? collection && collection.write? end + + # The Document excerpt_separator, from the YAML Front-Matter or site + # default excerpt_separator value + # + # Returns the document excerpt_separator + def excerpt_separator + (data['excerpt_separator'] || site.config['excerpt_separator']).to_s + end + + # Whether to generate an excerpt + # + # Returns true if the excerpt separator is configured. + def generate_excerpt? + !excerpt_separator.empty? + end + + def next_doc + pos = collection.docs.index {|post| post.equal?(self) } + if pos && pos < collection.docs.length - 1 + collection.docs[pos + 1] + else + nil + end + end + + def previous_doc + pos = collection.docs.index {|post| post.equal?(self) } + if pos && pos > 0 + collection.docs[pos - 1] + else + nil + end + end + + def trigger_hooks(hook_name, *args) + Jekyll::Hooks.trigger collection.label.to_sym, hook_name, self, *args if collection + Jekyll::Hooks.trigger :documents, hook_name, self, *args + end + + def id + @id ||= File.join(File.dirname(url), (data['slug'] || basename_without_ext).to_s) + end + + # Calculate related posts. + # + # Returns an Array of related Posts. + def related_posts + Jekyll::RelatedPosts.new(self).build + end end end diff --git a/lib/jekyll/draft.rb b/lib/jekyll/draft.rb deleted file mode 100644 index 16daefdf..00000000 --- a/lib/jekyll/draft.rb +++ /dev/null @@ -1,40 +0,0 @@ -module Jekyll - - class Draft < Post - - # Valid post name regex (no date) - MATCHER = /^(.*)(\.[^.]+)$/ - - # Draft name validator. Draft filenames must be like: - # my-awesome-post.textile - # - # Returns true if valid, false if not. - def self.valid?(name) - name =~ MATCHER - end - - # Get the full path to the directory containing the draft files - def containing_dir(dir) - site.in_source_dir(dir, '_drafts') - end - - # The path to the draft source file, relative to the site source - def relative_path - File.join(@dir, '_drafts', @name) - end - - # Extract information from the post filename. - # - # name - The String filename of the post file. - # - # Returns nothing. - def process(name) - m, slug, ext = *name.match(MATCHER) - self.date = File.mtime(File.join(@base, name)) - self.slug = slug - self.ext = ext - end - - end - -end diff --git a/lib/jekyll/excerpt.rb b/lib/jekyll/excerpt.rb index 3d463944..36fcd11b 100644 --- a/lib/jekyll/excerpt.rb +++ b/lib/jekyll/excerpt.rb @@ -2,46 +2,43 @@ require 'forwardable' module Jekyll class Excerpt - include Convertible extend Forwardable - attr_accessor :post - attr_accessor :content, :output, :ext + attr_accessor :doc + attr_accessor :content, :ext + attr_writer :output - def_delegator :@post, :site, :site - def_delegator :@post, :name, :name - def_delegator :@post, :ext, :ext + def_delegators :@doc, :site, :name, :ext, :relative_path, :extname, + :render_with_liquid?, :collection, :related_posts - # Initialize this Post instance. + # Initialize this Excerpt instance. # - # site - The Site. - # base - The String path to the dir containing the post file. - # name - The String filename of the post file. + # doc - The Document. # - # Returns the new Post. - def initialize(post) - self.post = post - self.content = extract_excerpt(post.content) + # Returns the new Excerpt. + def initialize(doc) + self.doc = doc + self.content = extract_excerpt(doc.content) end - def to_liquid - post.to_liquid(post.class::EXCERPT_ATTRIBUTES_FOR_LIQUID) - end - - # Fetch YAML front-matter data from related post, without layout key + # Fetch YAML front-matter data from related doc, without layout key # - # Returns Hash of post data + # Returns Hash of doc data def data - @data ||= post.data.dup + @data ||= doc.data.dup @data.delete("layout") + @data.delete("excerpt") @data end + def trigger_hooks(*) + end + # 'Path' of the excerpt. # - # Returns the path for the post this excerpt belongs to with #excerpt appended + # Returns the path for the doc this excerpt belongs to with #excerpt appended def path - File.join(post.path, "#excerpt") + File.join(doc.path, "#excerpt") end # Check if excerpt includes a string @@ -51,28 +48,43 @@ module Jekyll (output && output.include?(something)) || content.include?(something) end - # The UID for this post (useful in feeds). - # e.g. /2008/11/05/my-awesome-post + # The UID for this doc (useful in feeds). + # e.g. /2008/11/05/my-awesome-doc # # Returns the String UID. def id - File.join(post.dir, post.slug, "#excerpt") + "#{doc.id}#excerpt" end def to_s output || content end - # Returns the shorthand String identifier of this Post. + def to_liquid + doc.data['excerpt'] = nil + @to_liquid ||= doc.to_liquid + doc.data['excerpt'] = self + @to_liquid + end + + # Returns the shorthand String identifier of this doc. def inspect "" end + def output + @output ||= Renderer.new(doc.site, self, site.site_payload).run + end + + def place_in_layout? + false + end + protected # Internal: Extract excerpt from the content # - # By default excerpt is your first paragraph of a post: everything before + # By default excerpt is your first paragraph of a doc: everything before # the first two new lines: # # --- @@ -86,16 +98,16 @@ module Jekyll # [1]: http://example.com/ # # This is fairly good option for Markdown and Textile files. But might cause - # problems for HTML posts (which is quite unusual for Jekyll). If default + # problems for HTML docs (which is quite unusual for Jekyll). If default # excerpt delimiter is not good for you, you might want to set your own via # configuration option `excerpt_separator`. For example, following is a good - # alternative for HTML posts: + # alternative for HTML docs: # # # file: _config.yml # excerpt_separator: "" # # Notice that all markdown-style link references will be appended to the - # excerpt. So the example post above will have this excerpt source: + # excerpt. So the example doc above will have this excerpt source: # # First paragraph with [link][1]. # @@ -104,8 +116,8 @@ module Jekyll # Excerpts are rendered same time as content is rendered. # # Returns excerpt String - def extract_excerpt(post_content) - head, _, tail = post_content.to_s.partition(post.excerpt_separator) + def extract_excerpt(doc_content) + head, _, tail = doc_content.to_s.partition(doc.excerpt_separator) if tail.empty? head diff --git a/lib/jekyll/hooks.rb b/lib/jekyll/hooks.rb index 98ea263f..9933337f 100644 --- a/lib/jekyll/hooks.rb +++ b/lib/jekyll/hooks.rb @@ -17,19 +17,19 @@ module Jekyll pre_render: [], post_write: [], }, - :page => { + :pages => { post_init: [], pre_render: [], post_render: [], post_write: [], }, - :post => { + :posts => { post_init: [], pre_render: [], post_render: [], post_write: [], }, - :document => { + :documents => { pre_render: [], post_render: [], post_write: [], @@ -57,10 +57,12 @@ module Jekyll # register a single hook to be called later, internal API def self.register_one(owner, event, priority, &block) - unless @registry[owner] - raise NotAvailable, "Hooks are only available for the following " << - "classes: #{@registry.keys.inspect}" - end + @registry[owner] ||={ + post_init: [], + pre_render: [], + post_render: [], + post_write: [], + } unless @registry[owner][event] raise NotAvailable, "Invalid hook. #{owner} supports only the " << diff --git a/lib/jekyll/page.rb b/lib/jekyll/page.rb index 431878b2..ca5bb268 100644 --- a/lib/jekyll/page.rb +++ b/lib/jekyll/page.rb @@ -36,7 +36,7 @@ module Jekyll site.frontmatter_defaults.find(File.join(dir, name), type, key) end - Jekyll::Hooks.trigger :page, :post_init, self + Jekyll::Hooks.trigger :pages, :post_init, self end # The generated directory into which the page will be placed diff --git a/lib/jekyll/post.rb b/lib/jekyll/post.rb deleted file mode 100644 index 85d168d9..00000000 --- a/lib/jekyll/post.rb +++ /dev/null @@ -1,334 +0,0 @@ -module Jekyll - class Post - include Comparable - include Convertible - - # Valid post name regex. - MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/ - - EXCERPT_ATTRIBUTES_FOR_LIQUID = %w[ - title - url - dir - date - id - categories - next - previous - tags - path - ] - - # Attributes for Liquid templates - ATTRIBUTES_FOR_LIQUID = EXCERPT_ATTRIBUTES_FOR_LIQUID + %w[ - content - excerpt - excerpt_separator - draft? - ] - - # Post name validator. Post filenames must be like: - # 2008-11-05-my-awesome-post.textile - # - # Returns true if valid, false if not. - def self.valid?(name) - name =~ MATCHER - end - - attr_accessor :site - attr_accessor :data, :extracted_excerpt, :content, :output, :ext - attr_accessor :date, :slug, :tags, :categories - - attr_reader :name - - # Initialize this Post instance. - # - # site - The Site. - # base - The String path to the dir containing the post file. - # name - The String filename of the post file. - # - # Returns the new Post. - def initialize(site, source, dir, name) - @site = site - @dir = dir - @base = containing_dir(dir) - @name = name - - self.categories = dir.split('/').reject { |x| x.empty? } - process(name) - read_yaml(@base, name) - - data.default_proc = proc do |hash, key| - site.frontmatter_defaults.find(relative_path, type, key) - end - - if data.key?('date') - self.date = Utils.parse_date(data["date"].to_s, "Post '#{relative_path}' does not have a valid date in the YAML front matter.") - end - - populate_categories - populate_tags - - Jekyll::Hooks.trigger :post, :post_init, self - end - - def published? - if data.key?('published') && data['published'] == false - false - else - true - end - end - - def populate_categories - categories_from_data = Utils.pluralized_array_from_hash(data, 'category', 'categories') - self.categories = ( - Array(categories) + categories_from_data - ).map { |c| c.to_s }.flatten.uniq - end - - def populate_tags - self.tags = Utils.pluralized_array_from_hash(data, "tag", "tags").flatten - end - - # Get the full path to the directory containing the post files - def containing_dir(dir) - site.in_source_dir(dir, '_posts') - end - - # Read the YAML frontmatter. - # - # base - The String path to the dir containing the file. - # name - The String filename of the file. - # - # Returns nothing. - def read_yaml(base, name) - super(base, name) - self.extracted_excerpt = extract_excerpt - end - - # The post excerpt. This is either a custom excerpt - # set in YAML front matter or the result of extract_excerpt. - # - # Returns excerpt string. - def excerpt - data.fetch('excerpt') { extracted_excerpt.to_s } - end - - # Public: the Post title, from the YAML Front-Matter or from the slug - # - # Returns the post title - def title - data.fetch('title') { titleized_slug } - end - - # Public: the Post excerpt_separator, from the YAML Front-Matter or site default - # excerpt_separator value - # - # Returns the post excerpt_separator - def excerpt_separator - (data['excerpt_separator'] || site.config['excerpt_separator']).to_s - end - - # Turns the post slug into a suitable title - def titleized_slug - slug.split('-').select {|w| w.capitalize! || w }.join(' ') - end - - # Public: the path to the post relative to the site source, - # from the YAML Front-Matter or from a combination of - # the directory it's in, "_posts", and the name of the - # post file - # - # Returns the path to the file relative to the site source - def path - data.fetch('path') { relative_path.sub(/\A\//, '') } - end - - # The path to the post source file, relative to the site source - def relative_path - File.join(*[@dir, "_posts", @name].map(&:to_s).reject(&:empty?)) - end - - # 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) - cmp = self.date <=> other.date - if 0 == cmp - cmp = self.slug <=> other.slug - end - return cmp - end - - # Extract information from the post filename. - # - # name - The String filename of the post file. - # - # Returns nothing. - def process(name) - m, cats, date, slug, ext = *name.match(MATCHER) - self.date = Utils.parse_date(date, "Post '#{relative_path}' does not have a valid date in the filename.") - 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 - # e.g. "/2008/11/05/" if the permalink style is :date, otherwise nothing. - # - # 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). - # - # Returns the String permalink. - def permalink - data && data['permalink'] - end - - def template - case site.permalink_style - when :pretty - "/:categories/:year/:month/:day/:title/" - when :none - "/:categories/:title.html" - when :date - "/:categories/:year/:month/:day/:title.html" - when :ordinal - "/:categories/:year/:y_day/:title.html" - else - site.permalink_style.to_s - end - end - - # The generated relative url of this post. - # - # Returns the String url. - def url - @url ||= URL.new({ - :template => template, - :placeholders => url_placeholders, - :permalink => permalink - }).to_s - end - - # Returns a hash of URL placeholder names (as symbols) mapping to the - # desired placeholder replacements. For details see "url.rb" - def url_placeholders - { - :year => date.strftime("%Y"), - :month => date.strftime("%m"), - :day => date.strftime("%d"), - :hour => date.strftime("%H"), - :minute => date.strftime("%M"), - :second => date.strftime("%S"), - :title => slug, - :i_day => date.strftime("%-d"), - :i_month => date.strftime("%-m"), - :categories => (categories || []).map { |c| c.to_s.downcase }.uniq.join('/'), - :short_month => date.strftime("%b"), - :short_year => date.strftime("%y"), - :y_day => date.strftime("%j"), - :output_ext => output_ext - } - end - - # The UID for this post (useful in feeds). - # e.g. /2008/11/05/my-awesome-post - # - # Returns the String UID. - def id - File.join(dir, slug) - end - - # Calculate related posts. - # - # Returns an Array of related Posts. - def related_posts(posts) - Jekyll::RelatedPosts.new(self).build - end - - # Add any necessary layouts to this post. - # - # layouts - A Hash of {"name" => "layout"}. - # site_payload - The site payload hash. - # - # Returns nothing. - def render(layouts, site_payload) - # construct payload - payload = Utils.deep_merge_hashes({ - "site" => { "related_posts" => related_posts(site_payload["site"]["posts"]) }, - "page" => to_liquid(self.class::EXCERPT_ATTRIBUTES_FOR_LIQUID) - }, site_payload) - - if generate_excerpt? - extracted_excerpt.do_layout(payload, {}) - end - - do_layout(payload.merge({"page" => to_liquid}), layouts) - end - - # Obtain destination 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 = site.in_dest_dir(dest, URL.unescape_path(url)) - path = File.join(path, "index.html") if self.url.end_with?("/") - path << output_ext unless path.end_with?(output_ext) - path - end - - # Returns the shorthand String identifier of this Post. - def inspect - "" - end - - def next - pos = site.posts.index {|post| post.equal?(self) } - if pos && pos < site.posts.length - 1 - site.posts[pos + 1] - else - nil - end - end - - def previous - pos = site.posts.index {|post| post.equal?(self) } - if pos && pos > 0 - site.posts[pos - 1] - else - nil - end - end - - # Returns if this Post is a Draft - def draft? - is_a?(Jekyll::Draft) - end - - protected - - def extract_excerpt - if generate_excerpt? - Jekyll::Excerpt.new(self) - else - "" - end - end - - def generate_excerpt? - !excerpt_separator.empty? - end - end -end diff --git a/lib/jekyll/publisher.rb b/lib/jekyll/publisher.rb index e86e96b6..6bb8a882 100644 --- a/lib/jekyll/publisher.rb +++ b/lib/jekyll/publisher.rb @@ -15,7 +15,7 @@ module Jekyll end def hidden_in_the_future?(thing) - thing.is_a?(Post) && !@site.future && thing.date > @site.time + thing.respond_to?(:date) && !@site.future && thing.date.to_i > @site.time.to_i end end -end \ No newline at end of file +end diff --git a/lib/jekyll/reader.rb b/lib/jekyll/reader.rb index b8cdae18..149a7a7a 100644 --- a/lib/jekyll/reader.rb +++ b/lib/jekyll/reader.rb @@ -22,12 +22,12 @@ module Jekyll # Sorts posts, pages, and static files. def sort_files! - site.posts.sort! + site.collections.values.each(&:sort!) site.pages.sort_by!(&:name) site.static_files.sort_by!(&:relative_path) end - # Recursively traverse directories to find posts, pages and static files + # Recursively traverse directories to find pages and static files # that will become part of the site according to the rules in # filter_entries. # @@ -56,8 +56,8 @@ module Jekyll # # Returns nothing. def retrieve_posts(dir) - site.posts.concat(PostReader.new(site).read(dir)) - site.posts.concat(DraftReader.new(site).read(dir)) if site.show_drafts + site.posts.docs.concat(PostReader.new(site).read_posts(dir)) + site.posts.docs.concat(PostReader.new(site).read_drafts(dir)) if site.show_drafts end # Recursively traverse directories with the read_directories function. diff --git a/lib/jekyll/readers/collection_reader.rb b/lib/jekyll/readers/collection_reader.rb index 04324fa1..6a54321d 100644 --- a/lib/jekyll/readers/collection_reader.rb +++ b/lib/jekyll/readers/collection_reader.rb @@ -1,5 +1,7 @@ module Jekyll class CollectionReader + SPECIAL_COLLECTIONS = %w{posts data}.freeze + attr_reader :site, :content def initialize(site) @site = site @@ -11,9 +13,9 @@ module Jekyll # Returns nothing. def read site.collections.each do |_, collection| - collection.read unless collection.label.eql?('data') + collection.read unless SPECIAL_COLLECTIONS.include?(collection.label) end end end -end \ No newline at end of file +end diff --git a/lib/jekyll/readers/draft_reader.rb b/lib/jekyll/readers/draft_reader.rb deleted file mode 100644 index d62029b2..00000000 --- a/lib/jekyll/readers/draft_reader.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Jekyll - class DraftReader - attr_reader :site, :unfiltered_content - def initialize(site) - @site = site - @unfiltered_content = Array.new - end - - # Read all the files in /

/_drafts and create a new Draft - # object with each one. - # - # dir - The String relative path of the directory to read. - # - # Returns nothing. - def read(dir) - @unfiltered_content = read_content(dir, '_drafts') - @unfiltered_content.select{ |draft| site.publisher.publish?(draft) } - end - - # Read all the content files from //magic_dir - # and return them with the type klass. - # - # dir - The String relative path of the directory to read. - # magic_dir - The String relative directory to , - # looks for content here. - # klass - The return type of the content. - # - # Returns klass type of content files - def read_content(dir, magic_dir) - @site.reader.get_entries(dir, magic_dir).map do |entry| - Draft.new(site, site.source, dir, entry) if Draft.valid?(entry) - end.reject do |entry| - entry.nil? - end - end - end -end diff --git a/lib/jekyll/readers/post_reader.rb b/lib/jekyll/readers/post_reader.rb index 73bdd85d..c41ef10a 100644 --- a/lib/jekyll/readers/post_reader.rb +++ b/lib/jekyll/readers/post_reader.rb @@ -3,18 +3,40 @@ module Jekyll attr_reader :site, :unfiltered_content def initialize(site) @site = site - @unfiltered_content = Array.new end - # Read all the files in //_posts and create a new Post + # Read all the files in //_drafts and create a new + # Document object with each one. + # + # dir - The String relative path of the directory to read. + # + # Returns nothing. + def read_drafts(dir) + read_publishable(dir, '_drafts', Document::DATELESS_FILENAME_MATCHER) + end + + # Read all the files in //_posts and create a new Document # object with each one. # # dir - The String relative path of the directory to read. # # Returns nothing. - def read(dir) - @unfiltered_content = read_content(dir, '_posts') - @unfiltered_content.select{ |post| site.publisher.publish?(post) } + def read_posts(dir) + read_publishable(dir, '_posts', Document::DATE_FILENAME_MATCHER) + end + + # Read all the files in // and create a new + # Document object with each one insofar as it matches the regexp matcher. + # + # dir - The String relative path of the directory to read. + # + # Returns nothing. + def read_publishable(dir, magic_dir, matcher) + read_content(dir, magic_dir, matcher).tap do |docs| + docs.each(&:read) + end.select do |doc| + site.publisher.publish?(doc) + end end # Read all the content files from //magic_dir @@ -26,12 +48,15 @@ module Jekyll # klass - The return type of the content. # # Returns klass type of content files - def read_content(dir, magic_dir) + def read_content(dir, magic_dir, matcher) @site.reader.get_entries(dir, magic_dir).map do |entry| - Post.new(site, site.source, dir, entry) if Post.valid?(entry) - end.reject do |entry| - entry.nil? - end + next unless entry =~ matcher + path = @site.in_source_dir(File.join(dir, magic_dir, entry)) + Document.new(path, { + site: @site, + collection: @site.posts + }) + end.reject(&:nil?) end end end diff --git a/lib/jekyll/regenerator.rb b/lib/jekyll/regenerator.rb index d5d74ec9..18b94f6b 100644 --- a/lib/jekyll/regenerator.rb +++ b/lib/jekyll/regenerator.rb @@ -17,8 +17,8 @@ module Jekyll # Returns a boolean. def regenerate?(document) case document - when Post, Page - document.asset_file? || document.data['regenerate'] || + when Page + document.asset_file? || document.data['regenerate'] || source_modified_or_dest_missing?( site.in_source_dir(document.relative_path), document.destination(@site.dest) ) @@ -87,7 +87,7 @@ module Jekyll return true if disabled? # objects that don't have a path are always regenerated - return true if path.nil? + return true if path.nil? # Check for path in cache if cache.has_key? path diff --git a/lib/jekyll/related_posts.rb b/lib/jekyll/related_posts.rb index 9666b52e..f7dc0962 100644 --- a/lib/jekyll/related_posts.rb +++ b/lib/jekyll/related_posts.rb @@ -46,7 +46,7 @@ module Jekyll end def most_recent_posts - @most_recent_posts ||= (site.posts.reverse - [post]).first(10) + @most_recent_posts ||= (site.posts.docs.reverse - [post]).first(10) end def display(output) diff --git a/lib/jekyll/renderer.rb b/lib/jekyll/renderer.rb index b79cb6a7..529ff84b 100644 --- a/lib/jekyll/renderer.rb +++ b/lib/jekyll/renderer.rb @@ -23,7 +23,7 @@ module Jekyll # # Returns the output extname including the leading period. def output_ext - converters.first.output_ext(document.extname) + @output_ext ||= converters.first.output_ext(document.extname) end ###################### @@ -31,11 +31,18 @@ module Jekyll ###################### def run + Jekyll.logger.debug "Rendering:", document.relative_path + payload = Utils.deep_merge_hashes({ "page" => document.to_liquid }, site_payload || site.site_payload) - Jekyll::Hooks.trigger :document, :pre_render, document, payload + if document.collection.label == 'posts' && document.is_a?(Document) + payload['site']['related_posts'] = document.related_posts + end + + Jekyll.logger.debug "Pre-Render Hooks:", document.relative_path + document.trigger_hooks(:pre_render, payload) info = { filters: [Jekyll::Filters], @@ -49,13 +56,16 @@ module Jekyll output = document.content if document.render_with_liquid? + Jekyll.logger.debug "Rendering Liquid:", document.relative_path output = render_liquid(output, payload, info, document.path) end + Jekyll.logger.debug "Rendering Markup:", document.relative_path output = convert(output) document.content = output if document.place_in_layout? + Jekyll.logger.debug "Rendering Layout:", document.relative_path place_in_layouts( output, payload, diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb index 78d4db8b..66bfd163 100644 --- a/lib/jekyll/site.rb +++ b/lib/jekyll/site.rb @@ -4,7 +4,7 @@ require 'csv' module Jekyll class Site attr_reader :source, :dest, :config - attr_accessor :layouts, :posts, :pages, :static_files, :drafts, + attr_accessor :layouts, :pages, :static_files, :drafts, :exclude, :include, :lsi, :highlighter, :permalink_style, :time, :future, :unpublished, :safe, :plugins, :limit_posts, :show_drafts, :keep_files, :baseurl, :data, :file_read_opts, @@ -74,7 +74,6 @@ module Jekyll def reset self.time = (config['time'] ? Utils.parse_date(config['time'].to_s, "Invalid time in _config.yml.") : Time.now) self.layouts = {} - self.posts = [] self.pages = [] self.static_files = [] self.data = {} @@ -170,14 +169,14 @@ module Jekyll collection.docs.each do |document| if regenerator.regenerate?(document) document.output = Jekyll::Renderer.new(self, document, payload).run - Jekyll::Hooks.trigger :document, :post_render, document + document.trigger_hooks(:post_render) end end end - [posts, pages].flatten.each do |page_or_post| - if regenerator.regenerate?(page_or_post) - page_or_post.render(layouts, payload) + pages.flatten.each do |page| + if regenerator.regenerate?(page) + page.render(layouts, payload) end end rescue Errno::ENOENT @@ -202,6 +201,10 @@ module Jekyll Jekyll::Hooks.trigger :site, :post_write, self end + def posts + collections['posts'] ||= Collection.new(self, 'posts') + end + # Construct a Hash of Posts indexed by the specified Post attribute. # # post_attr - The String name of the Post attribute. @@ -219,7 +222,7 @@ module Jekyll # 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 { |h, key| h[key] = [] } - posts.each { |p| p.send(post_attr.to_sym).each { |t| hash[t] << p } } + posts.each { |p| p.data[post_attr].each { |t| hash[t] << p } } hash.values.each { |posts| posts.sort!.reverse! } hash end @@ -391,8 +394,8 @@ module Jekyll # Returns nothing def limit_posts! if limit_posts > 0 - limit = posts.length < limit_posts ? posts.length : limit_posts - self.posts = posts[-limit, limit] + limit = posts.docs.length < limit_posts ? posts.docs.length : limit_posts + self.posts.docs = posts.docs[-limit, limit] end end diff --git a/lib/jekyll/tags/post_url.rb b/lib/jekyll/tags/post_url.rb index ce97642f..2d3e8f44 100644 --- a/lib/jekyll/tags/post_url.rb +++ b/lib/jekyll/tags/post_url.rb @@ -14,7 +14,7 @@ module Jekyll end def ==(other) - other.name.match(@name_regex) + other.basename.match(@name_regex) end def deprecated_equality(other) @@ -32,11 +32,11 @@ module Jekyll # # Returns the post slug with the subdirectory (relative to _posts) def post_slug(other) - path = other.name.split("/")[0...-1].join("/") + path = other.basename.split("/")[0...-1].join("/") if path.nil? || path == "" - other.slug + other.data['slug'] else - path + '/' + other.slug + path + '/' + other.data['slug'] end end end diff --git a/lib/jekyll/utils.rb b/lib/jekyll/utils.rb index cc01c8cc..86d24cef 100644 --- a/lib/jekyll/utils.rb +++ b/lib/jekyll/utils.rb @@ -8,6 +8,13 @@ module Jekyll SLUGIFY_DEFAULT_REGEXP = Regexp.new('[^[:alnum:]]+').freeze SLUGIFY_PRETTY_REGEXP = Regexp.new("[^[:alnum:]._~!$&'()+,;=@]+").freeze + # Non-destructive version of deep_merge_hashes! See that method. + # + # Returns the merged hashes. + def deep_merge_hashes(master_hash, other_hash) + deep_merge_hashes!(master_hash.dup, other_hash) + end + # Merges a master hash with another hash, recursively. # # master_hash - the "parent" hash whose values will be overridden @@ -17,16 +24,14 @@ module Jekyll # http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html # # Thanks to whoever made it. - def deep_merge_hashes(master_hash, other_hash) - target = master_hash.dup - - other_hash.each_key do |key| - if other_hash[key].is_a? Hash and target[key].is_a? Hash - target[key] = Utils.deep_merge_hashes(target[key], other_hash[key]) + def deep_merge_hashes!(target, overwrite) + overwrite.each_key do |key| + if overwrite[key].is_a? Hash and target[key].is_a? Hash + target[key] = Utils.deep_merge_hashes(target[key], overwrite[key]) next end - target[key] = other_hash[key] + target[key] = overwrite[key] end target diff --git a/site/_docs/plugins.md b/site/_docs/plugins.md index 6be050d8..e29d7624 100644 --- a/site/_docs/plugins.md +++ b/site/_docs/plugins.md @@ -504,8 +504,8 @@ Jekyll::Hooks.register :post, :post_render do |post| end {% endhighlight %} -Jekyll provides hooks for :site, :page, -:post, and :document. In all cases, Jekyll calls your +Jekyll provides hooks for :site, :pages, +:posts, and :documents. In all cases, Jekyll calls your hooks with the container object as the first callback parameter. But in the case of :pre_render, your hook will also receive a payload hash as a second parameter which allows you full control over the variables that are @@ -569,7 +569,7 @@ The complete list of available hooks is below: -

:page

+

:pages

:post_init

@@ -580,7 +580,7 @@ The complete list of available hooks is below: -

:page

+

:pages

:pre_render

@@ -591,7 +591,7 @@ The complete list of available hooks is below: -

:page

+

:pages

:post_render

@@ -602,7 +602,7 @@ The complete list of available hooks is below: -

:page

+

:pages

:post_write

@@ -613,7 +613,7 @@ The complete list of available hooks is below: -

:post

+

:posts

:post_init

@@ -624,7 +624,7 @@ The complete list of available hooks is below: -

:post

+

:posts

:pre_render

@@ -635,7 +635,7 @@ The complete list of available hooks is below: -

:post

+

:posts

:post_render

@@ -646,7 +646,7 @@ The complete list of available hooks is below: -

:post

+

:posts

:post_write

@@ -657,7 +657,18 @@ The complete list of available hooks is below: -

:document

+

:documents

+ + +

:post_init

+ + +

Whenever a document is initialized

+ + + + +

:documents

:pre_render

@@ -668,7 +679,7 @@ The complete list of available hooks is below: -

:document

+

:documents

:post_render

@@ -679,7 +690,7 @@ The complete list of available hooks is below: -

:document

+

:documents

:post_write

@@ -854,7 +865,7 @@ LESS.js files during generation. - [Jekyll CO₂](https://github.com/wdenton/jekyll-co2): Generates HTML showing the monthly change in atmospheric CO₂ at the Mauna Loa observatory in Hawaii. - [remote-include](http://www.northfieldx.co.uk/remote-include/): Includes files using remote URLs - [jekyll-minifier](https://github.com/digitalsparky/jekyll-minifier): Minifies HTML, XML, CSS, and Javascript both inline and as separate files utilising yui-compressor and htmlcompressor. -- [Jekyll views router](https://bitbucket.org/nyufac/jekyll-views-router): Simple router between generator plugins and templates. +- [Jekyll views router](https://bitbucket.org/nyufac/jekyll-views-router): Simple router between generator plugins and templates. #### Editors diff --git a/test/helper.rb b/test/helper.rb index db3b8822..16f015b7 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -20,6 +20,8 @@ require 'rspec/mocks' require 'jekyll' +Jekyll.logger = Logger.new(StringIO.new) + unless jruby? require 'rdiscount' require 'redcarpet' @@ -31,7 +33,7 @@ require 'shoulda' include Jekyll # FIXME: If we really need this we lost the game. -STDERR.reopen(test(?e, '/dev/null') ? '/dev/null' : 'NUL:') +# STDERR.reopen(test(?e, '/dev/null') ? '/dev/null' : 'NUL:') # Report with color. Minitest::Reporters.use! [ @@ -66,6 +68,7 @@ class JekyllUnitTest < Minitest::Test def build_configs(overrides, base_hash = Jekyll::Configuration::DEFAULTS) Utils.deep_merge_hashes(base_hash, overrides) + .fix_common_issues.backwards_compatibilize.add_default_collections end def site_configuration(overrides = {}) @@ -108,23 +111,13 @@ class JekyllUnitTest < Minitest::Test ENV[key] = old_value end - def capture_stdout - $old_stdout = $stdout - $stdout = StringIO.new + def capture_output + stderr = StringIO.new + Jekyll.logger = Logger.new stderr yield - $stdout.rewind - return $stdout.string - ensure - $stdout = $old_stdout - end - - def capture_stderr - $old_stderr = $stderr - $stderr = StringIO.new - yield - $stderr.rewind - return $stderr.string - ensure - $stderr = $old_stderr + stderr.rewind + return stderr.string.to_s end + alias_method :capture_stdout, :capture_output + alias_method :capture_stderr, :capture_output end diff --git a/test/test_collections.rb b/test/test_collections.rb index 3cae5c30..be3a9320 100644 --- a/test/test_collections.rb +++ b/test/test_collections.rb @@ -80,8 +80,9 @@ class TestCollections < JekyllUnitTest @site.process end - should "not contain any collections" do - assert_equal Hash.new, @site.collections + should "contain only the defaul collections" do + refute_equal Hash.new, @site.collections + refute_nil @site.collections end end diff --git a/test/test_configuration.rb b/test/test_configuration.rb index bd99dc8b..553dfda3 100644 --- a/test/test_configuration.rb +++ b/test/test_configuration.rb @@ -1,6 +1,8 @@ require 'helper' class TestConfiguration < JekyllUnitTest + @@defaults = Jekyll::Configuration::DEFAULTS.add_default_collections.freeze + context "#stringify_keys" do setup do @mixed_keys = Configuration[{ @@ -131,20 +133,20 @@ class TestConfiguration < JekyllUnitTest should "fire warning with no _config.yml" do allow(SafeYAML).to receive(:load_file).with(@path) { raise SystemCallError, "No such file or directory - #{@path}" } allow($stderr).to receive(:puts).with("Configuration file: none".yellow) - assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({}) + assert_equal @@defaults, Jekyll.configuration({}) end should "load configuration as hash" do allow(SafeYAML).to receive(:load_file).with(@path).and_return(Hash.new) allow($stdout).to receive(:puts).with("Configuration file: #{@path}") - assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({}) + assert_equal @@defaults, Jekyll.configuration({}) end should "fire warning with bad config" do allow(SafeYAML).to receive(:load_file).with(@path).and_return(Array.new) allow($stderr).to receive(:puts).and_return(("WARNING: ".rjust(20) + "Error reading configuration. Using defaults (and options).").yellow) allow($stderr).to receive(:puts).and_return("Configuration file: (INVALID) #{@path}".yellow) - assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({}) + assert_equal @@defaults, Jekyll.configuration({}) end should "fire warning when user-specified config file isn't there" do @@ -170,27 +172,27 @@ class TestConfiguration < JekyllUnitTest } end - should "load default config if no config_file is set" do + should "load default plus posts config if no config_file is set" do allow(SafeYAML).to receive(:load_file).with(@paths[:default]).and_return({}) allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}") - assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({}) + assert_equal @@defaults, Jekyll.configuration({}) end should "load different config if specified" do allow(SafeYAML).to receive(:load_file).with(@paths[:other]).and_return({"baseurl" => "http://wahoo.dev"}) allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}") - assert_equal Utils.deep_merge_hashes(Jekyll::Configuration::DEFAULTS, { "baseurl" => "http://wahoo.dev" }), Jekyll.configuration({ "config" => @paths[:other] }) + assert_equal Utils.deep_merge_hashes(@@defaults, { "baseurl" => "http://wahoo.dev" }), Jekyll.configuration({ "config" => @paths[:other] }) end should "load default config if path passed is empty" do allow(SafeYAML).to receive(:load_file).with(@paths[:default]).and_return({}) allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}") - assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({ "config" => @paths[:empty] }) + assert_equal @@defaults, Jekyll.configuration({ "config" => @paths[:empty] }) end should "successfully load a TOML file" do Jekyll.logger.log_level = :warn - assert_equal Jekyll::Configuration::DEFAULTS.merge({ "baseurl" => "/you-beautiful-blog-you", "title" => "My magnificent site, wut" }), Jekyll.configuration({ "config" => [@paths[:toml]] }) + assert_equal @@defaults.clone.merge({ "baseurl" => "/you-beautiful-blog-you", "title" => "My magnificent site, wut" }), Jekyll.configuration({ "config" => [@paths[:toml]] }) Jekyll.logger.log_level = :info end @@ -203,7 +205,7 @@ class TestConfiguration < JekyllUnitTest allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}") allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}") allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:toml]}") - assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({ "config" => [@paths[:default], @paths[:other], @paths[:toml]] }) + assert_equal @@defaults, Jekyll.configuration({ "config" => [@paths[:default], @paths[:other], @paths[:toml]] }) end should "load multiple config files and last config should win" do @@ -211,7 +213,7 @@ class TestConfiguration < JekyllUnitTest allow(SafeYAML).to receive(:load_file).with(@paths[:other]).and_return({"baseurl" => "http://wahoo.dev"}) allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}") allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}") - assert_equal Utils.deep_merge_hashes(Jekyll::Configuration::DEFAULTS, { "baseurl" => "http://wahoo.dev" }), Jekyll.configuration({ "config" => [@paths[:default], @paths[:other]] }) + assert_equal Utils.deep_merge_hashes(@@defaults, { "baseurl" => "http://wahoo.dev" }), Jekyll.configuration({ "config" => [@paths[:default], @paths[:other]] }) end end end diff --git a/test/test_document.rb b/test/test_document.rb index f8b9db66..9f337c27 100644 --- a/test/test_document.rb +++ b/test/test_document.rb @@ -2,6 +2,10 @@ require 'helper' class TestDocument < JekyllUnitTest + def assert_equal_value(key, one, other) + assert_equal(one[key], other[key]) + end + context "a document in a collection" do setup do @site = fixture_site({ @@ -36,10 +40,8 @@ class TestDocument < JekyllUnitTest end should "know its data" do - assert_equal({ - "title" => "Jekyll.configuration", - "whatever" => "foo.bar" - }, @document.data) + assert_equal "Jekyll.configuration", @document.data["title"] + assert_equal "foo.bar", @document.data["whatever"] end context "with YAML ending in three dots" do @@ -53,10 +55,8 @@ class TestDocument < JekyllUnitTest end should "know its data" do - assert_equal({ - "title" => "YAML with Dots", - "whatever" => "foo.bar" - }, @document.data) + assert_equal "YAML with Dots", @document.data["title"] + assert_equal "foo.bar", @document.data["whatever"] end end @@ -88,13 +88,9 @@ class TestDocument < JekyllUnitTest end should "know the frontmatter defaults" do - assert_equal({ - "title"=>"Example slide", - "layout"=>"slide", - "nested"=> { - "key"=>"myval" - } - }, @document.data) + assert_equal "Example slide", @document.data["title"] + assert_equal "slide", @document.data["layout"] + assert_equal({"key"=>"myval"}, @document.data["nested"]) end end @@ -117,14 +113,9 @@ class TestDocument < JekyllUnitTest end should "override default values in the document frontmatter" do - assert_equal({ - "title"=>"Override title", - "layout"=>"slide", - "nested"=> { - "test1"=>"override1", - "test2"=>"override2" - } - }, @document.data) + assert_equal "Override title", @document.data["title"] + assert_equal "slide", @document.data["layout"] + assert_equal({"test1"=>"override1","test2"=>"override2"}, @document.data["nested"]) end end @@ -146,13 +137,9 @@ class TestDocument < JekyllUnitTest end should "know the frontmatter defaults" do - assert_equal({ - "title"=>"Example slide", - "layout"=>"slide", - "nested"=> { - "key"=>"value123" - } - }, @document.data) + assert_equal "Example slide", @document.data["title"] + assert_equal "slide", @document.data["layout"] + assert_equal({"key"=>"value123"}, @document.data["nested"]) end end @@ -174,10 +161,9 @@ class TestDocument < JekyllUnitTest end should "not know the specified frontmatter defaults" do - assert_equal({ - "title"=>"Example slide", - "layout"=>"slide" - }, @document.data) + assert_equal "Example slide", @document.data["title"] + assert_equal "slide", @document.data["layout"] + assert_equal nil, @document.data["nested"] end end diff --git a/test/test_draft.rb b/test/test_draft.rb deleted file mode 100644 index 7312c8c9..00000000 --- a/test/test_draft.rb +++ /dev/null @@ -1,55 +0,0 @@ -require 'helper' - -class TestDraft < JekyllUnitTest - def setup_draft(file) - Draft.new(@site, source_dir, '', file) - end - - context "A Draft" do - setup do - clear_dest - @site = Site.new(site_configuration) - end - - should "ensure valid drafts are valid" do - assert Draft.valid?("2008-09-09-foo-bar.textile") - assert Draft.valid?("foo/bar/2008-09-09-foo-bar.textile") - assert Draft.valid?("lol2008-09-09-foo-bar.textile") - - assert !Draft.valid?("blah") - end - - should "make properties accessible through #[]" do - draft = setup_draft('draft-properties.text') - # ! need to touch the file! Or get its timestamp - date = File.mtime(File.join(source_dir, '_drafts', 'draft-properties.text')) - ymd = date.strftime("%Y/%m/%d") - - attrs = { - categories: %w(foo bar baz), - content: "All the properties.\n\nPlus an excerpt.\n", - date: date, - dir: "/foo/bar/baz/#{ymd}", - excerpt: "All the properties.\n\n", - foo: 'bar', - id: "/foo/bar/baz/#{ymd}/draft-properties", - layout: 'default', - name: nil, - path: "_drafts/draft-properties.text", - permalink: nil, - published: nil, - tags: %w(ay bee cee), - title: 'Properties Draft', - url: "/foo/bar/baz/#{ymd}/draft-properties.html" - } - - attrs.each do |attr, val| - attr_str = attr.to_s - result = draft[attr_str] - assert_equal val, result, "For :" - end - end - - end - -end diff --git a/test/test_excerpt.rb b/test/test_excerpt.rb index a5025cda..322ba3c6 100644 --- a/test/test_excerpt.rb +++ b/test/test_excerpt.rb @@ -2,33 +2,36 @@ require 'helper' class TestExcerpt < JekyllUnitTest def setup_post(file) - Post.new(@site, source_dir, '', file) + Document.new(@site.in_source_dir(File.join('_posts', file)), { + site: @site, + collection: @site.posts + }).tap(&:read) end - def do_render(post) - layouts = { "default" => Layout.new(@site, source_dir('_layouts'), "simple.html")} - post.render(layouts, {"site" => {"posts" => []}}) + def do_render(document) + @site.layouts = { "default" => Layout.new(@site, source_dir('_layouts'), "simple.html")} + payload = {"site" => {"posts" => []}} + document.output = Jekyll::Renderer.new(@site, document, payload).run end context "With extraction disabled" do setup do clear_dest - @site = Site.new(site_configuration('excerpt_separator' => '')) + @site = fixture_site('excerpt_separator' => '') @post = setup_post("2013-07-22-post-excerpt-with-layout.markdown") end should "not be generated" do - excerpt = @post.send(:extract_excerpt) - assert_equal true, excerpt.empty? + refute @post.generate_excerpt? end end context "An extracted excerpt" do setup do clear_dest - @site = Site.new(site_configuration) + @site = fixture_site @post = setup_post("2013-07-22-post-excerpt-with-layout.markdown") - @excerpt = @post.send :extract_excerpt + @excerpt = @post.data['excerpt'] end context "#include(string)" do @@ -45,7 +48,7 @@ class TestExcerpt < JekyllUnitTest context "#id" do should "contain the UID for the post" do - assert_equal @excerpt.id, "#{@post.id}/#excerpt" + assert_equal @excerpt.id, "#{@post.id}#excerpt" end should "return a string" do assert_same @post.id.class, String @@ -53,8 +56,8 @@ class TestExcerpt < JekyllUnitTest end context "#to_s" do - should "return its content if no output present" do - assert_equal @excerpt.content, @excerpt.to_s + should "return rendered output" do + assert_equal @excerpt.output, @excerpt.to_s end should "return its output if output present" do @@ -82,17 +85,6 @@ class TestExcerpt < JekyllUnitTest assert_equal %w[first second third jekyllrb.com], @excerpt.to_liquid["tags"] assert_equal "_posts/2013-07-22-post-excerpt-with-layout.markdown", @excerpt.to_liquid["path"] end - - should "consider inheritance" do - klass = Class.new(Jekyll::Post) - assert_gets_called = false - klass.send(:define_method, :assert_gets_called) { assert_gets_called = true } - klass.const_set(:EXCERPT_ATTRIBUTES_FOR_LIQUID, Jekyll::Post::EXCERPT_ATTRIBUTES_FOR_LIQUID + ['assert_gets_called']) - post = klass.new(@site, source_dir, '', "2008-02-02-published.markdown") - Jekyll::Excerpt.new(post).to_liquid - - assert assert_gets_called, 'assert_gets_called did not get called on post.' - end end context "#content" do @@ -111,11 +103,11 @@ class TestExcerpt < JekyllUnitTest setup do @rendered_post = @post.dup do_render(@rendered_post) - @extracted_excerpt = @rendered_post.send :extracted_excerpt + @extracted_excerpt = @rendered_post.data['excerpt'] end should "be the first paragraph of the page" do - assert_equal "

First paragraph with link ref.

\n\n", @extracted_excerpt.content + assert_equal "

First paragraph with link ref.

\n\n", @extracted_excerpt.output end should "link properly" do @@ -128,9 +120,9 @@ class TestExcerpt < JekyllUnitTest context "A whole-post excerpt" do setup do clear_dest - @site = Site.new(site_configuration) + @site = fixture_site @post = setup_post("2008-02-02-published.markdown") - @excerpt = @post.send :extract_excerpt + @excerpt = @post.data['excerpt'] end should "be generated" do diff --git a/test/test_front_matter_defaults.rb b/test/test_front_matter_defaults.rb index 7d2e26be..99efedea 100644 --- a/test/test_front_matter_defaults.rb +++ b/test/test_front_matter_defaults.rb @@ -69,7 +69,7 @@ class TestFrontMatterDefaults < JekyllUnitTest }] })) @site.process - @affected = @site.posts.find { |page| page.relative_path =~ /^\/win/ } + @affected = @site.posts.docs.find { |page| page.relative_path =~ /win\// } @not_affected = @site.pages.find { |page| page.relative_path == "about.html" } end @@ -95,7 +95,7 @@ class TestFrontMatterDefaults < JekyllUnitTest })) @site.process @affected = @site.pages - @not_affected = @site.posts + @not_affected = @site.posts.docs end should "affect only the specified type and all paths" do @@ -120,7 +120,7 @@ class TestFrontMatterDefaults < JekyllUnitTest })) @site.process @affected = @site.pages - @not_affected = @site.posts + @not_affected = @site.posts.docs end should "affect only the specified type and all paths" do diff --git a/test/test_new_command.rb b/test/test_new_command.rb index 7321b462..33bd1044 100644 --- a/test/test_new_command.rb +++ b/test/test_new_command.rb @@ -25,14 +25,15 @@ class TestNewCommand < JekyllUnitTest should 'create a new directory' do assert !File.exist?(@full_path) - capture_stdout { Jekyll::Commands::New.process(@args) } + Jekyll::Commands::New.process(@args) assert File.exist?(@full_path) end should 'display a success message' do - output = capture_stdout { Jekyll::Commands::New.process(@args) } - success_message = "New jekyll site installed in #{@full_path}. \n" - assert_equal success_message, output + Jekyll::Commands::New.process(@args) + output = Jekyll.logger.messages.last + success_message = "New jekyll site installed in #{@full_path}." + assert_includes output, success_message end should 'copy the static files in site template to the new directory' do diff --git a/test/test_post.rb b/test/test_post.rb deleted file mode 100644 index 2956309f..00000000 --- a/test/test_post.rb +++ /dev/null @@ -1,815 +0,0 @@ -# encoding: utf-8 - -require 'helper' - -class TestPost < JekyllUnitTest - def setup_post(file) - Post.new(@site, source_dir, '', file) - end - - def do_render(post) - layouts = { "default" => Layout.new(@site, source_dir('_layouts'), "simple.html")} - post.render(layouts, {"site" => {"posts" => []}}) - end - - context "A Post" do - setup do - clear_dest - @site = fixture_site - end - - should "ensure valid posts are valid" do - assert Post.valid?("2008-09-09-foo-bar.textile") - assert Post.valid?("foo/bar/2008-09-09-foo-bar.textile") - assert Post.valid?("2008-09-09-foo-bar.markdown") - assert Post.valid?("foo/bar/2008-09-09-foo-bar.markdown") - - assert !Post.valid?("lol2008-09-09-foo-bar.textile") - assert !Post.valid?("lol2008-09-09-foo-bar.markdown") - assert !Post.valid?("blah") - end - - should "make properties accessible through #[]" do - post = setup_post('2013-12-20-properties.text') - - attrs = { - categories: %w(foo bar baz MixedCase), - content: "All the properties.\n\nPlus an excerpt.\n", - date: Time.new(2013, 12, 20), - dir: "/foo/bar/baz/mixedcase/2013/12/20", - excerpt: "All the properties.\n\n", - foo: 'bar', - id: "/foo/bar/baz/mixedcase/2013/12/20/properties", - layout: 'default', - name: nil, - path: "_posts/2013-12-20-properties.text", - permalink: nil, - published: nil, - tags: %w(ay bee cee), - title: 'Properties Post', - url: "/foo/bar/baz/mixedcase/2013/12/20/properties.html" - } - - attrs.each do |attr, val| - attr_str = attr.to_s - result = post[attr_str] - assert_equal val, result, "For :" - end - end - - context "processing posts" do - setup do - @post = Post.allocate - @post.site = @site - - @real_file = "2008-10-18-foo-bar.markdown" - @fake_file = "2008-09-09-foo-bar.markdown" - @source = source_dir('_posts') - end - - should "keep date, title, and markup type" do - @post.categories = [] - @post.process(@fake_file) - - assert_equal Time.parse("2008-09-09"), @post.date - assert_equal "foo-bar", @post.slug - assert_equal ".markdown", @post.ext - assert_equal "/2008/09/09", @post.dir - assert_equal "/2008/09/09/foo-bar", @post.id - end - - should "ignore subfolders" do - post = Post.allocate - post.categories = ['foo'] - post.site = @site - post.process("cat1/2008-09-09-foo-bar.markdown") - assert_equal 1, post.categories.size - assert_equal "foo", post.categories[0] - - post = Post.allocate - post.categories = ['foo', 'bar'] - post.site = @site - post.process("cat2/CAT3/2008-09-09-foo-bar.markdown") - assert_equal 2, post.categories.size - assert_equal "foo", post.categories[0] - assert_equal "bar", post.categories[1] - - end - - should "create url based on date and title" do - @post.categories = [] - @post.process(@fake_file) - assert_equal "/2008/09/09/foo-bar.html", @post.url - end - - should "raise a good error on invalid post date" do - assert_raises Jekyll::Errors::FatalException do - @post.process("2009-27-03-foo-bar.markdown") - end - end - - should "escape urls" do - @post.categories = [] - @post.process("2009-03-12-hash-#1.markdown") - assert_equal "/2009/03/12/hash-%231.html", @post.url - assert_equal "/2009/03/12/hash-#1", @post.id - end - - should "escape urls with non-alphabetic characters" do - @post.categories = [] - @post.process("2014-03-22-escape-+ %20[].markdown") - assert_equal "/2014/03/22/escape-+%20%2520%5B%5D.html", @post.url - assert_equal "/2014/03/22/escape-+ %20[]", @post.id - end - - should "return a UTF-8 escaped string" do - assert_equal Encoding::UTF_8, URL.escape_path("/rails笔记/2014/04/20/escaped/").encoding - end - - should "return a UTF-8 unescaped string" do - assert_equal Encoding::UTF_8, URL.unescape_path("/rails%E7%AC%94%E8%AE%B0/2014/04/20/escaped/".encode(Encoding::ASCII)).encoding - end - - should "respect permalink in yaml front matter" do - file = "2008-12-03-permalinked-post.markdown" - @post.process(file) - @post.read_yaml(@source, file) - - assert_equal "my_category/permalinked-post", @post.permalink - assert_equal "/my_category", @post.dir - assert_equal "/my_category/permalinked-post", @post.url - end - - should "not be writable outside of destination" do - unexpected = File.expand_path("../../../baddie.html", dest_dir) - File.delete unexpected if File.exist?(unexpected) - post = setup_post("2014-01-06-permalink-traversal.md") - do_render(post) - post.write(dest_dir) - - assert !File.exist?(unexpected), "../../../baddie.html should not exist." - assert File.exist?(File.expand_path("baddie.html", dest_dir)) - end - - context "with CRLF linebreaks" do - setup do - @real_file = "2009-05-24-yaml-linebreak.markdown" - @source = source_dir('win/_posts') - end - should "read yaml front-matter" do - @post.read_yaml(@source, @real_file) - - assert_equal({"title" => "Test title", "layout" => "post", "tag" => "Ruby"}, @post.data) - assert_equal "This is the content", @post.content - end - end - - context "with three dots ending YAML header" do - setup do - @real_file = "2014-03-03-yaml-with-dots.md" - end - should "should read the YAML header" do - @post.read_yaml(@source, @real_file) - - assert_equal({"title" => "Test Post Where YAML Ends in Dots"}, - @post.data) - end - end - - context "with embedded triple dash" do - setup do - @real_file = "2010-01-08-triple-dash.markdown" - end - should "consume the embedded dashes" do - @post.read_yaml(@source, @real_file) - - assert_equal({"title" => "Foo --- Bar"}, @post.data) - assert_equal "Triple the fun!", @post.content - end - end - - context "with site wide permalink" do - setup do - @post.categories = [] - end - - context "with unspecified (date) style" do - setup do - @post.process(@fake_file) - end - - should "process the url correctly" do - assert_equal "/:categories/:year/:month/:day/:title.html", @post.template - assert_equal "/2008/09/09/foo-bar.html", @post.url - end - end - - context "with unspecified (date) style and a category" do - setup do - @post.categories << "beer" - @post.process(@fake_file) - end - - should "process the url correctly" do - assert_equal "/:categories/:year/:month/:day/:title.html", @post.template - assert_equal "/beer/2008/09/09/foo-bar.html", @post.url - end - end - - context "with unspecified (date) style and a numeric category" do - setup do - @post.categories << 2013 - @post.process(@fake_file) - end - - should "process the url correctly" do - assert_equal "/:categories/:year/:month/:day/:title.html", @post.template - assert_equal "/2013/2008/09/09/foo-bar.html", @post.url - end - end - - context "with specified layout of nil" do - setup do - file = '2013-01-12-nil-layout.markdown' - @post = setup_post(file) - @post.process(file) - end - - should "layout of nil is respected" do - assert_equal "nil", @post.data["layout"] - end - end - - context "with unspecified (date) style and categories" do - setup do - @post.categories << "food" - @post.categories << "beer" - @post.process(@fake_file) - end - - should "process the url correctly" do - assert_equal "/:categories/:year/:month/:day/:title.html", @post.template - assert_equal "/food/beer/2008/09/09/foo-bar.html", @post.url - end - end - - context "with space (categories)" do - setup do - @post.categories << "french cuisine" - @post.categories << "belgian beer" - @post.process(@fake_file) - end - - should "process the url correctly" do - assert_equal "/:categories/:year/:month/:day/:title.html", @post.template - assert_equal "/french%20cuisine/belgian%20beer/2008/09/09/foo-bar.html", @post.url - end - end - - context "with mixed case (category)" do - setup do - @post.categories << "MixedCase" - @post.process(@fake_file) - end - - should "process the url correctly" do - assert_equal "/:categories/:year/:month/:day/:title.html", @post.template - assert_equal "/mixedcase/2008/09/09/foo-bar.html", @post.url - end - end - - context "with duplicated mixed case (categories)" do - setup do - @post.categories << "MixedCase" - @post.categories << "Mixedcase" - @post.process(@fake_file) - end - - should "process the url correctly" do - assert_equal "/:categories/:year/:month/:day/:title.html", @post.template - assert_equal "/mixedcase/2008/09/09/foo-bar.html", @post.url - end - end - - context "with none style" do - setup do - @post.site.permalink_style = :none - @post.process(@fake_file) - end - - should "process the url correctly" do - assert_equal "/:categories/:title.html", @post.template - assert_equal "/foo-bar.html", @post.url - end - end - - context "with pretty style" do - setup do - @post.site.permalink_style = :pretty - @post.process(@fake_file) - end - - should "process the url correctly" do - assert_equal "/:categories/:year/:month/:day/:title/", @post.template - assert_equal "/2008/09/09/foo-bar/", @post.url - end - end - - context "with ordinal style" do - setup do - @post.site.permalink_style = :ordinal - @post.process(@fake_file) - end - - should "process the url correctly" do - assert_equal "/:categories/:year/:y_day/:title.html", @post.template - assert_equal "/2008/253/foo-bar.html", @post.url - end - end - - context "with custom date permalink" do - setup do - @post.site.permalink_style = '/:categories/:year/:i_month/:i_day/:title/' - @post.process(@fake_file) - end - - should "process the url correctly" do - assert_equal "/2008/9/9/foo-bar/", @post.url - end - end - - context "with custom abbreviated month date permalink" do - setup do - @post.site.permalink_style = '/:categories/:year/:short_month/:day/:title/' - @post.process(@fake_file) - end - - should "process the url correctly" do - assert_equal "/2008/Sep/09/foo-bar/", @post.url - end - end - - context "with prefix style and no extension" do - setup do - @post.site.permalink_style = "/prefix/:title" - @post.process(@fake_file) - end - - should "process the url correctly" do - assert_equal "/prefix/:title", @post.template - assert_equal "/prefix/foo-bar", @post.url - end - end - end - - should "read yaml front-matter" do - @post.read_yaml(@source, @real_file) - - assert_equal({"title" => "Foo Bar", "layout" => "default"}, @post.data) - assert_equal "# {{ page.title }}\n\nBest **post** ever", @post.content - end - - should "transform markdown" do - @post.process(@real_file) - @post.read_yaml(@source, @real_file) - assert_equal "

{{ page.title }}

\n\n

Best post ever

", @post.transform.strip - end - - context "#excerpt" do - setup do - file = "2013-01-02-post-excerpt.markdown" - @post = setup_post(file) - @post.process(file) - @post.read_yaml(@source, file) - do_render(@post) - end - - should "return first paragraph by default" do - assert @post.excerpt.include?("First paragraph"), "contains first paragraph" - assert !@post.excerpt.include?("Second paragraph"), "does not contains second paragraph" - assert !@post.excerpt.include?("Third paragraph"), "does not contains third paragraph" - end - - should "correctly resolve link references" do - assert @post.excerpt.include?("www.jekyllrb.com"), "contains referenced link URL" - end - - should "return rendered HTML" do - assert_equal "

First paragraph with link ref.

\n\n", - @post.excerpt - end - - context "with excerpt_separator setting" do - setup do - file = "2013-01-02-post-excerpt.markdown" - - @post.site.config['excerpt_separator'] = "\n---\n" - - @post.process(file) - @post.read_yaml(@source, file) - @post.transform - end - - should "respect given separator" do - assert @post.excerpt.include?("First paragraph"), "contains first paragraph" - assert @post.excerpt.include?("Second paragraph"), "contains second paragraph" - assert !@post.excerpt.include?("Third paragraph"), "does not contains third paragraph" - end - - should "replace separator with new-lines" do - assert !@post.excerpt.include?("---"), "does not contains separator" - end - end - - context "with page's excerpt_separator setting" do - setup do - file = "2015-01-08-post-excerpt-separator.markdown" - - @post.process(file) - @post.read_yaml(@source, file) - @post.transform - end - - should "respect given separator" do - assert @post.excerpt.include?("First paragraph"), "contains first paragraph" - assert @post.excerpt.include?("Second paragraph"), "contains second paragraph" - assert !@post.excerpt.include?("Third paragraph"), "does not contains third paragraph" - end - end - - context "with custom excerpt" do - setup do - file = "2013-04-11-custom-excerpt.markdown" - @post = setup_post(file) - do_render(@post) - end - - should "use custom excerpt" do - assert_equal("I can set a custom excerpt", @post.excerpt) - end - - should "expose custom excerpt to liquid" do - assert @post.content.include?("I can use the excerpt: I can set a custom excerpt"), "Exposes incorrect excerpt to liquid." - end - - end - - end - end - - context "when in a site" do - setup do - clear_dest - @site = fixture_site - @site.posts = [setup_post('2008-02-02-published.markdown'), - setup_post('2009-01-27-categories.markdown')] - end - - should "have next post" do - assert_equal(@site.posts.last, @site.posts.first.next) - end - - should "have previous post" do - assert_equal(@site.posts.first, @site.posts.last.previous) - end - - should "not have previous post if first" do - assert_equal(nil, @site.posts.first.previous) - end - - should "not have next post if last" do - assert_equal(nil, @site.posts.last.next) - end - end - - context "initializing posts" do - should "recognize date in yaml" do - post = setup_post("2010-01-09-date-override.markdown") - do_render(post) - assert_equal Time, post.date.class - assert_equal Time, post.to_liquid["date"].class - assert_equal "/2010/01/10/date-override.html", post.url - assert_equal "

Post with a front matter date

\n\n

10 Jan 2010

", post.output.strip - end - - should "recognize time in yaml" do - post = setup_post("2010-01-09-time-override.markdown") - do_render(post) - assert_equal Time, post.date.class - assert_equal Time, post.to_liquid["date"].class - assert_equal "/2010/01/10/time-override.html", post.url - assert_equal "

Post with a front matter time

\n\n

10 Jan 2010

", post.output.strip - end - - should "recognize time with timezone in yaml" do - post = setup_post("2010-01-09-timezone-override.markdown") - do_render(post) - assert_equal Time, post.date.class - assert_equal Time, post.to_liquid["date"].class - assert_equal "/2010/01/10/timezone-override.html", post.url - assert_equal "

Post with a front matter time with timezone

\n\n

10 Jan 2010

", post.output.strip - end - - should "to_liquid prioritizes post attributes over data" do - post = setup_post("2010-01-16-override-data.markdown") - assert_equal Array, post.tags.class - assert_equal Array, post.to_liquid["tags"].class - assert_equal Time, post.date.class - assert_equal Time, post.to_liquid["date"].class - end - - should "to_liquid should consider inheritance" do - klass = Class.new(Jekyll::Post) - assert_gets_called = false - klass.send(:define_method, :assert_gets_called) { assert_gets_called = true } - klass.const_set(:EXCERPT_ATTRIBUTES_FOR_LIQUID, Jekyll::Post::EXCERPT_ATTRIBUTES_FOR_LIQUID + ['assert_gets_called']) - post = klass.new(@site, source_dir, '', "2008-02-02-published.markdown") - do_render(post) - - assert assert_gets_called, 'assert_gets_called did not get called on post.' - end - - should "recognize category in yaml" do - post = setup_post("2009-01-27-category.markdown") - assert post.categories.include?('foo') - end - - should "recognize several categories in yaml" do - post = setup_post("2009-01-27-categories.markdown") - assert post.categories.include?('foo') - assert post.categories.include?('bar') - assert post.categories.include?('baz') - end - - should "recognize empty category in yaml" do - post = setup_post("2009-01-27-empty-category.markdown") - assert_equal [], post.categories - end - - should "recognize empty categories in yaml" do - post = setup_post("2009-01-27-empty-categories.markdown") - assert_equal [], post.categories - end - - should "recognize number category in yaml" do - post = setup_post("2013-05-10-number-category.markdown") - assert post.categories.include?('2013') - assert !post.categories.include?(2013) - end - - should "recognize mixed case category in yaml" do - post = setup_post("2014-07-05-mixed-case-category.markdown") - assert post.categories.include?('MixedCase') - assert !post.categories.include?('mixedcase') - end - - should "recognize tag in yaml" do - post = setup_post("2009-05-18-tag.markdown") - assert post.tags.include?('code') - end - - should "recognize tags in yaml" do - post = setup_post("2009-05-18-tags.markdown") - assert post.tags.include?('food') - assert post.tags.include?('cooking') - assert post.tags.include?('pizza') - end - - should "recognize empty tag in yaml" do - post = setup_post("2009-05-18-empty-tag.markdown") - assert_equal [], post.tags - end - - should "recognize empty tags in yaml" do - post = setup_post("2009-05-18-empty-tags.markdown") - assert_equal [], post.tags - end - - should "allow no yaml" do - post = setup_post("2009-06-22-no-yaml.markdown") - assert_equal "No YAML.", post.content - end - - should "allow empty yaml" do - post = setup_post("2009-06-22-empty-yaml.markdown") - assert_equal "Empty YAML.", post.content - end - - context "rendering" do - setup do - clear_dest - end - - should "render properly" do - post = setup_post("2008-10-18-foo-bar.markdown") - do_render(post) - assert_equal "<<<

Foo Bar

\n\n

Best post ever

\n >>>", post.output - end - - should "write properly" do - post = setup_post("2008-10-18-foo-bar.markdown") - do_render(post) - post.write(dest_dir) - - assert File.directory?(dest_dir) - assert File.exist?(File.join(dest_dir, '2008', '10', '18', 'foo-bar.html')) - end - - should "write properly when url has hash" do - post = setup_post("2009-03-12-hash-#1.markdown") - do_render(post) - post.write(dest_dir) - - assert File.directory?(dest_dir) - assert File.exist?(File.join(dest_dir, '2009', '03', '12', - 'hash-#1.html')) - end - - should "write properly when url has space" do - post = setup_post("2014-03-22-escape-+ %20[].markdown") - do_render(post) - post.write(dest_dir) - - assert File.directory?(dest_dir) - assert File.exist?(File.join(dest_dir, '2014', '03', '22', - 'escape-+ %20[].html')) - end - - should "write properly when category has different letter case" do - %w(2014-07-05-mixed-case-category.markdown 2014-07-05-another-mixed-case-category.markdown).each do |file| - post = setup_post(file) - do_render(post) - post.write(dest_dir) - end - - assert File.directory?(dest_dir) - assert File.exist?(File.join(dest_dir, 'mixedcase', '2014', '07', '05', - 'mixed-case-category.html')) - assert File.exist?(File.join(dest_dir, 'mixedcase', '2014', '07', '05', - 'another-mixed-case-category.html')) - end - - should "write properly without html extension" do - post = setup_post("2008-10-18-foo-bar.markdown") - post.site.permalink_style = ":title/" - do_render(post) - post.write(dest_dir) - - assert File.directory?(dest_dir) - assert File.exist?(File.join(dest_dir, 'foo-bar', 'index.html')) - end - - should "write properly with extensionless site permalink" do - post = setup_post("2008-10-18-foo-bar.markdown") - post.site.permalink_style = ":title" - do_render(post) - post.write(dest_dir) - - assert File.directory?(dest_dir) - assert File.exist?(File.join(dest_dir, 'foo-bar.html')) - end - - should "write properly with extensionless post permalink" do - post = setup_post("2015-02-20-extensionless-permalink.markdown") - do_render(post) - post.write(dest_dir) - - assert File.directory?(dest_dir) - assert File.exist?(File.join(dest_dir, 'extensionless-permalink.html')) - assert_equal "

/extensionless-permalink

\n", post.content - end - - should "insert data" do - post = setup_post("2008-11-21-complex.markdown") - do_render(post) - - assert_equal "<<<

url: /2008/11/21/complex.html\ndate: #{Time.parse("2008-11-21")}\nid: /2008/11/21/complex

\n >>>", post.output - end - - should "include templates" do - post = setup_post("2008-12-13-include.markdown") - do_render(post) - - assert_equal "<<<
\n

Tom Preston-Werner\ngithub.com/mojombo

\n\n

This is cool

\n >>>", post.output - end - - should "render date specified in front matter properly" do - post = setup_post("2010-01-09-date-override.markdown") - do_render(post) - - assert_equal "

Post with a front matter date

\n\n

10 Jan 2010

", post.output.strip - end - - should "render time specified in front matter properly" do - post = setup_post("2010-01-09-time-override.markdown") - do_render(post) - - assert_equal "

Post with a front matter time

\n\n

10 Jan 2010

", post.output.strip - end - - end - end - - should "generate categories and topics" do - post = Post.new(@site, File.join(File.dirname(__FILE__), *%w[source]), 'foo', 'bar/2008-12-12-topical-post.markdown') - assert_equal ['foo'], post.categories - end - end - - context "converter file extension settings" do - setup do - @site = fixture_site - end - - should "process .md as markdown under default configuration" do - post = setup_post '2011-04-12-md-extension.md' - conv = post.converters.first - assert conv.kind_of? Jekyll::Converters::Markdown - end - - should "process .text as identity under default configuration" do - post = setup_post '2011-04-12-text-extension.text' - conv = post.converters.first - assert conv.kind_of? Jekyll::Converters::Identity - end - - should "process .text as markdown under alternate configuration" do - @site.config['markdown_ext'] = 'markdown,mdw,mdwn,md,text' - post = setup_post '2011-04-12-text-extension.text' - conv = post.converters.first - assert conv.kind_of? Jekyll::Converters::Markdown - end - - should "process .md as markdown under alternate configuration" do - @site.config['markdown_ext'] = 'markdown,mkd,mkdn,md,text' - post = setup_post '2011-04-12-text-extension.text' - conv = post.converters.first - assert conv.kind_of? Jekyll::Converters::Markdown - end - - should "process .mkdn under text if it is not in the markdown config" do - @site.config['markdown_ext'] = 'markdown,mkd,md,text' - post = setup_post '2013-08-01-mkdn-extension.mkdn' - conv = post.converters.first - assert conv.kind_of? Jekyll::Converters::Identity - end - - should "process .Rmd under text if it is not in the markdown config" do - @site.config['markdown_ext'] = 'markdown,mkd,md,text' - post = setup_post '2014-11-24-Rmd-extension.Rmd' - assert_equal 1, post.converters.size - conv = post.converters.first - assert conv.kind_of?(Jekyll::Converters::Identity), "The converter for .Rmd should be the Identity converter." - end - - end - - context "site config with category" do - setup do - front_matter_defaults = { - 'defaults' => [{ - 'scope' => { 'path' => '' }, - 'values' => { 'category' => 'article' } - }] - } - @site = fixture_site(front_matter_defaults) - end - - should "return category if post does not specify category" do - post = setup_post("2009-01-27-no-category.markdown") - assert post.categories.include?('article'), "Expected post.categories to include 'article' but did not." - end - - should "override site category if set on post" do - post = setup_post("2009-01-27-category.markdown") - assert post.categories.include?('foo'), "Expected post.categories to include 'foo' but did not." - assert !post.categories.include?('article'), "Did not expect post.categories to include 'article' but it did." - end - end - - context "site config with categories" do - setup do - front_matter_defaults = { - 'defaults' => [{ - 'scope' => { 'path' => '' }, - 'values' => { 'categories' => ['article'] } - }] - } - @site = fixture_site(front_matter_defaults) - end - - should "return categories if post does not specify categories" do - post = setup_post("2009-01-27-no-category.markdown") - assert post.categories.include?('article'), "Expected post.categories to include 'article' but did not." - end - - should "override site categories if set on post" do - post = setup_post("2009-01-27-categories.markdown") - ['foo', 'bar', 'baz'].each do |category| - assert post.categories.include?(category), "Expected post.categories to include '#{category}' but did not." - end - assert !post.categories.include?('article'), "Did not expect post.categories to include 'article' but it did." - end - end - -end diff --git a/test/test_related_posts.rb b/test/test_related_posts.rb index 4c2c6bf9..cd114c04 100644 --- a/test/test_related_posts.rb +++ b/test/test_related_posts.rb @@ -13,7 +13,7 @@ class TestRelatedPosts < JekyllUnitTest last_post = @site.posts.last related_posts = Jekyll::RelatedPosts.new(last_post).build - last_10_recent_posts = (@site.posts.reverse - [last_post]).first(10) + last_10_recent_posts = (@site.posts.docs.reverse - [last_post]).first(10) assert_equal last_10_recent_posts, related_posts end end @@ -38,8 +38,8 @@ class TestRelatedPosts < JekyllUnitTest end should "index Jekyll::Post objects" do - @site.posts = @site.posts.first(1) - expect_any_instance_of(::ClassifierReborn::LSI).to receive(:add_item).with(kind_of(Jekyll::Post)) + @site.posts.docs = @site.posts.docs.first(1) + expect_any_instance_of(::ClassifierReborn::LSI).to receive(:add_item).with(kind_of(Jekyll::Document)) Jekyll::RelatedPosts.new(@site.posts.last).build_index end diff --git a/test/test_site.rb b/test/test_site.rb index 1ad9d337..811d79d0 100644 --- a/test/test_site.rb +++ b/test/test_site.rb @@ -190,9 +190,9 @@ class TestSite < JekyllUnitTest end should "read posts" do - @site.posts.concat(PostReader.new(@site).read('')) + @site.posts.docs.concat(PostReader.new(@site).read_posts('')) posts = Dir[source_dir('_posts', '**', '*')] - posts.delete_if { |post| File.directory?(post) && !Post.valid?(post) } + posts.delete_if { |post| File.directory?(post) && !(post =~ Document::DATE_FILENAME_MATCHER) } assert_equal posts.size - @num_invalid_posts, @site.posts.size end @@ -219,8 +219,8 @@ class TestSite < JekyllUnitTest @site.process posts = Dir[source_dir("**", "_posts", "**", "*")] - posts.delete_if { |post| File.directory?(post) && !Post.valid?(post) } - categories = %w(2013 bar baz category foo z_category MixedCase Mixedcase publish_test win).sort + posts.delete_if { |post| File.directory?(post) && !(post =~ Document::DATE_FILENAME_MATCHER) } + categories = %w(2013 bar baz category foo z_category MixedCase Mixedcase es publish_test win).sort assert_equal posts.size - @num_invalid_posts, @site.posts.size assert_equal categories, @site.categories.keys.sort @@ -498,7 +498,7 @@ class TestSite < JekyllUnitTest sleep 1 @site.process mtime3 = File.stat(dest).mtime.to_i - refute_equal mtime2, mtime3 # must be regenerated + refute_equal mtime2, mtime3 # must be regenerated sleep 1 @site.process @@ -522,7 +522,7 @@ class TestSite < JekyllUnitTest @site.process assert File.file?(dest) mtime2 = File.stat(dest).mtime.to_i - refute_equal mtime1, mtime2 # must be regenerated + refute_equal mtime1, mtime2 # must be regenerated end end diff --git a/test/test_tags.rb b/test/test_tags.rb index b08bd213..9536cf70 100644 --- a/test/test_tags.rb +++ b/test/test_tags.rb @@ -12,7 +12,7 @@ class TestTags < JekyllUnitTest site = fixture_site({"highlighter" => "rouge"}.merge(override)) if override['read_posts'] - site.posts.concat(PostReader.new(site).read('')) + site.posts.docs.concat(PostReader.new(site).read_posts('')) end info = { :filters => [Jekyll::Filters], :registers => { :site => site } } @@ -455,8 +455,8 @@ CONTENT end should "have the url to the \"nested\" post from 2008-11-21" do - assert_match %r{3\s/2008/11/21/nested/}, @result - assert_match %r{4\s/2008/11/21/nested/}, @result + assert_match %r{3\s/es/2008/11/21/nested/}, @result + assert_match %r{4\s/es/2008/11/21/nested/}, @result end end @@ -655,11 +655,9 @@ CONTENT context "include tag with variable and liquid filters" do setup do - site = fixture_site({'pygments' => true}) - post = Post.new(site, source_dir, '', "2013-12-17-include-variable-filters.markdown") - layouts = { "default" => Layout.new(site, source_dir('_layouts'), "simple.html")} - post.render(layouts, {"site" => {"posts" => []}}) - @content = post.content + site = fixture_site({'pygments' => true}).tap(&:read).tap(&:render) + post = site.posts.docs.find {|p| p.basename.eql? "2013-12-17-include-variable-filters.markdown" } + @content = post.output end should "include file as variable with liquid filters" do @@ -687,11 +685,9 @@ CONTENT context "relative include tag with variable and liquid filters" do setup do - site = fixture_site('pygments' => true) - post = Post.new(site, source_dir, '', "2014-09-02-relative-includes.markdown") - layouts = { "default" => Layout.new(site, source_dir('_layouts'), "simple.html")} - post.render(layouts, {"site" => {"posts" => []}}) - @content = post.content + site = fixture_site({'pygments' => true}).tap(&:read).tap(&:render) + post = site.posts.docs.find {|p| p.basename.eql? "2014-09-02-relative-includes.markdown" } + @content = post.output end should "include file as variable with liquid filters" do