collections: posts as collection

This commit is contained in:
Parker Moore 2015-10-18 15:53:09 -07:00
parent d63acc140d
commit b89f943bf2
35 changed files with 496 additions and 1557 deletions

View File

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

View File

@ -30,7 +30,6 @@ require 'kramdown'
require 'colorator' require 'colorator'
SafeYAML::OPTIONS[:suppress_warnings] = true SafeYAML::OPTIONS[:suppress_warnings] = true
Liquid::Template.error_mode = :strict
module Jekyll module Jekyll
@ -53,7 +52,6 @@ module Jekyll
autoload :CollectionReader, 'jekyll/readers/collection_reader' autoload :CollectionReader, 'jekyll/readers/collection_reader'
autoload :DataReader, 'jekyll/readers/data_reader' autoload :DataReader, 'jekyll/readers/data_reader'
autoload :LayoutReader, 'jekyll/readers/layout_reader' autoload :LayoutReader, 'jekyll/readers/layout_reader'
autoload :DraftReader, 'jekyll/readers/draft_reader'
autoload :PostReader, 'jekyll/readers/post_reader' autoload :PostReader, 'jekyll/readers/post_reader'
autoload :PageReader, 'jekyll/readers/page_reader' autoload :PageReader, 'jekyll/readers/page_reader'
autoload :StaticFileReader, 'jekyll/readers/static_file_reader' autoload :StaticFileReader, 'jekyll/readers/static_file_reader'
@ -136,7 +134,7 @@ module Jekyll
# #
# Returns the new logger. # Returns the new logger.
def logger=(writer) def logger=(writer)
@logger = LogAdapter.new(writer) @logger = LogAdapter.new(writer, (ENV["JEKYLL_LOG_LEVEL"] || :info).to_sym)
end end
# Public: An array of sites # Public: An array of sites

View File

@ -1,6 +1,7 @@
module Jekyll module Jekyll
class Collection class Collection
attr_reader :site, :label, :metadata attr_reader :site, :label, :metadata
attr_writer :docs
# Create a new Collection. # Create a new Collection.
# #
@ -22,6 +23,14 @@ module Jekyll
@docs ||= [] @docs ||= []
end 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. # Fetch the static files in this collection.
# Defaults to an empty array if no static files have been read in. # 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 if Utils.has_yaml_header? full_path
doc = Jekyll::Document.new(full_path, { site: site, collection: self }) doc = Jekyll::Document.new(full_path, { site: site, collection: self })
doc.read doc.read
docs << doc if site.publisher.publish?(doc) docs << doc if site.publisher.publish?(doc) || !write?
else else
relative_dir = Jekyll.sanitized_path(relative_directory, File.dirname(file_path)).chomp("/.") 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) 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. # Returns true if the 'write' metadata is true, false otherwise.
def write? def write?
!!metadata['output'] !!metadata.fetch('output', false)
end end
# The URL template to render collection's documents at. # The URL template to render collection's documents at.

View File

@ -5,7 +5,7 @@ module Jekyll
# Default options. Overridden by values in _config.yml. # Default options. Overridden by values in _config.yml.
# Strings rather than symbols are used for compatibility with YAML. # Strings rather than symbols are used for compatibility with YAML.
DEFAULTS = { DEFAULTS = Configuration[{
# Where things are # Where things are
'source' => Dir.pwd, 'source' => Dir.pwd,
'destination' => File.join(Dir.pwd, '_site'), 'destination' => File.join(Dir.pwd, '_site'),
@ -13,7 +13,7 @@ module Jekyll
'layouts_dir' => '_layouts', 'layouts_dir' => '_layouts',
'data_dir' => '_data', 'data_dir' => '_data',
'includes_dir' => '_includes', 'includes_dir' => '_includes',
'collections' => nil, 'collections' => {},
# Handling Reading # Handling Reading
'safe' => false, 'safe' => false,
@ -80,7 +80,7 @@ module Jekyll
'coderay_css' => 'style' 'coderay_css' => 'style'
} }
} }
} }]
# Public: Turn all keys into string # Public: Turn all keys into string
# #
@ -186,7 +186,7 @@ module Jekyll
$stderr.puts "#{err}" $stderr.puts "#{err}"
end end
configuration.fix_common_issues.backwards_compatibilize configuration.fix_common_issues.backwards_compatibilize.add_default_collections
end end
# Public: Split a CSV string into an array containing its values # Public: Split a CSV string into an array containing its values
@ -275,6 +275,21 @@ module Jekyll
config config
end 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) def renamed_key(old, new, config, allowed_values = nil)
if config.key?(old) if config.key?(old)
Jekyll::Deprecator.deprecation_message "The '#{old}' configuration" + Jekyll::Deprecator.deprecation_message "The '#{old}' configuration" +
@ -283,5 +298,21 @@ module Jekyll
config[new] = config.delete(old) config[new] = config.delete(old)
end end
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
end end

View File

