bridgetown-core 0.21.4 → 1.0.0.alpha4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +35 -0
  3. data/Rakefile +5 -5
  4. data/bin/bridgetown +2 -0
  5. data/bridgetown-core.gemspec +3 -0
  6. data/lib/bridgetown-core/cache.rb +3 -5
  7. data/lib/bridgetown-core/cleaner.rb +2 -10
  8. data/lib/bridgetown-core/collection.rb +62 -86
  9. data/lib/bridgetown-core/commands/base.rb +62 -2
  10. data/lib/bridgetown-core/commands/build.rb +33 -12
  11. data/lib/bridgetown-core/commands/concerns/actions.rb +2 -2
  12. data/lib/bridgetown-core/commands/concerns/build_options.rb +3 -10
  13. data/lib/bridgetown-core/commands/concerns/configuration_overridable.rb +3 -1
  14. data/lib/bridgetown-core/commands/console.rb +3 -3
  15. data/lib/bridgetown-core/commands/doctor.rb +13 -11
  16. data/lib/bridgetown-core/commands/new.rb +14 -6
  17. data/lib/bridgetown-core/commands/plugins.rb +8 -11
  18. data/lib/bridgetown-core/commands/serve/servlet.rb +4 -4
  19. data/lib/bridgetown-core/commands/serve.rb +37 -37
  20. data/lib/bridgetown-core/commands/start.rb +106 -0
  21. data/lib/bridgetown-core/commands/webpack/webpack.defaults.js.erb +2 -2
  22. data/lib/bridgetown-core/commands/webpack.rb +1 -1
  23. data/lib/bridgetown-core/component.rb +2 -6
  24. data/lib/bridgetown-core/concerns/liquid_renderable.rb +2 -2
  25. data/lib/bridgetown-core/concerns/site/configurable.rb +1 -13
  26. data/lib/bridgetown-core/concerns/site/content.rb +7 -118
  27. data/lib/bridgetown-core/concerns/site/extensible.rb +3 -4
  28. data/lib/bridgetown-core/concerns/site/localizable.rb +3 -1
  29. data/lib/bridgetown-core/concerns/site/processable.rb +8 -20
  30. data/lib/bridgetown-core/concerns/site/renderable.rb +19 -30
  31. data/lib/bridgetown-core/concerns/site/ssr.rb +53 -0
  32. data/lib/bridgetown-core/concerns/site/writable.rb +6 -9
  33. data/lib/bridgetown-core/configuration.rb +19 -48
  34. data/lib/bridgetown-core/configurations/minitesting.rb +1 -1
  35. data/lib/bridgetown-core/configurations/turbo.rb +1 -1
  36. data/lib/bridgetown-core/converter.rb +1 -0
  37. data/lib/bridgetown-core/converters/erb_templates.rb +8 -5
  38. data/lib/bridgetown-core/converters/liquid_templates.rb +5 -2
  39. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +1 -1
  40. data/lib/bridgetown-core/converters/smartypants.rb +1 -0
  41. data/lib/bridgetown-core/current.rb +4 -0
  42. data/lib/bridgetown-core/drops/collection_drop.rb +1 -1
  43. data/lib/bridgetown-core/drops/drop.rb +4 -4
  44. data/lib/bridgetown-core/drops/generated_page_drop.rb +23 -0
  45. data/lib/bridgetown-core/drops/resource_drop.rb +3 -3
  46. data/lib/bridgetown-core/drops/site_drop.rb +3 -47
  47. data/lib/bridgetown-core/entry_filter.rb +1 -0
  48. data/lib/bridgetown-core/filters/url_filters.rb +2 -0
  49. data/lib/bridgetown-core/filters.rb +11 -12
  50. data/lib/bridgetown-core/frontmatter_defaults.rb +44 -82
  51. data/lib/bridgetown-core/{page.rb → generated_page.rb} +34 -60
  52. data/lib/bridgetown-core/generators/prototype_generator.rb +46 -58
  53. data/lib/bridgetown-core/helpers.rb +8 -3
  54. data/lib/bridgetown-core/hooks.rb +2 -2
  55. data/lib/bridgetown-core/layout.rb +15 -4
  56. data/lib/bridgetown-core/liquid_renderer.rb +1 -3
  57. data/lib/bridgetown-core/log_adapter.rb +1 -1
  58. data/lib/bridgetown-core/log_writer.rb +7 -1
  59. data/lib/bridgetown-core/model/base.rb +12 -4
  60. data/lib/bridgetown-core/model/builder_origin.rb +23 -11
  61. data/lib/bridgetown-core/model/origin.rb +3 -0
  62. data/lib/bridgetown-core/model/plugin_origin.rb +34 -0
  63. data/lib/bridgetown-core/model/repo_origin.rb +1 -1
  64. data/lib/bridgetown-core/plugin_manager.rb +10 -10
  65. data/lib/bridgetown-core/publisher.rb +1 -1
  66. data/lib/bridgetown-core/rack/boot.rb +55 -0
  67. data/lib/bridgetown-core/rack/logger.rb +22 -0
  68. data/lib/bridgetown-core/rack/roda.rb +66 -0
  69. data/lib/bridgetown-core/rack/routes.rb +88 -0
  70. data/lib/bridgetown-core/rack/static_indexes.rb +30 -0
  71. data/lib/bridgetown-core/reader.rb +16 -48
  72. data/lib/bridgetown-core/readers/layout_reader.rb +2 -2
  73. data/lib/bridgetown-core/readers/plugin_content_reader.rb +8 -7
  74. data/lib/bridgetown-core/renderer.rb +2 -12
  75. data/lib/bridgetown-core/resource/base.rb +34 -11
  76. data/lib/bridgetown-core/resource/permalink_processor.rb +23 -12
  77. data/lib/bridgetown-core/resource/relations.rb +2 -3
  78. data/lib/bridgetown-core/resource/taxonomy_term.rb +1 -5
  79. data/lib/bridgetown-core/resource/transformer.rb +8 -6
  80. data/lib/bridgetown-core/ruby_template_view.rb +6 -8
  81. data/lib/bridgetown-core/site.rb +4 -8
  82. data/lib/bridgetown-core/static_file.rb +14 -21
  83. data/lib/bridgetown-core/tags/find.rb +6 -6
  84. data/lib/bridgetown-core/tags/highlight.rb +5 -5
  85. data/lib/bridgetown-core/tags/include.rb +22 -32
  86. data/lib/bridgetown-core/tags/link.rb +4 -0
  87. data/lib/bridgetown-core/tags/live_reload_dev_js.rb +13 -0
  88. data/lib/bridgetown-core/tags/post_url.rb +9 -14
  89. data/lib/bridgetown-core/tags/render_content.rb +2 -2
  90. data/lib/bridgetown-core/tasks/bridgetown_tasks.rake +60 -0
  91. data/lib/bridgetown-core/url.rb +5 -4
  92. data/lib/bridgetown-core/utils/aux.rb +57 -0
  93. data/lib/bridgetown-core/utils/ruby_exec.rb +3 -45
  94. data/lib/bridgetown-core/utils/ruby_front_matter.rb +22 -7
  95. data/lib/bridgetown-core/utils.rb +44 -11
  96. data/lib/bridgetown-core/version.rb +2 -2
  97. data/lib/bridgetown-core/watcher.rb +4 -6
  98. data/lib/bridgetown-core.rb +16 -23
  99. data/lib/site_template/Gemfile.erb +6 -2
  100. data/lib/site_template/README.md +6 -6
  101. data/lib/site_template/Rakefile +49 -0
  102. data/lib/site_template/bridgetown.config.yml +2 -3
  103. data/lib/site_template/config/puma.rb +27 -0
  104. data/lib/site_template/config.ru +7 -0
  105. data/lib/site_template/package.json.erb +1 -9
  106. data/lib/site_template/server/roda_app.rb +22 -0
  107. data/lib/site_template/server/routes/hello.rb.sample +10 -0
  108. data/lib/site_template/src/_components/head.liquid +2 -1
  109. data/lib/site_template/src/_layouts/default.liquid +1 -1
  110. data/lib/site_template/src/about.md +0 -1
  111. data/lib/site_template/src/posts.md +2 -3
  112. metadata +63 -18
  113. data/lib/bridgetown-core/concerns/data_accessible.rb +0 -20
  114. data/lib/bridgetown-core/concerns/validatable.rb +0 -56
  115. data/lib/bridgetown-core/document.rb +0 -437
  116. data/lib/bridgetown-core/drops/document_drop.rb +0 -80
  117. data/lib/bridgetown-core/drops/excerpt_drop.rb +0 -19
  118. data/lib/bridgetown-core/drops/page_drop.rb +0 -18
  119. data/lib/bridgetown-core/excerpt.rb +0 -200
  120. data/lib/bridgetown-core/readers/data_reader.rb +0 -89
  121. data/lib/bridgetown-core/readers/page_reader.rb +0 -26
  122. data/lib/bridgetown-core/readers/post_reader.rb +0 -109
  123. data/lib/bridgetown-core/regenerator.rb +0 -202
  124. data/lib/bridgetown-core/related_posts.rb +0 -55
  125. data/lib/site_template/config/.keep +0 -0
  126. data/lib/site_template/start.js +0 -17
  127. data/lib/site_template/sync.js +0 -35
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/indifferent"
4
+
5
+ class Roda
6
+ module RodaPlugins
7
+ module BridgetownSSR
8
+ def self.configure(app, _opts = {}, &block)
9
+ app.opts[:bridgetown_site] = Bridgetown::Site.start_ssr!(&block)
10
+ end
11
+ end
12
+
13
+ register_plugin :bridgetown_ssr, BridgetownSSR
14
+ end
15
+ end
16
+
17
+ module Bridgetown
18
+ module Rack
19
+ class Roda < ::Roda
20
+ plugin :hooks
21
+ plugin :common_logger, Bridgetown::Rack::Logger.new($stdout), method: :info
22
+ plugin :json
23
+ plugin :json_parser
24
+ plugin :cookies
25
+ plugin :public, root: Bridgetown::Current.preloaded_configuration.destination
26
+ plugin :not_found do
27
+ output_folder = Bridgetown::Current.preloaded_configuration.destination
28
+ File.read(File.join(output_folder, "404.html"))
29
+ rescue Errno::ENOENT
30
+ "404 Not Found"
31
+ end
32
+ plugin :error_handler do |e|
33
+ puts "\n#{e.class} (#{e.message}):\n\n"
34
+ puts e.backtrace
35
+ output_folder = Bridgetown::Current.preloaded_configuration.destination
36
+ File.read(File.join(output_folder, "500.html"))
37
+ rescue Errno::ENOENT
38
+ "500 Internal Server Error"
39
+ end
40
+
41
+ def _roda_run_main_route(r) # rubocop:disable Naming/MethodParameterName
42
+ if self.class.opts[:bridgetown_site]
43
+ # The site had previously been initialized via the bridgetown_ssr plugin
44
+ Bridgetown::Current.site ||= self.class.opts[:bridgetown_site]
45
+ end
46
+ Bridgetown::Current.preloaded_configuration ||=
47
+ self.class.opts[:bridgetown_preloaded_config]
48
+
49
+ r.public
50
+
51
+ r.root do
52
+ output_folder = Bridgetown::Current.preloaded_configuration.destination
53
+ File.read(File.join(output_folder, "index.html"))
54
+ end
55
+
56
+ super
57
+ end
58
+
59
+ # Helper shorthand for Bridgetown::Current.site
60
+ # @return [Bridgetown::Site]
61
+ def bridgetown_site
62
+ Bridgetown::Current.site
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Rack
5
+ class Routes
6
+ class << self
7
+ attr_accessor :tracked_subclasses, :router_block
8
+
9
+ def inherited(base)
10
+ Bridgetown::Rack::Routes.track_subclass base
11
+ super
12
+ end
13
+
14
+ def track_subclass(klass)
15
+ Bridgetown::Rack::Routes.tracked_subclasses ||= {}
16
+ Bridgetown::Rack::Routes.tracked_subclasses[klass.name] = klass
17
+ end
18
+
19
+ def reload_subclasses
20
+ Bridgetown::Rack::Routes.tracked_subclasses&.each_key do |klassname|
21
+ Kernel.const_get(klassname)
22
+ rescue NameError
23
+ Bridgetown::Rack::Routes.tracked_subclasses.delete klassname
24
+ end
25
+ end
26
+
27
+ def route(&block)
28
+ self.router_block = block
29
+ end
30
+
31
+ def merge(roda_app)
32
+ return unless router_block
33
+
34
+ new(roda_app).handle_routes
35
+ end
36
+
37
+ def start!(roda_app)
38
+ if Bridgetown.env.development? &&
39
+ !Bridgetown::Current.preloaded_configuration.skip_live_reload
40
+ setup_live_reload roda_app.request
41
+ end
42
+
43
+ Bridgetown::Rack::Routes.tracked_subclasses&.each_value do |klass|
44
+ klass.merge roda_app
45
+ end
46
+
47
+ if defined?(Bridgetown::Routes::RodaRouter)
48
+ Bridgetown::Routes::RodaRouter.start!(roda_app)
49
+ end
50
+
51
+ nil
52
+ end
53
+
54
+ def setup_live_reload(request)
55
+ request.get "_bridgetown/live_reload" do
56
+ {
57
+ last_mod: File.stat(
58
+ File.join(Bridgetown::Current.preloaded_configuration.destination, "index.html")
59
+ ).mtime.to_i,
60
+ }
61
+ rescue StandardError => e
62
+ { last_mod: 0, error: e.message }
63
+ end
64
+ end
65
+ end
66
+
67
+ def initialize(roda_app)
68
+ @_roda_app = roda_app
69
+ end
70
+
71
+ def handle_routes
72
+ instance_exec(@_roda_app.request, &self.class.router_block)
73
+ end
74
+
75
+ ruby2_keywords def method_missing(method_name, *args, &block)
76
+ if @_roda_app.respond_to?(method_name.to_sym)
77
+ @_roda_app.send method_name.to_sym, *args, &block
78
+ else
79
+ super
80
+ end
81
+ end
82
+
83
+ def respond_to_missing?(method_name, include_private = false)
84
+ @_roda_app.respond_to?(method_name.to_sym, include_private) || super
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "roda/plugins/public"
4
+
5
+ Roda::RodaPlugins::Public::RequestMethods.module_eval do
6
+ SPLIT = Regexp.union(*[File::SEPARATOR, File::ALT_SEPARATOR].compact) # rubocop:disable Lint/ConstantDefinitionInBlock
7
+ def public_path_segments(path) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
8
+ segments = []
9
+
10
+ path.split(SPLIT).each do |seg|
11
+ next if seg.empty? || seg == "."
12
+
13
+ seg == ".." ? segments.pop : segments << seg
14
+ end
15
+
16
+ path = ::File.join(roda_class.opts[:public_root], *segments)
17
+ unless ::File.file?(path)
18
+ path = ::File.join(path, "index.html")
19
+ if ::File.file?(path)
20
+ segments << "index.html"
21
+ else
22
+ segments[segments.size - 1] = "#{segments.last}.html"
23
+ end
24
+ end
25
+
26
+ segments
27
+ rescue IndexError
28
+ nil
29
+ end
30
+ end
@@ -12,36 +12,33 @@ module Bridgetown
12
12
  # Read Site data from disk and load it into internal data structures.
