bridgetown-core 0.13.0 → 0.14.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/bin/bridgetown +0 -25
  3. data/bridgetown-core.gemspec +4 -1
  4. data/lib/bridgetown-core.rb +4 -1
  5. data/lib/bridgetown-core/cleaner.rb +1 -0
  6. data/lib/bridgetown-core/command.rb +10 -4
  7. data/lib/bridgetown-core/commands/console.rb +1 -2
  8. data/lib/bridgetown-core/commands/doctor.rb +1 -2
  9. data/lib/bridgetown-core/commands/new.rb +0 -3
  10. data/lib/bridgetown-core/commands/plugins.rb +169 -0
  11. data/lib/bridgetown-core/{convertible.rb → concerns/convertible.rb} +2 -2
  12. data/lib/bridgetown-core/concerns/site/configurable.rb +153 -0
  13. data/lib/bridgetown-core/concerns/site/content.rb +111 -0
  14. data/lib/bridgetown-core/concerns/site/extensible.rb +56 -0
  15. data/lib/bridgetown-core/concerns/site/processable.rb +74 -0
  16. data/lib/bridgetown-core/concerns/site/renderable.rb +50 -0
  17. data/lib/bridgetown-core/concerns/site/writable.rb +31 -0
  18. data/lib/bridgetown-core/configuration.rb +2 -9
  19. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +0 -3
  20. data/lib/bridgetown-core/document.rb +1 -1
  21. data/lib/bridgetown-core/drops/site_drop.rb +1 -1
  22. data/lib/bridgetown-core/external.rb +17 -21
  23. data/lib/bridgetown-core/filters.rb +10 -0
  24. data/lib/bridgetown-core/generators/prototype_generator.rb +1 -1
  25. data/lib/bridgetown-core/hooks.rb +62 -62
  26. data/lib/bridgetown-core/layout.rb +10 -4
  27. data/lib/bridgetown-core/page.rb +9 -2
  28. data/lib/bridgetown-core/plugin.rb +2 -0
  29. data/lib/bridgetown-core/plugin_manager.rb +62 -12
  30. data/lib/bridgetown-core/reader.rb +5 -0
  31. data/lib/bridgetown-core/readers/data_reader.rb +5 -2
  32. data/lib/bridgetown-core/readers/layout_reader.rb +9 -2
  33. data/lib/bridgetown-core/readers/plugin_content_reader.rb +48 -0
  34. data/lib/bridgetown-core/renderer.rb +7 -10
  35. data/lib/bridgetown-core/site.rb +20 -463
  36. data/lib/bridgetown-core/utils.rb +1 -27
  37. data/lib/bridgetown-core/utils/ruby_exec.rb +1 -4
  38. data/lib/bridgetown-core/version.rb +2 -2
  39. data/lib/bridgetown-core/watcher.rb +5 -1
  40. data/lib/site_template/plugins/{.keep → builders/.keep} +0 -0
  41. data/lib/site_template/plugins/site_builder.rb +4 -0
  42. data/lib/site_template/src/_includes/navbar.html +1 -0
  43. data/lib/site_template/src/posts.md +15 -0
  44. data/lib/site_template/start.js +1 -1
  45. metadata +58 -6
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Site::Content
5
+ # Construct a Hash of Posts indexed by the specified Post attribute.
6
+ #
7
+ # post_attr - The String name of the Post attribute.
8
+ #
9
+ # Examples
10
+ #
11
+ # post_attr_hash('categories')
12
+ # # => { 'tech' => [<Post A>, <Post B>],
13
+ # # 'ruby' => [<Post B>] }
14
+ #
15
+ # Returns the Hash: { attr => posts } where
16
+ # attr - One of the values for the requested attribute.
17
+ # posts - The Array of Posts with the given attr value.
18
+ def post_attr_hash(post_attr)
19
+ # Build a hash map based on the specified post attribute ( post attr =>
20
+ # array of posts ) then sort each array in reverse order.
21
+ @post_attr_hash[post_attr] ||= begin
22
+ hash = Hash.new { |h, key| h[key] = [] }
23
+ posts.docs.each do |p|
24
+ p.data[post_attr]&.each { |t| hash[t] << p }
25
+ end
26
+ hash.each_value { |posts| posts.sort!.reverse! }
27
+ hash
28
+ end
29
+ end
30
+
31
+ def tags
32
+ post_attr_hash("tags")
33
+ end
34
+
35
+ def categories
36
+ post_attr_hash("categories")
37
+ end
38
+
39
+ def metadata
40
+ data["site_metadata"] ||= ActiveSupport::HashWithIndifferentAccess.new
41
+ end
42
+
43
+ # The Hash payload containing site-wide data.
44
+ #
45
+ # Returns the Hash: { "site" => data } where data is a Hash with keys:
46
+ # "time" - The Time as specified in the configuration or the
47
+ # current time if none was specified.
48
+ # "posts" - The Array of Posts, sorted chronologically by post date
49
+ # and then title.
50
+ # "pages" - The Array of all Pages.
51
+ # "html_pages" - The Array of HTML Pages.
52
+ # "categories" - The Hash of category values and Posts.
53
+ # See Site#post_attr_hash for type info.
54
+ # "tags" - The Hash of tag values and Posts.
55
+ # See Site#post_attr_hash for type info.
56
+ def site_payload
57
+ Drops::UnifiedPayloadDrop.new self
58
+ end
59
+ alias_method :to_liquid, :site_payload
60
+
61
+ # The list of collections and their corresponding Bridgetown::Collection instances.
62
+ # If config['collections'] is set, a new instance is created
63
+ # for each item in the collection, a new hash is returned otherwise.
64
+ #
65
+ # Returns a Hash containing collection name-to-instance pairs.
66
+ def collections
67
+ @collections ||= collection_names.each_with_object(
68
+ ActiveSupport::HashWithIndifferentAccess.new
69
+ ) do |name, hsh|
70
+ hsh[name] = Bridgetown::Collection.new(self, name)
71
+ end
72
+ end
73
+
74
+ # The list of collection names.
75
+ #
76
+ # Returns an array of collection names from the configuration,
77
+ # or an empty array if the `collections` key is not set.
78
+ def collection_names
79
+ case config["collections"]
80
+ when Hash
81
+ config["collections"].keys
82
+ when Array
83
+ config["collections"]
84
+ when nil
85
+ []
86
+ else
87
+ raise ArgumentError, "Your `collections` key must be a hash or an array."
88
+ end
89
+ end
90
+
91
+ # Get all the documents
92
+ #
93
+ # Returns an Array of all Documents
94
+ def documents
95
+ collections.each_with_object(Set.new) do |(_, collection), set|
96
+ set.merge(collection.docs).merge(collection.files)
97
+ end.to_a
98
+ end
99
+
100
+ # Get the to be written documents
101
+ #
102
+ # Returns an Array of Documents which should be written
103
+ def docs_to_write
104
+ documents.select(&:write?)
105
+ end
106
+
107
+ def posts
108
+ collections["posts"] ||= Collection.new(self, "posts")
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Site::Extensible
5
+ # Load necessary libraries, plugins, converters, and generators.
6
+ #
7
+ # Returns nothing.
8
+ def setup
9
+ plugin_manager.require_plugin_files
10
+ self.converters = instantiate_subclasses(Bridgetown::Converter)
11
+ self.generators = instantiate_subclasses(Bridgetown::Generator)
12
+ end
13
+
14
+ # Run each of the Generators.
15
+ #
16
+ # Returns nothing.
17
+ def generate
18
+ generators.each do |generator|
19
+ start = Time.now
20
+ generator.generate(self)
21
+
22
+ next unless ENV["BRIDGETOWN_LOG_LEVEL"] == "debug"
23
+
24
+ generator_name = if generator.class.respond_to?(:custom_name)
25
+ generator.class.custom_name
26
+ else
27
+ generator.class.name
28
+ end
29
+ Bridgetown.logger.debug "Generating:",
30
+ "#{generator_name} finished in #{Time.now - start} seconds."
31
+ end
32
+ end
33
+
34
+ # 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
+ def find_converter_instance(klass)
38
+ @find_converter_instance ||= {}
39
+ @find_converter_instance[klass] ||= begin
40
+ converters.find { |converter| converter.instance_of?(klass) } || \
41
+ raise("No Converters found for #{klass}")
42
+ end
43
+ end
44
+
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
+
50
+ def instantiate_subclasses(klass)
51
+ klass.descendants.sort.map do |c|
52
+ c.new(config)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Site::Processable
5
+ # Public: Read, process, and write this Site to output.
6
+ #
7
+ # Returns nothing.
8
+ def process
9
+ reset
10
+ read
11
+ generate # Extensible
12
+ render # Renderable
13
+ cleanup # Writable
14
+ write # Writable
15
+ print_stats if config["profile"]
16
+ end
17
+
18
+ # rubocop:disable Metrics/AbcSize
19
+ #
20
+ # Reset Site details.
21
+ #
22
+ # Returns nothing
23
+ def reset
24
+ self.time = if config["time"]
25
+ Utils.parse_date(config["time"].to_s, "Invalid time in bridgetown.config.yml.")
26
+ else
27
+ Time.now
28
+ end
29
+ self.layouts = ActiveSupport::HashWithIndifferentAccess.new
30
+ self.pages = []
31
+ self.static_files = []
32
+ self.data = ActiveSupport::HashWithIndifferentAccess.new
33
+ @post_attr_hash = {}
34
+ @collections = nil
35
+ @documents = nil
36
+ @docs_to_write = nil
37
+ @regenerator.clear_cache
38
+ @liquid_renderer.reset
39
+ frontmatter_defaults.reset
40
+
41
+ raise ArgumentError, "limit_posts must be a non-negative number" if limit_posts.negative?
42
+
43
+ Bridgetown::Cache.clear_if_config_changed config
44
+ Bridgetown::Hooks.trigger :site, :after_reset, self
45
+ end
46
+ # rubocop:enable Metrics/AbcSize
47
+
48
+ # Read Site data from disk and load it into internal data structures.
49
+ #
50
+ # Returns nothing.
51
+ def read
52
+ Bridgetown::Hooks.trigger :site, :pre_read, self
53
+ reader.read
54
+ limit_posts!
55
+ Bridgetown::Hooks.trigger :site, :post_read, self
56
+ end
57
+
58
+ private
59
+
60
+ # Limits the current posts; removes the posts which exceed the limit_posts
61
+ #
62
+ # Returns nothing
63
+ def limit_posts!
64
+ if limit_posts.positive?
65
+ limit = posts.docs.length < limit_posts ? posts.docs.length : limit_posts
66
+ posts.docs = posts.docs[-limit, limit]
67
+ end
68
+ end
69
+
70
+ def print_stats
71
+ Bridgetown.logger.info @liquid_renderer.stats_table
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Site::Renderable
5
+ # Render the site to the destination.
6
+ #
7
+ # Returns nothing.
8
+ def render
9
+ payload = site_payload
10
+
11
+ Bridgetown::Hooks.trigger :site, :pre_render, self, payload
12
+
13
+ execute_inline_ruby_for_layouts!
14
+
15
+ render_docs(payload)
16
+ render_pages(payload)
17
+
18
+ Bridgetown::Hooks.trigger :site, :post_render, self, payload
19
+ end
20
+
21
+ def execute_inline_ruby_for_layouts!
22
+ return unless config.should_execute_inline_ruby?
23
+
24
+ layouts.each_value do |layout|
25
+ Bridgetown::Utils::RubyExec.search_data_for_ruby_code(layout, self)
26
+ end
27
+ end
28
+
29
+ def render_docs(payload)
30
+ collections.each_value do |collection|
31
+ collection.docs.each do |document|
32
+ render_regenerated(document, payload)
33
+ end
34
+ end
35
+ end
36
+
37
+ def render_pages(payload)
38
+ pages.each do |page|
39
+ render_regenerated(page, payload)
40
+ end
41
+ end
42
+
43
+ def render_regenerated(document, payload)
44
+ return unless regenerator.regenerate?(document)
45
+
46
+ document.output = Bridgetown::Renderer.new(self, document, payload).run
47
+ document.trigger_hooks(:post_render)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Site::Writable
5
+ # Remove orphaned files and empty directories in destination.
6
+ #
7
+ # Returns nothing.
8
+ def cleanup
9
+ @cleaner.cleanup!
10
+ end
11
+
12
+ # Write static files, pages, and posts.
13
+ #
14
+ # Returns nothing.
15
+ def write
16
+ each_site_file do |item|
17
+ item.write(dest) if regenerator.regenerate?(item)
18
+ end
19
+ regenerator.write_metadata
20
+ Bridgetown::Hooks.trigger :site, :post_write, self
21
+ end
22
+
23
+ def each_site_file
24
+ %w(pages static_files docs_to_write).each do |type|
25
+ send(type).each do |item|
26
+ yield item
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -4,7 +4,7 @@ module Bridgetown
4
4
  # TODO: refactor this whole object! Already had to fix obscure
