bridgetown-core 0.19.3 → 0.20.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.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/bridgetown-core.gemspec +1 -1
  3. data/lib/bridgetown-core.rb +30 -11
  4. data/lib/bridgetown-core/cleaner.rb +7 -1
  5. data/lib/bridgetown-core/collection.rb +173 -77
  6. data/lib/bridgetown-core/commands/base.rb +9 -0
  7. data/lib/bridgetown-core/commands/configure.rb +4 -0
  8. data/lib/bridgetown-core/commands/console.rb +4 -0
  9. data/lib/bridgetown-core/concerns/data_accessible.rb +1 -0
  10. data/lib/bridgetown-core/concerns/site/configurable.rb +7 -3
  11. data/lib/bridgetown-core/concerns/site/content.rb +57 -15
  12. data/lib/bridgetown-core/concerns/site/processable.rb +1 -0
  13. data/lib/bridgetown-core/concerns/site/renderable.rb +26 -0
  14. data/lib/bridgetown-core/concerns/site/writable.rb +11 -1
  15. data/lib/bridgetown-core/concerns/validatable.rb +1 -0
  16. data/lib/bridgetown-core/configuration.rb +39 -19
  17. data/lib/bridgetown-core/converter.rb +14 -0
  18. data/lib/bridgetown-core/converters/identity.rb +0 -9
  19. data/lib/bridgetown-core/converters/markdown.rb +14 -4
  20. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +3 -0
  21. data/lib/bridgetown-core/current.rb +10 -0
  22. data/lib/bridgetown-core/document.rb +6 -14
  23. data/lib/bridgetown-core/drops/collection_drop.rb +1 -1
  24. data/lib/bridgetown-core/drops/page_drop.rb +4 -0
  25. data/lib/bridgetown-core/drops/resource_drop.rb +81 -0
  26. data/lib/bridgetown-core/drops/site_drop.rb +33 -8
  27. data/lib/bridgetown-core/drops/unified_payload_drop.rb +4 -0
  28. data/lib/bridgetown-core/entry_filter.rb +10 -23
  29. data/lib/bridgetown-core/errors.rb +0 -2
  30. data/lib/bridgetown-core/filters.rb +2 -1
  31. data/lib/bridgetown-core/generators/prototype_generator.rb +37 -19
  32. data/lib/bridgetown-core/layout.rb +2 -2
  33. data/lib/bridgetown-core/liquid_renderer/file.rb +1 -0
  34. data/lib/bridgetown-core/liquid_renderer/table.rb +1 -0
  35. data/lib/bridgetown-core/model/base.rb +138 -0
  36. data/lib/bridgetown-core/model/builder_origin.rb +40 -0
  37. data/lib/bridgetown-core/model/file_origin.rb +119 -0
  38. data/lib/bridgetown-core/model/origin.rb +38 -0
  39. data/lib/bridgetown-core/page.rb +9 -1
  40. data/lib/bridgetown-core/plugin_manager.rb +0 -2
  41. data/lib/bridgetown-core/publisher.rb +7 -1
  42. data/lib/bridgetown-core/reader.rb +25 -12
  43. data/lib/bridgetown-core/readers/data_reader.rb +3 -4
  44. data/lib/bridgetown-core/readers/post_reader.rb +1 -1
  45. data/lib/bridgetown-core/regenerator.rb +8 -1
  46. data/lib/bridgetown-core/related_posts.rb +1 -1
  47. data/lib/bridgetown-core/renderer.rb +5 -12
  48. data/lib/bridgetown-core/resource/base.rb +275 -0
  49. data/lib/bridgetown-core/resource/destination.rb +49 -0
  50. data/lib/bridgetown-core/resource/permalink_processor.rb +179 -0
  51. data/lib/bridgetown-core/resource/taxonomy_term.rb +25 -0
  52. data/lib/bridgetown-core/resource/taxonomy_type.rb +47 -0
  53. data/lib/bridgetown-core/resource/transformer.rb +173 -0
  54. data/lib/bridgetown-core/ruby_template_view.rb +4 -0
  55. data/lib/bridgetown-core/site.rb +9 -1
  56. data/lib/bridgetown-core/static_file.rb +33 -10
  57. data/lib/bridgetown-core/url.rb +1 -0
  58. data/lib/bridgetown-core/utils.rb +40 -40
  59. data/lib/bridgetown-core/utils/platforms.rb +1 -0
  60. data/lib/bridgetown-core/version.rb +2 -2
  61. data/lib/site_template/webpack.config.js.erb +8 -6
  62. metadata +28 -21
  63. data/lib/bridgetown-core/page_without_a_file.rb +0 -17
  64. data/lib/bridgetown-core/readers/collection_reader.rb +0 -23
  65. data/lib/bridgetown-core/utils/exec.rb +0 -26
  66. data/lib/bridgetown-core/utils/internet.rb +0 -37
  67. data/lib/bridgetown-core/utils/win_tz.rb +0 -75
