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

View File

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

View File

@ -1,6 +1,7 @@
module Jekyll
class Collection
attr_reader :site, :label, :metadata
attr_writer :docs
# Create a new Collection.
#
@ -22,6 +23,14 @@ module Jekyll
@docs ||= []
end
[:sort, :sort!, :each, :[], :reject, :first, :last, :size, :length].each do |method|
class_eval %Q"
def #{method}(*args, &blk)
docs.#{method}(*args, &blk)
end
"
end
# Fetch the static files in this collection.
# Defaults to an empty array if no static files have been read in.
#
@ -40,7 +49,7 @@ module Jekyll
if Utils.has_yaml_header? full_path
doc = Jekyll::Document.new(full_path, { site: site, collection: self })
doc.read
docs << doc if site.publisher.publish?(doc)
docs << doc if site.publisher.publish?(doc) || !write?
else
relative_dir = Jekyll.sanitized_path(relative_directory, File.dirname(file_path)).chomp("/.")
files << StaticFile.new(site, site.source, relative_dir, File.basename(full_path), self)
@ -163,7 +172,7 @@ module Jekyll
#
# Returns true if the 'write' metadata is true, false otherwise.
def write?
!!metadata['output']
!!metadata.fetch('output', false)
end
# The URL template to render collection's documents at.

View File

@ -5,7 +5,7 @@ module Jekyll
# Default options. Overridden by values in _config.yml.
# Strings rather than symbols are used for compatibility with YAML.
DEFAULTS = {
DEFAULTS = Configuration[{
# Where things are
'source' => Dir.pwd,
'destination' => File.join(Dir.pwd, '_site'),
@ -13,7 +13,7 @@ module Jekyll
'layouts_dir' => '_layouts',
'data_dir' => '_data',
'includes_dir' => '_includes',
'collections' => nil,
'collections' => {},
# Handling Reading
'safe' => false,
@ -80,7 +80,7 @@ module Jekyll
'coderay_css' => 'style'
}
}
}
}]
# Public: Turn all keys into string
#
@ -186,7 +186,7 @@ module Jekyll
$stderr.puts "#{err}"
end
configuration.fix_common_issues.backwards_compatibilize
configuration.fix_common_issues.backwards_compatibilize.add_default_collections
end
# Public: Split a CSV string into an array containing its values
@ -275,6 +275,21 @@ module Jekyll
config
end
def add_default_collections
config = clone
return config if config['collections'].nil?
if config['collections'].is_a?(Array)
config['collections'] = Hash[config['collections'].map{|c| [c, {}]}]
end
config['collections']['posts'] ||= {}
config['collections']['posts']['output'] = true
config['collections']['posts']['permalink'] = style_to_permalink(config['permalink'])
config
end
def renamed_key(old, new, config, allowed_values = nil)
if config.key?(old)
Jekyll::Deprecator.deprecation_message "The '#{old}' configuration" +
@ -283,5 +298,21 @@ module Jekyll
config[new] = config.delete(old)
end
end
private
def style_to_permalink(permalink_style)
case permalink_style.to_sym
when :pretty
"/:categories/:year/:month/:day/:title/"
when :none
"/:categories/:title.html"
when :date
"/:categories/:year/:month/:day/:title.html"
when :ordinal
"/:categories/:year/:y_day/:title.html"
else
permalink_style.to_s
end
end
end
end

View File

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

View File

