From cf19cdb935e4ec2b91f99655368f52eab7e68e22 Mon Sep 17 00:00:00 2001 From: Ashwin Maroli Date: Mon, 3 Feb 2020 12:43:50 +0530 Subject: [PATCH 1/6] Add PageDrop to provide Liquid templates with data --- lib/jekyll/drops/page_drop.rb | 14 ++++++++++++++ lib/jekyll/page.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 lib/jekyll/drops/page_drop.rb diff --git a/lib/jekyll/drops/page_drop.rb b/lib/jekyll/drops/page_drop.rb new file mode 100644 index 00000000000..3a6e961367b --- /dev/null +++ b/lib/jekyll/drops/page_drop.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module Jekyll + module Drops + class PageDrop < Drop + extend Forwardable + + mutable false + + def_delegators :@obj, :content, :dir, :name, :path, :url + private def_delegator :@obj, :data, :fallback_data + end + end +end diff --git a/lib/jekyll/page.rb b/lib/jekyll/page.rb index f353252d825..f8adbbd366e 100644 --- a/lib/jekyll/page.rb +++ b/lib/jekyll/page.rb @@ -70,6 +70,32 @@ def dir end end + # For backwards-compatibility in subclasses that do not redefine + # the `:to_liquid` method, stash existing definition under a new name + # + # TODO: Remove in Jekyll 5.0 + alias_method :legacy_to_liquid, :to_liquid + private :legacy_to_liquid + + # Private + # Subclasses can choose to optimize their `:to_liquid` method by wrapping + # it around this definition. + # + # TODO: Remove in Jekyll 5.0 + def liquid_drop + @liquid_drop ||= Drops::PageDrop.new(self) + end + private :liquid_drop + + # Public + # + # Liquid representation of current page + # + # TODO: Remove optional parameter in Jekyll 5.0 + def to_liquid(attrs = nil) + self.class == Jekyll::Page ? liquid_drop : legacy_to_liquid(attrs) + end + # The full path and filename of the post. Defined in the YAML of the post # body. # From 954121a7adc2c6d7d85f098f3e74e00939626cfa Mon Sep 17 00:00:00 2001 From: Ashwin Maroli Date: Mon, 3 Feb 2020 15:12:04 +0530 Subject: [PATCH 2/6] Merge front matter defaults into data Hash --- lib/jekyll/page.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/jekyll/page.rb b/lib/jekyll/page.rb index f8adbbd366e..2e346fc5f25 100644 --- a/lib/jekyll/page.rb +++ b/lib/jekyll/page.rb @@ -83,7 +83,13 @@ def dir # # TODO: Remove in Jekyll 5.0 def liquid_drop - @liquid_drop ||= Drops::PageDrop.new(self) + @liquid_drop ||= begin + defaults = site.frontmatter_defaults.all(relative_path, type) + unless defaults.empty? + Utils.deep_merge_hashes!(data, Utils.deep_merge_hashes!(defaults, data)) + end + Drops::PageDrop.new(self) + end end private :liquid_drop From dd45c94a64dda17daa148dfd7bca37c60135a9f9 Mon Sep 17 00:00:00 2001 From: Ashwin Maroli Date: Thu, 6 Feb 2020 23:10:25 +0530 Subject: [PATCH 3/6] Test if drop exposes data required by template --- test/test_page.rb | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/test_page.rb b/test/test_page.rb index 16aa3ff90f3..cca6d0e168b 100644 --- a/test/test_page.rb +++ b/test/test_page.rb @@ -50,6 +50,34 @@ def do_render(page) assert_equal "/+/%25%23%20+.html", @page.url end + should "make attributes accessible for use in Liquid templates" do + page = setup_page("/contacts", "index.html") + template = Liquid::Template.parse(<<~TEXT) + Name: {{ page.name }} + Path: {{ page.path }} + URL: {{ page.url }} + TEXT + expected = <<~TEXT + Name: index.html + Path: contacts/index.html + URL: /contacts/ + TEXT + assert_equal(expected, template.render!("page" => page.to_liquid)) + end + + should "make front matter data accessible for use in Liquid templates" do + page = setup_page("properties.html") + template = Liquid::Template.parse(<<~TEXT) + TITLE: {{ page.title }} + FOO: {{ page.foo }} + TEXT + expected = <<~TEXT + TITLE: Properties Page + FOO: bar + TEXT + assert_equal expected, template.render!("page" => page.to_liquid) + end + context "in a directory hierarchy" do should "create URL based on filename" do @page = setup_page("/contacts", "bar.html") From 135b4300e24ca39a23f8f744a4c56f7a904eb6ca Mon Sep 17 00:00:00 2001 From: Ashwin Maroli Date: Thu, 6 Feb 2020 23:39:41 +0530 Subject: [PATCH 4/6] Test if Liquid representation is of expected class --- test/test_page.rb | 8 ++++++++ test/test_page_without_a_file.rb | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/test/test_page.rb b/test/test_page.rb index cca6d0e168b..023560c90e6 100644 --- a/test/test_page.rb +++ b/test/test_page.rb @@ -50,6 +50,14 @@ def do_render(page) assert_equal "/+/%25%23%20+.html", @page.url end + should "be exposed to Liquid as a Liquid::Drop subclass" do + page = setup_page("properties.html") + liquid_rep = page.to_liquid + refute_equal Hash, liquid_rep.class + assert_equal true, liquid_rep.is_a?(Liquid::Drop) + assert_equal Jekyll::Drops::PageDrop, liquid_rep.class + end + should "make attributes accessible for use in Liquid templates" do page = setup_page("/contacts", "index.html") template = Liquid::Template.parse(<<~TEXT) diff --git a/test/test_page_without_a_file.rb b/test/test_page_without_a_file.rb index f06706962c2..6deca7b537c 100644 --- a/test/test_page_without_a_file.rb +++ b/test/test_page_without_a_file.rb @@ -76,6 +76,12 @@ def render_and_write end end end + + should "be exposed to Liquid as a Hash" do + liquid_rep = @page.to_liquid + refute_equal Jekyll::Drops::PageDrop, liquid_rep.class + assert_equal Hash, liquid_rep.class + end end context "with site-wide permalink configuration" do From f7cf9e9c7d5e41b053d1caadbcf2dfee049b4777 Mon Sep 17 00:00:00 2001 From: Ashwin Maroli Date: Fri, 7 Feb 2020 16:30:36 +0530 Subject: [PATCH 5/6] Document move to `Drops::PageDrop` --- docs/_docs/pages.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/docs/_docs/pages.md b/docs/_docs/pages.md index fb4bce83614..a62100a93fb 100644 --- a/docs/_docs/pages.md +++ b/docs/_docs/pages.md @@ -34,3 +34,38 @@ If you have a lot of pages, you can organize them into subfolders. The same subf ## Changing the output URL You might want to have a particular folder structure for your source files that changes for the built site. With [permalinks](/docs/permalinks) you have full control of the output URL. + +## Liquid Representation + +From Jekyll 4.1 onwards, there is a minor change in how instances of `Jekyll::Page` are exposed to layouts and other Liquid +templates. `Jekyll::Page` instances now use a `Liquid::Drop` instead of a `Hash`. This change results in greater performance +for a site with numerous *standlone pages not within a collection*. + +### For plugin developers + +While end-users need not take any extra action due to this change, plugin authors depending on the existing behavior *may* need +to make minor changes to their plugins. + +If a `Jekyll::Page` subclass' `to_liquid` method calls `super`, it will have to be slightly modified. +```ruby +class Foo::BarPage < Jekyll::Page + def to_liquid(*) + payload = super # This needs to be changed to `super.to_h` + # to obtain a Hash as in v4.0.0. + + do_something(payload) # Logic specific to `Foo::BarPage` objects + end +end +``` + +`Jekyll::Page` subclasses won't inherit the optimization automatically until the next major version. However, plugin authors +can opt-in to the optimization in their subclasses by wrapping the temporary `liquid_drop` method if the subclass doesn't +override the superclass method: +```ruby +class Foo::BarPage < Jekyll::Page + def to_liquid(*) + liquid_drop # Returns an instance of `Jekyll::Drops::PageDrop`. + # Will be removed in Jekyll 5.0. + end +end +``` From c315aeca3cf92e504eca35415084cf2962b4f661 Mon Sep 17 00:00:00 2001 From: Ashwin Maroli Date: Mon, 16 Mar 2020 22:17:40 +0530 Subject: [PATCH 6/6] Improve phrasing in documentation Co-Authored-By: Frank Taillandier --- docs/_docs/pages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_docs/pages.md b/docs/_docs/pages.md index a62100a93fb..73e969ae776 100644 --- a/docs/_docs/pages.md +++ b/docs/_docs/pages.md @@ -43,7 +43,7 @@ for a site with numerous *standlone pages not within a collection*. ### For plugin developers -While end-users need not take any extra action due to this change, plugin authors depending on the existing behavior *may* need +While end-users do not need to take any extra action due to this change, plugin authors depending on the existing behavior *may* need to make minor changes to their plugins. If a `Jekyll::Page` subclass' `to_liquid` method calls `super`, it will have to be slightly modified.