@ -135,21 +135,15 @@ module Jekyll
# #
# Returns the type of self. # Returns the type of self.
def type def type
if is_a?(Draft) if is_a?(Page)
:drafts
elsif is_a?(Post)
:posts
elsif is_a?(Page)
:pages :pages
end end
end end
# returns the owner symbol for hook triggering # returns the owner symbol for hook triggering
def hook_owner def hook_owner
if is_a?(Post) if is_a?(Page)
:post :pages
elsif is_a?(Page)
:page
end end
end end
@ -215,6 +209,7 @@ module Jekyll
used = Set.new([layout]) used = Set.new([layout])
while layout while layout
Jekyll.logger.debug "Rendering Layout:", path
payload = Utils.deep_merge_hashes(payload, {"content" => output, "page" => layout.data}) payload = Utils.deep_merge_hashes(payload, {"content" => output, "page" => layout.data})
self.output = render_liquid(layout.content, self.output = render_liquid(layout.content,
@ -245,6 +240,9 @@ module Jekyll
# #
# Returns nothing. # Returns nothing.
def do_layout(payload, layouts) 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 Jekyll::Hooks.trigger hook_owner, :pre_render, self, payload
info = { :filters => [Jekyll::Filters], :registers => { :site => site, :page => payload['page'] } } 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_prefix"] = converters.first.highlighter_prefix
payload["highlighter_suffix"] = converters.first.highlighter_suffix 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 self.content = transform
# output keeps track of what will finally be written # output keeps track of what will finally be written
self.output = content self.output = content
render_all_layouts(layouts, payload, info) if place_in_layout? 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 Jekyll::Hooks.trigger hook_owner, :post_render, self
end end

View File

@ -7,6 +7,8 @@ module Jekyll
attr_reader :path, :site, :extname, :output_ext, :content, :output, :collection attr_reader :path, :site, :extname, :output_ext, :content, :output, :collection
YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
DATELESS_FILENAME_MATCHER = /^(.*)(\.[^.]+)$/
DATE_FILENAME_MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
# Create a new Document. # Create a new Document.
# #
@ -21,6 +23,17 @@ module Jekyll
@output_ext = Jekyll::Renderer.new(site, self).output_ext @output_ext = Jekyll::Renderer.new(site, self).output_ext
@collection = relations[:collection] @collection = relations[:collection]
@has_yaml_header = nil @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 end
def output=(output) def output=(output)
@ -41,6 +54,27 @@ module Jekyll
@data ||= Hash.new @data ||= Hash.new
end 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. # The path to the document, relative to the site source.
# #
# Returns a String path which represents the relative path # 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. # Returns the Hash of key-value pairs for replacement in the URL.
def url_placeholders def url_placeholders
{ {
collection: collection.label, collection: collection.label,
path: cleaned_relative_path, path: cleaned_relative_path,
output_ext: output_ext, output_ext: output_ext,
name: Utils.slugify(basename_without_ext), name: Utils.slugify(basename_without_ext),
title: Utils.slugify(data['slug']) || 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 end
@ -165,6 +211,10 @@ module Jekyll
}).to_s }).to_s
end end
def [](key)
data[key]
end
# The full path to the output file. # The full path to the output file.
# #
# base_directory - the base path of the output directory # base_directory - the base path of the output directory
@ -190,7 +240,7 @@ module Jekyll
f.write(output) f.write(output)
end end
Jekyll::Hooks.trigger :document, :post_write, self trigger_hooks(:post_write)
end end
# Returns merged option hash for File.read of self.site (if exists) # Returns merged option hash for File.read of self.site (if exists)
@ -218,22 +268,23 @@ module Jekyll
def read(opts = {}) def read(opts = {})
@to_liquid = nil @to_liquid = nil
Jekyll.logger.debug "Reading:", relative_path
if yaml_file? if yaml_file?
@data = SafeYAML.load_file(path) @data = SafeYAML.load_file(path)
else else
begin begin
defaults = @site.frontmatter_defaults.all(url, collection.label.to_sym) defaults = @site.frontmatter_defaults.all(url, collection.label.to_sym)
unless defaults.empty? merge_data!(defaults) unless defaults.empty?
@data = defaults
end
self.content = File.read(path, merged_file_read_opts(opts)) self.content = File.read(path, merged_file_read_opts(opts))
if content =~ YAML_FRONT_MATTER_REGEXP if content =~ YAML_FRONT_MATTER_REGEXP
self.content = $POSTMATCH self.content = $POSTMATCH
data_file = SafeYAML.load($1) data_file = SafeYAML.load($1)
unless data_file.nil? merge_data!(data_file) if data_file
@data = Utils.deep_merge_hashes(defaults, data_file)
end
end end
post_read
rescue SyntaxError => e rescue SyntaxError => e
puts "YAML Exception reading #{path}: #{e.message}" puts "YAML Exception reading #{path}: #{e.message}"
rescue Exception => e rescue Exception => e
@ -242,19 +293,54 @@ module Jekyll
end end
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. # Create a Liquid-understandable version of this Document.
# #
# Returns a Hash representing this Document's data. # Returns a Hash representing this Document's data.
def to_liquid def to_liquid
@to_liquid ||= if data.is_a?(Hash) @to_liquid ||= if data.is_a?(Hash)
Utils.deep_merge_hashes data, { Utils.deep_merge_hashes Utils.deep_merge_hashes({
"output" => output, "output" => output,
"content" => content, "content" => content,
"relative_path" => relative_path, "relative_path" => relative_path,
"path" => relative_path, "path" => relative_path,
"url" => url, "url" => url,
"collection" => collection.label "collection" => collection.label,
} "next" => next_doc,
"previous" => previous_doc,
"id" => id,
}, data), { 'excerpt' => data['excerpt'].to_s }
else else
data data
end end
@ -272,7 +358,7 @@ module Jekyll
# #
# Returns the content of the document # Returns the content of the document
def to_s def to_s
content || '' output || content || 'NO CONTENT'
end end
# Compare this document against another document. # 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, # 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. # equal or greater than the other doc's path. See String#<=> for more details.
def <=>(anotherDocument) def <=>(anotherDocument)
path <=> anotherDocument.path cmp = data['date'] <=> anotherDocument.data['date']
if 0 == cmp
cmp = path <=> anotherDocument.path
end
cmp
end end
# Determine whether this document should be written. # Determine whether this document should be written.
@ -292,5 +382,54 @@ module Jekyll
def write? def write?
collection && collection.write? collection && collection.write?
end 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
end end

View File

@ -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

View File

