lifer 0.9.0 → 0.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1b6414082e8f6f1d9b18034f2f227890b85ac73b22ab50c25cb2e350812c1a90
4
- data.tar.gz: 5891afc57d102fd67422bf581edc4de839b05efbe10d104529a817391c0e7cad
3
+ metadata.gz: f1d33c1f633f27b80005a2e2e7c1919f43d86a5fdba9f12523dd4dcdb4e47422
4
+ data.tar.gz: 37927e9a39b49363ee0de3f34294c4e8ccf82b83a7a73b8d86ebc1bae8243947
5
5
  SHA512:
6
- metadata.gz: 7adb1041c558a2d60ef525bf1d12af4dd3d587a26f8ae0ef4e2f4c8fb9e04e14c648cf4bd72717aba143e747ccabe970ec1c859cd0c69b5259847887c0f99361
7
- data.tar.gz: a9ec79fce82554f69ba1634ce414b7b63014c8545f8f8c5a6201c194d375d57d09a36128db4f6b242c279bfe0fe419f3b333034268415379c1b9138d372d352f
6
+ metadata.gz: db511f0ace1e617851b7e4de0fd12f9a615c283db318d3c05013cdadbae40ab89cd6be06bc429f4c443caf5d1c67f1d385276f4596788eadbfe3d22916e9c20d
7
+ data.tar.gz: 917d189530c40b4c15fb57a9d1bfce28a809b00fd07267b5a2485997adcb8c3e557dcf242def3a515297b908a5000902a1e7fcbe4bff29240791a88681b2fa1f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  ## Next
2
2
 
3
+ ## v0.10.0
4
+
5
+ These commits collect work to let all layout files (either Liquid or ERB files)
6
+ provide a reference to parent, or "root", layout files that should wrap the via
7
+ frontmatter.
8
+
9
+ Previously, we did this for Liquid layouts using the custom `layout` tag. But
10
+ because there was no equivalent tag for ERB files, I realized it would be less
11
+ work to just provide the value via frontmatter. The same way for every type of
12
+ layout file.
13
+
14
+ End users (just me?) must update their Lifer project Liquid layouts accordingly:
15
+
16
+ ```diff
17
+ - {% layout "layouts/root_layout.html.liquid" %}
18
+ + ---
19
+ + layout: layouts/root_layout.html.liquid
20
+ + ---
21
+
22
+ Layout content.
23
+ ```
24
+
3
25
  ## v0.9.0
4
26
 
5
27
  Atom feeds now support entries with both `#published_at` and `#updated_at`
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lifer (0.9.0)
4
+ lifer (0.10.0)
5
5
  i18n (< 2)
6
6
  kramdown (~> 2.4)
7
7
  liquid (~> 5.6, < 6)
@@ -32,9 +32,9 @@ GEM
32
32
  irb (~> 1.10)
33
33
  reline (>= 0.3.8)
34
34
  diff-lcs (1.5.1)
35
- ffi (1.17.1-arm64-darwin)
36
- ffi (1.17.1-x86_64-darwin)
37
- ffi (1.17.1-x86_64-linux-gnu)
35
+ ffi (1.17.2-arm64-darwin)
36
+ ffi (1.17.2-x86_64-darwin)
37
+ ffi (1.17.2-x86_64-linux-gnu)
38
38
  i18n (1.14.7)
39
39
  concurrent-ruby (~> 1.0)
40
40
  io-console (0.7.2)
@@ -44,7 +44,7 @@ GEM
44
44
  kramdown (2.5.1)
45
45
  rexml (>= 3.3.9)
46
46
  language_server-protocol (3.17.0.3)
47
- liquid (5.8.2)
47
+ liquid (5.8.6)
48
48
  bigdecimal
49
49
  strscan (>= 3.1.1)
50
50
  listen (3.9.0)
@@ -107,7 +107,7 @@ GEM
107
107
  sorbet-runtime (>= 0.5.10782)
108
108
  sorbet-runtime (0.5.11558)
109
109
  stringio (3.1.1)
110
- strscan (3.1.2)
110
+ strscan (3.1.5)
111
111
  xpath (3.2.0)
112
112
  nokogiri (~> 1.8)
113
113
  yard (0.9.37)
