bridgetown-core 0.12.1 → 0.15.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +3 -1
  3. data/bin/bridgetown +9 -48
  4. data/bridgetown-core.gemspec +7 -2
  5. data/lib/bridgetown-core.rb +20 -4
  6. data/lib/bridgetown-core/cleaner.rb +1 -0
  7. data/lib/bridgetown-core/commands/apply.rb +73 -0
  8. data/lib/bridgetown-core/commands/base.rb +45 -0
  9. data/lib/bridgetown-core/commands/build.rb +91 -86
  10. data/lib/bridgetown-core/commands/clean.rb +30 -29
  11. data/lib/bridgetown-core/commands/concerns/actions.rb +107 -0
  12. data/lib/bridgetown-core/commands/concerns/build_options.rb +76 -0
  13. data/lib/bridgetown-core/commands/concerns/configuration_overridable.rb +18 -0
  14. data/lib/bridgetown-core/commands/concerns/summarizable.rb +13 -0
  15. data/lib/bridgetown-core/commands/console.rb +46 -38
  16. data/lib/bridgetown-core/commands/doctor.rb +125 -135
  17. data/lib/bridgetown-core/commands/new.rb +120 -158
  18. data/lib/bridgetown-core/commands/plugins.rb +206 -0
  19. data/lib/bridgetown-core/commands/registrations.rb +16 -0
  20. data/lib/bridgetown-core/commands/serve.rb +214 -217
  21. data/lib/bridgetown-core/{convertible.rb → concerns/convertible.rb} +2 -2
  22. data/lib/bridgetown-core/concerns/site/configurable.rb +153 -0
  23. data/lib/bridgetown-core/concerns/site/content.rb +111 -0
  24. data/lib/bridgetown-core/concerns/site/extensible.rb +56 -0
  25. data/lib/bridgetown-core/concerns/site/processable.rb +74 -0
  26. data/lib/bridgetown-core/concerns/site/renderable.rb +50 -0
  27. data/lib/bridgetown-core/concerns/site/writable.rb +31 -0
  28. data/lib/bridgetown-core/configuration.rb +98 -108
  29. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +0 -3
  30. data/lib/bridgetown-core/document.rb +1 -1
  31. data/lib/bridgetown-core/drops/bridgetown_drop.rb +6 -1
  32. data/lib/bridgetown-core/drops/page_drop.rb +1 -1
  33. data/lib/bridgetown-core/drops/site_drop.rb +1 -2
  34. data/lib/bridgetown-core/external.rb +17 -21
  35. data/lib/bridgetown-core/filters.rb +10 -0
  36. data/lib/bridgetown-core/generators/prototype_generator.rb +3 -1
  37. data/lib/bridgetown-core/hooks.rb +62 -62
  38. data/lib/bridgetown-core/layout.rb +10 -4
  39. data/lib/bridgetown-core/liquid_renderer.rb +2 -0
  40. data/lib/bridgetown-core/liquid_renderer/file_system.rb +5 -1
  41. data/lib/bridgetown-core/page.rb +11 -19
  42. data/lib/bridgetown-core/plugin.rb +2 -0
  43. data/lib/bridgetown-core/plugin_manager.rb +68 -14
  44. data/lib/bridgetown-core/reader.rb +5 -0
  45. data/lib/bridgetown-core/readers/data_reader.rb +15 -2
  46. data/lib/bridgetown-core/readers/layout_reader.rb +9 -2
  47. data/lib/bridgetown-core/readers/plugin_content_reader.rb +48 -0
  48. data/lib/bridgetown-core/renderer.rb +51 -32
  49. data/lib/bridgetown-core/site.rb +20 -449
  50. data/lib/bridgetown-core/static_file.rb +1 -5
  51. data/lib/bridgetown-core/tags/include.rb +12 -0
  52. data/lib/bridgetown-core/tags/render_content.rb +27 -16
  53. data/lib/bridgetown-core/tags/with.rb +15 -0
  54. data/lib/bridgetown-core/utils.rb +2 -27
  55. data/lib/bridgetown-core/utils/ruby_exec.rb +66 -0
  56. data/lib/bridgetown-core/version.rb +2 -2
  57. data/lib/bridgetown-core/watcher.rb +21 -10
  58. data/lib/site_template/Gemfile.erb +19 -0
  59. data/lib/site_template/package.json +1 -0
  60. data/lib/site_template/plugins/{.keep → builders/.keep} +0 -0
  61. data/lib/site_template/plugins/site_builder.rb +4 -0
  62. data/lib/site_template/src/_components/footer.html +3 -0
  63. data/lib/site_template/src/_components/head.html +9 -0
  64. data/lib/site_template/src/{_includes → _components}/navbar.html +1 -0
  65. data/lib/site_template/src/_layouts/default.html +3 -3
  66. data/lib/site_template/src/{_components/.keep → favicon.ico} +0 -0
  67. data/lib/site_template/src/posts.md +15 -0
  68. data/lib/site_template/start.js +1 -1
  69. data/lib/site_template/webpack.config.js +3 -3
  70. metadata +106 -18
  71. data/lib/bridgetown-core/command.rb +0 -106
  72. data/lib/bridgetown-core/commands/help.rb +0 -34
  73. data/lib/site_template/src/_includes/footer.html +0 -3
  74. data/lib/site_template/src/_includes/head.html +0 -9
