bridgetown-core 0.15.0 → 0.17.1

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +14 -0
  3. data/bridgetown-core.gemspec +4 -1
  4. data/lib/bridgetown-core.rb +8 -2
  5. data/lib/bridgetown-core/commands/concerns/actions.rb +2 -1
  6. data/lib/bridgetown-core/commands/console.rb +4 -4
  7. data/lib/bridgetown-core/concerns/data_accessible.rb +19 -0
  8. data/lib/bridgetown-core/concerns/layout_placeable.rb +17 -0
  9. data/lib/bridgetown-core/concerns/liquid_renderable.rb +20 -0
  10. data/lib/bridgetown-core/concerns/publishable.rb +10 -0
  11. data/lib/bridgetown-core/concerns/site/configurable.rb +66 -31
  12. data/lib/bridgetown-core/concerns/site/content.rb +90 -31
  13. data/lib/bridgetown-core/concerns/site/extensible.rb +15 -12
  14. data/lib/bridgetown-core/concerns/site/localizable.rb +20 -0
  15. data/lib/bridgetown-core/concerns/site/processable.rb +14 -12
  16. data/lib/bridgetown-core/concerns/site/renderable.rb +21 -2
  17. data/lib/bridgetown-core/concerns/site/writable.rb +16 -2
  18. data/lib/bridgetown-core/concerns/validatable.rb +59 -0
  19. data/lib/bridgetown-core/configuration.rb +10 -3
  20. data/lib/bridgetown-core/converter.rb +34 -0
  21. data/lib/bridgetown-core/converters/erb_templates.rb +78 -0
  22. data/lib/bridgetown-core/converters/markdown.rb +6 -23
  23. data/lib/bridgetown-core/converters/smartypants.rb +0 -10
  24. data/lib/bridgetown-core/document.rb +11 -55
  25. data/lib/bridgetown-core/drops/site_drop.rb +1 -1
  26. data/lib/bridgetown-core/errors.rb +2 -0
  27. data/lib/bridgetown-core/excerpt.rb +1 -6
  28. data/lib/bridgetown-core/filters.rb +11 -48
  29. data/lib/bridgetown-core/filters/condition_helpers.rb +56 -0
  30. data/lib/bridgetown-core/frontmatter_defaults.rb +17 -0
  31. data/lib/bridgetown-core/layout.rb +24 -1
  32. data/lib/bridgetown-core/liquid_renderer/file_system.rb +1 -1
  33. data/lib/bridgetown-core/page.rb +41 -26
  34. data/lib/bridgetown-core/plugin_manager.rb +10 -2
  35. data/lib/bridgetown-core/reader.rb +1 -0
  36. data/lib/bridgetown-core/readers/collection_reader.rb +1 -0
  37. data/lib/bridgetown-core/readers/data_reader.rb +4 -3
  38. data/lib/bridgetown-core/readers/defaults_reader.rb +27 -0
  39. data/lib/bridgetown-core/readers/layout_reader.rb +1 -0
  40. data/lib/bridgetown-core/readers/page_reader.rb +1 -0
  41. data/lib/bridgetown-core/readers/post_reader.rb +1 -0
  42. data/lib/bridgetown-core/readers/static_file_reader.rb +1 -0
  43. data/lib/bridgetown-core/regenerator.rb +1 -1
  44. data/lib/bridgetown-core/renderer.rb +40 -14
  45. data/lib/bridgetown-core/ruby_template_view.rb +113 -0
  46. data/lib/bridgetown-core/site.rb +2 -0
  47. data/lib/bridgetown-core/tags/class_map.rb +90 -0
  48. data/lib/bridgetown-core/tags/find.rb +86 -0
  49. data/lib/bridgetown-core/tags/t.rb +14 -0
  50. data/lib/bridgetown-core/tags/webpack_path.rb +19 -22
  51. data/lib/bridgetown-core/utils.rb +55 -2
  52. data/lib/bridgetown-core/utils/ruby_exec.rb +1 -1
  53. data/lib/bridgetown-core/version.rb +2 -2
  54. data/lib/site_template/src/_layouts/{default.html → default.liquid} +0 -0
  55. data/lib/site_template/src/_layouts/{home.html → home.liquid} +0 -0
  56. data/lib/site_template/src/_layouts/{page.html → page.liquid} +0 -0
  57. data/lib/site_template/src/_layouts/{post.html → post.liquid} +0 -0
  58. metadata +64 -10
  59. data/lib/bridgetown-core/concerns/convertible.rb +0 -235