@@ -0,0 +1,66 @@
1
+ class Lifer::Builder
2
+ class HTML
3
+ # A base class for all HTML builder adapters. The methods provided by this
4
+ # class are either required or reusable by builder subclasses. See the
5
+ # committed HTML builder adapter classes for example implementations.
6
+ class FromAny
7
+ class << self
8
+ # Build and render an entry.
9
+ #
10
+ # @param entry [Lifer::Entry] The entry to be rendered.
11
+ # @return [String] The rendered entry.
12
+ def build(entry:)
13
+ new(entry: entry).render
14
+ end
15
+ end
16
+
17
+ # The base class does not provide a render method, but any subclass
18
+ # should be expected to.
19
+ #
20
+ # @raise [NotImplementedError]
21
+ def render
22
+ raise NotImplementedError,
23
+ "subclasses must implement a custom `#render` method"
24
+ end
25
+
26
+ private
27
+
28
+ # The frontmatter provided by the layout file.
29
+ #
30
+ # @return [Hash] The frontmatter represented as a hash.
31
+ def frontmatter
32
+ return {} unless frontmatter?
33
+
34
+ Lifer::Utilities.symbolize_keys(
35
+ YAML.load layout_file_contents(raw: true)[Lifer::FRONTMATTER_REGEX, 1],
36
+ permitted_classes: [Time]
37
+ )
38
+ end
39
+
40
+ # Checks whether frontmatter is present in the layout file.
41
+ #
42
+ # @return [boolean]
43
+ def frontmatter?
44
+ @frontmatter ||=
45
+ layout_file_contents(raw: true).match?(Lifer::FRONTMATTER_REGEX)
46
+ end
47
+
48
+ # The contents of the layout file.
49
+ #
50
+ # @param raw [boolean] Whether to include or exclude frontmatter from
51
+ # the contents.
52
+ # @return [String] The contents of the layout file.
53
+ def layout_file_contents(raw: false)
54
+ cache_variable = "@layout_file_contents_#{raw}"
55
+ cached_value = instance_variable_get cache_variable
56
+
57
+ return cached_value if cached_value
58
+
59
+ contents = File.read layout_file
60
+ contents = contents.gsub(Lifer::FRONTMATTER_REGEX, "") unless raw
61
+
62
+ instance_variable_set cache_variable, contents
63
+ end
64
+ end
65
+ end
66
+ end
@@ -23,23 +23,21 @@ class Lifer::Builder::HTML
23
23
  # </body>
24
24
  # </html>
25
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
-
26
+ class FromERB < FromAny
37
27
  # Reads the entry as ERB, given our renderer context (see the documentation
38
28
  # for `#build_binding_context`) and renders the production-ready entry.
39
29
  #
40
30
  # @return [String] The rendered entry.
41
31
  def render
42
- ERB.new(File.read layout_file).result context
32
+ document = ERB.new(layout_file_contents).result context
33
+
34
+ return document unless (relative_layout_path = frontmatter[:layout])
35
+
36
+ document_binding = binding.tap { |binding|
37
+ binding.local_variable_set :content, document
38
+ }
39
+ layout_path = "%s/%s" % [Lifer.root, relative_layout_path]
40
+ ERB.new(File.read layout_path).result(document_binding)
43
41
  end
44
42
 
45
43
  private
@@ -22,8 +22,6 @@ class Lifer::Builder::HTML::FromLiquid
22
22
  Liquid::LocalFileSystem.new(Lifer.root, "%s.html.liquid")
23
23
 
24
24
  environment.register_filter Lifer::Builder::HTML::FromLiquid::Filters
25
- environment.register_tag "layout",
26
- Lifer::Builder::HTML::FromLiquid::LayoutTag
27
25
  end
28
26
  end
29
27
  end
@@ -1,10 +1,5 @@
1
1
  require "liquid"
2
2
 
3
- require_relative "from_liquid/drops"
4
- require_relative "from_liquid/filters"
5
- require_relative "from_liquid/layout_tag"
6
- require_relative "from_liquid/liquid_env"
7
-
8
3
  class Lifer::Builder::HTML
9
4
  # If the HTML builder is given a Liquid template, it uses this class to parse
10
5
  # the Liquid into HTML. Lifer project metadata is provided as context. For
@@ -28,16 +23,10 @@ class Lifer::Builder::HTML
28
23
  # </body>
29
24
  # </html>
30
25
  #
31
- class FromLiquid
32
- class << self
33
- # Render and build a Lifer entry.
34
- #
35
- # @param entry [Lifer::Entry] The entry to render.
36
- # @return [String] The rendered entry, ready for output.
37
- def build(entry:) = new(entry:).render
38
- end
39
-
40
- attr_accessor :entry, :layout_file
26
+ class FromLiquid < FromAny
27
+ require_relative "from_liquid/drops"
28
+ require_relative "from_liquid/filters"
29
+ require_relative "from_liquid/liquid_env"
41
30
 