@@ -11,6 +11,7 @@ module Bridgetown
11
11
  # Read Site data from disk and load it into internal data structures.
12
12
  #
13
13
  # Returns nothing.
14
+ # rubocop:disable Metrics/AbcSize
14
15
  def read
15
16
  @site.layouts = LayoutReader.new(site).read
16
17
  read_directories
@@ -18,7 +19,11 @@ module Bridgetown
18
19
  sort_files!
19
20
  @site.data = DataReader.new(site).read(site.config["data_dir"])
20
21
  CollectionReader.new(site).read
22
+ Bridgetown::PluginManager.source_manifests.map(&:content).compact.each do |plugin_content_dir|
23
+ PluginContentReader.new(site, plugin_content_dir).read
24
+ end
21
25
  end
26
+ # rubocop:enable Metrics/AbcSize
22
27
 
23
28
  # Sorts posts, pages, and static files.
24
29
  def sort_files!
@@ -5,7 +5,7 @@ module Bridgetown
5
5
  attr_reader :site, :content
6
6
  def initialize(site)
7
7
  @site = site
8
- @content = {}
8
+ @content = ActiveSupport::HashWithIndifferentAccess.new
9
9
  @entry_filter = EntryFilter.new(site)
10
10
  end
11
11
 
@@ -18,6 +18,7 @@ module Bridgetown
18
18
  def read(dir)
19
19
  base = site.in_source_dir(dir)
20
20
  read_data_to(base, @content)
21
+ merge_environment_specific_metadata!
21
22
  @content
22
23
  end
23
24
 
@@ -40,7 +41,10 @@ module Bridgetown
40
41
  next if @entry_filter.symlink?(path)
41
42
 
42
43
  if File.directory?(path)
43
- read_data_to(path, data[sanitize_filename(entry)] = {})
44
+ read_data_to(
45
+ path,
46
+ data[sanitize_filename(entry)] = ActiveSupport::HashWithIndifferentAccess.new
47
+ )
44
48
  else
45
49
  key = sanitize_filename(File.basename(entry, ".*"))
46
50
  data[key] = read_data_file(path)
@@ -67,6 +71,15 @@ module Bridgetown
67
71
  end
68
72
  end
69
73
 
74
+ def merge_environment_specific_metadata!
75
+ if @content["site_metadata"]
76
+ @content["site_metadata"][Bridgetown.environment]&.each_key do |k|
77
+ @content["site_metadata"][k] = @content["site_metadata"][Bridgetown.environment][k]
78
+ end
79
+ @content["site_metadata"].delete(Bridgetown.environment)
80
+ end
81
+ end
82
+
70
83
  def sanitize_filename(name)
71
84
  name.gsub(%r![^\w\s-]+|(?<=^|\b\s)\s+(?=$|\s?\b)!, "")
72
85
  .gsub(%r!\s+!, "_")
@@ -14,6 +14,13 @@ module Bridgetown
14
14
  Layout.new(site, layout_directory, layout_file)
15
15
  end
16
16
 