@@ -8,6 +8,12 @@ module Bridgetown
8
8
  highlighter_prefix "\n"
9
9
  highlighter_suffix "\n"
10
10
 
11
+ def initialize(config = {})
12
+ super
13
+
14
+ self.class.input @config["markdown_ext"].split(",")
15
+ end
16
+
11
17
  def setup
12
18
  return if @setup ||= false
13
19
 
@@ -51,25 +57,6 @@ module Bridgetown
51
57
  self.class.constants - [:KramdownParser, :PRIORITIES]
52
58
  end
53
59
 
54
- # Does the given extension match this converter's list of acceptable extensions?
55
- # Takes one argument: the file's extension (including the dot).
56
- #
57
- # ext - The String extension to check.
58
- #
59
- # Returns true if it matches, false otherwise.
60
- def matches(ext)
61
- extname_list.include?(ext.downcase)
62
- end
63
-
64
- # Public: The extension to be given to the output file (including the dot).
65
- #
66
- # ext - The String extension or original file.
67
- #
68
- # Returns The String output file extension.
69
- def output_ext(_ext)
70
- ".html"
71
- end
72
-
73
60
  # Logic to do the content conversion.
74
61
  #
75
62
  # content - String content of file (without front matter).
@@ -82,10 +69,6 @@ module Bridgetown
82
69
  end
83
70
  end
84
71
 
85
- def extname_list
86
- @extname_list ||= @config["markdown_ext"].split(",").map! { |e| ".#{e.downcase}" }
87
- end
88
-
89
72
  private
90
73
 
91
74
  def custom_processor
@@ -30,16 +30,6 @@ module Bridgetown
30
30
  @config[:input] = :SmartyPants
31
31
  end
32
32
 
33
- # Does the given extension match this converter's list of acceptable extensions?
34
- # Takes one argument: the file's extension (including the dot).
35
- #
36
- # ext - The String extension to check.
37
- #
38
- # Returns true if it matches, false otherwise.
39
- def matches(_ext)
40
- false
41
- end
42
-
43
33
  # Public: The extension to be given to the output file (including the dot).
44
34
  #
45
35
  # ext - The String extension or original file.
@@ -2,8 +2,12 @@
2
2
 
3
3
  module Bridgetown
4
4
  class Document
5
- include Comparable
6
5
  extend Forwardable
6
+ include DataAccessible
7
+ include Comparable
8
+ include LayoutPlaceable
9
+ include LiquidRenderable
10
+ include Publishable
7
11
 
8
12
  attr_reader :path, :site, :extname, :collection, :type
9
13
  attr_accessor :content, :output
@@ -59,7 +63,7 @@ module Bridgetown
59
63
  # Returns a Hash containing the data. An empty hash is returned if
60
64
  # no data was read.
61
65
  def data
62
- @data ||= ActiveSupport::HashWithIndifferentAccess.new
66
+ @data ||= HashWithDotAccess::Hash.new
63
67
  end
64
68
 
65
69
  # Merge some data in with this document's data.
@@ -95,6 +99,9 @@ module Bridgetown
95
99
  @relative_path ||= path.sub("#{site.collections_path}/", "")
96
100
  end
97
101
 
102
+ # FIXME: spinning up a new Renderer object just to get an extension
103
+ # seems excessive
104
+ #
98
105
  # The output extension of the document.
99
106
  #
100
107
  # Returns the output extension
@@ -143,38 +150,6 @@ module Bridgetown
143
150
  YAML_FILE_EXTS.include?(extname)
144
151
  end
145
152
 
146
- # TODO: Depricated
147
- # Used to determine CoffeeScript and Sass/SCSS files.
148
- def asset_file?
149
- false
150
- end
151
-
152
- # Determine whether the file should be rendered with Liquid.
153
- #
154
- # Returns false if the document is either an asset file or a yaml file,
155
- # or if the document doesn't contain any Liquid Tags or Variables,
156
- # true otherwise.
157
- def render_with_liquid?
158
- return false if data["render_with_liquid"] == false
159
-
160
- !(yaml_file? || !Utils.has_liquid_construct?(content))
161
- end
162
-
163
- # Determine whether the file should be rendered with a layout.
164
- #
165
- # Returns true if the Front Matter specifies that `layout` is set to `none`.
166
- def no_layout?
167
- data["layout"] == "none"
168
- end
169
-
170
- # Determine whether the file should be placed into layouts.
171
- #
172
- # Returns false if the document is set to `layouts: none`, or is either an
173
- # asset file or a yaml file. Returns true otherwise.
174
- def place_in_layout?
175
- !(asset_file? || yaml_file? || no_layout?)
176
- end
177
-
178
153
  # The URL template where the document would be accessible.