5
5
  # bugs just making minor changes, and all the indirection is
6
6
  # quite hard to decipher. -JW
7
- class Configuration < Hash
7
+ class Configuration < ActiveSupport::HashWithIndifferentAccess
8
8
  # Default options. Overridden by values in bridgetown.config.yml.
9
9
  # Strings rather than symbols are used for compatibility with YAML.
10
10
  DEFAULTS = {
@@ -91,7 +91,7 @@ module Bridgetown
91
91
  #
92
92
  # Returns a Configuration filled with defaults.
93
93
  def from(user_config)
94
- Utils.deep_merge_hashes(DEFAULTS, Configuration[user_config].stringify_keys)
94
+ Utils.deep_merge_hashes(DEFAULTS, Configuration[user_config])
95
95
  .merge_environment_specific_options!
96
96
  .add_default_collections
97
97
  .add_default_excludes
@@ -99,13 +99,6 @@ module Bridgetown
99
99
  end
100
100
  end
101
101
 
102
- # Public: Turn all keys into string
103
- #
104
- # Return a copy of the hash where all its keys are strings
105
- def stringify_keys
106
- each_with_object({}) { |(k, v), hsh| hsh[k.to_s] = v }
107
- end
108
-
109
102
  def get_config_value_with_override(config_key, override)
110
103
  override[config_key] || self[config_key] || DEFAULTS[config_key]
111
104
  end
@@ -11,8 +11,6 @@ module Kramdown
11
11
  attr_reader :options, :parser
12
12
 
13
13
  # The implementation is basically the core logic in +Kramdown::Document#initialize+
14
- #
15
- # rubocop:disable Naming/MemoizedInstanceVariableName
16
14
  def setup(options)
17
15
  @cache ||= {}
18
16
 
@@ -36,7 +34,6 @@ module Kramdown
36
34
  end
37
35
  end
38
36
  end
39
- # rubocop:enable Naming/MemoizedInstanceVariableName
40
37
 
41
38
  private
42
39
 
@@ -59,7 +59,7 @@ module Bridgetown
59
59
  # Returns a Hash containing the data. An empty hash is returned if
60
60
  # no data was read.
61
61
  def data
62
- @data ||= {}
62
+ @data ||= ActiveSupport::HashWithIndifferentAccess.new
63
63
  end
64
64
 
65
65
  # Merge some data in with this document's data.
@@ -7,7 +7,7 @@ module Bridgetown
7
7
 
8
8
  mutable false
9
9
 
10
- def_delegator :@obj, :site_data, :data
10
+ def_delegator :@obj, :data
11
11
  def_delegators :@obj, :time, :pages, :static_files, :tags, :categories
12
12
 
13
13
  private def_delegator :@obj, :config, :fallback_data
@@ -10,13 +10,11 @@ module Bridgetown
10
10
  #
11
11
  def require_if_present(names)
12
12
  Array(names).each do |name|
13
- begin
14
- require name
15
- rescue LoadError
16
- Bridgetown.logger.debug "Couldn't load #{name}. Skipping."
17
- yield(name, version_constraint(name)) if block_given?
18
- false
19
- end
13
+ require name
14
+ rescue LoadError
15
+ Bridgetown.logger.debug "Couldn't load #{name}. Skipping."
16
+ yield(name, version_constraint(name)) if block_given?
17
+ false
20
18
  end
21
19
  end
22
20
 
@@ -38,23 +36,21 @@ module Bridgetown
38
36
  #
39
37
  def require_with_graceful_fail(names)
40
38
  Array(names).each do |name|
41
- begin
42
- Bridgetown.logger.debug "Requiring:", name.to_s
43
- require name
44
- rescue LoadError => e
45
- Bridgetown.logger.error "Dependency Error:", <<~MSG
46
- Yikes! It looks like you don't have #{name} or one of its dependencies installed.
47
- In order to use Bridgetown as currently configured, you'll need to install this gem.
39
+ Bridgetown.logger.debug "Requiring:", name.to_s
40
+ require name
41
+ rescue LoadError => e
42
+ Bridgetown.logger.error "Dependency Error:", <<~MSG
43
+ Yikes! It looks like you don't have #{name} or one of its dependencies installed.
44
+ In order to use Bridgetown as currently configured, you'll need to install this gem.
48
45
 
49
- If you've run Bridgetown with `bundle exec`, ensure that you have included the #{name}
50
- gem in your Gemfile as well.
46
+ If you've run Bridgetown with `bundle exec`, ensure that you have included the #{name}
47
+ gem in your Gemfile as well.
51
48
 
52
- The full error message from Ruby is: '#{e.message}'
49
+ The full error message from Ruby is: '#{e.message}'
53
50
 
54
- If you run into trouble, you can find helpful resources at https://bridgetownrb.com/help/!
55
- MSG
56
- raise Bridgetown::Errors::MissingDependencyException, name
57
- end
51
+ If you run into trouble, you can find helpful resources at https://www.bridgetownrb.com/docs/community/
52
+ MSG
53
+ raise Bridgetown::Errors::MissingDependencyException, name
58
54
  end
59
55
  end
60
56
  end