17
+ Bridgetown::PluginManager.source_manifests.map(&:layouts).compact.each do |plugin_layouts|
18
+ layout_entries(plugin_layouts).each do |layout_file|
19
+ @layouts[layout_name(layout_file)] ||= \
20
+ Layout.new(site, plugin_layouts, layout_file, from_plugin: true)
21
+ end
22
+ end
23
+
17
24
  @layouts
18
25
  end
19
26
 
@@ -23,8 +30,8 @@ module Bridgetown
23
30
 
24
31
  private
25
32
 
26
- def layout_entries
27
- entries_in layout_directory
33
+ def layout_entries(dir = layout_directory)
34
+ entries_in dir
28
35
  end
29
36
 
30
37
  def entries_in(dir)
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ class PluginContentReader
5
+ attr_reader :site, :content_dir
6
+
7
+ def initialize(site, plugin_content_dir)
8
+ @site = site
9
+ @content_dir = plugin_content_dir
10
+ @content_files = Set.new
11
+ end
12
+
13
+ def read
14
+ return unless content_dir
15
+
16
+ Find.find(content_dir) do |path|
17
+ next if File.directory?(path)
18
+
19
+ if File.symlink?(path)
20
+ Bridgetown.logger.warn "Plugin content reader:", "Ignored symlinked asset: #{path}"
21
+ else
22
+ read_content_file(path)
23
+ end
24
+ end
25
+ end
26
+
27
+ def read_content_file(path)
28
+ dir = File.dirname(path.sub("#{content_dir}/", ""))
29
+ name = File.basename(path)
30
+
31
+ @content_files << if Utils.has_yaml_header?(path)
32
+ Bridgetown::Page.new(site, content_dir, dir, name, from_plugin: true)
33
+ else
34
+ Bridgetown::StaticFile.new(site, content_dir, "/#{dir}", name)
35
+ end
36
+
37
+ add_to(site.pages, Bridgetown::Page)
38
+ add_to(site.static_files, Bridgetown::StaticFile)
39
+ end
40
+
41
+ def add_to(content_type, klass)
42
+ existing_paths = content_type.map(&:relative_path).compact
43
+ @content_files.select { |item| item.is_a?(klass) }.each do |item|
44
+ content_type << item unless existing_paths.include?(item.relative_path)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -5,11 +5,16 @@ module Bridgetown
5
5
  attr_reader :document, :site
6
6
  attr_writer :layouts, :payload
7
7
 
8
+ class << self
9
+ attr_accessor :cached_partials
10
+ end
11
+
8
12
  def initialize(site, document, site_payload = nil)
9
13
  @site = site
10
14
  @document = document
11
15
  @payload = site_payload
12
16
  @layouts = nil
17
+ self.class.cached_partials ||= {}
13
18
  end
14
19
 
15
20
  # Fetches the payload used in Liquid rendering.
@@ -57,7 +62,6 @@ module Bridgetown
57
62
  assign_highlighter_options!
58
63
  assign_layout_data!
59
64
 
60
- Bridgetown.logger.debug "Pre-Render Hooks:", document.relative_path
61
65
  document.trigger_hooks(:pre_render, payload)
62
66
 
63
67
  render_document
@@ -68,16 +72,14 @@ module Bridgetown
68
72
  # Returns String rendered document output
69
73
  # rubocop: disable Metrics/AbcSize
70
74
  def render_document
71
- info = {
72
- registers: { site: site, page: payload["page"] },
73
- strict_filters: liquid_options["strict_filters"],
74
- strict_variables: liquid_options["strict_variables"],
75
- }
75
+ liquid_context = provide_liquid_context
76
+
77
+ execute_inline_ruby!
76
78
 
77
79
  output = document.content
78
80
  if document.render_with_liquid?
79
81
  Bridgetown.logger.debug "Rendering Liquid:", document.relative_path
80
- output = render_liquid(output, payload, info, document.path)
82
+ output = render_liquid(output, payload, liquid_context, document.path)
81
83
  end
82
84
 
83
85
  Bridgetown.logger.debug "Rendering Markup:", document.relative_path
@@ -86,45 +88,47 @@ module Bridgetown
86
88
 
87
89
  if document.place_in_layout?
88
90
  Bridgetown.logger.debug "Rendering Layout:", document.relative_path
89
- output = place_in_layouts(output, payload, info)
91
+ output = place_in_layouts(output, payload, liquid_context)
90
92
  end