179
154
  #
180
155
  # Returns the URL template for the document.
@@ -195,7 +170,7 @@ module Bridgetown
195
170
  #
196
171
  # Returns the permalink or nil if no permalink was set in the data.
197
172
  def permalink
198
- data && data.is_a?(Hash) && data["permalink"]
173
+ data&.permalink
199
174
  end
200
175
 
201
176
  # The computed URL for the document. See `Bridgetown::URL#to_s` for more details.
@@ -209,10 +184,6 @@ module Bridgetown
209
184
  ).to_s
210
185
  end
211
186
 
212
- def [](key)
213
- data[key]
214
- end
215
-
216
187
  # The full path to the output file.
217
188
  #
218
189
  # base_directory - the base path of the output directory
@@ -243,14 +214,6 @@ module Bridgetown
243
214
  trigger_hooks(:post_write)
244
215
  end
245
216
 
246
- # Whether the file is published or not, as indicated in YAML front-matter
247
- #
248
- # Returns 'false' if the 'published' key is specified in the
249
- # YAML front-matter and is 'false'. Otherwise returns 'true'.
250
- def published?
251
- !(data.key?("published") && data["published"] == false)
252
- end
253
-
254
217
  # Read in the file and assign the content and data based on the file contents.
255
218
  # Merge the frontmatter of the file with the frontmatter default
256
219
  # values
@@ -287,13 +250,6 @@ module Bridgetown
287
250
  "#<#{self.class} #{relative_path} collection=#{collection.label}>"
288
251
  end
289
252
 
290
- # The string representation for this document.
291
- #
292
- # Returns the content of the document
293
- def to_s
294
- output || content || "NO CONTENT"
295
- end
296
-
297
253
  # Compare this document against another document.
298
254
  # Comparison is a comparison between the 2 paths of the documents.
299
255
  #
@@ -339,7 +295,7 @@ module Bridgetown
339
295
 
340
296
  def previous_doc
341
297
  pos = collection.docs.index { |post| post.equal?(self) }
342
- collection.docs[pos - 1] if pos && pos.positive?
298
+ collection.docs[pos - 1] if pos&.positive?
343
299
  end
344
300
 
345
301
  def trigger_hooks(hook_name, *args)
@@ -8,7 +8,7 @@ module Bridgetown
8
8
  mutable false
9
9
 
10
10
  def_delegator :@obj, :data
11
- def_delegators :@obj, :time, :pages, :static_files, :tags, :categories
11
+ def_delegators :@obj, :locale, :time, :pages, :static_files, :tags, :categories
12
12
 
13
13
  private def_delegator :@obj, :config, :fallback_data
14
14
 
@@ -16,5 +16,7 @@ module Bridgetown
16
16
  PostURLError = Class.new(FatalException)
17
17
  InvalidURLError = Class.new(FatalException)
18
18
  InvalidConfigurationError = Class.new(FatalException)
19
+
20
+ WebpackAssetError = Class.new(FatalException)
19
21
  end
20
22
  end
@@ -3,6 +3,7 @@
3
3
  module Bridgetown
4
4
  class Excerpt
5
5
  extend Forwardable
6
+ include LiquidRenderable
6
7
 
7
8
  attr_accessor :doc
8
9
  attr_accessor :content, :ext
@@ -91,12 +92,6 @@ module Bridgetown
91
92
  false
92
93
  end
93
94
 
94
- def render_with_liquid?
95
- return false if data["render_with_liquid"] == false
96
-
97
- !(yaml_file? || !Utils.has_liquid_construct?(content))
98
- end
99
-
100
95
  protected
101
96
 
102
97
  # Internal: Extract excerpt from the content
@@ -7,6 +7,7 @@ module Bridgetown
7
7
  include URLFilters
8
8
  include GroupingFilters
9
9
  include DateFilters
10
+ include ConditionHelpers
10
11
 
11
12
  # Convert a Markdown string into HTML output.
12
13
  #
@@ -30,6 +31,7 @@ module Bridgetown
30
31
  ).convert(input.to_s)