@ -7,6 +7,8 @@ module Jekyll
attr_reader :path, :site, :extname, :output_ext, :content, :output, :collection
YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
DATELESS_FILENAME_MATCHER = /^(.*)(\.[^.]+)$/
DATE_FILENAME_MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
# Create a new Document.
#
@ -21,6 +23,17 @@ module Jekyll
@output_ext = Jekyll::Renderer.new(site, self).output_ext
@collection = relations[:collection]
@has_yaml_header = nil
subdirs = relative_path.split(File::SEPARATOR).reject do |c|
c.empty? || c.eql?(collection.relative_directory) || c.eql?("_drafts") || c.eql?(basename)
end
merge_data!({'categories' => subdirs })
data.default_proc = proc do |hash, key|
site.frontmatter_defaults.find(relative_path, collection.label, key)
end
trigger_hooks(:post_init)
end
def output=(output)
@ -41,6 +54,27 @@ module Jekyll
@data ||= Hash.new
end
# Merge some data in with this document's data.
#
# Returns the merged data.
def merge_data!(other)
if other.key?('categories') && !other['categories'].nil?
if other['categories'].is_a?(String)
other['categories'] = other['categories'].split(" ").map(&:strip)
end
other['categories'] = (data['categories'] || []) | other['categories']
end
Utils.deep_merge_hashes!(data, other)
if data.key?('date') && !data['date'].is_a?(Time)
data['date'] = Utils.parse_date(data['date'].to_s, "Document '#{relative_path}' does not have a valid date in the YAML front matter.")
end
data
end
def date
data['date'] ||= site.time
end
# The path to the document, relative to the site source.
#
# Returns a String path which represents the relative path
@ -138,11 +172,23 @@ module Jekyll
# Returns the Hash of key-value pairs for replacement in the URL.
def url_placeholders
{
collection: collection.label,
path: cleaned_relative_path,
output_ext: output_ext,
name: Utils.slugify(basename_without_ext),
title: Utils.slugify(data['slug']) || Utils.slugify(basename_without_ext)
collection: collection.label,
path: cleaned_relative_path,
output_ext: output_ext,
name: Utils.slugify(basename_without_ext),
title: Utils.slugify(data['slug']) || Utils.slugify(basename_without_ext),
year: date.strftime("%Y"),
month: date.strftime("%m"),
day: date.strftime("%d"),
hour: date.strftime("%H"),
minute: date.strftime("%M"),
second: date.strftime("%S"),
i_day: date.strftime("%-d"),
i_month: date.strftime("%-m"),
categories: (data['categories'] || []).map { |c| c.to_s.downcase }.uniq.join('/'),
short_month: date.strftime("%b"),
short_year: date.strftime("%y"),
y_day: date.strftime("%j"),
}
end
@ -165,6 +211,10 @@ module Jekyll
}).to_s
end
def [](key)
data[key]
end
# The full path to the output file.
#
# base_directory - the base path of the output directory
@ -190,7 +240,7 @@ module Jekyll
f.write(output)
end
Jekyll::Hooks.trigger :document, :post_write, self
trigger_hooks(:post_write)
end
# Returns merged option hash for File.read of self.site (if exists)
@ -218,22 +268,23 @@ module Jekyll
def read(opts = {})
@to_liquid = nil
Jekyll.logger.debug "Reading:", relative_path
if yaml_file?
@data = SafeYAML.load_file(path)
else
begin
defaults = @site.frontmatter_defaults.all(url, collection.label.to_sym)
unless defaults.empty?
@data = defaults
end
merge_data!(defaults) unless defaults.empty?
self.content = File.read(path, merged_file_read_opts(opts))
if content =~ YAML_FRONT_MATTER_REGEXP
self.content = $POSTMATCH
data_file = SafeYAML.load($1)
unless data_file.nil?
@data = Utils.deep_merge_hashes(defaults, data_file)
end
merge_data!(data_file) if data_file
end
post_read
rescue SyntaxError => e
puts "YAML Exception reading #{path}: #{e.message}"
rescue Exception => e
@ -242,19 +293,54 @@ module Jekyll
end
end
def post_read
if DATE_FILENAME_MATCHER =~ relative_path
m, cats, date, slug, ext = *relative_path.match(DATE_FILENAME_MATCHER)
merge_data!({
"slug" => slug,
"ext" => ext
})
merge_data!({"date" => date}) if data['date'].nil? || data['date'].to_i == site.time.to_i
data['title'] ||= slug.split('-').select {|w| w.capitalize! || w }.join(' ')
end
populate_categories
populate_tags
if generate_excerpt?
data['excerpt'] = Jekyll::Excerpt.new(self)
end
end
def populate_categories
merge_data!({
"categories" => (
Array(data['categories']) + Utils.pluralized_array_from_hash(data, 'category', 'categories')
).map { |c| c.to_s }.flatten.uniq
})
end
def populate_tags
merge_data!({
"tags" => Utils.pluralized_array_from_hash(data, "tag", "tags").flatten
})
end
# Create a Liquid-understandable version of this Document.
#
# Returns a Hash representing this Document's data.
def to_liquid
@to_liquid ||= if data.is_a?(Hash)
Utils.deep_merge_hashes data, {
Utils.deep_merge_hashes Utils.deep_merge_hashes({
"output" => output,
"content" => content,
"relative_path" => relative_path,
"path" => relative_path,
"url" => url,
"collection" => collection.label
}
"collection" => collection.label,
"next" => next_doc,
"previous" => previous_doc,
"id" => id,
}, data), { 'excerpt' => data['excerpt'].to_s }
else
data
end
@ -272,7 +358,7 @@ module Jekyll
#
# Returns the content of the document
def to_s
content || ''
output || content || 'NO CONTENT'
end
# Compare this document against another document.
@ -281,7 +367,11 @@ module Jekyll
# Returns -1, 0, +1 or nil depending on whether this doc's path is less than,
# equal or greater than the other doc's path. See String#<=> for more details.
def <=>(anotherDocument)
path <=> anotherDocument.path
cmp = data['date'] <=> anotherDocument.data['date']
if 0 == cmp
cmp = path <=> anotherDocument.path
end
cmp
end
# Determine whether this document should be written.
@ -292,5 +382,54 @@ module Jekyll
def write?
collection && collection.write?
end
# The Document excerpt_separator, from the YAML Front-Matter or site
# default excerpt_separator value
#
# Returns the document excerpt_separator
def excerpt_separator
(data['excerpt_separator'] || site.config['excerpt_separator']).to_s
end
# Whether to generate an excerpt
#
# Returns true if the excerpt separator is configured.
def generate_excerpt?
!excerpt_separator.empty?
end
def next_doc
pos = collection.docs.index {|post| post.equal?(self) }
if pos && pos < collection.docs.length - 1
collection.docs[pos + 1]
else
nil
end
end
def previous_doc
pos = collection.docs.index {|post| post.equal?(self) }
if pos && pos > 0
collection.docs[pos - 1]
else
nil
end
end
def trigger_hooks(hook_name, *args)
Jekyll::Hooks.trigger collection.label.to_sym, hook_name, self, *args if collection
Jekyll::Hooks.trigger :documents, hook_name, self, *args
end
def id
@id ||= File.join(File.dirname(url), (data['slug'] || basename_without_ext).to_s)
end
# Calculate related posts.
#
# Returns an Array of related Posts.
def related_posts
Jekyll::RelatedPosts.new(self).build
end
end
end

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

