lifer 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/.gitignore +1 -0
- data/CHANGELOG.md +26 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +110 -25
- data/LICENSE +18 -0
- data/README.md +79 -14
- data/Rakefile +2 -4
- data/bin/lifer +4 -2
- data/lib/lifer/brain.rb +171 -21
- data/lib/lifer/builder/html/from_erb.rb +92 -0
- data/lib/lifer/builder/html/from_liquid/drops/collection_drop.rb +40 -0
- data/lib/lifer/builder/html/from_liquid/drops/collections_drop.rb +40 -0
- data/lib/lifer/builder/html/from_liquid/drops/entry_drop.rb +63 -0
- data/lib/lifer/builder/html/from_liquid/drops/frontmatter_drop.rb +45 -0
- data/lib/lifer/builder/html/from_liquid/drops/settings_drop.rb +42 -0
- data/lib/lifer/builder/html/from_liquid/drops.rb +15 -0
- data/lib/lifer/builder/html/from_liquid/filters.rb +27 -0
- data/lib/lifer/builder/html/from_liquid/layout_tag.rb +67 -0
- data/lib/lifer/builder/html/from_liquid.rb +116 -0
- data/lib/lifer/builder/html.rb +107 -51
- data/lib/lifer/builder/rss.rb +113 -0
- data/lib/lifer/builder/txt.rb +60 -0
- data/lib/lifer/builder.rb +100 -1
- data/lib/lifer/cli.rb +105 -0
- data/lib/lifer/collection.rb +87 -8
- data/lib/lifer/config.rb +159 -31
- data/lib/lifer/dev/response.rb +61 -0
- data/lib/lifer/dev/router.rb +44 -0
- data/lib/lifer/dev/server.rb +97 -0
- data/lib/lifer/entry/html.rb +39 -0
- data/lib/lifer/entry/markdown.rb +162 -0
- data/lib/lifer/entry/txt.rb +41 -0
- data/lib/lifer/entry.rb +142 -41
- data/lib/lifer/message.rb +58 -0
- data/lib/lifer/selection/all_markdown.rb +16 -0
- data/lib/lifer/selection/included_in_feeds.rb +15 -0
- data/lib/lifer/selection.rb +79 -0
- data/lib/lifer/shared/finder_methods.rb +35 -0
- data/lib/lifer/shared.rb +6 -0
- data/lib/lifer/templates/cli.txt.erb +10 -0
- data/lib/lifer/templates/config.yaml +77 -0
- data/lib/lifer/templates/its-a-living.png +0 -0
- data/lib/lifer/templates/layout.html.erb +1 -1
- data/lib/lifer/uri_strategy/pretty.rb +14 -6
- data/lib/lifer/uri_strategy/pretty_root.rb +24 -0
- data/lib/lifer/uri_strategy/pretty_yyyy_mm_dd.rb +32 -0
- data/lib/lifer/uri_strategy/root.rb +17 -0
- data/lib/lifer/uri_strategy/simple.rb +10 -6
- data/lib/lifer/uri_strategy.rb +46 -6
- data/lib/lifer/utilities.rb +117 -0
- data/lib/lifer/version.rb +3 -0
- data/lib/lifer.rb +130 -23
- data/lifer.gemspec +12 -6
- data/locales/en.yml +54 -0
- metadata +142 -9
- data/lib/lifer/layout.rb +0 -25
- data/lib/lifer/templates/config +0 -4
- data/lib/lifer/uri_strategy/base.rb +0 -15
@@ -0,0 +1,92 @@
|
|
1
|
+
require "erb"
|
2
|
+
|
3
|
+
class Lifer::Builder::HTML
|
4
|
+
# If the HTML builder is given an ERB template, it uses this class to parse
|
5
|
+
# the ERB into HTML. Lifer project metadata is provided as context. For
|
6
|
+
# example:
|
7
|
+
#
|
8
|
+
# <html>
|
9
|
+
# <head>
|
10
|
+
# <title><%= my_collection.name %></title>
|
11
|
+
# </head>
|
12
|
+
#
|
13
|
+
# <body>
|
14
|
+
# <h1><%= my_collection.name %></h1>
|
15
|
+
#
|
16
|
+
# <% my_collection.entries.each do |entry| %>
|
17
|
+
# <section>
|
18
|
+
# <h2><%= entry.title %></h2>
|
19
|
+
# <p><%= entry.summary %></p>
|
20
|
+
# <a href="<%= entry.permalink %>">Read more</a>
|
21
|
+
# </section>
|
22
|
+
# <% end %>
|
23
|
+
# </body>
|
24
|
+
# </html>
|
25
|
+
#
|
26
|
+
class FromERB
|
27
|
+
class << self
|
28
|
+
# Build and render an entry.
|
29
|
+
#
|
30
|
+
# @param entry [Lifer::Entry] The entry to be rendered.
|
31
|
+
# @return [String] The rendered entry.
|
32
|
+
def build(entry:)
|
33
|
+
new(entry: entry).render
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Reads the entry as ERB, given our renderer context (see the documentation
|
38
|
+
# for `#build_binding_context`) and renders the production-ready entry.
|
39
|
+
#
|
40
|
+
# @return [String] The rendered entry.
|
41
|
+
def render
|
42
|
+
ERB.new(File.read layout_file).result context
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
attr_reader :context, :entry, :layout_file
|
48
|
+
|
49
|
+
# @private
|
50
|
+
# @param entry [Lifer::Entry] The entry to be rendered.
|
51
|
+
# @return [void]
|
52
|
+
def initialize(entry:)
|
53
|
+
@entry = entry
|
54
|
+
@layout_file = entry.collection.layout_file
|
55
|
+
@context = build_binding_context
|
56
|
+
end
|
57
|
+
|
58
|
+
# @private
|
59
|
+
# Each collection name is provided as a local variable. This allows you to
|
60
|
+
# make ERB files that contain loops like:
|
61
|
+
#
|
62
|
+
# <% my_collection_name.entries.each do |entry| %>
|
63
|
+
# <%= entry.title %>
|
64
|
+
# <% end %>
|
65
|
+
#
|
66
|
+
# @return [Binding] A binding object with preset context from the current
|
67
|
+
# Lifer project and in-scope entry.
|
68
|
+
def build_binding_context
|
69
|
+
binding.tap { |binding|
|
70
|
+
Lifer.collections.each do |collection|
|
71
|
+
binding.local_variable_set collection.name, collection
|
72
|
+
|
73
|
+
collection_context_class.define_method(collection.name) do
|
74
|
+
collection
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
collections = collection_context_class.new Lifer.collections.to_a
|
79
|
+
|
80
|
+
binding.local_variable_set :collections, collections
|
81
|
+
binding.local_variable_set :settings, Lifer.settings
|
82
|
+
binding.local_variable_set :content,
|
83
|
+
ERB.new(entry.to_html).result(binding)
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
# @private
|
88
|
+
def collection_context_class
|
89
|
+
@collection_context_class ||= Class.new(Array) do end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Lifer::Builder::HTML::FromLiquid::Drops
|
2
|
+
# This drop allows users to access Lifer collection information from within
|
3
|
+
# Liquid templates. Example:
|
4
|
+
#
|
5
|
+
# {{ collection.name }}
|
6
|
+
#
|
7
|
+
# {% for entries in collection.entries %}
|
8
|
+
# {{ entry.title }}
|
9
|
+
# {% endfor %}
|
10
|
+
#
|
11
|
+
class CollectionDrop < Liquid::Drop
|
12
|
+
attr_accessor :lifer_collection
|
13
|
+
|
14
|
+
def initialize(lifer_collection) = (@lifer_collection = lifer_collection)
|
15
|
+
|
16
|
+
# The collection name.
|
17
|
+
#
|
18
|
+
# @return [Symbol]
|
19
|
+
def name = (@name ||= lifer_collection.name)
|
20
|
+
|
21
|
+
# Gets all entries in a collection and converts them to entry drops that can
|
22
|
+
# be accessed in Liquid templates. Example:
|
23
|
+
#
|
24
|
+
# {% for entry in collections.root.entries %}
|
25
|
+
# {{ entry.title }}
|
26
|
+
# {% endfor %}
|
27
|
+
#
|
28
|
+
# @return [Array<EntryDrop>]
|
29
|
+
def entries
|
30
|
+
@entries ||= lifer_collection.entries.map {
|
31
|
+
EntryDrop.new _1, collection: self
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
# The collection's layout file path.
|
36
|
+
#
|
37
|
+
# @return [String] The path to the layout file.
|
38
|
+
def layout_file = (@lifer_collection.layout_file)
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Lifer::Builder::HTML::FromLiquid::Drops
|
2
|
+
# This drop allows users to iterate over their Lifer collections in Liquid
|
3
|
+
# templates. Example:
|
4
|
+
#
|
5
|
+
# {% for collection in collections %}
|
6
|
+
# {{ collection.name }}
|
7
|
+
# {% endfor %}
|
8
|
+
#
|
9
|
+
class CollectionsDrop < Liquid::Drop
|
10
|
+
attr_accessor :collections
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@collections = Lifer.collections.map { CollectionDrop.new _1 }
|
14
|
+
end
|
15
|
+
|
16
|
+
# Allow collections to be iterable in Liquid templates.
|
17
|
+
#
|
18
|
+
# @yield [CollectionDrop] All available collection drops.
|
19
|
+
def each(&block)
|
20
|
+
|
21
|
+
collections.each(&block)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Allow collections to be rendered as an array in Liquid templates.
|
25
|
+
#
|
26
|
+
# @return [Array]
|
27
|
+
def to_a = @collections
|
28
|
+
|
29
|
+
# Dynamically define Liquid accessors based on the Lifer project's
|
30
|
+
# collection names. For example, to get the root collection's name:
|
31
|
+
#
|
32
|
+
# {{ collections.root.name }}
|
33
|
+
#
|
34
|
+
# @param arg [String] The name of a collection.
|
35
|
+
# @return [CollectionDrop, NilClass]
|
36
|
+
def liquid_method_missing(arg)
|
37
|
+
collections.detect { arg.to_sym == _1.name }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Lifer::Builder::HTML::FromLiquid::Drops
|
2
|
+
# This drop represents a Lifer entry and allows users to access entry
|
3
|
+
# metadata and content in Liquid templates.
|
4
|
+
#
|
5
|
+
# Example usage:
|
6
|
+
#
|
7
|
+
# <h1>{{ entry.title }}</h1>
|
8
|
+
# <small>Published on <datetime>{{ entry.date }}</datetime></small>
|
9
|
+
#
|
10
|
+
class EntryDrop < Liquid::Drop
|
11
|
+
attr_accessor :lifer_entry, :collection
|
12
|
+
|
13
|
+
def initialize(lifer_entry, collection:)
|
14
|
+
@lifer_entry = lifer_entry
|
15
|
+
@collection = collection
|
16
|
+
end
|
17
|
+
|
18
|
+
# The entry author (or authors).
|
19
|
+
#
|
20
|
+
# @return [String]
|
21
|
+
def author = authors
|
22
|
+
|
23
|
+
# The entry authors (or author).
|
24
|
+
#
|
25
|
+
# @return [String]
|
26
|
+
def authors = (@authors ||= lifer_entry.authors.join(", "))
|
27
|
+
|
28
|
+
# The entry content.
|
29
|
+
#
|
30
|
+
# @return [String]
|
31
|
+
def content = (@content ||= lifer_entry.to_html)
|
32
|
+
|
33
|
+
# The entry date (as a string).
|
34
|
+
#
|
35
|
+
# @return [String]
|
36
|
+
def date = (@date ||= lifer_entry.date)
|
37
|
+
|
38
|
+
# The entry frontmatter data.
|
39
|
+
#
|
40
|
+
# @return [FrontmatterDrop]
|
41
|
+
def frontmatter = (@frontmatter ||= FrontmatterDrop.new(lifer_entry))
|
42
|
+
|
43
|
+
# The path to the entry.
|
44
|
+
#
|
45
|
+
# @return [String] The path to the entry.
|
46
|
+
def path = (@path ||= lifer_entry.path)
|
47
|
+
|
48
|
+
# The entry permalink.
|
49
|
+
#
|
50
|
+
# @return [String] The entry permalink.
|
51
|
+
def permalink = (@permalink ||= lifer_entry.permalink)
|
52
|
+
|
53
|
+
# The summary of the entry.
|
54
|
+
#
|
55
|
+
# @return [String] The summary of the entry.
|
56
|
+
def summary = (@summary ||= lifer_entry.summary)
|
57
|
+
|
58
|
+
# The entry title.
|
59
|
+
#
|
60
|
+
# @return [String] The entry title.
|
61
|
+
def title = (@title ||= lifer_entry.title)
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Lifer::Builder::HTML::FromLiquid::Drops
|
2
|
+
# Markdown entries may contain YAML frontmatter. And if they do, we need a way
|
3
|
+
# for the Liquid templates to access that data.
|
4
|
+
#
|
5
|
+
# Example usage:
|
6
|
+
#
|
7
|
+
# {{ entry.frontmatter.any_available_frontmatter_key }}
|
8
|
+
#
|
9
|
+
class FrontmatterDrop < Liquid::Drop
|
10
|
+
def initialize(entry)
|
11
|
+
@frontmatter = Lifer::Utilities.stringify_keys(entry.frontmatter)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Ensure that the frontmatter can be output wholly into a rendered template
|
15
|
+
# if need be.
|
16
|
+
#
|
17
|
+
# @return [String]
|
18
|
+
def to_s = frontmatter.to_json
|
19
|
+
|
20
|
+
# Dynamically define Liquid accessors based on the Lifer settings object.
|
21
|
+
# For example, to get a collections URI strategy:
|
22
|
+
#
|
23
|
+
# {{ settings.my_collection.uri_strategy }}
|
24
|
+
#
|
25
|
+
# @param arg [String] The name of a collection.
|
26
|
+
# @return [CollectionDrop, NilClass]
|
27
|
+
def liquid_method_missing(arg)
|
28
|
+
value = frontmatter[arg]
|
29
|
+
|
30
|
+
if value.is_a?(Hash)
|
31
|
+
as_drop(value)
|
32
|
+
elsif value.is_a?(Array) && value.all? { _1.is_a?(Hash) }
|
33
|
+
value.map { as_drop(_1) }
|
34
|
+
else
|
35
|
+
value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :frontmatter
|
42
|
+
|
43
|
+
def as_drop(hash) = self.class.new(hash)
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Lifer::Builder::HTML::FromLiquid::Drops
|
2
|
+
# This drop allows users to access the current Lifer project settings from
|
3
|
+
# Liquid templates. Example:
|
4
|
+
#
|
5
|
+
# {{ settings.my_collection.uri_strategy }}
|
6
|
+
#
|
7
|
+
class SettingsDrop < Liquid::Drop
|
8
|
+
def initialize(settings = Lifer.settings)
|
9
|
+
@settings = Lifer::Utilities.stringify_keys(settings)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Ensure the settings tree can be output to a rendered template if need be.
|
13
|
+
#
|
14
|
+
# @return [String]
|
15
|
+
def to_s = settings.to_json
|
16
|
+
|
17
|
+
# Dynamically define Liquid accessors based on the Lifer settings object.
|
18
|
+
# For example, to get a collections URI strategy:
|
19
|
+
#
|
20
|
+
# {{ settings.my_collection.uri_strategy }}
|
21
|
+
#
|
22
|
+
# @param arg [String] The name of a collection.
|
23
|
+
# @return [CollectionDrop, NilClass]
|
24
|
+
def liquid_method_missing(arg)
|
25
|
+
value = settings[arg]
|
26
|
+
|
27
|
+
if value.is_a?(Hash)
|
28
|
+
as_drop(value)
|
29
|
+
elsif value.is_a?(Array) && value.all? { _1.is_a?(Hash) }
|
30
|
+
value.map { as_drop(_1) }
|
31
|
+
else
|
32
|
+
value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_accessor :settings
|
39
|
+
|
40
|
+
def as_drop(hash) = self.class.new(hash)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Lifer::Builder::HTML::FromLiquid
|
2
|
+
# This module contains all of custom Liquid data drops used in order to render
|
3
|
+
# Lifer entries.
|
4
|
+
#
|
5
|
+
# For more information about drops, see the `liquid` gem source code. (The
|
6
|
+
# docs are awful.)
|
7
|
+
#
|
8
|
+
module Drops; end
|
9
|
+
end
|
10
|
+
|
11
|
+
require_relative "drops/collection_drop"
|
12
|
+
require_relative "drops/collections_drop"
|
13
|
+
require_relative "drops/entry_drop"
|
14
|
+
require_relative "drops/frontmatter_drop"
|
15
|
+
require_relative "drops/settings_drop"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# This module provides Liquid filters to be used within Liquid templates.
|
2
|
+
# In many cases these utilities exist to be pseudo-compatible with Jekyll.
|
3
|
+
#
|
4
|
+
# For example, a filter (in a Liquid template):
|
5
|
+
#
|
6
|
+
# {{ entry.date | date_to_xmlschema }}
|
7
|
+
#
|
8
|
+
module Lifer::Builder::HTML::FromLiquid::Filters
|
9
|
+
# @!visibility private
|
10
|
+
Util = Lifer::Utilities
|
11
|
+
|
12
|
+
# Converts date to ISO-8601 format.
|
13
|
+
#
|
14
|
+
# @param input [String] A date string, I hope.
|
15
|
+
# @return [String] The transformed date string.
|
16
|
+
def date_to_xmlschema(input) = Util.date_as_iso8601(input)
|
17
|
+
|
18
|
+
# Transforms a string to kabab-case.
|
19
|
+
#
|
20
|
+
# For example:
|
21
|
+
#
|
22
|
+
# Before: hello_there
|
23
|
+
# After: hello-there
|
24
|
+
# @param input [String] A string.
|
25
|
+
# @return [String] The transformed string.
|
26
|
+
def handleize(input) = Util.handleize(input)
|
27
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class Lifer::Builder::HTML::FromLiquid
|
2
|
+
# Note that if you want to learn more about the shape of this class, check out
|
3
|
+
# `Liquid::Block` in the `liquid` gem.
|
4
|
+
#
|
5
|
+
# The layout tag is a bit magic. The idea here is to emulate how Jekyll
|
6
|
+
# handles `layout:` YAML frontmatter within entries to change the normal
|
7
|
+
# parent layout to an override parent layout--but without the need for
|
8
|
+
# frontmatter.
|
9
|
+
#
|
10
|
+
# The reason we took this strategy was to avoid pre-processing every entry for
|
11
|
+
# frontmatter when we didn't need to. Maybe in the long run this was a bad
|
12
|
+
# call? I don't know.
|
13
|
+
#
|
14
|
+
# Example usage (from a Liquid template):
|
15
|
+
#
|
16
|
+
# {% layout "path/to/my_liquid_layout_template" %}
|
17
|
+
#
|
18
|
+
# (The required `endlayout` tag will be appended to the end of the file
|
19
|
+
# on render if you do not insert it yourself.
|
20
|
+
#
|
21
|
+
class LayoutTag < Liquid::Block
|
22
|
+
# The name of the tag in Liquid templates, `layout`.
|
23
|
+
#
|
24
|
+
NAME = :layout
|
25
|
+
|
26
|
+
# The end name of the tag in Liquid templates, `endlayout`.
|
27
|
+
#
|
28
|
+
ENDNAME = ("end%s" % NAME).to_sym
|
29
|
+
|
30
|
+
def initialize(layout, path, options)
|
31
|
+
@path = path.delete("\"").strip
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
# A layout tag wraps an entire document and outputs it inside of whatever
|
36
|
+
# the `@layout` is. This lets a child document specify a parernt layout!
|
37
|
+
# Very confusing stuff.
|
38
|
+
#
|
39
|
+
# @param context [Liquid::Context] All of the context of the Liquid
|
40
|
+
# document that would be rendered.
|
41
|
+
# @return [String] A rendered document.
|
42
|
+
def render(context)
|
43
|
+
document_context = context.environments.first
|
44
|
+
parse_options = document_context["parse_options"]
|
45
|
+
liquid_file_system = parse_options[:environment].file_system
|
46
|
+
render_options = document_context["render_options"]
|
47
|
+
|
48
|
+
current_layout_file = File
|
49
|
+
.read(document_context["entry"]["collection"]["layout_file"])
|
50
|
+
.gsub(/\{%\s*#{tag_name}.+%\}/, "")
|
51
|
+
|
52
|
+
content_with_layout = Liquid::Template
|
53
|
+
.parse(current_layout_file, error_mode: :strict)
|
54
|
+
.render(document_context, render_options)
|
55
|
+
|
56
|
+
Liquid::Template
|
57
|
+
.parse(
|
58
|
+
liquid_file_system.read_template_file(@path),
|
59
|
+
**parse_options
|
60
|
+
)
|
61
|
+
.render(
|
62
|
+
document_context.merge({"content" => content_with_layout}),
|
63
|
+
**render_options
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require "liquid"
|
2
|
+
|
3
|
+
require_relative "from_liquid/drops"
|
4
|
+
require_relative "from_liquid/filters"
|
5
|
+
require_relative "from_liquid/layout_tag"
|
6
|
+
|
7
|
+
class Lifer::Builder::HTML
|
8
|
+
# If the HTML builder is given a Liquid template, it uses this class to parse
|
9
|
+
# the Liquid into HTML. Lifer project metadata is provided as context. For
|
10
|
+
# example:
|
11
|
+
#
|
12
|
+
# <html>
|
13
|
+
# <head>
|
14
|
+
# <title>{{ collections.my_collection.name }}</title>
|
15
|
+
# </head>
|
16
|
+
#
|
17
|
+
# <body>
|
18
|
+
# <h1>{{ collections.my_collection.name }}</h1>
|
19
|
+
#
|
20
|
+
# {% for entry in collections.my_collection.entries %}
|
21
|
+
# <section>
|
22
|
+
# <h2>{{ entry.title }}</h2>
|
23
|
+
# <p>{{ entry.summary }}</p>
|
24
|
+
# <a href="{{ entry.permalink }}">Read more</a>
|
25
|
+
# </section>
|
26
|
+
# {% endfor %}
|
27
|
+
# </body>
|
28
|
+
# </html>
|
29
|
+
#
|
30
|
+
class FromLiquid
|
31
|
+
class << self
|
32
|
+
# Render and build a Lifer entry.
|
33
|
+
#
|
34
|
+
# @param entry [Lifer::Entry] The entry to render.
|
35
|
+
# @return [String] The rendered entry, ready for output.
|
36
|
+
def build(entry:) = new(entry:).render
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_accessor :entry, :layout_file
|
40
|
+
|
41
|
+
# Reads the entry as Liquid, given our document context, and renders
|
42
|
+
# an entry.
|
43
|
+
#
|
44
|
+
# @return [String] The rendered entry.
|
45
|
+
def render
|
46
|
+
document_context = context.merge!(
|
47
|
+
"content" => Liquid::Template
|
48
|
+
.parse(entry.to_html, **parse_options)
|
49
|
+
.render(context, **render_options)
|
50
|
+
)
|
51
|
+
Liquid::Template
|
52
|
+
.parse(layout, **parse_options)
|
53
|
+
.render(document_context, **render_options)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def initialize(entry:)
|
59
|
+
@entry = entry
|
60
|
+
@layout_file = entry.collection.layout_file
|
61
|
+
end
|
62
|
+
|
63
|
+
def context
|
64
|
+
collections = Drops::CollectionsDrop.new
|
65
|
+
collection = collections
|
66
|
+
.to_a
|
67
|
+
.detect { _1.name.to_sym == entry.collection.name }
|
68
|
+
|
69
|
+
{
|
70
|
+
"collections" => collections,
|
71
|
+
"entry" => Drops::EntryDrop.new(entry, collection:),
|
72
|
+
"parse_options" => parse_options,
|
73
|
+
"render_options" => render_options,
|
74
|
+
"settings" => Drops::SettingsDrop.new
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
# @private
|
79
|
+
# It's possible for the provided layout to request a parent layout, which
|
80
|
+
# makes this method a bit complicated.
|
81
|
+
#
|
82
|
+
# @return [String] A Liquid layout document, ready for parsing.
|
83
|
+
def layout
|
84
|
+
contents = File.read layout_file
|
85
|
+
|
86
|
+
return contents unless contents.match?(/\{%\s*#{LayoutTag::NAME}.*%\}/)
|
87
|
+
|
88
|
+
contents + "\n{% #{LayoutTag::ENDNAME} %}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def liquid_environment
|
92
|
+
@liquid_environment ||= Liquid::Environment.build do |environment|
|
93
|
+
environment.file_system =
|
94
|
+
Liquid::LocalFileSystem.new(Lifer.root, "%s.html.liquid")
|
95
|
+
|
96
|
+
environment.register_filter Lifer::Builder::HTML::FromLiquid::Filters
|
97
|
+
environment.register_tag "layout",
|
98
|
+
Lifer::Builder::HTML::FromLiquid::LayoutTag
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def parse_options
|
103
|
+
{
|
104
|
+
environment: liquid_environment,
|
105
|
+
error_mode: :strict
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
def render_options
|
110
|
+
{
|
111
|
+
strict_variables: true,
|
112
|
+
strict_filters: true
|
113
|
+
}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|