From 82c3ee365f5a8b948ce2ea853d75a954fa709939 Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Mon, 21 Dec 2015 22:45:26 -0500 Subject: [PATCH 01/15] Initial work on using Liquid::Drops instead of Hashes. The properties of Liquid::Drops are only evaluated when they're asked for and therefore save computation time. This prevents a lot of GC time cleaning up objects that are not needed, because they're not created unless requested. Additionally, this saves time for actual computation of those values because they can be computed only if needed. It's funny how much it helps when you only do what is needed. Far less overhead. --- lib/jekyll.rb | 1 + lib/jekyll/collection.rb | 9 +--- lib/jekyll/convertible.rb | 15 +++---- lib/jekyll/document.rb | 42 +------------------ lib/jekyll/drops/collection_drop.rb | 22 ++++++++++ lib/jekyll/drops/document_drop.rb | 50 +++++++++++++++++++++++ lib/jekyll/drops/immutable_drop.rb | 29 +++++++++++++ lib/jekyll/drops/jekyll_drop.rb | 21 ++++++++++ lib/jekyll/drops/mutable_drop.rb | 28 +++++++++++++ lib/jekyll/drops/site_drop.rb | 38 +++++++++++++++++ lib/jekyll/drops/unified_payload_drop.rb | 24 +++++++++++ lib/jekyll/drops/url_drop.rb | 52 ++++++++++++++++++++++++ lib/jekyll/page.rb | 8 ++-- lib/jekyll/renderer.rb | 31 ++++++-------- lib/jekyll/site.rb | 20 +-------- lib/jekyll/url.rb | 14 +++++++ 16 files changed, 303 insertions(+), 101 deletions(-) create mode 100644 lib/jekyll/drops/collection_drop.rb create mode 100644 lib/jekyll/drops/document_drop.rb create mode 100644 lib/jekyll/drops/immutable_drop.rb create mode 100644 lib/jekyll/drops/jekyll_drop.rb create mode 100644 lib/jekyll/drops/mutable_drop.rb create mode 100644 lib/jekyll/drops/site_drop.rb create mode 100644 lib/jekyll/drops/unified_payload_drop.rb create mode 100644 lib/jekyll/drops/url_drop.rb diff --git a/lib/jekyll.rb b/lib/jekyll.rb index 3e8e639d..a90a5a0b 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -172,6 +172,7 @@ end require_all 'jekyll/commands' require_all 'jekyll/converters' require_all 'jekyll/converters/markdown' +require_all 'jekyll/drops' require_all 'jekyll/generators' require_all 'jekyll/tags' diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb index c4a1f456..f74ccf6f 100644 --- a/lib/jekyll/collection.rb +++ b/lib/jekyll/collection.rb @@ -170,14 +170,7 @@ module Jekyll # # Returns a representation of this collection for use in Liquid. def to_liquid - metadata.merge({ - "label" => label, - "docs" => docs, - "files" => files, - "directory" => directory, - "output" => write?, - "relative_directory" => relative_directory - }) + Drops::CollectionDrop.new self end # Whether the collection's documents ought to be written as individual diff --git a/lib/jekyll/convertible.rb b/lib/jekyll/convertible.rb index 016ff245..db34307a 100644 --- a/lib/jekyll/convertible.rb +++ b/lib/jekyll/convertible.rb @@ -210,13 +210,8 @@ module Jekyll while layout Jekyll.logger.debug "Rendering Layout:", path - payload = Utils.deep_merge_hashes( - payload, - { - "content" => output, - "layout" => layout.data - } - ) + payload.content = output + payload.layout = layout.data self.output = render_liquid(layout.content, payload, @@ -250,11 +245,11 @@ module Jekyll 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'] } } + 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 + payload.highlighter_prefix = converters.first.highlighter_prefix + payload.highlighter_suffix = converters.first.highlighter_suffix if render_with_liquid? Jekyll.logger.debug "Rendering Liquid:", self.relative_path diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb index 528fed68..622f1180 100644 --- a/lib/jekyll/document.rb +++ b/lib/jekyll/document.rb @@ -38,12 +38,10 @@ module Jekyll end def output=(output) - @to_liquid = nil @output = output end def content=(content) - @to_liquid = nil @content = content end @@ -181,27 +179,7 @@ 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'], mode: "pretty", cased: true) || Utils - .slugify(basename_without_ext, mode: "pretty", cased: true), - slug: 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"), - } + @url_placeholders ||= Drops::UrlDrop.new(self) end # The permalink for this Document. @@ -278,8 +256,6 @@ module Jekyll # # Returns nothing. def read(opts = {}) - @to_liquid = nil - Jekyll.logger.debug "Reading:", relative_path if yaml_file? @@ -353,21 +329,7 @@ module Jekyll # # Returns a Hash representing this Document's data. def to_liquid - @to_liquid ||= if data.is_a?(Hash) - Utils.deep_merge_hashes Utils.deep_merge_hashes({ - "output" => output, - "content" => content, - "relative_path" => relative_path, - "path" => relative_path, - "url" => url, - "collection" => collection.label, - "next" => next_doc, - "previous" => previous_doc, - "id" => id, - }, data), { 'excerpt' => data['excerpt'].to_s } - else - data - end + @to_liquid ||= Drops::DocumentDrop.new(self) end # The inspect string for this document. diff --git a/lib/jekyll/drops/collection_drop.rb b/lib/jekyll/drops/collection_drop.rb new file mode 100644 index 00000000..30c9f865 --- /dev/null +++ b/lib/jekyll/drops/collection_drop.rb @@ -0,0 +1,22 @@ +# encoding: UTF-8 +require "jekyll/drops/immutable_drop" + +module Jekyll + module Drops + class CollectionDrop < ImmutableDrop + extend Forwardable + + def_delegators :@obj, :label, :docs, :files, :directory, :relative_directory + + def output + @obj.write? + end + + private + def data + @obj.metadata + end + + end + end +end diff --git a/lib/jekyll/drops/document_drop.rb b/lib/jekyll/drops/document_drop.rb new file mode 100644 index 00000000..4d7207d5 --- /dev/null +++ b/lib/jekyll/drops/document_drop.rb @@ -0,0 +1,50 @@ +# encoding: UTF-8 + +module Jekyll + module Drops + class DocumentDrop < ImmutableDrop + + def output + @obj.output + end + + def content + @obj.content + end + + def relative_path + @obj.relative_path + end + alias_method :path, :relative_path + + def url + @obj.url + end + + def collection + @obj.collection.label + end + + def next + @obj.next_doc + end + + def previous + @obj.previous_doc + end + + def id + @obj.id + end + + def excerpt + data['excerpt'].to_s + end + + def data + @obj.data + end + + end + end +end diff --git a/lib/jekyll/drops/immutable_drop.rb b/lib/jekyll/drops/immutable_drop.rb new file mode 100644 index 00000000..51257cff --- /dev/null +++ b/lib/jekyll/drops/immutable_drop.rb @@ -0,0 +1,29 @@ +# encoding: UTF-8 + +module Jekyll + module Drops + class ImmutableDrop < Liquid::Drop + + def initialize(obj) + @obj = obj + end + + def [](key) + if respond_to? key + public_send key + else + data[key] + end + end + + def []=(key, val) + if respond_to? key + raise ArgumentError.new("Key #{key} cannot be set in the drop.") + else + data[key] = val + end + end + + end + end +end diff --git a/lib/jekyll/drops/jekyll_drop.rb b/lib/jekyll/drops/jekyll_drop.rb new file mode 100644 index 00000000..c4009da0 --- /dev/null +++ b/lib/jekyll/drops/jekyll_drop.rb @@ -0,0 +1,21 @@ +# encoding: UTF-8 + +module Jekyll + module Drops + class JekyllDrop < Liquid::Drop + class << self + def global + @global ||= JekyllDrop.new + end + end + + def version + Jekyll::VERSION + end + + def environment + Jekyll.env + end + end + end +end diff --git a/lib/jekyll/drops/mutable_drop.rb b/lib/jekyll/drops/mutable_drop.rb new file mode 100644 index 00000000..1551728e --- /dev/null +++ b/lib/jekyll/drops/mutable_drop.rb @@ -0,0 +1,28 @@ +# encoding: UTF-8 + +module Jekyll + module Drops + class MutableDrop < Liquid::Drop + + def initialize(obj) + @obj = obj + @mutations = {} + end + + def [](key) + if @mutations.key? key + @mutations[key] + elsif respond_to? key + public_send key + else + data[key] + end + end + + def []=(key, val) + @mutations[key] = val + end + + end + end +end diff --git a/lib/jekyll/drops/site_drop.rb b/lib/jekyll/drops/site_drop.rb new file mode 100644 index 00000000..77d6a416 --- /dev/null +++ b/lib/jekyll/drops/site_drop.rb @@ -0,0 +1,38 @@ +# encoding: UTF-8 + +module Jekyll + module Drops + class SiteDrop < ImmutableDrop + extend Forwardable + + def_delegator :@obj, :site_data, :data + def_delegators :@obj, :time, :pages, :static_files, :documents + + def posts + @site_posts ||= @obj.posts.docs.sort { |a, b| b <=> a } + end + + def html_pages + @site_html_pages ||= @obj.pages.select { |page| page.html? || page.url.end_with?("/") } + end + + def categories + @site_categories ||= @obj.post_attr_hash('categories') + end + + def tags + @site_tags ||= @obj.post_attr_hash('tags') + end + + def collections + @site_collections ||= @obj.collections.values.map(&:to_liquid) + end + + private + def data + @obj.config + end + + end + end +end diff --git a/lib/jekyll/drops/unified_payload_drop.rb b/lib/jekyll/drops/unified_payload_drop.rb new file mode 100644 index 00000000..65733b20 --- /dev/null +++ b/lib/jekyll/drops/unified_payload_drop.rb @@ -0,0 +1,24 @@ +# encoding: UTF-8 + +module Jekyll + module Drops + class UnifiedPayloadDrop < Liquid::Drop + + attr_accessor :page, :layout, :content, :paginator + attr_accessor :highlighter_prefix, :highlighter_suffix + + def initialize(site) + @site = site + end + + def jekyll + JekyllDrop.global + end + + def site + @site_drop ||= SiteDrop.new(@site) + end + + end + end +end diff --git a/lib/jekyll/drops/url_drop.rb b/lib/jekyll/drops/url_drop.rb new file mode 100644 index 00000000..047acacf --- /dev/null +++ b/lib/jekyll/drops/url_drop.rb @@ -0,0 +1,52 @@ +# encoding: UTF-8 + +module Jekyll + module Drops + class UrlDrop < ImmutableDrop + def collection + @obj.collection.label + end + + def path + @obj.cleaned_relative_path + end + + def output_ext + @obj.output_ext + end + + def name + Utils.slugify(@obj.basename_without_ext) + end + + def title + Utils.slugify(@obj.data['slug'], mode: "pretty", cased: true) || + Utils.slugify(@obj.basename_without_ext, mode: "pretty", cased: true) + end + + def slug + Utils.slugify(@obj.data['slug']) || Utils.slugify(@obj.basename_without_ext) + end + + def categories + category_set = Set.new + Array(@obj.data['categories']).each do |category| + category_set << category.to_s.downcase + end + category_set.to_a.join('/') + end + + def year; @obj.date.strftime("%Y"); end + def month; @obj.date.strftime("%m"); end + def day; @obj.date.strftime("%d"); end + def hour; @obj.date.strftime("%H"); end + def minute; @obj.date.strftime("%M"); end + def second; @obj.date.strftime("%S"); end + def i_day; @obj.date.strftime("%-d"); end + def i_month; @obj.date.strftime("%-m"); end + def short_month; @obj.date.strftime("%b"); end + def short_year; @obj.date.strftime("%y"); end + def y_day; @obj.date.strftime("%j"); end + end + end +end diff --git a/lib/jekyll/page.rb b/lib/jekyll/page.rb index a62a9940..0644e1b6 100644 --- a/lib/jekyll/page.rb +++ b/lib/jekyll/page.rb @@ -117,12 +117,10 @@ module Jekyll # # Returns nothing. def render(layouts, site_payload) - payload = Utils.deep_merge_hashes({ - "page" => to_liquid, - 'paginator' => pager.to_liquid - }, site_payload) + site_payload.page = to_liquid + site_payload.paginator = pager.to_liquid - do_layout(payload, layouts) + do_layout(site_payload, layouts) end # The path to the source file diff --git a/lib/jekyll/renderer.rb b/lib/jekyll/renderer.rb index 529ff84b..09763cca 100644 --- a/lib/jekyll/renderer.rb +++ b/lib/jekyll/renderer.rb @@ -3,12 +3,12 @@ module Jekyll class Renderer - attr_reader :document, :site, :site_payload + attr_reader :document, :site, :payload def initialize(site, document, site_payload = nil) - @site = site - @document = document - @site_payload = site_payload + @site = site + @document = document + @payload = site_payload || site.site_payload end # Determine which converters to use based on this document's @@ -33,12 +33,10 @@ 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) + payload.page = document.to_liquid if document.collection.label == 'posts' && document.is_a?(Document) - payload['site']['related_posts'] = document.related_posts + payload.site['related_posts'] = document.related_posts end Jekyll.logger.debug "Pre-Render Hooks:", document.relative_path @@ -46,12 +44,12 @@ module Jekyll info = { filters: [Jekyll::Filters], - registers: { :site => site, :page => payload['page'] } + 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 + payload.highlighter_prefix = converters.first.highlighter_prefix + payload.highlighter_suffix = converters.first.highlighter_suffix output = document.content @@ -135,14 +133,9 @@ module Jekyll used = Set.new([layout]) while layout - payload = Utils.deep_merge_hashes( - payload, - { - "content" => output, - "page" => document.to_liquid, - "layout" => layout.data - } - ) + payload.content = output + payload.page = document.to_liquid + payload.layout = layout.data output = render_liquid( layout.content, diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb index 5e9402f4..4dc3ac84 100644 --- a/lib/jekyll/site.rb +++ b/lib/jekyll/site.rb @@ -259,25 +259,7 @@ module Jekyll # "tags" - The Hash of tag values and Posts. # See Site#post_attr_hash for type info. def site_payload - { - "jekyll" => { - "version" => Jekyll::VERSION, - "environment" => Jekyll.env - }, - "site" => Utils.deep_merge_hashes(config, - Utils.deep_merge_hashes(Hash[collections.map{|label, coll| [label, coll.docs]}], { - "time" => time, - "posts" => posts.docs.sort { |a, b| b <=> a }, - "pages" => pages, - "static_files" => static_files, - "html_pages" => pages.select { |page| page.html? || page.url.end_with?("/") }, - "categories" => post_attr_hash('categories'), - "tags" => post_attr_hash('tags'), - "collections" => collections.values.map(&:to_liquid), - "documents" => documents, - "data" => site_data - })) - } + Drops::UnifiedPayloadDrop.new self end # Get the implementation class for the given Converter. diff --git a/lib/jekyll/url.rb b/lib/jekyll/url.rb index 6e8042b0..b338c23a 100644 --- a/lib/jekyll/url.rb +++ b/lib/jekyll/url.rb @@ -59,6 +59,14 @@ module Jekyll # # Returns the unsanitized String URL def generate_url(template) + if @placeholders.is_a? Drops::UrlDrop + generate_url_from_drop(template) + else + generate_url_from_hash(template) + end + end + + def generate_url_from_hash(template) @placeholders.inject(template) do |result, token| break result if result.index(':').nil? if token.last.nil? @@ -70,6 +78,12 @@ module Jekyll end end + def generate_url_from_drop(template) + template.gsub(/(:[a-z_]+)/) do |match| + @placeholders.public_send(match.sub(':', '')) + end.gsub(/\/\//, '/') + end + # Returns a sanitized String URL, stripping "../../" and multiples of "/", # as well as the beginning "/" so we can enforce and ensure it. From ebe3c106047f5843f354c8b88a640a146bd1df2d Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Mon, 21 Dec 2015 23:33:33 -0500 Subject: [PATCH 02/15] Drops: fix accessing of site collections via site.COL_NAME --- lib/jekyll/drops/collection_drop.rb | 2 +- lib/jekyll/drops/document_drop.rb | 5 +++-- lib/jekyll/drops/immutable_drop.rb | 4 ++-- lib/jekyll/drops/mutable_drop.rb | 2 +- lib/jekyll/drops/site_drop.rb | 10 +++++++++- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/jekyll/drops/collection_drop.rb b/lib/jekyll/drops/collection_drop.rb index 30c9f865..2c447cb4 100644 --- a/lib/jekyll/drops/collection_drop.rb +++ b/lib/jekyll/drops/collection_drop.rb @@ -13,7 +13,7 @@ module Jekyll end private - def data + def fallback_data @obj.metadata end diff --git a/lib/jekyll/drops/document_drop.rb b/lib/jekyll/drops/document_drop.rb index 4d7207d5..9319424b 100644 --- a/lib/jekyll/drops/document_drop.rb +++ b/lib/jekyll/drops/document_drop.rb @@ -38,10 +38,11 @@ module Jekyll end def excerpt - data['excerpt'].to_s + fallback_data['excerpt'].to_s end - def data + private + def fallback_data @obj.data end diff --git a/lib/jekyll/drops/immutable_drop.rb b/lib/jekyll/drops/immutable_drop.rb index 51257cff..d7948beb 100644 --- a/lib/jekyll/drops/immutable_drop.rb +++ b/lib/jekyll/drops/immutable_drop.rb @@ -12,7 +12,7 @@ module Jekyll if respond_to? key public_send key else - data[key] + fallback_data[key] end end @@ -20,7 +20,7 @@ module Jekyll if respond_to? key raise ArgumentError.new("Key #{key} cannot be set in the drop.") else - data[key] = val + fallback_data[key] = val end end diff --git a/lib/jekyll/drops/mutable_drop.rb b/lib/jekyll/drops/mutable_drop.rb index 1551728e..9dd4048d 100644 --- a/lib/jekyll/drops/mutable_drop.rb +++ b/lib/jekyll/drops/mutable_drop.rb @@ -15,7 +15,7 @@ module Jekyll elsif respond_to? key public_send key else - data[key] + fallback_data[key] end end diff --git a/lib/jekyll/drops/site_drop.rb b/lib/jekyll/drops/site_drop.rb index 77d6a416..82c55230 100644 --- a/lib/jekyll/drops/site_drop.rb +++ b/lib/jekyll/drops/site_drop.rb @@ -8,6 +8,14 @@ module Jekyll def_delegator :@obj, :site_data, :data def_delegators :@obj, :time, :pages, :static_files, :documents + def [](key) + if !respond_to?(key) && @obj.collections.key?(key) + @obj.collections[key].docs + else + super(key) + end + end + def posts @site_posts ||= @obj.posts.docs.sort { |a, b| b <=> a } end @@ -29,7 +37,7 @@ module Jekyll end private - def data + def fallback_data @obj.config end From 03488b1cde735eaac9a75a64cc155324f4167e64 Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Mon, 21 Dec 2015 23:36:31 -0500 Subject: [PATCH 03/15] DocumentDrop: use def_delegators instead of duplicating methods --- lib/jekyll/drops/document_drop.rb | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/lib/jekyll/drops/document_drop.rb b/lib/jekyll/drops/document_drop.rb index 9319424b..c4170d86 100644 --- a/lib/jekyll/drops/document_drop.rb +++ b/lib/jekyll/drops/document_drop.rb @@ -3,18 +3,10 @@ module Jekyll module Drops class DocumentDrop < ImmutableDrop + extend Forwardable - def output - @obj.output - end + def_delegators :@obj, :id, :output, :content, :to_s, :relative_path, :url - def content - @obj.content - end - - def relative_path - @obj.relative_path - end alias_method :path, :relative_path def url @@ -33,10 +25,6 @@ module Jekyll @obj.previous_doc end - def id - @obj.id - end - def excerpt fallback_data['excerpt'].to_s end From 4935e85f7cd1f09cefbe9988fd0e52eeb49d6f9a Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Mon, 21 Dec 2015 23:41:36 -0500 Subject: [PATCH 04/15] CollectionDrop: to_s should work like Array#to_s --- lib/jekyll/drops/collection_drop.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/jekyll/drops/collection_drop.rb b/lib/jekyll/drops/collection_drop.rb index 2c447cb4..8dc8c6b8 100644 --- a/lib/jekyll/drops/collection_drop.rb +++ b/lib/jekyll/drops/collection_drop.rb @@ -6,7 +6,12 @@ module Jekyll class CollectionDrop < ImmutableDrop extend Forwardable - def_delegators :@obj, :label, :docs, :files, :directory, :relative_directory + def_delegators :@obj, :label, :docs, :files, :directory, + :relative_directory + + def to_s + @obj.docs.map(&:to_s).join(' ') + end def output @obj.write? From 532bb9e9cb4851405ed595ed315724779b93b286 Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Tue, 22 Dec 2015 21:33:42 -0500 Subject: [PATCH 05/15] Further consolidation in the Drops. --- lib/jekyll/drops/collection_drop.rb | 2 +- lib/jekyll/drops/document_drop.rb | 8 +------- lib/jekyll/drops/site_drop.rb | 13 +++---------- lib/jekyll/drops/unified_payload_drop.rb | 7 ++++++- 4 files changed, 11 insertions(+), 19 deletions(-) diff --git a/lib/jekyll/drops/collection_drop.rb b/lib/jekyll/drops/collection_drop.rb index 8dc8c6b8..b8a1bdd7 100644 --- a/lib/jekyll/drops/collection_drop.rb +++ b/lib/jekyll/drops/collection_drop.rb @@ -10,7 +10,7 @@ module Jekyll :relative_directory def to_s - @obj.docs.map(&:to_s).join(' ') + docs.to_s end def output diff --git a/lib/jekyll/drops/document_drop.rb b/lib/jekyll/drops/document_drop.rb index c4170d86..0da6986b 100644 --- a/lib/jekyll/drops/document_drop.rb +++ b/lib/jekyll/drops/document_drop.rb @@ -9,10 +9,6 @@ module Jekyll alias_method :path, :relative_path - def url - @obj.url - end - def collection @obj.collection.label end @@ -30,9 +26,7 @@ module Jekyll end private - def fallback_data - @obj.data - end + def_delegator :@obj, :data, :fallback_data end end diff --git a/lib/jekyll/drops/site_drop.rb b/lib/jekyll/drops/site_drop.rb index 82c55230..4c632a0d 100644 --- a/lib/jekyll/drops/site_drop.rb +++ b/lib/jekyll/drops/site_drop.rb @@ -6,10 +6,11 @@ module Jekyll extend Forwardable def_delegator :@obj, :site_data, :data - def_delegators :@obj, :time, :pages, :static_files, :documents + def_delegators :@obj, :time, :pages, :static_files, :documents, + :tags, :categories def [](key) - if !respond_to?(key) && @obj.collections.key?(key) + if @obj.collections.key?(key) && key != "posts" @obj.collections[key].docs else super(key) @@ -24,14 +25,6 @@ module Jekyll @site_html_pages ||= @obj.pages.select { |page| page.html? || page.url.end_with?("/") } end - def categories - @site_categories ||= @obj.post_attr_hash('categories') - end - - def tags - @site_tags ||= @obj.post_attr_hash('tags') - end - def collections @site_collections ||= @obj.collections.values.map(&:to_liquid) end diff --git a/lib/jekyll/drops/unified_payload_drop.rb b/lib/jekyll/drops/unified_payload_drop.rb index 65733b20..6db48f66 100644 --- a/lib/jekyll/drops/unified_payload_drop.rb +++ b/lib/jekyll/drops/unified_payload_drop.rb @@ -2,7 +2,7 @@ module Jekyll module Drops - class UnifiedPayloadDrop < Liquid::Drop + class UnifiedPayloadDrop < ImmutableDrop attr_accessor :page, :layout, :content, :paginator attr_accessor :highlighter_prefix, :highlighter_suffix @@ -19,6 +19,11 @@ module Jekyll @site_drop ||= SiteDrop.new(@site) end + private + def fallback_data + @fallback_data ||= {} + end + end end end From 233589e15076c41ac4b95ad6e0595f2f6a4a7ac2 Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Tue, 22 Dec 2015 21:34:24 -0500 Subject: [PATCH 06/15] document: throw ArgumentError if compared to non-doc --- lib/jekyll/document.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb index 622f1180..0a416eff 100644 --- a/lib/jekyll/document.rb +++ b/lib/jekyll/document.rb @@ -353,7 +353,7 @@ 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 <=>(other) - return nil if !other.respond_to?(:data) + return ArgumentError.new("document cannot be compared against #{other}") unless other.respond_to?(:data) cmp = data['date'] <=> other.data['date'] cmp = path <=> other.path if cmp == 0 cmp From 659f0869e078249c2dfa1a6b088f8f934b9956f5 Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Tue, 22 Dec 2015 21:35:28 -0500 Subject: [PATCH 07/15] features/collections: drops don't output like a hash -- update accordingly --- features/collections.feature | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/features/collections.feature b/features/collections.feature index d97918bd..1fcc7cce 100644 --- a/features/collections.feature +++ b/features/collections.feature @@ -13,7 +13,7 @@ Feature: Collections And the "_site/methods/configuration.html" file should not exist Scenario: Rendered collection - Given I have an "index.html" page that contains "Collections: {{ site.collections }}" + Given I have an "index.html" page that contains "Collections: output => {{ site.collections[0].output }} label => {{ site.collections[0].label }}" And I have an "collection_metadata.html" page that contains "Methods metadata: {{ site.collections[0].foo }} {{ site.collections[0] }}" And I have fixture collections And I have a "_config.yml" file with content: @@ -25,8 +25,8 @@ Feature: Collections """ When I run jekyll build Then the _site directory should exist - And I should see "Collections: {\"output\"=>true" in "_site/index.html" - And I should see "\"label\"=>\"methods\"," in "_site/index.html" + And I should see "Collections: output => true" in "_site/index.html" + And I should see "label => methods" in "_site/index.html" And I should see "Methods metadata: bar" in "_site/collection_metadata.html" And I should see "