View File

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

View File

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

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

View File

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

View File

@ -1,5 +1,7 @@
module Jekyll
class CollectionReader
SPECIAL_COLLECTIONS = %w{posts data}.freeze
attr_reader :site, :content
def initialize(site)
@site = site
@ -11,9 +13,9 @@ module Jekyll
# Returns nothing.
def read
site.collections.each do |_, collection|
collection.read unless collection.label.eql?('data')
collection.read unless SPECIAL_COLLECTIONS.include?(collection.label)
end
end
end
end
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
def initialize(site)
@site = site
@unfiltered_content = Array.new
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.
#
# dir - The String relative path of the directory to read.
#
# Returns nothing.
def read(dir)
@unfiltered_content = read_content(dir, '_posts')
@unfiltered_content.select{ |post| site.publisher.publish?(post) }
def read_posts(dir)
read_publishable(dir, '_posts', Document::DATE_FILENAME_MATCHER)
end
# Read all the files in <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
# Read all the content files from <source>/<dir>/magic_dir
@ -26,12 +48,15 @@ module Jekyll
# klass - The return type of the content.
#
# Returns klass type of content files
def read_content(dir, magic_dir)
def read_content(dir, magic_dir, matcher)
@site.reader.get_entries(dir, magic_dir).map do |entry|
Post.new(site, site.source, dir, entry) if Post.valid?(entry)
end.reject do |entry|
entry.nil?
end
next unless entry =~ matcher
path = @site.in_source_dir(File.join(dir, magic_dir, entry))
Document.new(path, {
site: @site,
collection: @site.posts
})
end.reject(&:nil?)
end
end
end

View File

