bridgetown-core 0.15.0 → 0.16.0.beta1

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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +14 -0
  3. data/bridgetown-core.gemspec +1 -0
  4. data/lib/bridgetown-core.rb +6 -1
  5. data/lib/bridgetown-core/concerns/data_accessible.rb +19 -0
  6. data/lib/bridgetown-core/concerns/layout_placeable.rb +17 -0
  7. data/lib/bridgetown-core/concerns/liquid_renderable.rb +20 -0
  8. data/lib/bridgetown-core/concerns/publishable.rb +10 -0
  9. data/lib/bridgetown-core/concerns/site/configurable.rb +62 -31
  10. data/lib/bridgetown-core/concerns/site/content.rb +88 -29
  11. data/lib/bridgetown-core/concerns/site/extensible.rb +15 -12
  12. data/lib/bridgetown-core/concerns/site/processable.rb +12 -10
  13. data/lib/bridgetown-core/concerns/site/renderable.rb +22 -2
  14. data/lib/bridgetown-core/concerns/site/writable.rb +16 -2
  15. data/lib/bridgetown-core/concerns/validatable.rb +59 -0
  16. data/lib/bridgetown-core/configuration.rb +1 -0
  17. data/lib/bridgetown-core/converter.rb +34 -0
  18. data/lib/bridgetown-core/converters/erb_templates.rb +61 -0
  19. data/lib/bridgetown-core/converters/markdown.rb +6 -23
  20. data/lib/bridgetown-core/converters/smartypants.rb +0 -10
  21. data/lib/bridgetown-core/document.rb +8 -52
  22. data/lib/bridgetown-core/errors.rb +2 -0
  23. data/lib/bridgetown-core/excerpt.rb +1 -6
  24. data/lib/bridgetown-core/filters.rb +2 -0
  25. data/lib/bridgetown-core/layout.rb +24 -1
  26. data/lib/bridgetown-core/liquid_renderer/file_system.rb +1 -1
  27. data/lib/bridgetown-core/page.rb +33 -24
  28. data/lib/bridgetown-core/regenerator.rb +1 -1
  29. data/lib/bridgetown-core/renderer.rb +38 -12
  30. data/lib/bridgetown-core/ruby_template_view.rb +84 -0
  31. data/lib/bridgetown-core/tags/class_map.rb +90 -0
  32. data/lib/bridgetown-core/tags/webpack_path.rb +48 -16
  33. data/lib/bridgetown-core/version.rb +2 -2
  34. metadata +24 -3
  35. data/lib/bridgetown-core/concerns/convertible.rb +0 -235
@@ -3,17 +3,19 @@
3
3
  module Bridgetown
4
4
  module Site::Extensible
5
5
  # Load necessary libraries, plugins, converters, and generators.
6
- #
7
- # Returns nothing.
6
+ # @see Bridgetown::Converter
7
+ # @see Bridgetown::Generator
8
+ # @see PluginManager
9
+ # @return [void]
8
10
  def setup
9
11
  plugin_manager.require_plugin_files
10
12
  self.converters = instantiate_subclasses(Bridgetown::Converter)
11
13
  self.generators = instantiate_subclasses(Bridgetown::Generator)
12
14
  end
13
15
 
14
- # Run each of the Generators.
15
- #
16
- # Returns nothing.
16
+ # Run all Generators.
17
+ # @see Bridgetown::Generator
18
+ # @return [void]
17
19
  def generate
18
20
  generators.each do |generator|
19
21
  start = Time.now
@@ -32,8 +34,9 @@ module Bridgetown
32
34
  end
33
35
 
34
36
  # Get the implementation class for the given Converter.
35
- # Returns the Converter instance implementing the given Converter.
36
- # klass - The Class of the Converter to fetch.
37
+ # @param klass [Object] The Class of the Converter to fetch.
38
+ # @return [Bridgetown::Converter] Returns the {Bridgetown::Converter}
39
+ # instance implementing the given +Converter+.
37
40
  def find_converter_instance(klass)
38
41
  @find_converter_instance ||= {}
39
42
  @find_converter_instance[klass] ||= begin
@@ -42,11 +45,11 @@ module Bridgetown
42
45
  end
43
46
  end
44
47
 