13
13
  #
14
14
  # Returns nothing.
15
- def read # rubocop:todo Metrics/AbcSize
15
+ def read
16
16
  site.defaults_reader.read
17
- site.layouts = LayoutReader.new(site).read
17
+ read_layouts
18
18
  read_directories
19
- read_included_excludes
19
+ read_includes
20
20
  sort_files!
21
- site.data = if site.uses_resource?
22
- site.collections.data.read
23
- site.collections.data.merge_data_resources
24
- else
25
- DataReader.new(site).read
26
- end
21
+ site.data = site.collections.data.read.merge_data_resources
27
22
  read_collections
28
- Bridgetown::PluginManager.source_manifests.map(&:content).compact.each do |plugin_content_dir|
29
- PluginContentReader.new(site, plugin_content_dir).read
23
+ Bridgetown::PluginManager.source_manifests.select(&:content).each do |manifest|
24
+ PluginContentReader.new(site, manifest).read
30
25
  end
31
26
  end
32
27
 
28
+ def read_layouts
29
+ site.layouts = LayoutReader.new(site).read
30
+ end
31
+
33
32
  def read_collections
34
33
  site.collections.each_value do |collection|
35
- next if site.uses_resource? && collection.data?
36
- next if !site.uses_resource? && collection.legacy_reader?
34
+ next if collection.data?
37
35
 