Whatever: foo.bar

" in "_site/methods/configuration.html" @@ -45,7 +45,7 @@ Feature: Collections And I should see "

Whatever: foo.bar

" in "_site/methods/configuration/index.html" Scenario: Rendered document in a layout - Given I have an "index.html" page that contains "Collections: {{ site.collections }}" + Given I have an "index.html" page that contains "Collections: output => {{ site.collections[0].output }} label => {{ site.collections[0].label }} foo => {{ site.collections[0].foo }}" And I have a default layout that contains "
Tom Preston-Werner
{{content}}" And I have fixture collections And I have a "_config.yml" file with content: @@ -57,8 +57,9 @@ Feature: Collections """ When I run jekyll build Then the _site directory should exist - And I should see "Collections: {\"output\"=>true" in "_site/index.html" - And I should see "\"label\"=>\"methods\"," in "_site/index.html" + And I should see "Collections: output => true" in "_site/index.html" + And I should see "label => methods" in "_site/index.html" + And I should see "foo => bar" in "_site/index.html" And I should see "

Run your generators! default

" in "_site/methods/site/generate.html" And I should see "
Tom Preston-Werner
" in "_site/methods/site/generate.html" From 30ceda52ef6f00a1edefcd8c9ce1bf1e81c1028c Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Tue, 22 Dec 2015 21:36:15 -0500 Subject: [PATCH 08/15] features/hooks: global payload _is_ global -- not new for each page --- features/hooks.feature | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/hooks.feature b/features/hooks.feature index 489f256b..b561c00c 100644 --- a/features/hooks.feature +++ b/features/hooks.feature @@ -89,11 +89,11 @@ Feature: Hooks And I have a "_plugins/ext.rb" file with content: """ Jekyll::Hooks.register :pages, :pre_render do |page, payload| - payload['myparam'] = 'special' if page.name == 'page1.html' + payload.page['myparam'] = 'special' if page.name == 'page1.html' end """ - And I have a "page1.html" page that contains "{{ myparam }}" - And I have a "page2.html" page that contains "{{ myparam }}" + And I have a "page1.html" page that contains "{{ page.myparam }}" + And I have a "page2.html" page that contains "{{ page.myparam }}" When I run jekyll build Then I should see "special" in "_site/page1.html" And I should not see "special" in "_site/page2.html" From 9bb59e9999ef7ecd6ce64022fc6394906c9f43e5 Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Tue, 22 Dec 2015 21:36:29 -0500 Subject: [PATCH 09/15] features/post_data: do NOT allow page.path to be overridden --- features/post_data.feature | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/post_data.feature b/features/post_data.feature index 6ebfb27d..0b43c8c9 100644 --- a/features/post_data.feature +++ b/features/post_data.feature @@ -233,14 +233,14 @@ Feature: Post data | dir | dir/ | | dir/nested | dir/nested/ | - Scenario: Override page.path variable + Scenario: Cannot override page.path variable Given I have a _posts directory And I have the following post: | title | date | path | content | - | override | 2013-04-12 | override-path.html | Custom path: {{ page.path }} | + | override | 2013-04-12 | override-path.html | Non-custom path: {{ page.path }} | When I run jekyll build Then the _site directory should exist - And I should see "Custom path: override-path.html" in "_site/2013/04/12/override.html" + And I should see "Non-custom path: _posts/2013-04-12-override.markdown in "_site/2013/04/12/override.html" Scenario: Disable a post from being published Given I have a _posts directory From cd2688ab6665367b3e65ce7d1e878bb00599f4b9 Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Tue, 22 Dec 2015 22:37:48 -0500 Subject: [PATCH 10/15] test_excerpt & _page: use Drop instead of Hash to mock payload --- test/test_excerpt.rb | 3 +-- test/test_page.rb | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/test/test_excerpt.rb b/test/test_excerpt.rb index 322ba3c6..61ffded3 100644 --- a/test/test_excerpt.rb +++ b/test/test_excerpt.rb @@ -10,8 +10,7 @@ class TestExcerpt < JekyllUnitTest 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 + document.output = Jekyll::Renderer.new(@site, document, @site.site_payload).run end context "With extraction disabled" do diff --git a/test/test_page.rb b/test/test_page.rb index 625812aa..16b9e060 100644 --- a/test/test_page.rb +++ b/test/test_page.rb @@ -9,7 +9,7 @@ class TestPage < JekyllUnitTest def do_render(page) layouts = { "default" => Layout.new(@site, source_dir('_layouts'), "simple.html")} - page.render(layouts, {"site" => {"posts" => []}}) + page.render(layouts, @site.site_payload) end context "A Page" do From 6a72d4a98690d5b13eca50c42bcbb9c8c3daca35 Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Tue, 22 Dec 2015 22:44:56 -0500 Subject: [PATCH 11/15] features/post_data: Fix undefined feature step. --- features/post_data.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/post_data.feature b/features/post_data.feature index 0b43c8c9..2792a829 100644 --- a/features/post_data.feature +++ b/features/post_data.feature @@ -240,7 +240,7 @@ Feature: Post data | override | 2013-04-12 | override-path.html | Non-custom path: {{ page.path }} | When I run jekyll build Then the _site directory should exist - And I should see "Non-custom path: _posts/2013-04-12-override.markdown in "_site/2013/04/12/override.html" + And I should see "Non-custom path: _posts/2013-04-12-override.markdown" in "_site/2013/04/12/override.html" Scenario: Disable a post from being published Given I have a _posts directory From bff1726a5a24551164d958eca93983b96a975203 Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Thu, 24 Dec 2015 15:06:32 -0500 Subject: [PATCH 12/15] immutable_drop: use custom error for bad set --- lib/jekyll.rb | 1 + lib/jekyll/drops/immutable_drop.rb | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/jekyll.rb b/lib/jekyll.rb index a90a5a0b..de6b6c69 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -32,6 +32,7 @@ require 'colorator' SafeYAML::OPTIONS[:suppress_warnings] = true module Jekyll + StandardError = Class.new(::StandardError) # internal requires autoload :Cleaner, 'jekyll/cleaner' diff --git a/lib/jekyll/drops/immutable_drop.rb b/lib/jekyll/drops/immutable_drop.rb index d7948beb..630cbfe1 100644 --- a/lib/jekyll/drops/immutable_drop.rb +++ b/lib/jekyll/drops/immutable_drop.rb @@ -3,6 +3,7 @@ module Jekyll module Drops class ImmutableDrop < Liquid::Drop + IllegalDropModification = Class.new(Jekyll::StandardError) def initialize(obj) @obj = obj @@ -18,7 +19,7 @@ module Jekyll def []=(key, val) if respond_to? key - raise ArgumentError.new("Key #{key} cannot be set in the drop.") + raise IllegalDropModification.new("Key #{key} cannot be set in the drop.") else fallback_data[key] = val end From d070a77716f65fc9cbdef1aed91943959e9616e0 Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Thu, 24 Dec 2015 15:07:20 -0500 Subject: [PATCH 13/15] url: fix issue with bad URL escaping when using Drop --- lib/jekyll/url.rb | 8 ++++++-- lib/jekyll/utils.rb | 13 +++++++------ test/test_url.rb | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/lib/jekyll/url.rb b/lib/jekyll/url.rb index b338c23a..a001f44d 100644 --- a/lib/jekyll/url.rb +++ b/lib/jekyll/url.rb @@ -79,8 +79,12 @@ module Jekyll end def generate_url_from_drop(template) - template.gsub(/(:[a-z_]+)/) do |match| - @placeholders.public_send(match.sub(':', '')) + template.gsub(/:([a-z_]+)/) do |match| + if replacement = @placeholders.public_send(match.sub(':', '')) + self.class.escape_path replacement + else + ''.freeze + end end.gsub(/\/\//, '/') end diff --git a/lib/jekyll/utils.rb b/lib/jekyll/utils.rb index 94da8bd5..a2c6139d 100644 --- a/lib/jekyll/utils.rb +++ b/lib/jekyll/utils.rb @@ -174,13 +174,14 @@ module Jekyll SLUGIFY_PRETTY_REGEXP end - slug = string. - # Strip according to the mode - gsub(re, '-'). - # Remove leading/trailing hyphen - gsub(/^\-|\-$/i, '') + # Strip according to the mode + slug = string.gsub(re, '-') - cased ? slug : slug.downcase + # Remove leading/trailing hyphen + slug.gsub!(/^\-|\-$/i, '') + + slug.downcase! unless cased + slug end # Add an appropriate suffix to template so that it matches the specified diff --git a/test/test_url.rb b/test/test_url.rb index e4529be7..578e8962 100644 --- a/test/test_url.rb +++ b/test/test_url.rb @@ -54,5 +54,23 @@ class TestURL < JekyllUnitTest ).to_s end + should "handle UrlDrop as a placeholder in addition to a hash" do + site = fixture_site({ + "collections" => { + "methods" => { + "output" => true + } + }, + }) + site.read + doc = site.collections["methods"].docs.find do |doc| + doc.relative_path == "_methods/escape-+ #%20[].md" + end + assert_equal '/methods/escape-+-20/escape-20.html', URL.new( + :template => '/methods/:title/:name:output_ext', + :placeholders => doc.url_placeholders + ).to_s + end + end end From fcce0d5482e6cd541e3e6857cad00a6969a9dd4e Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Thu, 24 Dec 2015 15:07:31 -0500 Subject: [PATCH 14/15] document: fix issue with bad comparison --- lib/jekyll/document.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb index 0a416eff..a1bf6bad 100644 --- a/lib/jekyll/document.rb +++ b/lib/jekyll/document.rb @@ -12,11 +12,13 @@ module Jekyll # Create a new Document. # - # site - the Jekyll::Site instance to which this Document belongs # path - the path to the file + # relations - a hash with keys :site and :collection, the values of which + # are the Jekyll::Site and Jekyll::Collection to which this + # Document belong. # # Returns nothing. - def initialize(path, relations) + def initialize(path, relations = {}) @site = relations[:site] @path = path @extname = File.extname(path) @@ -354,9 +356,11 @@ module Jekyll # equal or greater than the other doc's path. See String#<=> for more details. def <=>(other) return ArgumentError.new("document cannot be compared against #{other}") unless other.respond_to?(:data) - cmp = data['date'] <=> other.data['date'] - cmp = path <=> other.path if cmp == 0 - cmp + if data['date'] && other.data['date'] && (cmp = data['date'] <=> other.data['date']) != 0 + cmp + else + path <=> other.path + end end # Determine whether this document should be written. From b2b634e767c7a8775b41e8f8a28353577d84e340 Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Thu, 24 Dec 2015 15:07:48 -0500 Subject: [PATCH 15/15] drops: use def_delegator more liberally where acceptable --- lib/jekyll/drops/collection_drop.rb | 9 ++------- lib/jekyll/drops/document_drop.rb | 13 +++---------- lib/jekyll/drops/site_drop.rb | 4 +--- lib/jekyll/drops/url_drop.rb | 13 +++++-------- 4 files changed, 11 insertions(+), 28 deletions(-) diff --git a/lib/jekyll/drops/collection_drop.rb b/lib/jekyll/drops/collection_drop.rb index b8a1bdd7..ccb3045a 100644 --- a/lib/jekyll/drops/collection_drop.rb +++ b/lib/jekyll/drops/collection_drop.rb @@ -6,6 +6,7 @@ module Jekyll class CollectionDrop < ImmutableDrop extend Forwardable + def_delegator :@obj, :write?, :output def_delegators :@obj, :label, :docs, :files, :directory, :relative_directory @@ -13,14 +14,8 @@ module Jekyll docs.to_s end - def output - @obj.write? - end - private - def fallback_data - @obj.metadata - end + def_delegator :@obj, :metadata, :fallback_data end end diff --git a/lib/jekyll/drops/document_drop.rb b/lib/jekyll/drops/document_drop.rb index 0da6986b..81c9bcd2 100644 --- a/lib/jekyll/drops/document_drop.rb +++ b/lib/jekyll/drops/document_drop.rb @@ -5,22 +5,15 @@ module Jekyll class DocumentDrop < ImmutableDrop extend Forwardable + def_delegator :@obj, :next_doc, :next + def_delegator :@obj, :previous_doc, :previous + def_delegator :@obj, :relative_path, :path def_delegators :@obj, :id, :output, :content, :to_s, :relative_path, :url - alias_method :path, :relative_path - def collection @obj.collection.label end - def next - @obj.next_doc - end - - def previous - @obj.previous_doc - end - def excerpt fallback_data['excerpt'].to_s end diff --git a/lib/jekyll/drops/site_drop.rb b/lib/jekyll/drops/site_drop.rb index 4c632a0d..61ac8098 100644 --- a/lib/jekyll/drops/site_drop.rb +++ b/lib/jekyll/drops/site_drop.rb @@ -30,9 +30,7 @@ module Jekyll end private - def fallback_data - @obj.config - end + def_delegator :@obj, :config, :fallback_data end end diff --git a/lib/jekyll/drops/url_drop.rb b/lib/jekyll/drops/url_drop.rb index 047acacf..163591dd 100644 --- a/lib/jekyll/drops/url_drop.rb +++ b/lib/jekyll/drops/url_drop.rb @@ -3,18 +3,15 @@ module Jekyll module Drops class UrlDrop < ImmutableDrop + extend Forwardable + + def_delegator :@obj, :cleaned_relative_path, :path + def_delegator :@obj, :output_ext, :output_ext + def collection @obj.collection.label end - def path - @obj.cleaned_relative_path - end - - def output_ext - @obj.output_ext - end - def name Utils.slugify(@obj.basename_without_ext) end