45
- # klass - class or module containing the subclasses.
46
- # Returns array of instances of subclasses of parameter.
47
- # Create array of instances of the subclasses of the class or module
48
- # passed in as argument.
49
-
48
+ # Create an array of instances of the subclasses of the class or module
49
+ # passed in as argument.
50
+ # @param klass [Class, Module] - class or module containing the subclasses.
51
+ # @return [Array<Object>] Returns an array of instances of subclasses of
52
+ # +klass+.
50
53
  def instantiate_subclasses(klass)
51
54
  klass.descendants.sort.map do |c|
52
55
  c.new(config)
@@ -2,9 +2,14 @@
2
2
 
3
3
  module Bridgetown
4
4
  module Site::Processable
5
- # Public: Read, process, and write this Site to output.
6
- #
7
- # Returns nothing.
5
+ # Reset, Read, Generate, Render, Cleanup, Process, and Write this Site to output.
6
+ # @return [void]
7
+ # @see #reset
8
+ # @see #read
9
+ # @see #generate
10
+ # @see #render
11
+ # @see #cleanup
12
+ # @see #write
8
13
  def process
9
14
  reset
10
15
  read
@@ -16,10 +21,9 @@ module Bridgetown
16
21
  end
17
22
 
18
23
  # rubocop:disable Metrics/AbcSize
19
- #
24
+
20
25
  # Reset Site details.
21
- #
22
- # Returns nothing
26
+ # @return [void]
23
27
  def reset
24
28
  self.time = if config["time"]
25
29
  Utils.parse_date(config["time"].to_s, "Invalid time in bridgetown.config.yml.")
@@ -43,11 +47,11 @@ module Bridgetown
43
47
  Bridgetown::Cache.clear_if_config_changed config
44
48
  Bridgetown::Hooks.trigger :site, :after_reset, self
45
49
  end
50
+
46
51
  # rubocop:enable Metrics/AbcSize
47
52
 
48
53
  # Read Site data from disk and load it into internal data structures.
49
- #
50
- # Returns nothing.
54
+ # @return [void]
51
55
  def read
52
56
  Bridgetown::Hooks.trigger :site, :pre_read, self
53
57
  reader.read
@@ -58,8 +62,6 @@ module Bridgetown
58
62
  private
59
63
 
60
64
  # Limits the current posts; removes the posts which exceed the limit_posts
61
- #
62
- # Returns nothing
63
65
  def limit_posts!
64
66
  if limit_posts.positive?
65
67
  limit = posts.docs.length < limit_posts ? posts.docs.length : limit_posts
@@ -3,8 +3,7 @@
3
3
  module Bridgetown
4
4
  module Site::Renderable
5
5
  # Render the site to the destination.
6
- #
7
- # Returns nothing.
6
+ # @return [void]
8
7
  def render
9
8
  payload = site_payload
10
9
 
@@ -18,6 +17,14 @@ module Bridgetown
18
17
  Bridgetown::Hooks.trigger :site, :post_render, self, payload
19
18
  end
20
19
 
20
+ # Executes inline Ruby frontmatter if
21
+ # +ENV+["BRIDGETOWN_RUBY_IN_FRONTMATTER"] equals "true"
22
+ #
23
+ # @example
24
+ # calculation: !ruby/string:Rb |
25
+ # [2 * 4, 5 + 2].min
26
+ # @return [void]
27
+ # @see https://www.bridgetownrb.com/docs/front-matter#ruby-front-matter
21
28
  def execute_inline_ruby_for_layouts!
22
29
  return unless config.should_execute_inline_ruby?
23
30
 
@@ -26,6 +33,10 @@ module Bridgetown
26
33
  end
27
34
  end
28
35
 
36
+ # Renders all documents
37
+ # @param payload [Hash] A hash of site data.
38
+ # @return [void]
39
+ # @see Bridgetown::Site::Content#site_payload
29
40
  def render_docs(payload)
30
41
  collections.each_value do |collection|
31
42
  collection.docs.each do |document|
@@ -34,12 +45,21 @@ module Bridgetown
34
45
  end
35
46
  end
36
47
 
48
+ # Renders all pages
49
+ # @param payload [Hash] A hash of site data.
50
+ # @return [void]
51
+ # @see Bridgetown::Site::Content#site_payload
37
52
  def render_pages(payload)