38
36
  collection.read
39
37
  end
40
38
  end
41
39
 
42
- # Sorts posts, pages, and static files.
40
+ # Sorts generated pages and static files.
43
41
  def sort_files!
44
- site.collections.posts.docs.sort! unless site.uses_resource?
45
42
  site.generated_pages.sort_by!(&:name)
46
43
  site.static_files.sort_by!(&:relative_path)
47
44
  end
@@ -74,24 +71,11 @@ module Bridgetown
74
71
  end
75
72
  end
76
73
 
77
- retrieve_posts(dir) unless site.uses_resource?
78
74
  retrieve_dirs(base, dir, dot_dirs)
79
75
  retrieve_pages(dir, dot_pages)
80
76
  retrieve_static_files(dir, dot_static_files)
81
77
  end
82
78
 
83
- # Retrieves all the posts(posts) from the given directory
84
- # and add them to the site and sort them.
85
- #
86
- # dir - The String representing the directory to retrieve the posts from.
87
- #
88
- # Returns nothing.
89
- def retrieve_posts(dir)
90
- return if outside_configured_directory?(dir)
91
-
92
- post_reader.read_posts(dir)
93
- end
94
-
95
79
  # Recursively traverse directories with the read_directories function.
96
80
  #
97
81
  # base - The String representing the site's base directory.