31
32
  end
32
33
 
34
+ # TODO: This should be removed, there is no Sass converter
33
35
  # Convert a Sass string into CSS output.
34
36
  #
35
37
  # input - The Sass String to convert.
@@ -41,6 +43,7 @@ module Bridgetown
41
43
  ).convert(input)
42
44
  end
43
45
 
46
+ # TODO: This should be removed, there is no Scss converter
44
47
  # Convert a Scss string into CSS output.
45
48
  #
46
49
  # input - The Scss String to convert.
@@ -135,6 +138,14 @@ module Bridgetown
135
138
  input.split.length
136
139
  end
137
140
 
141
+ # Calculates the average reading time of the supplied content.
142
+ # @param input [String] the String of content to analyze.
143
+ # @return [Float] the number of minutes required to read the content.
144
+ def reading_time(input, round_to = 0)
145
+ wpm = @context.registers[:site].config[:reading_time_wpm] || 250
146
+ (number_of_words(input).to_f / wpm).ceil(round_to)
147
+ end
148
+
138
149
  # Join an array of things into a string by separating with commas and the
139
150
  # word "and" for the last one.
140
151
  #
@@ -421,54 +432,6 @@ module Bridgetown
421
432
  end
422
433
  end
423
434
  end
424
-
425
- # ----------- The following set of code was *adapted* from Liquid::If
426
- # ----------- ref: https://git.io/vp6K6
427
-
428
- # Parse a string to a Liquid Condition
429
- def parse_condition(exp)
430
- parser = Liquid::Parser.new(exp)
431
- condition = parse_binary_comparison(parser)
432
-
433
- parser.consume(:end_of_string)
434
- condition
435
- end
436
-
437
- # Generate a Liquid::Condition object from a Liquid::Parser object additionally processing
438
- # the parsed expression based on whether the expression consists of binary operations with
439
- # Liquid operators `and` or `or`
440
- #
441
- # - parser: an instance of Liquid::Parser
442
- #
443
- # Returns an instance of Liquid::Condition
444
- def parse_binary_comparison(parser)
445
- condition = parse_comparison(parser)
446
- first_condition = condition
447
- while (binary_operator = parser.id?("and") || parser.id?("or"))
448
- child_condition = parse_comparison(parser)
449
- condition.send(binary_operator, child_condition)
450
- condition = child_condition
451
- end
452
- first_condition
453
- end
454
-
455
- # Generates a Liquid::Condition object from a Liquid::Parser object based on whether the parsed
456
- # expression involves a "comparison" operator (e.g. <, ==, >, !=, etc)
457
- #
458
- # - parser: an instance of Liquid::Parser
459
- #
460
- # Returns an instance of Liquid::Condition
461
- def parse_comparison(parser)
462
- left_operand = Liquid::Expression.parse(parser.expression)
463
- operator = parser.consume?(:comparison)
464
-
465
- # No comparison-operator detected. Initialize a Liquid::Condition using only left operand
466
- return Liquid::Condition.new(left_operand) unless operator
467
-
468
- # Parse what remained after extracting the left operand and the `:comparison` operator
469
- # and initialize a Liquid::Condition object using the operands and the comparison-operator
470
- Liquid::Condition.new(left_operand, operator, Liquid::Expression.parse(parser.expression))
471
- end
472
435
  end
473
436
  end