38
53
  pages.each do |page|
39
54
  render_regenerated(page, payload)
40
55
  end
41
56
  end
42
57
 
58
+ # Regenerates a site using {Bridgetown::Renderer}
59
+ # @param document [Post] The document to regenerate.
60
+ # @param payload [Hash] A hash of site data.
61
+ # @return [void]
62
+ # @see Bridgetown::Renderer
43
63
  def render_regenerated(document, payload)
44
64
  return unless regenerator.regenerate?(document)
45
65
 
@@ -4,14 +4,14 @@ module Bridgetown
4
4
  module Site::Writable
5
5
  # Remove orphaned files and empty directories in destination.
6
6
  #
7
- # Returns nothing.
7
+ # @return [void]
8
8
  def cleanup
9
9
  @cleaner.cleanup!
10
10
  end
11
11
 
12
12
  # Write static files, pages, and posts.
13
13
  #
14
- # Returns nothing.
14
+ # @return [void]
15
15
  def write
16
16
  each_site_file do |item|
17
17
  item.write(dest) if regenerator.regenerate?(item)
@@ -20,6 +20,20 @@ module Bridgetown
20
20
  Bridgetown::Hooks.trigger :site, :post_write, self
21
21
  end
22
22
 
23
+ # Yields the pages from {#pages}, {#static_files}, and {#docs_to_write}.
24
+ #
25
+ # @yieldparam item [Document, Page, StaticFile] Yields a
26
+ # {#Bridgetown::Page}, {#Bridgetown::StaticFile}, or
27
+ # {#Bridgetown::Document} object.
28
+ #
29
+ # @return [void]
30
+ #
31
+ # @see #pages
32
+ # @see #static_files
33
+ # @see #docs_to_write
34
+ # @see Page
35
+ # @see StaticFile
36
+ # @see Document
23
37
  def each_site_file
24
38
  %w(pages static_files docs_to_write).each do |type|
25
39
  send(type).each do |item|
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Validatable
5
+ # FIXME: there should be ONE TRUE METHOD to read the YAML frontmatter
6
+ # in the entire project. Both this and the equivalent Document method
7
+ # should be extracted and generalized.
8
+ #
9
+ # Read the YAML frontmatter.
10
+ #
11
+ # base - The String path to the dir containing the file.
12
+ # name - The String filename of the file.
13
+ # opts - optional parameter to File.read, default at site configs
14
+ #
15
+ # Returns nothing.
16
+ # rubocop:disable Metrics/AbcSize
17
+ def read_yaml(base, name, opts = {})
18
+ filename = File.join(base, name)
19
+
20
+ begin
21
+ self.content = File.read(@path || site.in_source_dir(base, name),
22
+ **Utils.merged_file_read_opts(site, opts))
23
+ if content =~ Document::YAML_FRONT_MATTER_REGEXP
24
+ self.content = $POSTMATCH
25
+ self.data = SafeYAML.load(Regexp.last_match(1))&.with_indifferent_access
26
+ end
27
+ rescue Psych::SyntaxError => e
28
+ Bridgetown.logger.warn "YAML Exception reading #{filename}: #{e.message}"
29
+ raise e if site.config["strict_front_matter"]
30
+ rescue StandardError => e
31
+ Bridgetown.logger.warn "Error reading file #{filename}: #{e.message}"
32
+ raise e if site.config["strict_front_matter"]
33
+ end
34
+
35
+ self.data ||= ActiveSupport::HashWithIndifferentAccess.new
36
+
37
+ validate_data! filename
38
+ validate_permalink! filename
39
+
40
+ self.data
41
+ end
42
+ # rubocop:enable Metrics/AbcSize
43
+
44
+ # FIXME: why doesn't Document validate data too?
45
+ def validate_data!(filename)
46
+ unless self.data.is_a?(Hash)
47
+ raise Errors::InvalidYAMLFrontMatterError,
48
+ "Invalid YAML front matter in #{filename}"
49
+ end
50
+ end
51
+
52
+ # FIXME: Layouts don't have permalinks...d'oh
53
+ def validate_permalink!(filename)
54
+ if self.data["permalink"]&.to_s&.empty?
55
+ raise Errors::InvalidPermalinkError, "Invalid permalink in #{filename}"
56
+ end
57
+ end
58
+ end
59
+ end
@@ -19,6 +19,7 @@ module Bridgetown
19
19
  "data_dir" => "_data",