@@ -115,14 +99,9 @@ module Bridgetown
115
99
  #
116
100
  # Returns nothing.
117
101
  def retrieve_pages(dir, dot_pages)
118
- if site.uses_resource?
119
- dot_pages.each do |page_path|
120
- site.collections.pages.read_resource(site.in_source_dir(dir, page_path))
121
- end
122
- return
102
+ dot_pages.each do |page_path|
103
+ site.collections.pages.read_resource(site.in_source_dir(dir, page_path))
123
104
  end
124
-
125
- site.pages.concat(PageReader.new(site, dir).read(dot_pages))
126
105
  end
127
106
 
128
107
  # Retrieve all the static files from the current directory,
@@ -180,13 +159,7 @@ module Bridgetown
180
159
  !collections_dir.empty? && !dir.start_with?("/#{collections_dir}")
181
160
  end
182
161
 
183
- # Create a single PostReader instance to retrieve posts from all valid
184
- # directories in current site.
185
- def post_reader
186
- @post_reader ||= PostReader.new(site)
187
- end
188
-
189
- def read_included_excludes
162
+ def read_includes
190
163
  site.config.include.each do |entry|
191
164
  next if entry == ".htaccess"
192
165
 
@@ -200,12 +173,7 @@ module Bridgetown
200
173
  def read_included_file(entry_path)