@@ -16,8 +16,8 @@ module Bridgetown
16
16
  #
17
17
  # Returns @content, a Hash of the .yaml, .yml,
18
18
  # .json, and .csv files in the base directory
19
- def read(dir)
20
- base = site.in_source_dir(dir)
19
+ def read
20
+ base = site.in_source_dir(site.config.data_dir)
21
21
  read_data_to(base, @content)
22
22
  merge_environment_specific_metadata!
23
23
  @content = @content.with_dot_access
@@ -31,7 +31,7 @@ module Bridgetown
31
31
  #
32
32
  # Returns nothing
33
33
  def read_data_to(dir, data)
34
- return unless File.directory?(dir) && !@entry_filter.symlink?(dir)
34
+ return unless File.directory?(dir)
35
35
 
36
36
  entries = Dir.chdir(dir) do
37
37
  Dir["*.{yaml,yml,json,csv,tsv}"] + Dir["*"].select { |fn| File.directory?(fn) }
@@ -39,7 +39,6 @@ module Bridgetown
39
39
 
40
40
  entries.each do |entry|
41
41
  path = @site.in_source_dir(dir, entry)
42
- next if @entry_filter.symlink?(path)
43
42
 
44
43
  if File.directory?(path)
45
44
  read_data_to(
@@ -98,7 +98,7 @@ module Bridgetown
98
98
  if item.is_a?(Document)
99
99
  site.posts.docs << item
100
100
  elsif item.is_a?(StaticFile)
101
- site.posts.files << item
101
+ site.posts.static_files << item
102
102
  site.static_files << item
103
103
  end
104
104
 
@@ -19,7 +19,7 @@ module Bridgetown
19
19
  # Checks if a renderable object needs to be regenerated
20
20
  #
21
21
  # Returns a boolean.
22
- def regenerate?(document)
22
+ def regenerate?(document) # rubocop:todo Metrics/CyclomaticComplexity
23
23
  return true if disabled
24
24
 
25
25
  case document
@@ -27,6 +27,8 @@ module Bridgetown
27
27
  regenerate_page?(document)
28
28
  when Document
29
29
  regenerate_document?(document)
30
+ when Bridgetown::Resource::Base
31
+ regenerate_resource?(document)
30
32
  else
31
33
  source_path = document.respond_to?(:path) ? document.path : nil
32
34
  dest_path = document.destination(@site.dest) if document.respond_to?(:destination)
@@ -176,6 +178,11 @@ module Bridgetown
176
178
  )
177
179
  end
178
180
 
181
+ # TODO: need to manage dependencies, but for now always regenerate
182
+ def regenerate_resource?(_resource)
183
+ true
184
+ end
185
+
179
186
  def existing_file_modified?(path)
180
187
  # If one of this file dependencies have been modified,
181
188
  # set the regeneration bit for both the dependency and the file to true
