OMG COLLECTIONS ARE RENDERING CALL THE POLICE

This commit is contained in:
Parker Moore 2014-04-02 16:31:02 -04:00
parent 7fef0302a7
commit 75f49a751e
9 changed files with 279 additions and 17 deletions

View File

@ -17,7 +17,7 @@ Feature: Collections
And I have a configuration file with: And I have a configuration file with:
| key | value | | key | value |
| collections | ['methods'] | | collections | ['methods'] |
| render | \n methods: /methods/:subdir/:title:extname | | render | ['methods'] |
When I run jekyll When I run jekyll
Then the _site directory should exist Then the _site directory should exist
And I should see "Collections: methods" in "_site/index.html" And I should see "Collections: methods" in "_site/index.html"

View File

@ -53,6 +53,7 @@ require 'jekyll/cleaner'
require 'jekyll/entry_filter' require 'jekyll/entry_filter'
require 'jekyll/layout_reader' require 'jekyll/layout_reader'
require 'jekyll/publisher' require 'jekyll/publisher'
require 'jekyll/renderer'
# extensions # extensions
require 'jekyll/plugin' require 'jekyll/plugin'

View File

@ -4,6 +4,8 @@ module Jekyll
class Site class Site
# Handles the cleanup of a site's destination before it is built. # Handles the cleanup of a site's destination before it is built.
class Cleaner class Cleaner
attr_reader :site
def initialize(site) def initialize(site)
@site = site @site = site
end end
@ -27,7 +29,7 @@ module Jekyll
# Returns a Set with the file paths # Returns a Set with the file paths
def existing_files def existing_files
files = Set.new files = Set.new
Dir.glob(File.join(@site.dest, "**", "*"), File::FNM_DOTMATCH) do |file| Dir.glob(File.join(site.dest, "**", "*"), File::FNM_DOTMATCH) do |file|
files << file unless file =~ /\/\.{1,2}$/ || file =~ keep_file_regex files << file unless file =~ /\/\.{1,2}$/ || file =~ keep_file_regex
end end
files files
@ -38,7 +40,7 @@ module Jekyll
# Returns a Set with the file paths # Returns a Set with the file paths
def new_files def new_files
files = Set.new files = Set.new
@site.each_site_file { |item| files << item.destination(@site.dest) } site.each_site_file { |item| files << item.destination(site.dest) }
files files
end end
@ -64,7 +66,7 @@ module Jekyll
# #
# Returns the regular expression # Returns the regular expression
def keep_file_regex def keep_file_regex
or_list = @site.keep_files.join("|") or_list = site.keep_files.join("|")
pattern = "\/(#{or_list.gsub(".", "\.")})" pattern = "\/(#{or_list.gsub(".", "\.")})"
Regexp.new pattern Regexp.new pattern
end end

View File

@ -22,8 +22,12 @@ module Jekyll
docs.sort! docs.sort!
end end
def relative_directory
"_#{label}"
end
def directory def directory
Jekyll.sanitized_path(site.source, "_#{label}") Jekyll.sanitized_path(site.source, relative_directory)
end end
def allowed_document?(path) def allowed_document?(path)

View File