201
174
  dir = File.dirname(entry_path).sub(site.source, "")
202
175
  file = Array(File.basename(entry_path))
203
- if Utils.has_yaml_header?(entry_path)
204
- # TODO: does this need to get incorporated into the resource engine?
205
- site.pages.concat(PageReader.new(site, dir).read(file))
206
- else
207
- retrieve_static_files(dir, file)
208
- end
176
+ retrieve_static_files(dir, file)
209
177
  end
210
178
  end
211
179
  end
@@ -47,10 +47,10 @@ module Bridgetown
47
47
  Layout.label_for_file(file)
48
48
  end
49
49
 
50
- def within(directory)
50
+ def within(directory, &block)
51
51
  return unless File.exist?(directory)
52
52
 
53
- Dir.chdir(directory) { yield }
53
+ Dir.chdir(directory, &block)
54
54
  end
55
55
  end
56
56
  end
@@ -2,11 +2,14 @@
2
2
 
3
3
  module Bridgetown
4
4
  class PluginContentReader
5
- attr_reader :site, :content_dir
5
+ attr_reader :site, :manifest, :content_dir
6
6
 
7
- def initialize(site, plugin_content_dir)
7
+ # @param site [Bridgetown::Site]
8
+ # @param manifest [Bridgetown::Plugin::SourceManifest]
9
+ def initialize(site, manifest)
8
10
  @site = site
9
- @content_dir = plugin_content_dir
11
+ @manifest = manifest
12
+ @content_dir = manifest.content
10
13
  @content_files = Set.new
11
14
  end
12
15
 
@@ -28,13 +31,11 @@ module Bridgetown
28
31
  dir = File.dirname(path.sub("#{content_dir}/", ""))
29
32
  name = File.basename(path)
30
33
 
31
- @content_files << if Utils.has_yaml_header?(path)
32
- Bridgetown::Page.new(site, content_dir, dir, name, from_plugin: true)
34
+ @content_files << if Utils.has_yaml_header?(path) || Utils.has_rbfm_header?(path)
35
+ site.collections.pages.read_resource(path, manifest: manifest)
33
36
  else
34
37
  Bridgetown::StaticFile.new(site, content_dir, "/#{dir}", name)
35
38
  end
36
-
37
- add_to(site.pages, Bridgetown::Page)
38
39
  add_to(site.static_files, Bridgetown::StaticFile)
39
40
  end
40
41
 
@@ -65,7 +65,7 @@ module Bridgetown
65
65
  def execute_inline_ruby!
66
66
  return unless site.config.should_execute_inline_ruby?
67
67
 
68
- Bridgetown::Utils::RubyExec.search_data_for_ruby_code(document, self)
68
+ Bridgetown::Utils::RubyExec.search_data_for_ruby_code(document)
69
69
  end
70
70
 
71
71
  # Convert the document using the converters which match this renderer's document.
@@ -98,7 +98,6 @@ module Bridgetown
98
98
 
99
99
  while layout
100
100
  output = render_layout(output, layout)