@ -17,8 +17,8 @@ module Jekyll
# Returns a boolean.
def regenerate?(document)
case document
when Post, Page
document.asset_file? || document.data['regenerate'] ||
when Page
document.asset_file? || document.data['regenerate'] ||
source_modified_or_dest_missing?(
site.in_source_dir(document.relative_path), document.destination(@site.dest)
)
@ -87,7 +87,7 @@ module Jekyll
return true if disabled?
# objects that don't have a path are always regenerated
return true if path.nil?
return true if path.nil?
# Check for path in cache
if cache.has_key? path

View File

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

View File

@ -23,7 +23,7 @@ module Jekyll
#
# Returns the output extname including the leading period.
def output_ext
converters.first.output_ext(document.extname)
@output_ext ||= converters.first.output_ext(document.extname)
end
######################
@ -31,11 +31,18 @@ module Jekyll
######################
def run
Jekyll.logger.debug "Rendering:", document.relative_path
payload = Utils.deep_merge_hashes({
"page" => document.to_liquid
}, site_payload || site.site_payload)
Jekyll::Hooks.trigger :document, :pre_render, document, payload
if document.collection.label == 'posts' && document.is_a?(Document)
payload['site']['related_posts'] = document.related_posts
end
Jekyll.logger.debug "Pre-Render Hooks:", document.relative_path
document.trigger_hooks(:pre_render, payload)
info = {
filters: [Jekyll::Filters],
@ -49,13 +56,16 @@ module Jekyll
output = document.content
if document.render_with_liquid?
Jekyll.logger.debug "Rendering Liquid:", document.relative_path
output = render_liquid(output, payload, info, document.path)
end
Jekyll.logger.debug "Rendering Markup:", document.relative_path
output = convert(output)
document.content = output
if document.place_in_layout?
Jekyll.logger.debug "Rendering Layout:", document.relative_path
place_in_layouts(
output,
payload,

View File

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

View File

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

View File

@ -8,6 +8,13 @@ module Jekyll
SLUGIFY_DEFAULT_REGEXP = Regexp.new('[^[:alnum:]]+').freeze
SLUGIFY_PRETTY_REGEXP = Regexp.new("[^[:alnum:]._~!$&'()+,;=@]+").freeze
# Non-destructive version of deep_merge_hashes! See that method.
#
# Returns the merged hashes.
def deep_merge_hashes(master_hash, other_hash)
deep_merge_hashes!(master_hash.dup, other_hash)
end
# Merges a master hash with another hash, recursively.
#
# master_hash - the "parent" hash whose values will be overridden
@ -17,16 +24,14 @@ module Jekyll
# http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
#
# Thanks to whoever made it.
def deep_merge_hashes(master_hash, other_hash)
target = master_hash.dup
other_hash.each_key do |key|
if other_hash[key].is_a? Hash and target[key].is_a? Hash
target[key] = Utils.deep_merge_hashes(target[key], other_hash[key])
def deep_merge_hashes!(target, overwrite)
overwrite.each_key do |key|
if overwrite[key].is_a? Hash and target[key].is_a? Hash
target[key] = Utils.deep_merge_hashes(target[key], overwrite[key])
next
end
target[key] = other_hash[key]
target[key] = overwrite[key]
end
target

View File

@ -504,8 +504,8 @@ Jekyll::Hooks.register :post, :post_render do |post|
end
{% endhighlight %}
Jekyll provides hooks for <code>:site</code>, <code>:page</code>,
<code>:post</code>, and <code>:document</code>. In all cases, Jekyll calls your
Jekyll provides hooks for <code>:site</code>, <code>:pages</code>,
<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
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
@ -569,7 +569,7 @@ The complete list of available hooks is below:
</tr>
<tr>
<td>
<p><code>:page</code></p>
<p><code>:pages</code></p>
</td>
<td>
<p><code>:post_init</code></p>
@ -580,7 +580,7 @@ The complete list of available hooks is below:
</tr>
<tr>
<td>
<p><code>:page</code></p>
<p><code>:pages</code></p>
</td>
<td>
<p><code>:pre_render</code></p>
@ -591,7 +591,7 @@ The complete list of available hooks is below:
</tr>
<tr>
<td>
<p><code>:page</code></p>
<p><code>:pages</code></p>
</td>
<td>
<p><code>:post_render</code></p>
@ -602,7 +602,7 @@ The complete list of available hooks is below:
</tr>
<tr>
<td>
<p><code>:page</code></p>
<p><code>:pages</code></p>
</td>
<td>
<p><code>:post_write</code></p>
@ -613,7 +613,7 @@ The complete list of available hooks is below:
</tr>
<tr>
<td>
<p><code>:post</code></p>
<p><code>:posts</code></p>
</td>
<td>
<p><code>:post_init</code></p>
@ -624,7 +624,7 @@ The complete list of available hooks is below:
</tr>
<tr>
<td>
<p><code>:post</code></p>
<p><code>:posts</code></p>
</td>
<td>
<p><code>:pre_render</code></p>
@ -635,7 +635,7 @@ The complete list of available hooks is below:
</tr>
<tr>
<td>
<p><code>:post</code></p>
<p><code>:posts</code></p>
</td>
<td>
<p><code>:post_render</code></p>
@ -646,7 +646,7 @@ The complete list of available hooks is below:
</tr>
<tr>
<td>
<p><code>:post</code></p>
<p><code>:posts</code></p>
</td>
<td>
<p><code>:post_write</code></p>
@ -657,7 +657,18 @@ The complete list of available hooks is below:
</tr>
<tr>
<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>
<p><code>:pre_render</code></p>
@ -668,7 +679,7 @@ The complete list of available hooks is below:
</tr>
<tr>
<td>
<p><code>:document</code></p>
<p><code>:documents</code></p>
</td>
<td>
<p><code>:post_render</code></p>
@ -679,7 +690,7 @@ The complete list of available hooks is below:
</tr>
<tr>
<td>
<p><code>:document</code></p>
<p><code>:documents</code></p>
</td>
<td>
<p><code>:post_write</code></p>
@ -854,7 +865,7 @@ LESS.js files during generation.
- [Jekyll CO₂](https://github.com/wdenton/jekyll-co2): Generates HTML showing the monthly change in atmospheric CO₂ at the Mauna Loa observatory in Hawaii.
- [remote-include](http://www.northfieldx.co.uk/remote-include/): Includes files using remote URLs
- [jekyll-minifier](https://github.com/digitalsparky/jekyll-minifier): Minifies HTML, XML, CSS, and Javascript both inline and as separate files utilising yui-compressor and htmlcompressor.
- [Jekyll views router](https://bitbucket.org/nyufac/jekyll-views-router): Simple router between generator plugins and templates.
- [Jekyll views router](https://bitbucket.org/nyufac/jekyll-views-router): Simple router between generator plugins and templates.
#### Editors

View File

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

View File

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

View File

@ -1,6 +1,8 @@
require 'helper'
class TestConfiguration < JekyllUnitTest
@@defaults = Jekyll::Configuration::DEFAULTS.add_default_collections.freeze
context "#stringify_keys" do
setup do
@mixed_keys = Configuration[{
@ -131,20 +133,20 @@ class TestConfiguration < JekyllUnitTest
should "fire warning with no _config.yml" do
allow(SafeYAML).to receive(:load_file).with(@path) { raise SystemCallError, "No such file or directory - #{@path}" }
allow($stderr).to receive(:puts).with("Configuration file: none".yellow)
assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({})
assert_equal @@defaults, Jekyll.configuration({})
end
should "load configuration as hash" do
allow(SafeYAML).to receive(:load_file).with(@path).and_return(Hash.new)
allow($stdout).to receive(:puts).with("Configuration file: #{@path}")
assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({})
assert_equal @@defaults, Jekyll.configuration({})
end
should "fire warning with bad config" do
allow(SafeYAML).to receive(:load_file).with(@path).and_return(Array.new)
allow($stderr).to receive(:puts).and_return(("WARNING: ".rjust(20) + "Error reading configuration. Using defaults (and options).").yellow)
allow($stderr).to receive(:puts).and_return("Configuration file: (INVALID) #{@path}".yellow)
assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({})
assert_equal @@defaults, Jekyll.configuration({})
end
should "fire warning when user-specified config file isn't there" do
@ -170,27 +172,27 @@ class TestConfiguration < JekyllUnitTest
}
end
should "load default config if no config_file is set" do
should "load default plus posts config if no config_file is set" do
allow(SafeYAML).to receive(:load_file).with(@paths[:default]).and_return({})
allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}")
assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({})
assert_equal @@defaults, Jekyll.configuration({})
end
should "load different config if specified" do
allow(SafeYAML).to receive(:load_file).with(@paths[:other]).and_return({"baseurl" => "http://wahoo.dev"})
allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}")
assert_equal Utils.deep_merge_hashes(Jekyll::Configuration::DEFAULTS, { "baseurl" => "http://wahoo.dev" }), Jekyll.configuration({ "config" => @paths[:other] })
assert_equal Utils.deep_merge_hashes(@@defaults, { "baseurl" => "http://wahoo.dev" }), Jekyll.configuration({ "config" => @paths[:other] })
end
should "load default config if path passed is empty" do
allow(SafeYAML).to receive(:load_file).with(@paths[:default]).and_return({})
allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}")
assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({ "config" => @paths[:empty] })
assert_equal @@defaults, Jekyll.configuration({ "config" => @paths[:empty] })
end
should "successfully load a TOML file" do
Jekyll.logger.log_level = :warn
assert_equal Jekyll::Configuration::DEFAULTS.merge({ "baseurl" => "/you-beautiful-blog-you", "title" => "My magnificent site, wut" }), Jekyll.configuration({ "config" => [@paths[:toml]] })
assert_equal @@defaults.clone.merge({ "baseurl" => "/you-beautiful-blog-you", "title" => "My magnificent site, wut" }), Jekyll.configuration({ "config" => [@paths[:toml]] })
Jekyll.logger.log_level = :info
end
@ -203,7 +205,7 @@ class TestConfiguration < JekyllUnitTest
allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}")
allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}")
allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:toml]}")
assert_equal Jekyll::Configuration::DEFAULTS, Jekyll.configuration({ "config" => [@paths[:default], @paths[:other], @paths[:toml]] })
assert_equal @@defaults, Jekyll.configuration({ "config" => [@paths[:default], @paths[:other], @paths[:toml]] })
end
should "load multiple config files and last config should win" do
@ -211,7 +213,7 @@ class TestConfiguration < JekyllUnitTest
allow(SafeYAML).to receive(:load_file).with(@paths[:other]).and_return({"baseurl" => "http://wahoo.dev"})
allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}")
allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:other]}")
assert_equal Utils.deep_merge_hashes(Jekyll::Configuration::DEFAULTS, { "baseurl" => "http://wahoo.dev" }), Jekyll.configuration({ "config" => [@paths[:default], @paths[:other]] })
assert_equal Utils.deep_merge_hashes(@@defaults, { "baseurl" => "http://wahoo.dev" }), Jekyll.configuration({ "config" => [@paths[:default], @paths[:other]] })
end
end
end