20
20
  "components_dir" => "_components",
21
21
  "includes_dir" => "_includes",
22
+ "partials_dir" => "_partials",
22
23
  "collections" => {},
23
24
 
24
25
  # Handling Reading
@@ -2,6 +2,20 @@
2
2
 
3
3
  module Bridgetown
4
4
  class Converter < Plugin
5
+ class << self
6
+ attr_accessor :extname_list
7
+
8
+ # Converters can provide one or more extensions they accept. Examples:
9
+ #
10
+ # * `input :erb`
11
+ # * `input %i(xls xlsx)`
12
+ def input(extnames)
13
+ extnames = Array(extnames)
14
+ self.extname_list ||= []
15
+ self.extname_list += extnames.map { |e| ".#{e.to_s.downcase}" }
16
+ end
17
+ end
18
+
5
19
  # Public: Get or set the highlighter prefix. When an argument is specified,
6
20
  # the prefix will be set. If no argument is specified, the current prefix
7
21
  # will be returned.
@@ -37,6 +51,26 @@ module Bridgetown
37
51
  @config = config
38
52
  end
39
53
 
54
+ # Does the given extension match this converter's list of acceptable extensions?
55
+ #
56
+ # @param [String] ext
57
+ # The file's extension (including the dot)
58
+ #
59
+ # @return [Boolean] Whether the extension matches one in the list
60
+ def matches(ext)
61
+ (self.class.extname_list || []).include?(ext.downcase)
62
+ end
63
+
64
+ # You can override this in Converter subclasses as needed. Default is ".html"
65
+ #
66
+ # @param [String] ext
67
+ # The extension of the original file
68
+ #
69
+ # @return [String] The output file extension (including the dot)
70
+ def output_ext(_ext)
71
+ ".html"
72
+ end
73
+
40
74
  # Get the highlighter prefix.
41
75
  #
42
76
  # Returns the String prefix.
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tilt/erb"
4
+ require "active_support/core_ext/hash/keys"
5
+
6
+ module Bridgetown
7
+ class ERBView < RubyTemplateView
8
+ include ERB::Util
9
+
10
+ def partial(partial_name, options = {})
11
+ options.merge!(options[:locals]) if options[:locals]
12
+
13
+ partial_segments = partial_name.split("/")
14
+ partial_segments.last.sub!(%r!^!, "_")
15
+ partial_name = partial_segments.join("/")
16
+
17
+ Tilt::ERBTemplate.new(
18
+ site.in_source_dir(site.config[:partials_dir], "#{partial_name}.erb"),
19
+ trim: "<>-",
20
+ outvar: "@_erbout"
21
+ ).render(self, options)
22
+ end
23
+
24
+ def markdownify
25
+ previous_buffer_state = @_erbout
26
+ @_erbout = +""
27
+ result = yield
28
+ @_erbout = previous_buffer_state
29
+
30
+ content = Bridgetown::Utils.reindent_for_markdown(result)
31
+ converter = site.find_converter_instance(Bridgetown::Converters::Markdown)
32
+ md_output = converter.convert(content).strip
33
+ @_erbout << md_output
34
+ end
35
+ end
36
+
37
+ module Converters
38
+ class ERBTemplates < Converter
39
+ input :erb
40
+
41
+ # Logic to do the content conversion.
42
+ #
43
+ # content - String content of file (without front matter).
44
+ #
45
+ # Returns a String of the converted content.
46
+ def convert(content, convertible)
47
+ erb_view = Bridgetown::ERBView.new(convertible)
48
+
49
+ erb_renderer = Tilt::ERBTemplate.new(trim: "<>-", outvar: "@_erbout") { content }
50
+
51
+ if convertible.is_a?(Bridgetown::Layout)
52
+ erb_renderer.render(erb_view) do
53
+ convertible.current_document_output
54
+ end
55
+ else
56
+ erb_renderer.render(erb_view)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -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
@@ -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.
@@ -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
  #