101
- add_regenerator_dependencies(layout)
102
101
 
103
102
  next unless (layout = site.layouts[layout.data["layout"]])
104
103
  break if used.include?(layout)
@@ -120,7 +119,7 @@ module Bridgetown
120
119
  !(document.is_a? Bridgetown::Excerpt)
121
120
 
122
121
  Bridgetown.logger.warn "Build Warning:", "Layout '#{document.data["layout"]}' requested " \
123
- "in #{document.relative_path} does not exist."
122
+ "in #{document.relative_path} does not exist."
124
123
  end
125
124
 
126
125
  # Render layout content into document.output
@@ -144,15 +143,6 @@ module Bridgetown
144
143
  end
145
144
  end
146
145
 
147
- def add_regenerator_dependencies(layout)
148
- return unless document.write?
149
-
150
- site.regenerator.add_dependency(
151
- site.in_source_dir(document.path),
152
- layout.path
153
- )
154
- end
155
-
156
146
  def permalink_ext
157
147
  document_permalink = document.permalink
158
148
  if document_permalink &&
@@ -53,7 +53,7 @@ module Bridgetown
53
53
  @layout = site.layouts[data.layout].tap do |layout|
54
54
  unless layout
55
55
  Bridgetown.logger.warn "Resource:", "Layout '#{data.layout}' " \
56
- "requested via #{relative_path} does not exist."
56
+ "requested via #{relative_path} does not exist."
57
57
  end
58
58
  end
59
59
  end
@@ -116,6 +116,8 @@ module Bridgetown
116
116
 
117
117
  def transform!
118
118
  transformer.process! unless collection.data?
119
+
120
+ self
119
121
  end
120
122
 
121
123
  def trigger_hooks(hook_name, *args)
@@ -261,19 +263,18 @@ module Bridgetown
261
263
  "#<#{self.class} #{id}>"
262
264
  end
263
265
 
264
- # Compare this document against another document.
265
- # Comparison is a comparison between the 2 paths of the documents.
266
+ # Compare this resource against another resource.
267
+ # Comparison is a comparison between the 2 dates or paths of the resources.
266
268
  #
267
- # Returns -1, 0, +1 or nil depending on whether this doc's path is less than,
268
- # equal or greater than the other doc's path. See String#<=> for more details.
269
+ # @return [Integer] -1, 0, or +1
269
270
  def <=>(other) # rubocop:todo Metrics/AbcSize
270
271
  return nil unless other.respond_to?(:data)
271
272
 
272
- if data.date.respond_to?(:to_datetime) && other.data.date.respond_to?(:to_datetime)
273
- return data.date.to_datetime <=> other.data.date.to_datetime
274
- end
273
+ cmp = if data.date.respond_to?(:to_datetime) && other.data.date.respond_to?(:to_datetime)
274
+ data.date.to_datetime <=> other.data.date.to_datetime
275
+ end
275
276
 
276
- cmp = data["date"] <=> other.data["date"]
277
+ cmp = data["date"] <=> other.data["date"] if cmp.nil?
277
278
  cmp = path <=> other.path if cmp.nil? || cmp.zero?
278
279
  cmp
279
280
  end
@@ -292,7 +293,9 @@ module Bridgetown
292
293
 
293
294
  private
294
295
 
295
- def ensure_default_data
296
+ def ensure_default_data # rubocop:todo Metrics/AbcSize
297
+ determine_locale
298
+
296
299
  slug = if matches = relative_path.to_s.match(DATE_FILENAME_MATCHER) # rubocop:disable Lint/AssignmentInCondition
297
300
  set_date_from_string(matches[1]) unless data.date
298
301
  matches[2]
@@ -300,6 +303,8 @@ module Bridgetown
300
303
  basename_without_ext
301
304
  end
302
305
 
306
+ slug.chomp!(".#{data.locale}") if data.locale && slug.ends_with?(".#{data.locale}")
307
+
303
308
  data.slug ||= slug
304
309
  data.title ||= Bridgetown::Utils.titleize_slug(slug)
305
310
  end
@@ -309,7 +314,7 @@ module Bridgetown
309
314
 
310
315
  data.date = Bridgetown::Utils.parse_date(
311
316
  new_date,
312
- "Document '#{relative_path}' does not have a valid date in the #{model}."
317
+ "Resource '#{relative_path}' does not have a valid date."
313
318
  )
314
319
  end
315
320
 
@@ -332,6 +337,24 @@ module Bridgetown
332
337
  end
333
338
  end
334
339
 