View File

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

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
def setup_post(file)
Post.new(@site, source_dir, '', file)
Document.new(@site.in_source_dir(File.join('_posts', file)), {
site: @site,
collection: @site.posts
}).tap(&:read)
end
def do_render(post)
layouts = { "default" => Layout.new(@site, source_dir('_layouts'), "simple.html")}
post.render(layouts, {"site" => {"posts" => []}})
def do_render(document)
@site.layouts = { "default" => Layout.new(@site, source_dir('_layouts'), "simple.html")}
payload = {"site" => {"posts" => []}}
document.output = Jekyll::Renderer.new(@site, document, payload).run
end
context "With extraction disabled" do
setup do
clear_dest
@site = Site.new(site_configuration('excerpt_separator' => ''))
@site = fixture_site('excerpt_separator' => '')
@post = setup_post("2013-07-22-post-excerpt-with-layout.markdown")
end
should "not be generated" do
excerpt = @post.send(:extract_excerpt)
assert_equal true, excerpt.empty?
refute @post.generate_excerpt?
end
end
context "An extracted excerpt" do
setup do
clear_dest
@site = Site.new(site_configuration)
@site = fixture_site
@post = setup_post("2013-07-22-post-excerpt-with-layout.markdown")
@excerpt = @post.send :extract_excerpt
@excerpt = @post.data['excerpt']
end
context "#include(string)" do
@ -45,7 +48,7 @@ class TestExcerpt < JekyllUnitTest
context "#id" do
should "contain the UID for the post" do
assert_equal @excerpt.id, "#{@post.id}/#excerpt"
assert_equal @excerpt.id, "#{@post.id}#excerpt"
end
should "return a string" do
assert_same @post.id.class, String
@ -53,8 +56,8 @@ class TestExcerpt < JekyllUnitTest
end
context "#to_s" do
should "return its content if no output present" do
assert_equal @excerpt.content, @excerpt.to_s
should "return rendered output" do
assert_equal @excerpt.output, @excerpt.to_s
end
should "return its output if output present" do
@ -82,17 +85,6 @@ class TestExcerpt < JekyllUnitTest
assert_equal %w[first second third jekyllrb.com], @excerpt.to_liquid["tags"]
assert_equal "_posts/2013-07-22-post-excerpt-with-layout.markdown", @excerpt.to_liquid["path"]
end
should "consider inheritance" do
klass = Class.new(Jekyll::Post)
assert_gets_called = false
klass.send(:define_method, :assert_gets_called) { assert_gets_called = true }
klass.const_set(:EXCERPT_ATTRIBUTES_FOR_LIQUID, Jekyll::Post::EXCERPT_ATTRIBUTES_FOR_LIQUID + ['assert_gets_called'])
post = klass.new(@site, source_dir, '', "2008-02-02-published.markdown")
Jekyll::Excerpt.new(post).to_liquid
assert assert_gets_called, 'assert_gets_called did not get called on post.'
end
end
context "#content" do
@ -111,11 +103,11 @@ class TestExcerpt < JekyllUnitTest
setup do
@rendered_post = @post.dup
do_render(@rendered_post)
@extracted_excerpt = @rendered_post.send :extracted_excerpt
@extracted_excerpt = @rendered_post.data['excerpt']
end
should "be the first paragraph of the page" do
assert_equal "<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
should "link properly" do
@ -128,9 +120,9 @@ class TestExcerpt < JekyllUnitTest
context "A whole-post excerpt" do
setup do
clear_dest
@site = Site.new(site_configuration)
@site = fixture_site
@post = setup_post("2008-02-02-published.markdown")
@excerpt = @post.send :extract_excerpt
@excerpt = @post.data['excerpt']
end
should "be generated" do