@@ -42,7 +42,7 @@ module Bridgetown
42
42
  end
43
43
 
44
44
  def lsi_related_posts
45
- self.class.lsi.find_related(post, 11)
45
+ self.class.lsi.find_related(post, 11) - [post]
46
46
  end
47
47
 
48
48
  def most_recent_posts
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bridgetown
4
+ # This class handles the output rendering and layout placement of pages and
5
+ # documents. For rendering of resources in particular, see Bridgetown::Resource::Transformer
4
6
  class Renderer
5
7
  attr_reader :document, :site
6
8
 
@@ -121,21 +123,11 @@ module Bridgetown
121
123
  "in #{document.relative_path} does not exist."
122
124
  end
123
125
 
124
- def converters_for_layout(layout)
125
- site.converters.select do |converter|
126
- if converter.method(:matches).arity == 1
127
- converter.matches(layout.ext)
128
- else
129
- converter.matches(layout.ext, layout)
130
- end
131
- end.sort
132
- end
133
-
134
126
  # Render layout content into document.output
135
127
  #
136
128
  # Returns String rendered content
137
129
  def render_layout(output, layout)
138
- layout_converters = converters_for_layout(layout)
130
+ layout_converters = site.matched_converters_for_convertible(layout)
139
131
 
140
132
  layout_content = layout.content.dup
141
133
  layout_converters.reduce(layout_content) do |layout_output, converter|
@@ -163,7 +155,8 @@ module Bridgetown
163
155
 
164
156
  def permalink_ext
165
157
  document_permalink = document.permalink
166
- if document_permalink && !document_permalink.end_with?("/")
158
+ if document_permalink &&
159
+ !document_permalink.end_with?("/")
167
160
  permalink_ext = File.extname(document_permalink)
168
161
  permalink_ext unless permalink_ext.empty?
169
162
  end