91
93
 
92
94
  output
93
95
  end
94
- # rubocop: enable Metrics/AbcSize
95
96
 
96
- # Convert the document using the converters which match this renderer's document.
97
- #
98
- # Returns String the converted content.
99
- def convert(content)
100
- converters.reduce(content) do |output, converter|
101
- begin
102
- converter.convert output
103
- rescue StandardError => e
104
- Bridgetown.logger.error "Conversion error:",
105
- "#{converter.class} encountered an error while "\
106
- "converting '#{document.relative_path}':"
107
- Bridgetown.logger.error("", e.to_s)
108
- raise e
109
- end
110
- end
97
+ def provide_liquid_context
98
+ {
99
+ registers: {
100
+ site: site,
101
+ page: payload["page"],
102
+ cached_partials: self.class.cached_partials,
103
+ },
104
+ strict_filters: liquid_options["strict_filters"],
105
+ strict_variables: liquid_options["strict_variables"],
106
+ }
107
+ end
108
+
109
+ def execute_inline_ruby!
110
+ return unless site.config.should_execute_inline_ruby?
111
+
112
+ Bridgetown::Utils::RubyExec.search_data_for_ruby_code(document, self)
111
113
  end
112
114
 
113
- # Render the given content with the payload and info
115
+ # rubocop: enable Metrics/AbcSize
116
+
117
+ # Render the given content with the payload and context
114
118
  #
115
119
  # content -
116
120
  # payload -
117
- # info -
121
+ # context -
118
122
  # path - (optional) the path to the file, for use in ex
119
123
  #
120
124
  # Returns String the content, rendered by Liquid.
121
- def render_liquid(content, payload, info, path = nil)
125
+ def render_liquid(content, payload, liquid_context, path = nil)
122
126
  template = site.liquid_renderer.file(path).parse(content)
123
127
  template.warnings.each do |e|
124
128
  Bridgetown.logger.warn "Liquid Warning:",
125
129
  LiquidRenderer.format_error(e, path || document.relative_path)
126
130
  end
127
- template.render!(payload, info)
131
+ template.render!(payload, liquid_context)
128
132
  # rubocop: disable Lint/RescueException
129
133
  rescue Exception => e
130
134
  Bridgetown.logger.error "Liquid Exception:",
@@ -133,6 +137,21 @@ module Bridgetown
133
137
  end
134
138
  # rubocop: enable Lint/RescueException
135
139
 
140
+ # Convert the document using the converters which match this renderer's document.
141
+ #
142
+ # Returns String the converted content.
143
+ def convert(content)
144
+ converters.reduce(content) do |output, converter|
145
+ converter.convert output
146
+ rescue StandardError => e
147
+ Bridgetown.logger.error "Conversion error:",
148
+ "#{converter.class} encountered an error while "\
149
+ "converting '#{document.relative_path}':"
150
+ Bridgetown.logger.error("", e.to_s)
151
+ raise e
152
+ end
153
+ end
154
+
136
155
  # Checks if the layout specified in the document actually exists
137
156
  #
138
157
  # layout - the layout to check
@@ -145,7 +164,7 @@ module Bridgetown
145
164
  # Render layouts and place document content inside.
146
165
  #
147
166
  # Returns String rendered content
148
- def place_in_layouts(content, payload, info)
167
+ def place_in_layouts(content, payload, liquid_context)
149
168
  output = content.dup
150
169
  layout = layouts[document.data["layout"].to_s]
151
170
  validate_layout(layout)
@@ -156,7 +175,7 @@ module Bridgetown
156
175
  payload["layout"] = nil
157
176
 
158
177
  while layout
159
- output = render_layout(output, layout, info)
178
+ output = render_layout(output, layout, liquid_context)
160
179
  add_regenerator_dependencies(layout)
161
180
 
162
181
  next unless (layout = site.layouts[layout.data["layout"]])
@@ -183,14 +202,14 @@ module Bridgetown
183
202
  # Render layout content into document.output
184
203
  #
185
204
  # Returns String rendered content
186
- def render_layout(output, layout, info)
205
+ def render_layout(output, layout, liquid_context)
187
206
  payload["content"] = output
188
207
  payload["layout"] = Utils.deep_merge_hashes(layout.data, payload["layout"] || {})