474
437
 
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Filters
5
+ module ConditionHelpers
6
+ # ----------- The following set of code was *adapted* from Liquid::If
7
+ # ----------- ref: https://git.io/vp6K6
8
+
9
+ # Parse a string to a Liquid Condition
10
+ def parse_condition(exp)
11
+ parser = Liquid::Parser.new(exp)
12
+ condition = parse_binary_comparison(parser)
13
+
14
+ parser.consume(:end_of_string)
15
+ condition
16
+ end
17
+
18
+ # Generate a Liquid::Condition object from a Liquid::Parser object additionally processing
19
+ # the parsed expression based on whether the expression consists of binary operations with
20
+ # Liquid operators `and` or `or`
21
+ #
22
+ # - parser: an instance of Liquid::Parser
23
+ #
24
+ # Returns an instance of Liquid::Condition
25
+ def parse_binary_comparison(parser)
26
+ condition = parse_comparison(parser)
27
+ first_condition = condition
28
+ while (binary_operator = parser.id?("and") || parser.id?("or"))
29
+ child_condition = parse_comparison(parser)
30
+ condition.send(binary_operator, child_condition)
31
+ condition = child_condition
32
+ end
33
+ first_condition
34
+ end
35
+
36
+ # Generates a Liquid::Condition object from a Liquid::Parser object based
37
+ # on whether the parsed expression involves a "comparison" operator
38
+ # (e.g. <, ==, >, !=, etc)
39
+ #
40
+ # - parser: an instance of Liquid::Parser
41
+ #
42
+ # Returns an instance of Liquid::Condition
43
+ def parse_comparison(parser)
44
+ left_operand = Liquid::Expression.parse(parser.expression)
45
+ operator = parser.consume?(:comparison)
46
+
47
+ # No comparison-operator detected. Initialize a Liquid::Condition using only left operand
48
+ return Liquid::Condition.new(left_operand) unless operator
49
+
50
+ # Parse what remained after extracting the left operand and the `:comparison` operator
51
+ # and initialize a Liquid::Condition object using the operands and the comparison-operator
52
+ Liquid::Condition.new(left_operand, operator, Liquid::Expression.parse(parser.expression))
53
+ end
54
+ end
55
+ end
56
+ end
@@ -54,6 +54,10 @@ module Bridgetown
54
54
  #
55
55
  # Returns the default value or nil if none was found
56
56
  def find(path, type, setting)
57
+ merged_data = {}
58
+ merge_data_cascade_for_path(path, merged_data)
59
+ return merged_data[setting] if merged_data.key?(setting)
60
+
57
61
  value = nil
58
62
  old_scope = nil
59
63
 
@@ -74,6 +78,9 @@ module Bridgetown
74
78
  # Returns a hash with all default values (an empty hash if there are none)
75
79
  def all(path, type)
76
80
  defaults = {}
81
+
82
+ merge_data_cascade_for_path(path, defaults)
83
+
77
84
  old_scope = nil
78
85
  matching_sets(path, type).each do |set|
79
86
  if has_precedence?(old_scope, set["scope"])
@@ -88,6 +95,16 @@ module Bridgetown
88
95
 
89
96
  private
90
97
 
98
+ def merge_data_cascade_for_path(path, merged_data)
99
+ absolute_path = @site.in_source_dir(path)
100
+ @site.defaults_reader.path_defaults
101
+ .select { |k, _v| absolute_path.include? k }
102
+ .sort_by { |k, _v| k.length }
103
+ .each do |defaults|
104
+ merged_data.merge!(defaults[1])
105
+ end
106
+ end
107
+
91
108
  # Checks if a given default setting scope matches the given path and type
92
109
  #
93
110
  # scope - the hash indicating the scope, as defined in bridgetown.config.yml
@@ -2,7 +2,9 @@
2
2
 
3
3
  module Bridgetown
4
4
  class Layout
5
- include Convertible
5
+ include DataAccessible
6
+ include LiquidRenderable
7
+ include Validatable
6
8
 
7
9
  # Gets the Site object.
8
10
  attr_reader :site
@@ -25,6 +27,12 @@ module Bridgetown
25
27
  # Gets/Sets the content of this layout.
26
28
  attr_accessor :content
27
29
 
30
+ # Gets/Sets the current document (for layout-compatible converters)
31
+ attr_accessor :current_document
32
+
33
+ # Gets/Sets the document output (for layout-compatible converters)
34
+ attr_accessor :current_document_output
35
+
28
36
  # Initialize a new Layout.
29
37
  #
30
38
  # site - The Site.
@@ -51,6 +59,14 @@ module Bridgetown
51
59
  read_yaml(base, name)
52
60
  end
53
61
 
62
+ # The inspect string for this document.
63
+ # Includes the relative path and the collection label.
64
+ #
65
+ # Returns the inspect string for this document.
66
+ def inspect
67
+ "#<#{self.class} #{@path}>"
68
+ end
69
+
54
70
  # Extract information from the layout filename.
55
71
  #
56
72
  # name - The String filename of the layout file.
@@ -59,5 +75,12 @@ module Bridgetown
59
75
  def process(name)
60
76
  self.ext = File.extname(name)
61
77
  end
78
+
79
+ # Provide this Layout's data to a Hash suitable for use by Liquid.
80
+ #
81
+ # Returns the Hash representation of this Layout.
82
+ def to_liquid
83
+ data
84
+ end
62
85
  end
63
86
  end