bridgetown-core 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
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