@ -3,7 +3,7 @@ module Jekyll
include Comparable include Comparable
attr_reader :path, :site attr_reader :path, :site
attr_accessor :content, :collection attr_accessor :content, :collection, :output
# Create a new Document. # Create a new Document.
# #
@ -37,10 +37,66 @@ module Jekyll
File.extname(path) File.extname(path)
end end
def cleaned_relative_path
relative_path[0 .. -extname.length - 1].sub(collection.relative_directory, "")
end
def yaml_file? def yaml_file?
%w[.yaml .yml].include?(extname) %w[.yaml .yml].include?(extname)
end end
def sass_file?
%w[.sass .scss].include?(extname)
end
def render_with_liquid?
!(sass_file? || yaml_file?)
end
def url_template
"/:collection/:path:output_ext"
end
def url_placeholders
{
collection: collection.label,
path: cleaned_relative_path,
output_ext: Jekyll::Renderer.new(site, self).output_ext
}
end
def permalink
return nil if data.nil? || data['permalink'].nil?
data['permalink']
end
def url
@url ||= URL.new({
:template => url_template,
:placeholders => url_placeholders,
:permalink => permalink
}).to_s
end
def destination(base_directory)
path = Jekyll.sanitized_path(base_directory, url)
path = File.join(path, "index.html") if url =~ /\/$/
path
end
# Write the generated Document file to the destination directory.
#
# dest - The String path to the destination dir.
#
# Returns nothing.
def write(dest)
path = destination(dest)
FileUtils.mkdir_p(File.dirname(path))
File.open(path, 'wb') do |f|
f.write(output)
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)
# and a given param # and a given param
# #
@ -84,8 +140,10 @@ module Jekyll
# Returns a Hash representing this Document's data. # Returns a Hash representing this Document's data.
def to_liquid def to_liquid
data.merge({ data.merge({
"content" => content, "content" => content,
"path" => path "path" => path,
"relative_path" => relative_path,
"url" => url
}) })
end end

133
lib/jekyll/renderer.rb Normal file
View File

@ -0,0 +1,133 @@
module Jekyll
class Renderer
attr_reader :document, :site
def initialize(site, document)
@site = site
@document = document
end
# Determine which converters to use based on this document's
# extension.
#
# Returns an array of Converter instances.
def converters
@converters ||= site.converters.select { |c| c.matches(document.extname) }
end
# Determine the extname the outputted file should have
#
# Returns the output extname including the leading period.
def output_ext
converters.first.output_ext(document.extname)
end
######################
## DAT RENDER THO
######################
def run
payload = Utils.deep_merge_hashes({
"page" => document.to_liquid
}, site.site_payload)
info = {
filters: [Jekyll::Filters],
registers: { :site => site, :page => payload['page'] }
}
# render and transform content (this becomes the final content of the object)
payload["highlighter_prefix"] = converters.first.highlighter_prefix
payload["highlighter_suffix"] = converters.first.highlighter_suffix
output = document.content
if document.render_with_liquid?
output = render_liquid(output, payload, info)
end
place_in_layouts(
convert(output),
payload,
info
)
end
# Convert the given content using the converters which match this renderer's document.
#
# content - the raw, unconverted content
#
# Returns the converted content.
def convert(content)
output = content.dup
converters.each do |converter|
begin
output = converter.convert(output)
rescue => e
Jekyll.logger.error "Conversion error:", "#{converter.class} encountered an error converting '#{document.relative_path}'."
raise e
end
end
output
end
# Render the given content with the payload and info
#
# content -
# payload -
# info -
# path - (optional) the path to the file, for use in ex
#
# Returns the content, rendered by Liquid.
def render_liquid(content, payload, info, path = nil)
Liquid::Template.parse(content).render!(payload, info)
rescue Tags::IncludeTagError => e
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{e.path}, included in #{path || document.relative_path}"
raise e
rescue Exception => e
Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{path || document.relative_path}"
raise e
end
# Render layouts and place given content inside.
#
# content - the content to be placed in the layout
#
#
# Returns the content placed in the Liquid-rendered layouts
def place_in_layouts(content, payload, info)
output = content.dup
layout = site.layouts[document.data["layout"]]
used = Set.new([layout])
while layout
payload = Utils.deep_merge_hashes(
payload,
{
"content" => output,
"page" => document.to_liquid,
"layout" => layout.data
}
)
output = render_liquid(
layout.content,
payload,
info,
File.join(site.config['layouts'], layout.name)
)
if layout = layouts[layout.data["layout"]]
if used.include?(layout)
layout = nil # avoid recursive chain
else
used << layout
end
end
end
output
end
end
end

View File