340
+ def determine_locale # rubocop:todo Metrics/AbcSize
341
+ unless data.locale
342
+ data.locale = locale_from_alt_data_or_filename.presence || site.config.default_locale
343
+ end
344
+
345
+ return unless data.locale_overrides.is_a?(Hash) && data.locale_overrides&.key?(data.locale)
346
+
347
+ data.merge!(data.locale_overrides[data.locale])
348
+ end
349
+
350
+ # Look for alternative front matter or look at the filename pattern: slug.locale.ext
351
+ def locale_from_alt_data_or_filename
352
+ found_locale = data.language || data.lang || basename_without_ext.split(".")[1..-1].last
353
+ return unless found_locale && site.config.available_locales.include?(found_locale.to_sym)
354
+
355
+ found_locale
356
+ end
357
+
335
358
  def format_url(url)
336
359
  url.to_s.sub(%r{index\.html?$}, "").sub(%r{\.html?$}, "")
337
360
  end
@@ -36,20 +36,20 @@ module Bridgetown
36
36
  new_url = url_segments.map do |segment|
37
37
  segment.starts_with?(":") ? process_segment(segment.sub(%r{^:}, "")) : segment
38
38
  end.select(&:present?).join("/")
39
-
40
39
  # No relative URLs should ever end in /index.html
41
40
  new_url.sub!(%r{/index$}, "") if final_ext == ".html"
42
41
 
43
- add_base_path finalize_permalink(new_url, permalink)
42
+ ensure_base_path finalize_permalink(new_url, permalink)
44
43
  end
45
44
 
46
45
  def process_segment(segment)
47
46
  segment = segment.to_sym
48
47
  if self.class.placeholder_processors[segment]
49
48
  segment_value = self.class.placeholder_processors[segment].(resource)
50
- if segment_value.is_a?(Hash)
49
+ case segment_value
50
+ when Hash
51
51
  segment_value[:raw_value]
52
- elsif segment_value.is_a?(Array)
52
+ when Array
53
53
  segment_value.map do |subsegment|
54
54
  Utils.slugify(subsegment, mode: slugify_mode)
55
55
  end.join("/")
@@ -66,13 +66,13 @@ module Bridgetown
66
66
 
67
67
  case permalink_style.to_sym
68
68
  when :pretty
69
- "#{collection_prefix}/:categories/:year/:month/:day/:slug/"
69
+ "/:locale/#{collection_prefix}/:categories/:year/:month/:day/:slug/"
70
70
  when :pretty_ext, :date
71
- "#{collection_prefix}/:categories/:year/:month/:day/:slug.*"
71
+ "/:locale/#{collection_prefix}/:categories/:year/:month/:day/:slug.*"
72
72
  when :simple
73
- "#{collection_prefix}/:categories/:slug/"
73
+ "/:locale/#{collection_prefix}/:categories/:slug/"
74
74
  when :simple_ext
75
- "#{collection_prefix}/:categories/:slug.*"
75
+ "/:locale/#{collection_prefix}/:categories/:slug.*"
76
76
  else
77
77
  permalink_style.to_s
78
78
  end
@@ -96,7 +96,7 @@ module Bridgetown
96
96
  end
97
97
  end
98
98
 
99
- def add_base_path(permalink)
99
+ def ensure_base_path(permalink)
100
100
  if resource.site.base_path.present?
101
101
  return "#{resource.site.base_path(strip_slash_only: true)}#{permalink}"
102
102
  end
@@ -108,7 +108,16 @@ module Bridgetown
108
108
 
109
109
  # @param resource [Bridgetown::Resource::Base]
110
110
  register_placeholder :path, ->(resource) do
111
- { raw_value: resource.relative_path_basename_without_prefix }
111
+ {
112
+ raw_value: resource.relative_path_basename_without_prefix.tap do |path|
113
+ if resource.site.config["collections_dir"].present?
114
+ path.delete_prefix! "#{resource.site.config["collections_dir"]}/"
115
+ end
116
+ if resource.data.locale && path.ends_with?(".#{resource.data.locale}")
117
+ path.chomp!(".#{resource.data.locale}")
118
+ end
119
+ end,
120
+ }
112
121
  end
113
122
 
114
123
  # @param resource [Bridgetown::Resource::Base]
@@ -128,8 +137,10 @@ module Bridgetown
128
137
 
129
138
  # @param resource [Bridgetown::Resource::Base]
130
139
  register_placeholder :locale, ->(resource) do