@ -2,46 +2,43 @@ require 'forwardable'
module Jekyll module Jekyll
class Excerpt class Excerpt
include Convertible
extend Forwardable extend Forwardable
attr_accessor :post attr_accessor :doc
attr_accessor :content, :output, :ext attr_accessor :content, :ext
attr_writer :output
def_delegator :@post, :site, :site def_delegators :@doc, :site, :name, :ext, :relative_path, :extname,
def_delegator :@post, :name, :name :render_with_liquid?, :collection, :related_posts
def_delegator :@post, :ext, :ext
# Initialize this Post instance. # Initialize this Excerpt instance.
# #
# site - The Site. # doc - The Document.
# base - The String path to the dir containing the post file.
# name - The String filename of the post file.
# #
# Returns the new Post. # Returns the new Excerpt.
def initialize(post) def initialize(doc)
self.post = post self.doc = doc
self.content = extract_excerpt(post.content) self.content = extract_excerpt(doc.content)
end end
def to_liquid # Fetch YAML front-matter data from related doc, without layout key
post.to_liquid(post.class::EXCERPT_ATTRIBUTES_FOR_LIQUID)
end
# Fetch YAML front-matter data from related post, without layout key
# #
# Returns Hash of post data # Returns Hash of doc data
def data def data
@data ||= post.data.dup @data ||= doc.data.dup
@data.delete("layout") @data.delete("layout")
@data.delete("excerpt")
@data @data
end end
def trigger_hooks(*)
end
# 'Path' of the excerpt. # '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 def path
File.join(post.path, "#excerpt") File.join(doc.path, "#excerpt")
end end
# Check if excerpt includes a string # Check if excerpt includes a string
@ -51,28 +48,43 @@ module Jekyll
(output && output.include?(something)) || content.include?(something) (output && output.include?(something)) || content.include?(something)
end end
# The UID for this post (useful in feeds). # The UID for this doc (useful in feeds).
# e.g. /2008/11/05/my-awesome-post # e.g. /2008/11/05/my-awesome-doc
# #
# Returns the String UID. # Returns the String UID.
def id def id
File.join(post.dir, post.slug, "#excerpt") "#{doc.id}#excerpt"
end end
def to_s def to_s
output || content output || content
end 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 def inspect
"<Excerpt: #{self.id}>" "<Excerpt: #{self.id}>"
end end
def output
@output ||= Renderer.new(doc.site, self, site.site_payload).run
end
def place_in_layout?
false
end
protected protected
# Internal: Extract excerpt from the content # 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: # the first two new lines:
# #
# --- # ---
@ -86,16 +98,16 @@ module Jekyll
# [1]: http://example.com/ # [1]: http://example.com/
# #
# This is fairly good option for Markdown and Textile files. But might cause # 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 # 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 # configuration option `excerpt_separator`. For example, following is a good
# alternative for HTML posts: # alternative for HTML docs:
# #
# # file: _config.yml # # file: _config.yml
# excerpt_separator: "<!-- more -->" # excerpt_separator: "<!-- more -->"
# #
# Notice that all markdown-style link references will be appended to the # 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]. # First paragraph with [link][1].
# #
@ -104,8 +116,8 @@ module Jekyll
# Excerpts are rendered same time as content is rendered. # Excerpts are rendered same time as content is rendered.
# #
# Returns excerpt String # Returns excerpt String
def extract_excerpt(post_content) def extract_excerpt(doc_content)
head, _, tail = post_content.to_s.partition(post.excerpt_separator) head, _, tail = doc_content.to_s.partition(doc.excerpt_separator)
if tail.empty? if tail.empty?
head head

View File