@ -14,14 +14,13 @@ module Jekyll
def initialize(config) def initialize(config)
self.config = config.clone self.config = config.clone
%w[ %w[safe lsi highlighter baseurl exclude include future unpublished
safe lsi highlighter baseurl exclude include future unpublished show_drafts limit_posts keep_files gems].each do |opt|
show_drafts limit_posts keep_files gems collections].each do |opt|
self.send("#{opt}=", config[opt]) self.send("#{opt}=", config[opt])
end end
self.source = File.expand_path(config['source']) self.source = File.expand_path(config['source'])
self.dest = File.expand_path(config['destination']) self.dest = File.expand_path(config['destination'])
self.permalink_style = config['permalink'].to_sym self.permalink_style = config['permalink'].to_sym
self.plugin_manager = Jekyll::PluginManager.new(self) self.plugin_manager = Jekyll::PluginManager.new(self)
@ -85,6 +84,14 @@ module Jekyll
end end
end end
def collections
@collections ||= if config['collections']
Hash[config['collections'].map { |coll| [coll, Jekyll::Collection.new(self, coll)] } ]
else
Hash.new
end
end
# Read Site data from disk and load it into internal data structures. # Read Site data from disk and load it into internal data structures.
# #
# Returns nothing. # Returns nothing.
@ -187,7 +194,6 @@ module Jekyll
# Returns nothing. # Returns nothing.
def read_collections def read_collections
if collections if collections
self.collections = Hash[collections.map { |coll| [coll, Jekyll::Collection.new(self, coll)] } ]
collections.each { |_, collection| collection.read } collections.each { |_, collection| collection.read }
end end
end end
@ -207,6 +213,14 @@ module Jekyll
def render def render
relative_permalinks_deprecation_method relative_permalinks_deprecation_method
if collections
collections.each do |label, collection|
collection.docs.each do |document|
document.output = Jekyll::Renderer.new(self, document).run
end
end
end
payload = site_payload payload = site_payload
[posts, pages].flatten.each do |page_or_post| [posts, pages].flatten.each do |page_or_post|
page_or_post.render(layouts, payload) page_or_post.render(layouts, payload)
@ -369,8 +383,20 @@ module Jekyll
end end
end end
def documents
docs = Set.new
if collections
collections.each do |label, coll|
if config['render'].include?(label)
docs = docs.merge(coll.docs)
end
end
end
docs
end
def each_site_file def each_site_file
%w(posts pages static_files).each do |type| %w(posts pages static_files documents).each do |type|
send(type).each do |item| send(type).each do |item|
yield item yield item
end end

View File

@ -24,9 +24,9 @@ module Jekyll
# template. Instead, the given permalink will be # template. Instead, the given permalink will be
# used as URL. # used as URL.
def initialize(options) def initialize(options)
@template = options[:template] @template = options[:template]
@placeholders = options[:placeholders] || {} @placeholders = options[:placeholders] || {}
@permalink = options[:permalink] @permalink = options[:permalink]
if (@template || @permalink).nil? if (@template || @permalink).nil?
raise ArgumentError, "One of :template or :permalink must be supplied." raise ArgumentError, "One of :template or :permalink must be supplied."

38
script/console Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env ruby
require 'pry'
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w{ .. lib })
require 'jekyll'
TEST_DIR = File.expand_path(File.join(File.dirname(__FILE__), *%w{ .. test }))
def fixture_site(overrides = {})
Jekyll::Site.new(site_configuration(overrides))
end
def build_configs(overrides, base_hash = Jekyll::Configuration::DEFAULTS)
Jekyll::Utils.deep_merge_hashes(base_hash, overrides)
end
def site_configuration(overrides = {})
build_configs({
"source" => source_dir,
"destination" => dest_dir
}, build_configs(overrides))
end
def dest_dir(*subdirs)
test_dir('dest', *subdirs)
end
def source_dir(*subdirs)
test_dir('source', *subdirs)
end
def test_dir(*subdirs)
File.join(TEST_DIR, *subdirs)
end
module Jekyll
binding.pry
end