@@ -0,0 +1,275 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Resource
5
+ class Base
6
+ include Comparable
7
+ include Bridgetown::Publishable
8
+ include Bridgetown::LayoutPlaceable
9
+ include Bridgetown::LiquidRenderable
10
+
11
+ # @return [HashWithDotAccess::Hash]
12
+ attr_reader :data
13
+
14
+ # @return [Destination]
15
+ attr_reader :destination
16
+
17
+ # @return [Bridgetown::Model::Base]
18
+ attr_reader :model
19
+
20
+ # @return [Bridgetown::Site]
21
+ attr_reader :site
22
+
23
+ # @return [String]
24
+ attr_accessor :content, :untransformed_content, :output
25
+
26
+ # @!attribute [r] collection
27
+ # @return [Bridgetown::Collection] collection of this resource
28
+
29
+ # @!attribute [r] relative_path
30
+ # @return [Pathname] the relative path of source file or
31
+ # file-like origin
32
+
33
+ DATE_FILENAME_MATCHER = %r!^(?>.+/)*?(\d{2,4}-\d{1,2}-\d{1,2})-([^/]*)(\.[^.]+)$!.freeze
34
+
35
+ # @param site [Bridgetown::Site]
36
+ # @param origin [Bridgetown::Resource::Origin]
37
+ def initialize(model:)
38
+ @model = model
39
+ @site = model.site
40
+ self.data = HashWithDotAccess::Hash.new
41
+
42
+ trigger_hooks(:post_init)
43
+ end
44
+
45
+ # @return [Bridgetown::Collection]
46
+ def collection
47
+ model.collection
48
+ end
49
+
50
+ # @return [Pathname]
51
+ def relative_path
52
+ model.origin.relative_path
53
+ end
54
+
55
+ # @return [Bridgetown::Resource::Transformer]
56
+ def transformer
57
+ @transformer ||= Bridgetown::Resource::Transformer.new(self)
58
+ end
59
+
60
+ # @param new_data [HashWithDotAccess::Hash]
61
+ def data=(new_data)
62
+ unless new_data.is_a?(HashWithDotAccess::Hash)
63
+ raise "#{self.class} data should be of type HashWithDotAccess::Hash"
64
+ end
65
+
66
+ @data = new_data
67
+ @data.default_proc = proc do |_, key|
68
+ site.frontmatter_defaults.find(
69
+ relative_path.to_s,
70
+ collection.label.to_sym,
71
+ key.to_s
72
+ )
73
+ end
74
+ end
75
+
76
+ # @return [Bridgetown::Resource::Base]
77
+ def read!
78
+ self.data = model.data_attributes
79
+ self.content = model.content # could be nil
80
+
81
+ unless collection.data?
82
+ self.untransformed_content = content
83
+ determine_slug_and_date
84
+ normalize_categories_and_tags
85
+ import_taxonomies_from_data
86
+ end
87
+
88
+ @destination = Destination.new(self) if requires_destination?
89
+
90
+ trigger_hooks(:post_read)
91
+
92
+ self
93
+ end
94
+ alias_method :read, :read! # TODO: eventually use the bang version only
95
+
96
+ def transform!
97
+ transformer.process! unless collection.data?
98
+ end
99
+
100
+ def trigger_hooks(hook_name, *args)
101
+ Bridgetown::Hooks.trigger collection.label.to_sym, hook_name, self, *args if collection
102
+ Bridgetown::Hooks.trigger :resources, hook_name, self, *args
103
+ end
104
+
105
+ def around_hook(hook_suffix)
106
+ trigger_hooks :"pre_#{hook_suffix}"
107
+ yield
108
+ trigger_hooks :"post_#{hook_suffix}"
109
+ end
110
+
111
+ def relative_path_basename_without_prefix
112
+ return_path = Pathname.new("")
113
+ relative_path.each_filename do |filename|
114
+ if matches = DATE_FILENAME_MATCHER.match(filename) # rubocop:disable Lint/AssignmentInCondition
115
+ filename = matches[2] + matches[3]
116
+ end
117
+
118
+ return_path += filename unless filename.starts_with?("_")
119
+ end
120
+
121
+ (return_path.dirname + return_path.basename(".*")).to_s
122
+ end
123
+
124
+ def basename_without_ext
125
+ relative_path.basename(".*").to_s
126
+ end
127
+
128
+ # @return [String]
129
+ def extname
130
+ relative_path.extname
131
+ end
132
+
133
+ # @return [String, nil]
134
+ def permalink
135
+ data&.permalink
136
+ end
137
+
138
+ def path
139
+ (model.origin.respond_to?(:original_path) ? model.origin.original_path : relative_path).to_s
140
+ end
141
+
142
+ def absolute_url
143
+ format_url destination&.absolute_url
144
+ end
145
+
146
+ def relative_url
147
+ format_url destination&.relative_url
148
+ end
149
+ alias_method :id, :relative_url
150
+
151
+ def date
152
+ data["date"] ||= site.time # TODO: this doesn't reflect documented behavior
153
+ end
154
+
155
+ # @return [Hash<String, Hash<String => Bridgetown::Resource::TaxonomyType,
156
+ # Array<Bridgetown::Resource::TaxonomyTerm>>>]
157
+ def taxonomies
158
+ @taxonomies ||= site.taxonomy_types.values.each_with_object(
159
+ HashWithDotAccess::Hash.new
160
+ ) do |taxonomy, hsh|
161
+ hsh[taxonomy.label] = {
162
+ type: taxonomy,
163
+ terms: [],
164
+ }
165
+ end
166
+ end
167
+
168
+ def requires_destination?
169
+ collection.write? && data.config&.output != false
170
+ end
171
+
172
+ def write?
173
+ requires_destination? && site.publisher.publish?(self)
174
+ end
175
+
176
+ # Write the generated Document file to the destination directory.
177
+ #
178
+ # dest - The String path to the destination dir.
179
+ #
180
+ # Returns nothing.
181
+ def write(_dest = nil)
182
+ destination.write(output)
183
+ trigger_hooks(:post_write)
184
+ end
185
+
186
+ def to_s
187
+ output || content || ""
188
+ end
189
+
190
+ # Create a Liquid-understandable version of this resource.
191
+ #
192
+ # @return [Drops::DocumentDrop] represents this resource's data.
193
+ def to_liquid
194
+ @to_liquid ||= Drops::ResourceDrop.new(self)
195
+ end
196
+
197
+ def inspect
198
+ "#<#{self.class} [#{collection.label}] #{relative_path}>"
199
+ end
200
+
201
+ # Compare this document against another document.
202
+ # Comparison is a comparison between the 2 paths of the documents.
203
+ #
204
+ # Returns -1, 0, +1 or nil depending on whether this doc's path is less than,
205
+ # equal or greater than the other doc's path. See String#<=> for more details.
206
+ def <=>(other) # rubocop:todo Metrics/AbcSize
207
+ return nil unless other.respond_to?(:data)
208
+
209
+ if data.date.respond_to?(:to_datetime) && other.data.date.respond_to?(:to_datetime)
210
+ return data.date.to_datetime <=> other.data.date.to_datetime
211
+ end
212
+
213
+ cmp = data["date"] <=> other.data["date"]
214
+ cmp = path <=> other.path if cmp.nil? || cmp.zero?
215
+ cmp
216
+ end
217
+
218
+ def next_resource
219
+ pos = collection.resources.index { |item| item.equal?(self) }
220
+ collection.resources[pos + 1] if pos && pos < collection.resources.length - 1
221
+ end
222
+ alias_method :next_doc, :next_resource
223
+
224
+ def previous_resource
225
+ pos = collection.docs.index { |item| item.equal?(self) }
226
+ collection.resources[pos - 1] if pos&.positive?
227
+ end
228
+ alias_method :previous_doc, :previous_resource
229
+
230
+ private
231
+
232
+ def determine_slug_and_date
233
+ return unless relative_path.to_s =~ DATE_FILENAME_MATCHER
234
+
235
+ new_date, slug = Regexp.last_match.captures
236
+ modify_date(new_date)
237
+
238
+ slug.gsub!(%r!\.*\z!, "")
239
+ data.slug ||= slug
240
+ end
241
+
242
+ def modify_date(new_date)
243
+ if !data.date || data.date.to_i == site.time.to_i
244
+ data.date = Utils.parse_date(
245
+ new_date,
246
+ "Document '#{relative_path}' does not have a valid date in the #{model}."
247
+ )
248
+ end
249
+ end
250
+
251
+ def normalize_categories_and_tags
252
+ data.categories = Bridgetown::Utils.pluralized_array_from_hash(
253
+ data, :category, :categories
254
+ )
255
+ data.tags = Bridgetown::Utils.pluralized_array_from_hash(
256
+ data, :tag, :tags
257
+ )
258
+ end
259
+
260
+ def import_taxonomies_from_data
261
+ taxonomies.each do |_label, metadata|
262
+ Array(data[metadata.type.key]).each do |term|
263
+ metadata.terms << TaxonomyTerm.new(
264
+ resource: self, label: term, type: metadata.type
265
+ )
266
+ end
267
+ end
268
+ end
269
+
270
+ def format_url(url)
271
+ url.to_s.sub(%r{index\.html?$}, "").sub(%r{\.html?$}, "")
272
+ end
273
+ end
274
+ end
275
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Resource
5
+ class Destination
6
+ # @return [Bridgetown::Resource::Base]
7
+ attr_accessor :resource
8
+
9
+ # @return [String]
10
+ attr_accessor :output_ext
11
+
12
+ # @param resource [Bridgetown::Resource::Base]
13
+ def initialize(resource)
14
+ @resource = resource
15
+ @output_ext = resource.transformer.final_ext
16
+ end
17
+
18
+ def absolute_url
19
+ Addressable::URI.parse(
20
+ resource.site.config.url.to_s + relative_url
21
+ ).normalize.to_s
22
+ end
23
+
24
+ def relative_url
25
+ @processor ||= PermalinkProcessor.new(resource)
26
+ @processor.transform
27
+ end
28
+
29
+ def final_ext
30
+ output_ext || resource.extname
31
+ end
32
+
33
+ def output_path
34
+ path = URL.unescape_path(relative_url)
35
+ path = path.delete_prefix(resource.site.baseurl) if resource.site.baseurl.present?
36
+ path = resource.site.in_dest_dir(path)
37
+ path = File.join(path, "index.html") if relative_url.end_with? "/"
38
+ path
39
+ end
40
+
41
+ def write(output)
42
+ path = output_path
43
+ FileUtils.mkdir_p(File.dirname(path))
44
+ Bridgetown.logger.debug "Writing:", path
45
+ File.write(path, output, mode: "wb")
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Resource
5
+ class PermalinkProcessor
6
+ # @return [Bridgetown::Resource::Base]
7
+ attr_accessor :resource
8
+
9
+ attr_accessor :slugify_mode
10
+
11
+ def self.placeholder_processors
12
+ @placeholder_processors || {}
13
+ end
14
+
15
+ def self.register_placeholder(key, block)
16
+ @placeholder_processors ||= {}
17
+ @placeholder_processors[key] = block
18
+ end
19
+
20
+ def initialize(resource)
21
+ @resource = resource
22
+ @slugify_mode = @resource.site.config.slugify_mode
23
+ end
24
+
25
+ def final_ext
26
+ resource.method(:destination).arity == 1 ? resource.extname : resource.destination.final_ext
27
+ end
28
+
29
+ def transform
30
+ permalink = resource.data.permalink ||
31
+ permalink_for_permalink_style(resource.collection.default_permalink)
32
+
33
+ # Strip out file extension and process each segment of a URL to swap out
34
+ # placeholders such as :categories or :title
35
+ url_segments = permalink.sub(%r{\.[^/]*$}, "").split("/")
36
+ new_url = url_segments.map do |segment|
37
+ segment.starts_with?(":") ? process_segment(segment.sub(%r{^:}, "")) : segment
38
+ end.select(&:present?).join("/")
39
+
40
+ # No relative URLs should ever end in /index.html
41
+ new_url.sub!(%r{/index$}, "") if final_ext == ".html"
42
+
43
+ add_baseurl finalize_permalink(new_url, permalink)
44
+ end
45
+
46
+ def process_segment(segment)
47
+ segment = segment.to_sym
48
+ if self.class.placeholder_processors[segment]
49
+ segment_value = self.class.placeholder_processors[segment].(resource)
50
+ if segment_value.is_a?(Hash)
51
+ segment_value[:raw_value]
52
+ elsif segment_value.is_a?(Array)
53
+ segment_value.map do |subsegment|
54
+ Utils.slugify(subsegment, mode: slugify_mode)
55
+ end.join("/")
56
+ else
57
+ Utils.slugify(segment_value, mode: slugify_mode)
58
+ end
59
+ else
60
+ segment
61
+ end
62
+ end
63
+
64
+ def permalink_for_permalink_style(permalink_style)
65
+ collection_prefix = ("/:collection" unless resource.collection.builtin?)
66
+
67
+ case permalink_style.to_sym
68
+ when :pretty
69
+ "#{collection_prefix}/:categories/:year/:month/:day/:slug/"
70
+ when :pretty_ext, :date
71
+ "#{collection_prefix}/:categories/:year/:month/:day/:slug.*"
72
+ when :simple
73
+ "#{collection_prefix}/:categories/:slug/"
74
+ when :simple_ext
75
+ "#{collection_prefix}/:categories/:slug.*"
76
+ else
77
+ permalink_style.to_s
78
+ end
79
+ end
80
+
81
+ # @param new_url [String]
82
+ # @param permalink [String]
83
+ def finalize_permalink(new_url, permalink)
84
+ # Handle .* style permalinks or files not ending in .html
85
+ if permalink.ends_with?(".*") || !%r{\.html?$}.match?(final_ext)
86
+ "/#{new_url}#{final_ext}"
87
+ # If permalink includes the file extension, add it back in to the URL
88
+ elsif permalink =~ %r{\.[^/]*$}
89
+ "/#{new_url}#{Regexp.last_match[0]}"
90
+ # Ensure index-style URLs get output correctly
91
+ elsif permalink.ends_with?("/")
92
+ "/#{new_url}/".sub(%r{^/index/$}, "/")
93
+ # We good :)
94
+ else
95
+ "/#{new_url}"
96
+ end
97
+ end
98
+
99
+ def add_baseurl(permalink)
100
+ resource.site.baseurl.present? ? "#{resource.site.baseurl}#{permalink}" : permalink
101
+ end
102
+
103
+ ### Default Placeholders Processors
104
+
105
+ # @param resource [Bridgetown::Resource::Base]
106
+ register_placeholder :path, ->(resource) do
107
+ { raw_value: resource.relative_path_basename_without_prefix }
108
+ end
109
+
110
+ # @param resource [Bridgetown::Resource::Base]
111
+ register_placeholder :name, ->(resource) do
112
+ resource.basename_without_ext
113
+ end
114
+
115
+ # @param resource [Bridgetown::Resource::Base]
116
+ register_placeholder :slug, ->(resource) do
117
+ resource.data.slug || placeholder_processors[:name].(resource)
118
+ end
119
+
120
+ # @param resource [Bridgetown::Resource::Base]
121
+ register_placeholder :title, ->(resource) do
122
+ resource.data.title || placeholder_processors[:slug].(resource)
123
+ end
124
+
125
+ # @param resource [Bridgetown::Resource::Base]
126
+ register_placeholder :locale, ->(resource) do
127
+ locale_data = resource.data.locale
128
+ resource.site.config.available_locales.include?(locale_data) ? locale_data : nil
129
+ end
130
+ register_placeholder :lang, placeholder_processors[:locale]
131
+
132
+ # @param resource [Bridgetown::Resource::Base]
133
+ register_placeholder :collection, ->(resource) do
134
+ resource.collection.label
135
+ end
136
+
137
+ # @param resource [Bridgetown::Resource::Base]
138
+ register_placeholder :categories, ->(resource) do
139
+ resource.taxonomies.category&.terms&.map(&:label)&.uniq
140
+ end
141
+
142
+ # YYYY
143
+ # @param resource [Bridgetown::Resource::Base]
144
+ register_placeholder :year, ->(resource) do
145
+ resource.date.strftime("%Y")
146
+ end
147
+
148
+ # MM: 01..12
149
+ # @param resource [Bridgetown::Resource::Base]
150
+ register_placeholder :month, ->(resource) do
151
+ resource.date.strftime("%m")
152
+ end
153
+
154
+ # DD: 01..31
155
+ # @param resource [Bridgetown::Resource::Base]
156
+ register_placeholder :day, ->(resource) do
157
+ resource.date.strftime("%d")
158
+ end
159
+
160
+ # D: 1..31
161
+ # @param resource [Bridgetown::Resource::Base]
162
+ register_placeholder :i_day, ->(resource) do
163
+ resource.date.strftime("%-d")
164
+ end
165
+
166
+ # M: 1..12
167
+ # @param resource [Bridgetown::Resource::Base]
168
+ register_placeholder :i_month, ->(resource) do
169
+ resource.date.strftime("%-m")
170
+ end
171
+
172
+ # YY: 00..99
173
+ # @param resource [Bridgetown::Resource::Base]
174
+ register_placeholder :short_year, ->(resource) do
175
+ resource.date.strftime("%y")
176
+ end
177
+ end
178
+ end
179
+ end