@ -17,19 +17,19 @@ module Jekyll
pre_render: [], pre_render: [],
post_write: [], post_write: [],
}, },
:page => { :pages => {
post_init: [], post_init: [],
pre_render: [], pre_render: [],
post_render: [], post_render: [],
post_write: [], post_write: [],
}, },
:post => { :posts => {
post_init: [], post_init: [],
pre_render: [], pre_render: [],
post_render: [], post_render: [],
post_write: [], post_write: [],
}, },
:document => { :documents => {
pre_render: [], pre_render: [],
post_render: [], post_render: [],
post_write: [], post_write: [],
@ -57,10 +57,12 @@ module Jekyll
# register a single hook to be called later, internal API # register a single hook to be called later, internal API
def self.register_one(owner, event, priority, &block) def self.register_one(owner, event, priority, &block)
unless @registry[owner] @registry[owner] ||={
raise NotAvailable, "Hooks are only available for the following " << post_init: [],
"classes: #{@registry.keys.inspect}" pre_render: [],
end post_render: [],
post_write: [],
}
unless @registry[owner][event] unless @registry[owner][event]
raise NotAvailable, "Invalid hook. #{owner} supports only the " << raise NotAvailable, "Invalid hook. #{owner} supports only the " <<

View File

@ -36,7 +36,7 @@ module Jekyll
site.frontmatter_defaults.find(File.join(dir, name), type, key) site.frontmatter_defaults.find(File.join(dir, name), type, key)
end end
Jekyll::Hooks.trigger :page, :post_init, self Jekyll::Hooks.trigger :pages, :post_init, self
end end
# The generated directory into which the page will be placed # The generated directory into which the page will be placed

View File

@ -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
"<Post: #{id}>"
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

View File

@ -15,7 +15,7 @@ module Jekyll
end end
def hidden_in_the_future?(thing) 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 end
end end

View File

@ -22,12 +22,12 @@ module Jekyll
# Sorts posts, pages, and static files. # Sorts posts, pages, and static files.
def sort_files! def sort_files!
site.posts.sort! site.collections.values.each(&:sort!)
site.pages.sort_by!(&:name) site.pages.sort_by!(&:name)
site.static_files.sort_by!(&:relative_path) site.static_files.sort_by!(&:relative_path)
end 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 # that will become part of the site according to the rules in
# filter_entries. # filter_entries.
# #
@ -56,8 +56,8 @@ module Jekyll
# #
# Returns nothing. # Returns nothing.
def retrieve_posts(dir) def retrieve_posts(dir)
site.posts.concat(PostReader.new(site).read(dir)) site.posts.docs.concat(PostReader.new(site).read_posts(dir))
site.posts.concat(DraftReader.new(site).read(dir)) if site.show_drafts site.posts.docs.concat(PostReader.new(site).read_drafts(dir)) if site.show_drafts
end end
# Recursively traverse directories with the read_directories function. # Recursively traverse directories with the read_directories function.

View File

@ -1,5 +1,7 @@
module Jekyll module Jekyll
class CollectionReader class CollectionReader
SPECIAL_COLLECTIONS = %w{posts data}.freeze
attr_reader :site, :content attr_reader :site, :content
def initialize(site) def initialize(site)
@site = site @site = site
@ -11,7 +13,7 @@ module Jekyll
# Returns nothing. # Returns nothing.
def read def read
site.collections.each do |_, collection| site.collections.each do |_, collection|
collection.read unless collection.label.eql?('data') collection.read unless SPECIAL_COLLECTIONS.include?(collection.label)
end end
end end

View File

@ -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 <source>/<dir>/_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 <source>/<dir>/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 <dir>,
# 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

View File

@ -3,18 +3,40 @@ module Jekyll
attr_reader :site, :unfiltered_content attr_reader :site, :unfiltered_content
def initialize(site) def initialize(site)
@site = site @site = site
@unfiltered_content = Array.new
end end
# Read all the files in <source>/<dir>/_posts and create a new Post # Read all the files in <source>/<dir>/_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 <source>/<dir>/_posts and create a new Document
# object with each one. # object with each one.
# #
# dir - The String relative path of the directory to read. # dir - The String relative path of the directory to read.
# #
# Returns nothing. # Returns nothing.
def read(dir) def read_posts(dir)
@unfiltered_content = read_content(dir, '_posts') read_publishable(dir, '_posts', Document::DATE_FILENAME_MATCHER)
@unfiltered_content.select{ |post| site.publisher.publish?(post) } end
# Read all the files in <source>/<dir>/<magic_dir> 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 end
# Read all the content files from <source>/<dir>/magic_dir # Read all the content files from <source>/<dir>/magic_dir
@ -26,12 +48,15 @@ module Jekyll
# klass - The return type of the content. # klass - The return type of the content.
# #
# Returns klass type of content files # 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| @site.reader.get_entries(dir, magic_dir).map do |entry|
Post.new(site, site.source, dir, entry) if Post.valid?(entry) next unless entry =~ matcher
end.reject do |entry| path = @site.in_source_dir(File.join(dir, magic_dir, entry))
entry.nil? Document.new(path, {
end site: @site,
collection: @site.posts
})
end.reject(&:nil?)
end end
end end
end end

View File

@ -17,7 +17,7 @@ module Jekyll
# Returns a boolean. # Returns a boolean.
def regenerate?(document) def regenerate?(document)
case document case document
when Post, Page when Page
document.asset_file? || document.data['regenerate'] || document.asset_file? || document.data['regenerate'] ||
source_modified_or_dest_missing?( source_modified_or_dest_missing?(
site.in_source_dir(document.relative_path), document.destination(@site.dest) site.in_source_dir(document.relative_path), document.destination(@site.dest)

View File

@ -46,7 +46,7 @@ module Jekyll
end end
def most_recent_posts def most_recent_posts
@most_recent_posts ||= (site.posts.reverse - [post]).first(10) @most_recent_posts ||= (site.posts.docs.reverse - [post]).first(10)
end end
def display(output) def display(output)

View File

@ -23,7 +23,7 @@ module Jekyll
# #
# Returns the output extname including the leading period. # Returns the output extname including the leading period.
def output_ext def output_ext
converters.first.output_ext(document.extname) @output_ext ||= converters.first.output_ext(document.extname)
end end
###################### ######################
@ -31,11 +31,18 @@ module Jekyll
###################### ######################
def run def run
Jekyll.logger.debug "Rendering:", document.relative_path
payload = Utils.deep_merge_hashes({ payload = Utils.deep_merge_hashes({
"page" => document.to_liquid "page" => document.to_liquid
}, site_payload || site.site_payload) }, 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 = { info = {
filters: [Jekyll::Filters], filters: [Jekyll::Filters],
@ -49,13 +56,16 @@ module Jekyll
output = document.content output = document.content
if document.render_with_liquid? if document.render_with_liquid?
Jekyll.logger.debug "Rendering Liquid:", document.relative_path
output = render_liquid(output, payload, info, document.path) output = render_liquid(output, payload, info, document.path)
end end
Jekyll.logger.debug "Rendering Markup:", document.relative_path
output = convert(output) output = convert(output)
document.content = output document.content = output
if document.place_in_layout? if document.place_in_layout?
Jekyll.logger.debug "Rendering Layout:", document.relative_path
place_in_layouts( place_in_layouts(
output, output,
payload, payload,

View File

@ -4,7 +4,7 @@ require 'csv'
module Jekyll module Jekyll
class Site class Site
attr_reader :source, :dest, :config 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, :exclude, :include, :lsi, :highlighter, :permalink_style,
:time, :future, :unpublished, :safe, :plugins, :limit_posts, :time, :future, :unpublished, :safe, :plugins, :limit_posts,
:show_drafts, :keep_files, :baseurl, :data, :file_read_opts, :show_drafts, :keep_files, :baseurl, :data, :file_read_opts,
@ -74,7 +74,6 @@ module Jekyll
def reset def reset
self.time = (config['time'] ? Utils.parse_date(config['time'].to_s, "Invalid time in _config.yml.") : Time.now) self.time = (config['time'] ? Utils.parse_date(config['time'].to_s, "Invalid time in _config.yml.") : Time.now)
self.layouts = {} self.layouts = {}
self.posts = []
self.pages = [] self.pages = []
self.static_files = [] self.static_files = []
self.data = {} self.data = {}
@ -170,14 +169,14 @@ module Jekyll
collection.docs.each do |document| collection.docs.each do |document|
if regenerator.regenerate?(document) if regenerator.regenerate?(document)
document.output = Jekyll::Renderer.new(self, document, payload).run document.output = Jekyll::Renderer.new(self, document, payload).run
Jekyll::Hooks.trigger :document, :post_render, document document.trigger_hooks(:post_render)
end end
end end
end end
[posts, pages].flatten.each do |page_or_post| pages.flatten.each do |page|
if regenerator.regenerate?(page_or_post) if regenerator.regenerate?(page)
page_or_post.render(layouts, payload) page.render(layouts, payload)
end end
end end
rescue Errno::ENOENT rescue Errno::ENOENT
@ -202,6 +201,10 @@ module Jekyll
Jekyll::Hooks.trigger :site, :post_write, self Jekyll::Hooks.trigger :site, :post_write, self
end end
def posts
collections['posts'] ||= Collection.new(self, 'posts')
end
# Construct a Hash of Posts indexed by the specified Post attribute. # Construct a Hash of Posts indexed by the specified Post attribute.
# #
# post_attr - The String name of the 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 => # Build a hash map based on the specified post attribute ( post attr =>
# array of posts ) then sort each array in reverse order. # array of posts ) then sort each array in reverse order.
hash = Hash.new { |h, key| h[key] = [] } 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.values.each { |posts| posts.sort!.reverse! }
hash hash
end end
@ -391,8 +394,8 @@ module Jekyll
# Returns nothing # Returns nothing
def limit_posts! def limit_posts!
if limit_posts > 0 if limit_posts > 0
limit = posts.length < limit_posts ? posts.length : limit_posts limit = posts.docs.length < limit_posts ? posts.docs.length : limit_posts
self.posts = posts[-limit, limit] self.posts.docs = posts.docs[-limit, limit]
end end
end end

View File

@ -14,7 +14,7 @@ module Jekyll
end end
def ==(other) def ==(other)
other.name.match(@name_regex) other.basename.match(@name_regex)
end end
def deprecated_equality(other) def deprecated_equality(other)
@ -32,11 +32,11 @@ module Jekyll
# #
# Returns the post slug with the subdirectory (relative to _posts) # Returns the post slug with the subdirectory (relative to _posts)
def post_slug(other) def post_slug(other)
path = other.name.split("/")[0...-1].join("/") path = other.basename.split("/")[0...-1].join("/")
if path.nil? || path == "" if path.nil? || path == ""
other.slug other.data['slug']
else else
path + '/' + other.slug path + '/' + other.data['slug']
end end
end end
end end

View File

@ -8,6 +8,13 @@ module Jekyll
SLUGIFY_DEFAULT_REGEXP = Regexp.new('[^[:alnum:]]+').freeze SLUGIFY_DEFAULT_REGEXP = Regexp.new('[^[:alnum:]]+').freeze
SLUGIFY_PRETTY_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. # Merges a master hash with another hash, recursively.
# #
# master_hash - the "parent" hash whose values will be overridden # 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 # http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
# #
# Thanks to whoever made it. # Thanks to whoever made it.
def deep_merge_hashes(master_hash, other_hash) def deep_merge_hashes!(target, overwrite)
target = master_hash.dup overwrite.each_key do |key|
if overwrite[key].is_a? Hash and target[key].is_a? Hash
other_hash.each_key do |key| target[key] = Utils.deep_merge_hashes(target[key], overwrite[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])
next next
end end
target[key] = other_hash[key] target[key] = overwrite[key]
end end
target target

View File

@ -504,8 +504,8 @@ Jekyll::Hooks.register :post, :post_render do |post|
end end
{% endhighlight %} {% endhighlight %}
Jekyll provides hooks for <code>:site</code>, <code>:page</code>, Jekyll provides hooks for <code>:site</code>, <code>:pages</code>,
<code>:post</code>, and <code>:document</code>. In all cases, Jekyll calls your <code>:posts</code>, and <code>:documents</code>. In all cases, Jekyll calls your
hooks with the container object as the first callback parameter. But in the hooks with the container object as the first callback parameter. But in the
case of <code>:pre_render</code>, your hook will also receive a payload hash as case of <code>:pre_render</code>, your hook will also receive a payload hash as
a second parameter which allows you full control over the variables that are 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:
</tr> </tr>
<tr> <tr>
<td> <td>
<p><code>:page</code></p> <p><code>:pages</code></p>
</td> </td>
<td> <td>
<p><code>:post_init</code></p> <p><code>:post_init</code></p>
@ -580,7 +580,7 @@ The complete list of available hooks is below:
</tr> </tr>
<tr> <tr>
<td> <td>
<p><code>:page</code></p> <p><code>:pages</code></p>
</td> </td>
<td> <td>
<p><code>:pre_render</code></p> <p><code>:pre_render</code></p>
@ -591,7 +591,7 @@ The complete list of available hooks is below:
</tr> </tr>
<tr> <tr>
<td> <td>
<p><code>:page</code></p> <p><code>:pages</code></p>
</td> </td>
<td> <td>
<p><code>:post_render</code></p> <p><code>:post_render</code></p>
@ -602,7 +602,7 @@ The complete list of available hooks is below:
</tr> </tr>
<tr> <tr>
<td> <td>
<p><code>:page</code></p> <p><code>:pages</code></p>
</td> </td>
<td> <td>
<p><code>:post_write</code></p> <p><code>:post_write</code></p>
@ -613,7 +613,7 @@ The complete list of available hooks is below:
</tr> </tr>
<tr> <tr>
<td> <td>
<p><code>:post</code></p> <p><code>:posts</code></p>
</td> </td>
<td> <td>
<p><code>:post_init</code></p> <p><code>:post_init</code></p>
@ -624,7 +624,7 @@ The complete list of available hooks is below:
</tr> </tr>
<tr> <tr>
<td> <td>
<p><code>:post</code></p> <p><code>:posts</code></p>
</td> </td>
<td> <td>
<p><code>:pre_render</code></p> <p><code>:pre_render</code></p>
@ -635,7 +635,7 @@ The complete list of available hooks is below:
</tr> </tr>
<tr> <tr>
<td> <td>
<p><code>:post</code></p> <p><code>:posts</code></p>
</td> </td>
<td> <td>
<p><code>:post_render</code></p> <p><code>:post_render</code></p>
@ -646,7 +646,7 @@ The complete list of available hooks is below:
</tr> </tr>
<tr> <tr>
<td> <td>
<p><code>:post</code></p> <p><code>:posts</code></p>
</td> </td>
<td> <td>
<p><code>:post_write</code></p> <p><code>:post_write</code></p>
@ -657,7 +657,18 @@ The complete list of available hooks is below:
</tr> </tr>
<tr> <tr>
<td> <td>
<p><code>:document</code></p> <p><code>:documents</code></p>
</td>
<td>
<p><code>:post_init</code></p>
</td>
<td>
<p>Whenever a document is initialized</p>
</td>
</tr>
<tr>
<td>
<p><code>:documents</code></p>
</td> </td>
<td> <td>
<p><code>:pre_render</code></p> <p><code>:pre_render</code></p>
@ -668,7 +679,7 @@ The complete list of available hooks is below:
</tr> </tr>
<tr> <tr>
<td> <td>
<p><code>:document</code></p> <p><code>:documents</code></p>
</td> </td>
<td> <td>
<p><code>:post_render</code></p> <p><code>:post_render</code></p>
@ -679,7 +690,7 @@ The complete list of available hooks is below:
</tr> </tr>
<tr> <tr>
<td> <td>
<p><code>:document</code></p> <p><code>:documents</code></p>
</td> </td>
<td> <td>
<p><code>:post_write</code></p> <p><code>:post_write</code></p>

View File

@ -20,6 +20,8 @@ require 'rspec/mocks'
require 'jekyll' require 'jekyll'
Jekyll.logger = Logger.new(StringIO.new)
unless jruby? unless jruby?
require 'rdiscount' require 'rdiscount'
require 'redcarpet' require 'redcarpet'
@ -31,7 +33,7 @@ require 'shoulda'
include Jekyll include Jekyll
# FIXME: If we really need this we lost the game. # 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. # Report with color.
Minitest::Reporters.use! [ Minitest::Reporters.use! [
@ -66,6 +68,7 @@ class JekyllUnitTest < Minitest::Test
def build_configs(overrides, base_hash = Jekyll::Configuration::DEFAULTS) def build_configs(overrides, base_hash = Jekyll::Configuration::DEFAULTS)
Utils.deep_merge_hashes(base_hash, overrides) Utils.deep_merge_hashes(base_hash, overrides)
.fix_common_issues.backwards_compatibilize.add_default_collections
end end
def site_configuration(overrides = {}) def site_configuration(overrides = {})
@ -108,23 +111,13 @@ class JekyllUnitTest < Minitest::Test
ENV[key] = old_value ENV[key] = old_value
end end
def capture_stdout def capture_output
$old_stdout = $stdout stderr = StringIO.new
$stdout = StringIO.new Jekyll.logger = Logger.new stderr
yield yield
$stdout.rewind stderr.rewind
return $stdout.string return stderr.string.to_s
ensure
$stdout = $old_stdout
end
def capture_stderr
$old_stderr = $stderr
$stderr = StringIO.new
yield
$stderr.rewind
return $stderr.string
ensure
$stderr = $old_stderr
end end
alias_method :capture_stdout, :capture_output
alias_method :capture_stderr, :capture_output
end end

View File

@ -80,8 +80,9 @@ class TestCollections < JekyllUnitTest
@site.process @site.process
end end
should "not contain any collections" do should "contain only the defaul collections" do
assert_equal Hash.new, @site.collections refute_equal Hash.new, @site.collections
refute_nil @site.collections
end end
end end

View File

@ -1,6 +1,8 @@
require 'helper' require 'helper'
class TestConfiguration < JekyllUnitTest class TestConfiguration < JekyllUnitTest
@@defaults = Jekyll::Configuration::DEFAULTS.add_default_collections.freeze
context "#stringify_keys" do context "#stringify_keys" do
setup do setup do
@mixed_keys = Configuration[{ @mixed_keys = Configuration[{
@ -131,20 +133,20 @@ class TestConfiguration < JekyllUnitTest
should "fire warning with no _config.yml" do 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(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) allow($stderr).to receive(:puts).with("Configuration file: none".yellow)
assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({}) assert_equal @@defaults, Jekyll.configuration({})
end end
should "load configuration as hash" do should "load configuration as hash" do
allow(SafeYAML).to receive(:load_file).with(@path).and_return(Hash.new) allow(SafeYAML).to receive(:load_file).with(@path).and_return(Hash.new)
allow($stdout).to receive(:puts).with("Configuration file: #{@path}") allow($stdout).to receive(:puts).with("Configuration file: #{@path}")
assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({}) assert_equal @@defaults, Jekyll.configuration({})
end end
should "fire warning with bad config" do should "fire warning with bad config" do
allow(SafeYAML).to receive(:load_file).with(@path).and_return(Array.new) 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(("WARNING: ".rjust(20) + "Error reading configuration. Using defaults (and options).").yellow)
allow($stderr).to receive(:puts).and_return("Configuration file: (INVALID) #{@path}".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 end
should "fire warning when user-specified config file isn't there" do should "fire warning when user-specified config file isn't there" do
@ -170,27 +172,27 @@ class TestConfiguration < JekyllUnitTest
} }
end 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(SafeYAML).to receive(:load_file).with(@paths[:default]).and_return({})
allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}") allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}")
assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({}) assert_equal @@defaults, Jekyll.configuration({})
end end
should "load different config if specified" do should "load different config if specified" do
allow(SafeYAML).to receive(:load_file).with(@paths[:other]).and_return({"baseurl" => "http://wahoo.dev"}) 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]}") 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 end
should "load default config if path passed is empty" do should "load default config if path passed is empty" do
allow(SafeYAML).to receive(:load_file).with(@paths[:default]).and_return({}) allow(SafeYAML).to receive(:load_file).with(@paths[:default]).and_return({})
allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}") 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 end
should "successfully load a TOML file" do should "successfully load a TOML file" do
Jekyll.logger.log_level = :warn 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 Jekyll.logger.log_level = :info
end 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[:default]}")
allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}") allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}")
allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:toml]}") 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 end
should "load multiple config files and last config should win" do 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(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[:default]}")
allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}") 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 end
end end

View File

@ -2,6 +2,10 @@ require 'helper'
class TestDocument < JekyllUnitTest class TestDocument < JekyllUnitTest
def assert_equal_value(key, one, other)
assert_equal(one[key], other[key])
end
context "a document in a collection" do context "a document in a collection" do
setup do setup do
@site = fixture_site({ @site = fixture_site({
@ -36,10 +40,8 @@ class TestDocument < JekyllUnitTest
end end
should "know its data" do should "know its data" do
assert_equal({ assert_equal "Jekyll.configuration", @document.data["title"]
"title" => "Jekyll.configuration", assert_equal "foo.bar", @document.data["whatever"]
"whatever" => "foo.bar"
}, @document.data)
end end
context "with YAML ending in three dots" do context "with YAML ending in three dots" do
@ -53,10 +55,8 @@ class TestDocument < JekyllUnitTest
end end
should "know its data" do should "know its data" do
assert_equal({ assert_equal "YAML with Dots", @document.data["title"]
"title" => "YAML with Dots", assert_equal "foo.bar", @document.data["whatever"]
"whatever" => "foo.bar"
}, @document.data)
end end
end end
@ -88,13 +88,9 @@ class TestDocument < JekyllUnitTest
end end
should "know the frontmatter defaults" do should "know the frontmatter defaults" do
assert_equal({ assert_equal "Example slide", @document.data["title"]
"title"=>"Example slide", assert_equal "slide", @document.data["layout"]
"layout"=>"slide", assert_equal({"key"=>"myval"}, @document.data["nested"])
"nested"=> {
"key"=>"myval"
}
}, @document.data)
end end
end end
@ -117,14 +113,9 @@ class TestDocument < JekyllUnitTest
end end
should "override default values in the document frontmatter" do should "override default values in the document frontmatter" do
assert_equal({ assert_equal "Override title", @document.data["title"]
"title"=>"Override title", assert_equal "slide", @document.data["layout"]
"layout"=>"slide", assert_equal({"test1"=>"override1","test2"=>"override2"}, @document.data["nested"])
"nested"=> {
"test1"=>"override1",
"test2"=>"override2"
}
}, @document.data)
end end
end end
@ -146,13 +137,9 @@ class TestDocument < JekyllUnitTest
end end
should "know the frontmatter defaults" do should "know the frontmatter defaults" do
assert_equal({ assert_equal "Example slide", @document.data["title"]
"title"=>"Example slide", assert_equal "slide", @document.data["layout"]
"layout"=>"slide", assert_equal({"key"=>"value123"}, @document.data["nested"])
"nested"=> {
"key"=>"value123"
}
}, @document.data)
end end
end end
@ -174,10 +161,9 @@ class TestDocument < JekyllUnitTest
end end
should "not know the specified frontmatter defaults" do should "not know the specified frontmatter defaults" do
assert_equal({ assert_equal "Example slide", @document.data["title"]
"title"=>"Example slide", assert_equal "slide", @document.data["layout"]
"layout"=>"slide" assert_equal nil, @document.data["nested"]
}, @document.data)
end end
end end

View File

@ -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 <draft[\"#{attr_str}\"]>:"
end
end
end
end

View File

@ -2,33 +2,36 @@ require 'helper'
class TestExcerpt < JekyllUnitTest class TestExcerpt < JekyllUnitTest
def setup_post(file) 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 end
def do_render(post) def do_render(document)
layouts = { "default" => Layout.new(@site, source_dir('_layouts'), "simple.html")} @site.layouts = { "default" => Layout.new(@site, source_dir('_layouts'), "simple.html")}
post.render(layouts, {"site" => {"posts" => []}}) payload = {"site" => {"posts" => []}}
document.output = Jekyll::Renderer.new(@site, document, payload).run
end end
context "With extraction disabled" do context "With extraction disabled" do
setup do setup do
clear_dest 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") @post = setup_post("2013-07-22-post-excerpt-with-layout.markdown")
end end
should "not be generated" do should "not be generated" do
excerpt = @post.send(:extract_excerpt) refute @post.generate_excerpt?
assert_equal true, excerpt.empty?
end end
end end
context "An extracted excerpt" do context "An extracted excerpt" do
setup do setup do
clear_dest clear_dest
@site = Site.new(site_configuration) @site = fixture_site
@post = setup_post("2013-07-22-post-excerpt-with-layout.markdown") @post = setup_post("2013-07-22-post-excerpt-with-layout.markdown")
@excerpt = @post.send :extract_excerpt @excerpt = @post.data['excerpt']
end end
context "#include(string)" do context "#include(string)" do
@ -45,7 +48,7 @@ class TestExcerpt < JekyllUnitTest
context "#id" do context "#id" do
should "contain the UID for the post" do should "contain the UID for the post" do
assert_equal @excerpt.id, "#{@post.id}/#excerpt" assert_equal @excerpt.id, "#{@post.id}#excerpt"
end end
should "return a string" do should "return a string" do
assert_same @post.id.class, String assert_same @post.id.class, String
@ -53,8 +56,8 @@ class TestExcerpt < JekyllUnitTest
end end
context "#to_s" do context "#to_s" do
should "return its content if no output present" do should "return rendered output" do
assert_equal @excerpt.content, @excerpt.to_s assert_equal @excerpt.output, @excerpt.to_s
end end
should "return its output if output present" do 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 %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"] assert_equal "_posts/2013-07-22-post-excerpt-with-layout.markdown", @excerpt.to_liquid["path"]
end 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 end
context "#content" do context "#content" do
@ -111,11 +103,11 @@ class TestExcerpt < JekyllUnitTest
setup do setup do
@rendered_post = @post.dup @rendered_post = @post.dup
do_render(@rendered_post) do_render(@rendered_post)
@extracted_excerpt = @rendered_post.send :extracted_excerpt @extracted_excerpt = @rendered_post.data['excerpt']
end end
should "be the first paragraph of the page" do should "be the first paragraph of the page" do
assert_equal "<p>First paragraph with <a href=\"http://www.jekyllrb.com/\">link ref</a>.</p>\n\n", @extracted_excerpt.content assert_equal "<p>First paragraph with <a href=\"http://www.jekyllrb.com/\">link ref</a>.</p>\n\n", @extracted_excerpt.output
end end
should "link properly" do should "link properly" do
@ -128,9 +120,9 @@ class TestExcerpt < JekyllUnitTest
context "A whole-post excerpt" do context "A whole-post excerpt" do
setup do setup do
clear_dest clear_dest
@site = Site.new(site_configuration) @site = fixture_site
@post = setup_post("2008-02-02-published.markdown") @post = setup_post("2008-02-02-published.markdown")
@excerpt = @post.send :extract_excerpt @excerpt = @post.data['excerpt']
end end
should "be generated" do should "be generated" do

View File

@ -69,7 +69,7 @@ class TestFrontMatterDefaults < JekyllUnitTest
}] }]
})) }))
@site.process @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" } @not_affected = @site.pages.find { |page| page.relative_path == "about.html" }
end end
@ -95,7 +95,7 @@ class TestFrontMatterDefaults < JekyllUnitTest
})) }))
@site.process @site.process
@affected = @site.pages @affected = @site.pages
@not_affected = @site.posts @not_affected = @site.posts.docs
end end
should "affect only the specified type and all paths" do should "affect only the specified type and all paths" do
@ -120,7 +120,7 @@ class TestFrontMatterDefaults < JekyllUnitTest
})) }))
@site.process @site.process
@affected = @site.pages @affected = @site.pages
@not_affected = @site.posts @not_affected = @site.posts.docs
end end
should "affect only the specified type and all paths" do should "affect only the specified type and all paths" do