189
208
 
190
209
  render_liquid(
191
210
  layout.content,
192
211
  payload,
193
- info,
212
+ liquid_context,
194
213
  layout.path
195
214
  )
196
215
  end
@@ -2,125 +2,43 @@
2
2
 
3
3
  module Bridgetown
4
4
  class Site
5
- attr_reader :root_dir, :source, :dest, :cache_dir, :config
5
+ require_all "bridgetown-core/concerns/site"
6
+
7
+ include Configurable
8
+ include Content
9
+ include Extensible
10
+ include Processable
11
+ include Renderable
12
+ include Writable
13
+
14
+ attr_reader :root_dir, :source, :dest, :cache_dir, :config,
15
+ :regenerator, :liquid_renderer, :components_load_paths,
16
+ :includes_load_paths
6
17
  attr_accessor :layouts, :pages, :static_files,
7
18
  :exclude, :include, :lsi, :highlighter, :permalink_style,
8
- :time, :future, :unpublished, :plugins, :limit_posts,
19
+ :time, :future, :unpublished, :limit_posts,
9
20
  :keep_files, :baseurl, :data, :file_read_opts,
10
- :plugin_manager
11
-
12
- attr_accessor :converters, :generators, :reader
13
- attr_reader :regenerator, :liquid_renderer, :components_load_paths,
14
- :includes_load_paths
21
+ :plugin_manager, :converters, :generators, :reader
15
22
 
16
23
  # Public: Initialize a new Site.
17
24
  #
18
25
  # config - A Hash containing site configuration details.
19
26
  def initialize(config)
20
- # Source and destination may not be changed after the site has been created.
21
- @root_dir = File.expand_path(config["root_dir"]).freeze
22
- @source = File.expand_path(config["source"]).freeze
23
- @dest = File.expand_path(config["destination"]).freeze
24
-
25
27
  self.config = config
26
28
 
27
- @cache_dir = in_root_dir(config["cache_dir"])
29
+ @plugin_manager = PluginManager.new(self)
30
+ @cleaner = Cleaner.new(self)
28
31
  @reader = Reader.new(self)
29
32
  @regenerator = Regenerator.new(self)
30
33
  @liquid_renderer = LiquidRenderer.new(self)
31
34
 
32
- Bridgetown.sites << self
33
-
34
- reset
35
- setup
36
-
37
- Bridgetown::Hooks.trigger :site, :after_init, self
38
- end
39
-
40
- # Public: Set the site's configuration. This handles side-effects caused by
41
- # changing values in the configuration.
42
- #
43
- # config - a Bridgetown::Configuration, containing the new configuration.
44
- #
45
- # Returns the new configuration.
46
- def config=(config)
47
- @config = config.clone
48
-
49
- %w(lsi highlighter baseurl exclude include future unpublished
50
- limit_posts keep_files).each do |opt|
51
- send("#{opt}=", config[opt])
52
- end
53
-
54
- configure_cache
55
- configure_plugins
56
- configure_component_paths
57
- configure_include_paths
58
- configure_file_read_opts
59
-
60
- self.permalink_style = config["permalink"].to_sym
61
-
62
- @config
63
- end
64
-
65
- # Public: Read, process, and write this Site to output.
66
- #
67
- # Returns nothing.
68
- def process
69
- reset
70
- read
71
- generate
72
- render
73
- cleanup
74
- write
75
- print_stats if config["profile"]
76
- end
77
-
78
- def print_stats
79
- Bridgetown.logger.info @liquid_renderer.stats_table
80
- end
81
-
82
- # rubocop:disable Metrics/MethodLength
83
- #
84
- # Reset Site details.
85
- #
86
- # Returns nothing
87
- def reset
88
- self.time = if config["time"]
89
- Utils.parse_date(config["time"].to_s, "Invalid time in bridgetown.config.yml.")
90
- else
91
- Time.now
92
- end
93
- self.layouts = {}
94
- self.pages = []
95
- self.static_files = []
96
- self.data = {}
97
- @post_attr_hash = {}
98
- @site_data = nil
99
- @collections = nil
100
- @documents = nil
101
- @docs_to_write = nil
102
- @regenerator.clear_cache
103
- @liquid_renderer.reset
104
- @site_cleaner = nil
105
- frontmatter_defaults.reset
106
-
107
- raise ArgumentError, "limit_posts must be a non-negative number" if limit_posts.negative?
108
-
109
- Bridgetown::Cache.clear_if_config_changed config
110
- Bridgetown::Hooks.trigger :site, :after_reset, self
111
- end
112
- # rubocop:enable Metrics/MethodLength
113
-
114
- # Load necessary libraries, plugins, converters, and generators.
115
- #
116
- # Returns nothing.
117
- def setup
118
35
  ensure_not_in_dest