42
31
  # Reads the entry as Liquid, given our document context, and renders
43
32
  # an entry.
@@ -49,13 +38,23 @@ class Lifer::Builder::HTML
49
38
  .parse(entry.to_html, **parse_options)
50
39
  .render(context, **render_options)
51
40
  )
41
+ document = Liquid::Template
42
+ .parse(layout_file_contents, **parse_options)
43
+ .render(document_context, **render_options)
44
+
45
+ return document unless (relative_layout_path = frontmatter[:layout])
46
+
47
+ layout_path = "%s/%s" % [Lifer.root, relative_layout_path]
48
+ document_context = context.merge! "content" => document
52
49
  Liquid::Template
53
- .parse(layout, **parse_options)
50
+ .parse(File.read layout_path, **parse_options)
54
51
  .render(document_context, **render_options)
55
52
  end
56
53
 
57
54
  private
58
55
 
56
+ attr_accessor :entry, :layout_file
57
+
59
58
  def initialize(entry:)
60
59
  @entry = entry
61
60
  @layout_file = entry.collection.layout_file
@@ -79,19 +78,6 @@ class Lifer::Builder::HTML
79
78
  }
80
79
  end
81
80
 
82
- # @private
83
- # It's possible for the provided layout to request a parent layout, which
84
- # makes this method a bit complicated.
85
- #
86
- # @return [String] A Liquid layout document, ready for parsing.
87
- def layout
88
- contents = File.read layout_file
89
-
90
- return contents unless contents.match?(/\{%\s*#{LayoutTag::NAME}.*%\}/)
91
-
92
- contents + "\n{% #{LayoutTag::ENDNAME} %}"
93
- end
94
-
95
81
  def liquid_environment = (@liquid_environment ||= LiquidEnv.global)
96
82
 
97
83
  def parse_options
@@ -103,6 +89,7 @@ class Lifer::Builder::HTML
103
89
 
104
90
  def render_options
105
91
  {
92
+ registers: {file_system: liquid_environment.file_system},
106
93
  strict_variables: true,
107
94
  strict_filters: true
108
95
  }
@@ -33,6 +33,7 @@ require "fileutils"
33
33
  class Lifer::Builder::HTML < Lifer::Builder
34
34
  self.name = :html
35
35
 
36
+ require_relative "html/from_any"
36
37
  require_relative "html/from_erb"
37
38
  require_relative "html/from_liquid"
38
39
 
data/lib/lifer/entry.rb CHANGED
@@ -39,10 +39,6 @@ module Lifer
39
39
  #
40
40
  FILENAME_DATE_FORMAT = /^(\d{4}-\d{1,2}-\d{1,2})-/
41
41
 
42
- # We expect frontmatter to be provided in the following format.
43
- #
44
- FRONTMATTER_REGEX = /^---\n(.*)---\n/m
45
-
46
42
  # If tags are represented in YAML frontmatter as a string, they're split on
47
43
  # commas and/or spaces.
48
44
  #
@@ -147,7 +143,7 @@ module Lifer
147
143
  def body
148
144
  return full_text.strip unless frontmatter?
149
145
 
150
- full_text.gsub(FRONTMATTER_REGEX, "").strip
146
+ full_text.gsub(Lifer::FRONTMATTER_REGEX, "").strip
151
147
  end
152
148
 
153
149
  def feedable?
@@ -168,7 +164,8 @@ module Lifer
168
164
  return {} unless frontmatter?
169
165
 
170
166
  Lifer::Utilities.symbolize_keys(
171
- YAML.load(full_text[FRONTMATTER_REGEX, 1], permitted_classes: [Time])
167
+ YAML.load full_text[Lifer::FRONTMATTER_REGEX, 1],
168
+ permitted_classes: [Time]
172
169
  )
173
170
  end
174
171
 
@@ -326,6 +323,6 @@ module Lifer
326
323
  File.basename(file).match(FILENAME_DATE_FORMAT)[1]
327
324
  end
328
325
 
329
- def frontmatter? = (full_text && full_text.match?(FRONTMATTER_REGEX))
326
+ def frontmatter? = (full_text && full_text.match?(Lifer::FRONTMATTER_REGEX))
330
327
  end
331
328
  end
data/lib/lifer/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Lifer
2
- VERSION = "0.9.0"
2
+ VERSION = "0.10.0"
3
3
  end
data/lib/lifer.rb CHANGED
@@ -24,6 +24,10 @@ module Lifer
24
24
  "(\\/\\.)+" # Contains a dot directory.
25
25
  ] | IGNORE_DIRECTORIES.map { |d| "^(#{d})" }
26
26
 
27
+ # We expect frontmatter in any file to be provided in the following format.
28
+ #
29
+ FRONTMATTER_REGEX = /^---\n(.*)---\n/m
30
+
27
31
  class << self
28
32
  # The first time `Lifer.brain` is referenced, we build a new `Lifer::Brain`
29
33
  # object that is used and reused until the current process has ended.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lifer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - benjamin wil
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-30 00:00:00.000000000 Z
11
+ date: 2025-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -159,6 +159,7 @@ files:
159
159
  - lib/lifer/brain.rb
160
160
  - lib/lifer/builder.rb
161
161
  - lib/lifer/builder/html.rb
162
+ - lib/lifer/builder/html/from_any.rb
162
163
  - lib/lifer/builder/html/from_erb.rb
163
164
  - lib/lifer/builder/html/from_liquid.rb
164
165
  - lib/lifer/builder/html/from_liquid/drops.rb
@@ -170,7 +171,6 @@ files:
170
171
  - lib/lifer/builder/html/from_liquid/drops/tag_drop.rb
171
172
  - lib/lifer/builder/html/from_liquid/drops/tags_drop.rb
172
173
  - lib/lifer/builder/html/from_liquid/filters.rb
173
- - lib/lifer/builder/html/from_liquid/layout_tag.rb
174
174
  - lib/lifer/builder/html/from_liquid/liquid_env.rb
175
175
  - lib/lifer/builder/rss.rb
176
176
  - lib/lifer/builder/txt.rb
@@ -210,8 +210,8 @@ licenses:
210
210
  - MIT
211
211
  metadata:
212
212
  allowed_push_host: https://rubygems.org
213
- homepage_uri: https://github.com/benjaminwil/lifer/blob/v0.9.0/README.md
214
- source_code_uri: https://github.com/benjaminwil/lifer/tree/v0.9.0
213
+ homepage_uri: https://github.com/benjaminwil/lifer/blob/v0.10.0/README.md
214
+ source_code_uri: https://github.com/benjaminwil/lifer/tree/v0.10.0
215
215
  changelog_uri: https://github.com/benjaminwil/lifer/blob/main/CHANGELOG.md
216
216
  post_install_message:
217
217
  rdoc_options: []
@@ -1,66 +0,0 @@
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
- # {% layout "path/to/my_liquid_layout_template" %}
16
- #
17
- # (The required `endlayout` tag will be appended to the end of the file
18
- # on render if you do not insert it yourself.
19
- #
20
- class LayoutTag < Liquid::Block
21
- # The name of the tag in Liquid templates, `layout`.
22
- #
23
- NAME = :layout
24
-
25
- # The end name of the tag in Liquid templates, `endlayout`.
26
- #
27
- ENDNAME = ("end%s" % NAME).to_sym
28
-
29
- def initialize(layout, path, options)
30
- @path = path.delete("\"").strip
31
- super
32
- end
33
-
34
- # A layout tag wraps an entire document and outputs it inside of whatever
35
- # the `@layout` is. This lets a child document specify a parernt layout!
36
- # Very confusing stuff.
37
- #
38
- # @param context [Liquid::Context] All of the context of the Liquid
39
- # document that would be rendered.
40
- # @return [String] A rendered document.
41
- def render(context)
42
- document_context = context.environments.first
43
- parse_options = document_context["parse_options"]
44
- liquid_file_system = parse_options[:environment].file_system
45
- render_options = document_context["render_options"]
46
-
47
- current_layout_file = File
48
- .read(document_context["entry"]["collection"]["layout_file"])
49
- .gsub(/\{%\s*#{tag_name}.+%\}/, "")
50
-
51
- content_with_layout = Liquid::Template
52
- .parse(current_layout_file, **parse_options)
53
- .render(document_context, **render_options)
54
-
55
- Liquid::Template
56
- .parse(
57
- liquid_file_system.read_template_file(@path),
58
- **parse_options
59
- )
60
- .render(
61
- document_context.merge({"content" => content_with_layout}),
62
- **render_options
63
- )
64
- end
65
- end
66
- end