View File

@ -25,14 +25,15 @@ class TestNewCommand < JekyllUnitTest
should 'create a new directory' do should 'create a new directory' do
assert !File.exist?(@full_path) assert !File.exist?(@full_path)
capture_stdout { Jekyll::Commands::New.process(@args) } Jekyll::Commands::New.process(@args)
assert File.exist?(@full_path) assert File.exist?(@full_path)
end end
should 'display a success message' do should 'display a success message' do
output = capture_stdout { Jekyll::Commands::New.process(@args) } Jekyll::Commands::New.process(@args)
success_message = "New jekyll site installed in #{@full_path}. \n" output = Jekyll.logger.messages.last
assert_equal success_message, output success_message = "New jekyll site installed in #{@full_path}."
assert_includes output, success_message
end end
should 'copy the static files in site template to the new directory' do should 'copy the static files in site template to the new directory' do

View File

@ -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 <post[\"#{attr_str}\"]>:"
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 "<h1 id=\"pagetitle-\">{{ page.title }}</h1>\n\n<p>Best <strong>post</strong> ever</p>", @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 "<p>First paragraph with <a href=\"http://www.jekyllrb.com/\">link ref</a>.</p>\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: <quote>I can set a custom excerpt</quote>"), "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 "<p>Post with a front matter date</p>\n\n<p>10 Jan 2010</p>", 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 "<p>Post with a front matter time</p>\n\n<p>10 Jan 2010</p>", 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 "<p>Post with a front matter time with timezone</p>\n\n<p>10 Jan 2010</p>", 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 "<<< <h1 id=\"foo-bar\">Foo Bar</h1>\n\n<p>Best <strong>post</strong> ever</p>\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 "<p>/extensionless-permalink</p>\n", post.content
end
should "insert data" do
post = setup_post("2008-11-21-complex.markdown")
do_render(post)
assert_equal "<<< <p>url: /2008/11/21/complex.html\ndate: #{Time.parse("2008-11-21")}\nid: /2008/11/21/complex</p>\n >>>", post.output
end
should "include templates" do
post = setup_post("2008-12-13-include.markdown")
do_render(post)
assert_equal "<<< <hr />\n<p>Tom Preston-Werner\ngithub.com/mojombo</p>\n\n<p>This <em>is</em> cool</p>\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 "<p>Post with a front matter date</p>\n\n<p>10 Jan 2010</p>", 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 "<p>Post with a front matter time</p>\n\n<p>10 Jan 2010</p>", 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