119
36
 
120
- plugin_manager.conscientious_require
37
+ Bridgetown.sites << self
38
+ Bridgetown::Hooks.trigger :site, :after_init, self
121
39
 
122
- self.converters = instantiate_subclasses(Bridgetown::Converter)
123
- self.generators = instantiate_subclasses(Bridgetown::Generator)
40
+ reset # Processable
41
+ setup # Extensible
124
42
  end
125
43
 
126
44
  # Check that the destination dir isn't the source dir or a directory
@@ -134,352 +52,5 @@ module Bridgetown
134
52
  end
135
53
  end
136
54
  end
137
-
138
- # The list of collections and their corresponding Bridgetown::Collection instances.
139
- # If config['collections'] is set, a new instance is created
140
- # for each item in the collection, a new hash is returned otherwise.
141
- #
142
- # Returns a Hash containing collection name-to-instance pairs.
143
- def collections
144
- @collections ||= collection_names.each_with_object({}) do |name, hsh|
145
- hsh[name] = Bridgetown::Collection.new(self, name)
146
- end
147
- end
148
-
149
- # The list of collection names.
150
- #
151
- # Returns an array of collection names from the configuration,
152
- # or an empty array if the `collections` key is not set.
153
- def collection_names
154
- case config["collections"]
155
- when Hash
156
- config["collections"].keys
157
- when Array
158
- config["collections"]
159
- when nil
160
- []
161
- else
162
- raise ArgumentError, "Your `collections` key must be a hash or an array."
163
- end
164
- end
165
-
166
- # Read Site data from disk and load it into internal data structures.
167
- #
168
- # Returns nothing.
169
- def read
170
- reader.read
171
- limit_posts!
172
- Bridgetown::Hooks.trigger :site, :post_read, self
173
- end
174
-
175
- # Run each of the Generators.
176
- #
177
- # Returns nothing.
178
- def generate
179
- generators.each do |generator|
180
- start = Time.now
181
- generator.generate(self)
182
- Bridgetown.logger.debug "Generating:",
183
- "#{generator.class} finished in #{Time.now - start} seconds."
184
- end
185
- end
186
-
187
- # Render the site to the destination.
188
- #
189
- # Returns nothing.
190
- def render
191
- payload = site_payload
192
-
193
- Bridgetown::Hooks.trigger :site, :pre_render, self, payload
194
-
195
- render_docs(payload)
196
- render_pages(payload)
197
-
198
- Bridgetown::Hooks.trigger :site, :post_render, self, payload
199
- end
200
-
201
- # Remove orphaned files and empty directories in destination.
202
- #
203
- # Returns nothing.
204
- def cleanup
205
- site_cleaner.cleanup!
206
- end
207
-
208
- # Write static files, pages, and posts.
209
- #
210
- # Returns nothing.
211
- def write
212
- each_site_file do |item|
213
- item.write(dest) if regenerator.regenerate?(item)
214
- end
215
- regenerator.write_metadata
216
- Bridgetown::Hooks.trigger :site, :post_write, self
217
- end
218
-
219
- def posts
220
- collections["posts"] ||= Collection.new(self, "posts")
221
- end
222
-
223
- # Construct a Hash of Posts indexed by the specified Post attribute.
224
- #
225
- # post_attr - The String name of the Post attribute.
226
- #
227
- # Examples
228
- #
229
- # post_attr_hash('categories')
230
- # # => { 'tech' => [<Post A>, <Post B>],
231
- # # 'ruby' => [<Post B>] }
232
- #
233
- # Returns the Hash: { attr => posts } where
234
- # attr - One of the values for the requested attribute.
235
- # posts - The Array of Posts with the given attr value.
236
- def post_attr_hash(post_attr)
237
- # Build a hash map based on the specified post attribute ( post attr =>
238
- # array of posts ) then sort each array in reverse order.
239
- @post_attr_hash[post_attr] ||= begin
240
- hash = Hash.new { |h, key| h[key] = [] }
241
- posts.docs.each do |p|
242
- p.data[post_attr]&.each { |t| hash[t] << p }
243
- end
244
- hash.each_value { |posts| posts.sort!.reverse! }
245
- hash
246
- end
247
- end
248
-
249
- def tags
250
- post_attr_hash("tags")
251
- end
252
-
253
- def categories
254
- post_attr_hash("categories")
255
- end
256
-
257
- # Prepare site data for site payload. The method maintains backward compatibility
258
- # if the key 'data' is already used in bridgetown.config.yml.
259
- #
260
- # Returns the Hash to be hooked to site.data.
261
- def site_data
262
- @site_data ||= (config["data"] || data)
263
- end
264
-
265
- # The Hash payload containing site-wide data.
266
- #
267
- # Returns the Hash: { "site" => data } where data is a Hash with keys:
268
- # "time" - The Time as specified in the configuration or the
269
- # current time if none was specified.
270
- # "posts" - The Array of Posts, sorted chronologically by post date
271
- # and then title.
272
- # "pages" - The Array of all Pages.
273
- # "html_pages" - The Array of HTML Pages.
274
- # "categories" - The Hash of category values and Posts.
275
- # See Site#post_attr_hash for type info.
276
- # "tags" - The Hash of tag values and Posts.
277
- # See Site#post_attr_hash for type info.
278
- def site_payload
279
- Drops::UnifiedPayloadDrop.new self
280
- end
281
- alias_method :to_liquid, :site_payload
282
-
283
- # Get the implementation class for the given Converter.
284
- # Returns the Converter instance implementing the given Converter.
285
- # klass - The Class of the Converter to fetch.
286
- def find_converter_instance(klass)
287
- @find_converter_instance ||= {}
288
- @find_converter_instance[klass] ||= begin
289
- converters.find { |converter| converter.instance_of?(klass) } || \
290
- raise("No Converters found for #{klass}")
291
- end
292
- end
293
-
294
- # klass - class or module containing the subclasses.
295
- # Returns array of instances of subclasses of parameter.
296
- # Create array of instances of the subclasses of the class or module
297
- # passed in as argument.
298
-
299
- def instantiate_subclasses(klass)
300
- klass.descendants.sort.map do |c|
301
- c.new(config)
302
- end
303
- end
304
-
305
- # Get the to be written documents
306
- #
307
- # Returns an Array of Documents which should be written
308
- def docs_to_write
309
- documents.select(&:write?)
310
- end
311
-
312
- # Get all the documents
313
- #
314
- # Returns an Array of all Documents
315
- def documents
316
- collections.each_with_object(Set.new) do |(_, collection), set|
317
- set.merge(collection.docs).merge(collection.files)
318
- end.to_a
319
- end
320
-
321
- def each_site_file
322
- %w(pages static_files docs_to_write).each do |type|
323
- send(type).each do |item|
324
- yield item
325
- end
326
- end
327
- end
328
-
329
- # Returns the FrontmatterDefaults or creates a new FrontmatterDefaults
330
- # if it doesn't already exist.
331
- #
332
- # Returns The FrontmatterDefaults
333
- def frontmatter_defaults
334
- @frontmatter_defaults ||= FrontmatterDefaults.new(self)
335
- end
336
-
337
- # Whether to perform a full rebuild without incremental regeneration
338
- #
339
- # Returns a Boolean: true for a full rebuild, false for normal build
340
- def incremental?(override = {})
341
- override["incremental"] || config["incremental"]
342
- end
343
-
344
- # Returns the publisher or creates a new publisher if it doesn't
345
- # already exist.
346
- #
347
- # Returns The Publisher
348
- def publisher
349
- @publisher ||= Publisher.new(self)
350
- end
351
-
352
- # Public: Prefix a given path with the root directory.
353
- #
354
- # paths - (optional) path elements to a file or directory within the
355
- # root directory
356
- #
357
- # Returns a path which is prefixed with the root_dir directory.
358
- def in_root_dir(*paths)
359
- paths.reduce(root_dir) do |base, path|
360
- Bridgetown.sanitized_path(base, path)
361
- end
362
- end
363
-
364
- # Public: Prefix a given path with the source directory.
365
- #
366
- # paths - (optional) path elements to a file or directory within the
367
- # source directory
368
- #
369
- # Returns a path which is prefixed with the source directory.
370
- def in_source_dir(*paths)
371
- paths.reduce(source) do |base, path|
372
- Bridgetown.sanitized_path(base, path)
373
- end
374
- end
375
-
376
- # Public: Prefix a given path with the destination directory.
377
- #
378
- # paths - (optional) path elements to a file or directory within the
379
- # destination directory
380
- #
381
- # Returns a path which is prefixed with the destination directory.
382
- def in_dest_dir(*paths)
383
- paths.reduce(dest) do |base, path|
384
- Bridgetown.sanitized_path(base, path)
385
- end
386
- end
387
-
388
- # Public: Prefix a given path with the cache directory.
389
- #
390
- # paths - (optional) path elements to a file or directory within the
391
- # cache directory
392
- #
393
- # Returns a path which is prefixed with the cache directory.
394
- def in_cache_dir(*paths)
395
- paths.reduce(cache_dir) do |base, path|
396
- Bridgetown.sanitized_path(base, path)
397
- end
398
- end
399
-
400
- # Public: The full path to the directory that houses all the collections registered
401
- # with the current site.
402
- #
403
- # Returns the source directory or the absolute path to the custom collections_dir
404
- def collections_path
405
- dir_str = config["collections_dir"]
406
- @collections_path ||= dir_str.empty? ? source : in_source_dir(dir_str)
407
- end
408
-
409
- private
410
-
411
- # Limits the current posts; removes the posts which exceed the limit_posts
412
- #
413
- # Returns nothing
414
- def limit_posts!
415
- if limit_posts.positive?
416
- limit = posts.docs.length < limit_posts ? posts.docs.length : limit_posts
417
- posts.docs = posts.docs[-limit, limit]
418
- end
419
- end
420
-
421
- # Returns the Cleaner or creates a new Cleaner if it doesn't
422
- # already exist.
423
- #
424
- # Returns The Cleaner
425
- def site_cleaner
426
- @site_cleaner ||= Cleaner.new(self)
427
- end
428
-
429
- # Disable Marshaling cache to disk in Safe Mode
430
- def configure_cache
431
- Bridgetown::Cache.cache_dir = in_root_dir(config["cache_dir"], "Bridgetown/Cache")
432
- Bridgetown::Cache.disable_disk_cache! if config["disable_disk_cache"]
433
- end
434
-
435
- def configure_plugins
436
- self.plugin_manager = Bridgetown::PluginManager.new(self)
437
- self.plugins = plugin_manager.plugins_path
438
- end
439
-
440
- def configure_component_paths
441
- @components_load_paths = config["components_dir"].then do |dir|
442
- dir.is_a?(Array) ? dir : [dir]
443
- end
444
- @components_load_paths.map! do |dir|
445
- if !!(dir =~ %r!^\.\.?\/!)
446
- # allow ./dir or ../../dir type options
447
- File.expand_path(dir.to_s, root_dir)
448
- else
449
- in_source_dir(dir.to_s)
450
- end
451
- end
452
- end
453
-
454
- def configure_include_paths
455
- @includes_load_paths = Array(in_source_dir(config["includes_dir"].to_s))
456
- end
457
-
458
- def configure_file_read_opts
459
- self.file_read_opts = {}
460
- file_read_opts[:encoding] = config["encoding"] if config["encoding"]
461
- self.file_read_opts = Bridgetown::Utils.merged_file_read_opts(self, {})
462
- end
463
-
464
- def render_docs(payload)
465
- collections.each_value do |collection|
466
- collection.docs.each do |document|
467
- render_regenerated(document, payload)
468
- end
469
- end
470
- end
471
-
472
- def render_pages(payload)
473
- pages.each do |page|
474
- render_regenerated(page, payload)
475
- end
476
- end
477
-
478
- def render_regenerated(document, payload)
479
- return unless regenerator.regenerate?(document)
480
-
481
- document.output = Bridgetown::Renderer.new(self, document, payload).run
482
- document.trigger_hooks(:post_render)
483
- end
484
55
  end
485
56
  end