131
- locale_data = resource.data.locale
132
- resource.site.config.available_locales.include?(locale_data) ? locale_data : nil
140
+ next nil if resource.data.locale&.to_sym == resource.site.config.default_locale
141
+
142
+ locale_data = resource.data.locale&.to_sym
143
+ resource.site.config.available_locales.include?(locale_data) ? locale_data.to_s : nil
133
144
  end
134
145
  register_placeholder :lang, placeholder_processors[:locale]
135
146
 
@@ -94,9 +94,8 @@ module Bridgetown
94
94
  # @return [Bridgetown::Resource::Base, Array<Bridgetown::Resource::Base>]
95
95
  def belongs_to_relation_for_type(type)
96
96
  if resource.data[type].is_a?(Array)
97
- other_collection_for_type(type).resources.select do |other_resource|
98
- other_resource.data.slug.in?(resource.data[type])
99
- end
97
+ other_slugs = other_collection_for_type(type).resources_by_slug
98
+ resource.data[type].map { |slug| other_slugs[slug] }.compact
100
99
  else
101
100
  other_collection_for_type(type).resources.find do |other_resource|
102
101
  other_resource.data.slug == resource.data[type]
@@ -3,11 +3,7 @@
3
3
  module Bridgetown
4
4
  module Resource
5
5
  class TaxonomyTerm
6
- attr_reader :resource
7
-
8
- attr_reader :label
9
-
10
- attr_reader :type
6
+ attr_reader :resource, :label, :type
11
7
 
12
8
  def initialize(resource:, label:, type:)
13
9
  @resource = resource
@@ -41,7 +41,7 @@ module Bridgetown
41
41
  def execute_inline_ruby!
42
42
  return unless site.config.should_execute_inline_ruby?
43
43
 
44
- Bridgetown::Utils::RubyExec.search_data_for_ruby_code(resource, self)
44
+ Bridgetown::Utils::RubyExec.search_data_for_ruby_code(resource)
45
45
  end
46
46
 
47
47
  def inspect
@@ -103,10 +103,12 @@ module Bridgetown
103
103
  end
104
104
 
105
105
  def warn_on_missing_layout(layout, layout_name)
106
- if layout.nil? && layout_name
107
- Bridgetown.logger.warn "Build Warning:", "Layout '#{layout_name}' " \
108
- "requested via #{resource.relative_path} does not exist."
109
- end
106
+ return unless layout.nil? && layout_name
107
+
108
+ Bridgetown.logger.warn(
109
+ "Build Warning:",
110
+ "Layout '#{layout_name}' requested via #{resource.relative_path} does not exist."
111
+ )
110
112
  end
111
113
 
112
114
  ### Transformation Actions
@@ -152,7 +154,7 @@ module Bridgetown
152
154
  layout_input = layout.content.dup
153
155
 
154
156
  layout_converters.inject(layout_input) do |content, converter|
155
- next(content) unless [2, -2].include?(converter.method(:convert).arity)
157
+ next(content) unless [2, -2].include?(converter.method(:convert).arity) # rubocop:disable Performance/CollectionLiteralInLoop
156
158
 
157
159
  layout.current_document = resource
158
160
  layout.current_document_output = output
@@ -61,19 +61,17 @@ module Bridgetown
61
61
  @helpers ||= Helpers.new(self, site)
62
62
  end
63
63
 
64
- # rubocop:disable Style/MissingRespondToMissing
65
- ruby2_keywords def method_missing(method, *args, &block)
66
- if helpers.respond_to?(method.to_sym)
67
- helpers.send method.to_sym, *args, &block
64
+ ruby2_keywords def method_missing(method_name, *args, &block)
65
+ if helpers.respond_to?(method_name.to_sym)
66
+ helpers.send method_name.to_sym, *args, &block
68
67
  else
69
68
  super
70
69
  end
71
70
  end
72
71
 
73
- def respond_to_missing?(method, include_private = false)
74
- helpers.respond_to?(method.to_sym, include_private) || super
72
+ def respond_to_missing?(method_name, include_private = false)
73
+ helpers.respond_to?(method_name.to_sym, include_private) || super
75
74
  end
76
- # rubocop:enable Style/MissingRespondToMissing
77
75
 
78
76
  private
79
77
 
@@ -84,7 +82,7 @@ module Bridgetown
84
82
  ["{% render \"#{component}\""]
85
83
  end
86
84
  unless options.empty?
87
- render_statement << ", " + options.keys.map { |k| "#{k}: #{k}" }.join(", ")
85
+ render_statement << ", #{options.keys.map { |k| "#{k}: #{k}" }.join(", ")}"
88
86
  end
89
87
  render_statement << " %}"
90
88
  if options[:_block_content]