View File

@ -13,7 +13,7 @@ class TestRelatedPosts < JekyllUnitTest
last_post = @site.posts.last last_post = @site.posts.last
related_posts = Jekyll::RelatedPosts.new(last_post).build 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 assert_equal last_10_recent_posts, related_posts
end end
end end
@ -38,8 +38,8 @@ class TestRelatedPosts < JekyllUnitTest
end end
should "index Jekyll::Post objects" do should "index Jekyll::Post objects" do
@site.posts = @site.posts.first(1) @site.posts.docs = @site.posts.docs.first(1)
expect_any_instance_of(::ClassifierReborn::LSI).to receive(:add_item).with(kind_of(Jekyll::Post)) expect_any_instance_of(::ClassifierReborn::LSI).to receive(:add_item).with(kind_of(Jekyll::Document))
Jekyll::RelatedPosts.new(@site.posts.last).build_index Jekyll::RelatedPosts.new(@site.posts.last).build_index
end end

View File

@ -190,9 +190,9 @@ class TestSite < JekyllUnitTest
end end
should "read posts" do 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 = 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 assert_equal posts.size - @num_invalid_posts, @site.posts.size
end end
@ -219,8 +219,8 @@ class TestSite < JekyllUnitTest
@site.process @site.process
posts = Dir[source_dir("**", "_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) }
categories = %w(2013 bar baz category foo z_category MixedCase Mixedcase publish_test win).sort 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 posts.size - @num_invalid_posts, @site.posts.size
assert_equal categories, @site.categories.keys.sort assert_equal categories, @site.categories.keys.sort

View File

@ -12,7 +12,7 @@ class TestTags < JekyllUnitTest
site = fixture_site({"highlighter" => "rouge"}.merge(override)) site = fixture_site({"highlighter" => "rouge"}.merge(override))
if override['read_posts'] if override['read_posts']
site.posts.concat(PostReader.new(site).read('')) site.posts.docs.concat(PostReader.new(site).read_posts(''))
end end
info = { :filters => [Jekyll::Filters], :registers => { :site => site } } info = { :filters => [Jekyll::Filters], :registers => { :site => site } }
@ -455,8 +455,8 @@ CONTENT
end end
should "have the url to the \"nested\" post from 2008-11-21" do 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{3\s/es/2008/11/21/nested/}, @result
assert_match %r{4\s/2008/11/21/nested/}, @result assert_match %r{4\s/es/2008/11/21/nested/}, @result
end end
end end
@ -655,11 +655,9 @@ CONTENT
context "include tag with variable and liquid filters" do context "include tag with variable and liquid filters" do
setup do setup do
site = fixture_site({'pygments' => true}) site = fixture_site({'pygments' => true}).tap(&:read).tap(&:render)
post = Post.new(site, source_dir, '', "2013-12-17-include-variable-filters.markdown") post = site.posts.docs.find {|p| p.basename.eql? "2013-12-17-include-variable-filters.markdown" }
layouts = { "default" => Layout.new(site, source_dir('_layouts'), "simple.html")} @content = post.output
post.render(layouts, {"site" => {"posts" => []}})
@content = post.content
end end
should "include file as variable with liquid filters" do should "include file as variable with liquid filters" do
@ -687,11 +685,9 @@ CONTENT
context "relative include tag with variable and liquid filters" do context "relative include tag with variable and liquid filters" do
setup do setup do
site = fixture_site('pygments' => true) site = fixture_site({'pygments' => true}).tap(&:read).tap(&:render)
post = Post.new(site, source_dir, '', "2014-09-02-relative-includes.markdown") post = site.posts.docs.find {|p| p.basename.eql? "2014-09-02-relative-includes.markdown" }
layouts = { "default" => Layout.new(site, source_dir('_layouts'), "simple.html")} @content = post.output
post.render(layouts, {"site" => {"posts" => []}})
@content = post.content
end end
should "include file as variable with liquid filters" do should "include file as variable with liquid filters" do