View File

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

View File

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

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

View File

@ -190,9 +190,9 @@ class TestSite < JekyllUnitTest
end
should "read posts" do
@site.posts.concat(PostReader.new(@site).read(''))
@site.posts.docs.concat(PostReader.new(@site).read_posts(''))
posts = Dir[source_dir('_posts', '**', '*')]
posts.delete_if { |post| File.directory?(post) && !Post.valid?(post) }
posts.delete_if { |post| File.directory?(post) && !(post =~ Document::DATE_FILENAME_MATCHER) }
assert_equal posts.size - @num_invalid_posts, @site.posts.size
end
@ -219,8 +219,8 @@ class TestSite < JekyllUnitTest
@site.process
posts = Dir[source_dir("**", "_posts", "**", "*")]
posts.delete_if { |post| File.directory?(post) && !Post.valid?(post) }
categories = %w(2013 bar baz category foo z_category MixedCase Mixedcase publish_test win).sort
posts.delete_if { |post| File.directory?(post) && !(post =~ Document::DATE_FILENAME_MATCHER) }
categories = %w(2013 bar baz category foo z_category MixedCase Mixedcase es publish_test win).sort
assert_equal posts.size - @num_invalid_posts, @site.posts.size
assert_equal categories, @site.categories.keys.sort
@ -498,7 +498,7 @@ class TestSite < JekyllUnitTest
sleep 1
@site.process
mtime3 = File.stat(dest).mtime.to_i
refute_equal mtime2, mtime3 # must be regenerated
refute_equal mtime2, mtime3 # must be regenerated
sleep 1
@site.process
@ -522,7 +522,7 @@ class TestSite < JekyllUnitTest
@site.process
assert File.file?(dest)
mtime2 = File.stat(dest).mtime.to_i
refute_equal mtime1, mtime2 # must be regenerated
refute_equal mtime1, mtime2 # must be regenerated
end
end

View File

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