bridgetown-core 0.19.2 → 0.21.0.beta3

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/bridgetown-core.gemspec +1 -2
  3. data/lib/bridgetown-core.rb +37 -29
  4. data/lib/bridgetown-core/cleaner.rb +9 -3
  5. data/lib/bridgetown-core/collection.rb +177 -78
  6. data/lib/bridgetown-core/commands/base.rb +9 -0
  7. data/lib/bridgetown-core/commands/build.rb +0 -11
  8. data/lib/bridgetown-core/commands/concerns/git_helpers.rb +20 -0
  9. data/lib/bridgetown-core/commands/configure.rb +8 -3
  10. data/lib/bridgetown-core/commands/console.rb +4 -0
  11. data/lib/bridgetown-core/commands/doctor.rb +1 -19
  12. data/lib/bridgetown-core/commands/new.rb +2 -2
  13. data/lib/bridgetown-core/commands/plugins.rb +14 -13
  14. data/lib/bridgetown-core/commands/serve.rb +0 -14
  15. data/lib/bridgetown-core/component.rb +178 -0
  16. data/lib/bridgetown-core/concerns/data_accessible.rb +1 -0
  17. data/lib/bridgetown-core/concerns/front_matter_importer.rb +52 -0
  18. data/lib/bridgetown-core/concerns/site/configurable.rb +10 -10
  19. data/lib/bridgetown-core/concerns/site/content.rb +56 -15
  20. data/lib/bridgetown-core/concerns/site/localizable.rb +3 -5
  21. data/lib/bridgetown-core/concerns/site/processable.rb +6 -4
  22. data/lib/bridgetown-core/concerns/site/renderable.rb +26 -0
  23. data/lib/bridgetown-core/concerns/site/writable.rb +12 -2
  24. data/lib/bridgetown-core/concerns/validatable.rb +2 -5
  25. data/lib/bridgetown-core/configuration.rb +51 -30
  26. data/lib/bridgetown-core/configurations/bt-postcss.rb +1 -3
  27. data/lib/bridgetown-core/configurations/netlify.rb +1 -0
  28. data/lib/bridgetown-core/configurations/tailwindcss.rb +1 -3
  29. data/lib/bridgetown-core/converter.rb +23 -0
  30. data/lib/bridgetown-core/converters/erb_templates.rb +51 -35
  31. data/lib/bridgetown-core/converters/identity.rb +0 -9
  32. data/lib/bridgetown-core/converters/liquid_templates.rb +1 -1
  33. data/lib/bridgetown-core/converters/markdown.rb +14 -4
  34. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +5 -38
  35. data/lib/bridgetown-core/converters/ruby_templates.rb +17 -0
  36. data/lib/bridgetown-core/converters/smartypants.rb +3 -1
  37. data/lib/bridgetown-core/core_ext/psych.rb +19 -0
  38. data/lib/bridgetown-core/current.rb +10 -0
  39. data/lib/bridgetown-core/document.rb +9 -16
  40. data/lib/bridgetown-core/drops/collection_drop.rb +1 -1
  41. data/lib/bridgetown-core/drops/page_drop.rb +4 -0
  42. data/lib/bridgetown-core/drops/relations_drop.rb +23 -0
  43. data/lib/bridgetown-core/drops/resource_drop.rb +83 -0
  44. data/lib/bridgetown-core/drops/site_drop.rb +33 -8
  45. data/lib/bridgetown-core/drops/unified_payload_drop.rb +5 -0
  46. data/lib/bridgetown-core/entry_filter.rb +17 -28
  47. data/lib/bridgetown-core/errors.rb +0 -2
  48. data/lib/bridgetown-core/filters.rb +3 -26
  49. data/lib/bridgetown-core/filters/from_liquid.rb +23 -0
  50. data/lib/bridgetown-core/filters/url_filters.rb +12 -0
  51. data/lib/bridgetown-core/frontmatter_defaults.rb +1 -1
  52. data/lib/bridgetown-core/generators/prototype_generator.rb +37 -19
  53. data/lib/bridgetown-core/helpers.rb +48 -9
  54. data/lib/bridgetown-core/layout.rb +28 -13
  55. data/lib/bridgetown-core/liquid_renderer/file.rb +1 -0
  56. data/lib/bridgetown-core/liquid_renderer/table.rb +1 -0
  57. data/lib/bridgetown-core/model/base.rb +138 -0
  58. data/lib/bridgetown-core/model/builder_origin.rb +40 -0
  59. data/lib/bridgetown-core/model/origin.rb +38 -0
  60. data/lib/bridgetown-core/model/repo_origin.rb +126 -0
  61. data/lib/bridgetown-core/page.rb +11 -2
  62. data/lib/bridgetown-core/plugin_manager.rb +1 -3
  63. data/lib/bridgetown-core/publisher.rb +8 -2
  64. data/lib/bridgetown-core/reader.rb +37 -22
  65. data/lib/bridgetown-core/readers/data_reader.rb +5 -5
  66. data/lib/bridgetown-core/readers/defaults_reader.rb +1 -1
  67. data/lib/bridgetown-core/readers/page_reader.rb +1 -0
  68. data/lib/bridgetown-core/readers/post_reader.rb +5 -4
  69. data/lib/bridgetown-core/regenerator.rb +9 -2
  70. data/lib/bridgetown-core/related_posts.rb +9 -6
  71. data/lib/bridgetown-core/renderer.rb +6 -13
  72. data/lib/bridgetown-core/resource/base.rb +313 -0
  73. data/lib/bridgetown-core/resource/destination.rb +49 -0
  74. data/lib/bridgetown-core/resource/permalink_processor.rb +179 -0
  75. data/lib/bridgetown-core/resource/relations.rb +132 -0
  76. data/lib/bridgetown-core/resource/taxonomy_term.rb +34 -0
  77. data/lib/bridgetown-core/resource/taxonomy_type.rb +56 -0
  78. data/lib/bridgetown-core/resource/transformer.rb +177 -0
  79. data/lib/bridgetown-core/ruby_template_view.rb +11 -11
  80. data/lib/bridgetown-core/site.rb +13 -6
  81. data/lib/bridgetown-core/static_file.rb +33 -10
  82. data/lib/bridgetown-core/tags/highlight.rb +2 -15
  83. data/lib/bridgetown-core/tags/include.rb +1 -1
  84. data/lib/bridgetown-core/tags/post_url.rb +2 -2
  85. data/lib/bridgetown-core/url.rb +1 -0
  86. data/lib/bridgetown-core/utils.rb +49 -43
  87. data/lib/bridgetown-core/utils/require_gems.rb +60 -0
  88. data/lib/bridgetown-core/utils/ruby_exec.rb +6 -9
  89. data/lib/bridgetown-core/utils/ruby_front_matter.rb +39 -0
  90. data/lib/bridgetown-core/version.rb +2 -2
  91. data/lib/bridgetown-core/watcher.rb +1 -1
  92. data/lib/bridgetown-core/yaml_parser.rb +22 -0
  93. data/lib/site_template/package.json.erb +2 -2
  94. data/lib/site_template/plugins/site_builder.rb +1 -1
  95. data/lib/site_template/src/_posts/0000-00-00-welcome-to-bridgetown.md.erb +1 -1
  96. data/lib/site_template/webpack.config.js.erb +26 -6
  97. metadata +39 -40
  98. data/lib/bridgetown-core/external.rb +0 -58
  99. data/lib/bridgetown-core/page_without_a_file.rb +0 -17
  100. data/lib/bridgetown-core/path_manager.rb +0 -31
  101. data/lib/bridgetown-core/readers/collection_reader.rb +0 -23
  102. data/lib/bridgetown-core/readers/static_file_reader.rb +0 -25
  103. data/lib/bridgetown-core/utils/exec.rb +0 -26
  104. data/lib/bridgetown-core/utils/internet.rb +0 -37
  105. data/lib/bridgetown-core/utils/platforms.rb +0 -80
  106. data/lib/bridgetown-core/utils/thread_event.rb +0 -31
  107. data/lib/bridgetown-core/utils/win_tz.rb +0 -75
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 82771f9cc6fa2993f61010025de7fab09c2be37d9dfb3f365d728a6dd3ed5b3a
4
- data.tar.gz: ab1cfb7e45eac85bb80852b33ab4ca1f5cc69b52f6a1bde70ea1c70c39859005
3
+ metadata.gz: fc153d4826e8e0678bea76a9e2c251040123418ab9cfe0b1243b7f38cb8f1ec5
4
+ data.tar.gz: '08d6c10878993b10963dc1a89058c8b0a9dbd57a5ed2b399963a68ea537ea5f8'
5
5
  SHA512:
6
- metadata.gz: 67ea143f786bf6825f6db8ffa4ba1ce336e5d20e6746bbb24a754df40e0ba5084c3a5a04b0965f7ee080de67f2312b79d0251f34d76174fd55ca3ba379667928
7
- data.tar.gz: 2ad31567525e3b3633c4620088df104d2fe5ae65010ffadab04baa33982c960640430f16f13eeab6718c27ec2e66574e5aeeb30be90086ab2707a9f465e9d9c5
6
+ metadata.gz: 7151ae352509556c2b6b7e524e56186a3c782746dc1f2090756a37e9325c9153e48a03daf720e64fd0450e472a43b96c3e9b90ec53896d409881ab33b8aabea0
7
+ data.tar.gz: 42289c4f1da1116e37fa50bdb49345264e2a56a719f1e76691eb294721a1b4c834909ab7dc22691d238bec4df509c6680c479fa7b6fec8831f69d79bee8fef6b
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
31
31
  s.required_ruby_version = ">= 2.5.0"
32
32
  s.required_rubygems_version = ">= 2.7.0"
33
33
 
34
+ s.add_runtime_dependency("activemodel", "~> 6.0")
34
35
  s.add_runtime_dependency("activesupport", "~> 6.0")
35
36
  s.add_runtime_dependency("addressable", "~> 2.4")
36
37
  s.add_runtime_dependency("amazing_print", "~> 1.2")
@@ -45,9 +46,7 @@ Gem::Specification.new do |s|
45
46
  s.add_runtime_dependency("liquid", "~> 5.0")
46
47
  s.add_runtime_dependency("liquid-component", ">= 0.1")
47
48
  s.add_runtime_dependency("listen", "~> 3.0")
48
- s.add_runtime_dependency("pathutil", "~> 0.9")
49
49
  s.add_runtime_dependency("rouge", "~> 3.0")
50
- s.add_runtime_dependency("safe_yaml", "~> 1.0")
51
50
  s.add_runtime_dependency("terminal-table", "~> 1.8")
52
51
  s.add_runtime_dependency("thor", "~> 1.1")
53
52
  s.add_runtime_dependency("tilt", "~> 2.0")
@@ -18,6 +18,7 @@ end
18
18
  require "rubygems"
19
19
 
20
20
  # stdlib
21
+ require "find"
21
22
  require "forwardable"
22
23
  require "fileutils"
23
24
  require "time"
@@ -27,19 +28,23 @@ require "logger"
27
28
  require "set"
28
29
  require "csv"
29
30
  require "json"
31
+ require "yaml"
30
32
 
31
33
  # 3rd party
32
34
  require "active_support"
33
35
  require "active_support/core_ext/hash/keys"
36
+ require "active_support/core_ext/module/delegation"
34
37
  require "active_support/core_ext/object/blank"
38
+ require "active_support/core_ext/object/deep_dup"
39
+ require "active_support/core_ext/object/inclusion"
35
40
  require "active_support/core_ext/string/inflections"
36
41
  require "active_support/core_ext/string/inquiry"
42
+ require "active_support/core_ext/string/output_safety"
37
43
  require "active_support/core_ext/string/starts_ends_with"
44
+ require "active_support/current_attributes"
38
45
  require "active_support/descendants_tracker"
39
46
  require "hash_with_dot_access"
40
- require "pathutil"
41
47
  require "addressable/uri"
42
- require "safe_yaml/load"
43
48
  require "liquid"
44
49
  require "liquid-component"
45
50
  require "kramdown"
@@ -47,6 +52,7 @@ require "colorator"
47
52
  require "i18n"
48
53
  require "faraday"
49
54
  require "thor"
55
+ require "zeitwerk"
50
56
 
51
57
  module HashWithDotAccess
52
58
  class Hash # :nodoc:
@@ -56,55 +62,45 @@ module HashWithDotAccess
56
62
  end
57
63
  end
58
64
 
59
- SafeYAML::OPTIONS[:suppress_warnings] = true
60
-
61
65
  # Create our little String subclass for Ruby Front Matter
62
66
  class Rb < String; end
63
- SafeYAML::OPTIONS[:whitelisted_tags] = ["!ruby/string:Rb"]
64
-
65
- if RUBY_VERSION.start_with?("3.0")
66
- # workaround for Ruby 3 preview 2, maybe can remove later
67
- # rubocop:disable Style/GlobalVars
68
- old_verbose = $VERBOSE
69
- $VERBOSE = nil
70
- SafeYAML::SafeToRubyVisitor.const_set(:INITIALIZE_ARITY, 2)
71
- $verbose = old_verbose
72
- # rubocop:enable Style/GlobalVars
73
- end
74
67
 
75
68
  module Bridgetown
76
- # internal requires
77
69
  autoload :Cleaner, "bridgetown-core/cleaner"
78
70
  autoload :Collection, "bridgetown-core/collection"
71
+ autoload :Component, "bridgetown-core/component"
79
72
  autoload :Configuration, "bridgetown-core/configuration"
80
73
  autoload :DataAccessible, "bridgetown-core/concerns/data_accessible"
81
74
  autoload :Deprecator, "bridgetown-core/deprecator"
82
75
  autoload :Document, "bridgetown-core/document"
83
76
  autoload :EntryFilter, "bridgetown-core/entry_filter"
77
+ # TODO: we have too many errors! This is silly
84
78
  autoload :Errors, "bridgetown-core/errors"
85
79
  autoload :Excerpt, "bridgetown-core/excerpt"
86
- autoload :External, "bridgetown-core/external"
87
80
  autoload :FrontmatterDefaults, "bridgetown-core/frontmatter_defaults"
81
+ autoload :FrontMatterImporter, "bridgetown-core/concerns/front_matter_importer"
88
82
  autoload :Hooks, "bridgetown-core/hooks"
89
83
  autoload :Layout, "bridgetown-core/layout"
90
84
  autoload :LayoutPlaceable, "bridgetown-core/concerns/layout_placeable"
91
85
  autoload :Cache, "bridgetown-core/cache"
92
- autoload :CollectionReader, "bridgetown-core/readers/collection_reader"
86
+ autoload :Current, "bridgetown-core/current"
87
+ # TODO: remove this when legacy content engine is gone:
93
88
  autoload :DataReader, "bridgetown-core/readers/data_reader"
94
89
  autoload :DefaultsReader, "bridgetown-core/readers/defaults_reader"
95
90
  autoload :LayoutReader, "bridgetown-core/readers/layout_reader"
91
+ # TODO: remove this when legacy content engine is gone:
96
92
  autoload :PostReader, "bridgetown-core/readers/post_reader"
93
+ # TODO: remove this when legacy content engine is gone:
97
94
  autoload :PageReader, "bridgetown-core/readers/page_reader"
98
95
  autoload :PluginContentReader, "bridgetown-core/readers/plugin_content_reader"
99
- autoload :StaticFileReader, "bridgetown-core/readers/static_file_reader"
100
96
  autoload :LogAdapter, "bridgetown-core/log_adapter"
101
97
  autoload :Page, "bridgetown-core/page"
102
- autoload :PageWithoutAFile, "bridgetown-core/page_without_a_file"
103
- autoload :PathManager, "bridgetown-core/path_manager"
98
+ autoload :GeneratedPage, "bridgetown-core/page"
104
99
  autoload :PluginManager, "bridgetown-core/plugin_manager"
105
100
  autoload :Publishable, "bridgetown-core/concerns/publishable"
106
101
  autoload :Publisher, "bridgetown-core/publisher"
107
102
  autoload :Reader, "bridgetown-core/reader"
103
+ # TODO: remove this when the incremental regenerator is gone:
108
104
  autoload :Regenerator, "bridgetown-core/regenerator"
109
105
  autoload :RelatedPosts, "bridgetown-core/related_posts"
110
106
  autoload :Renderer, "bridgetown-core/renderer"
@@ -119,6 +115,7 @@ module Bridgetown
119
115
  autoload :Validatable, "bridgetown-core/concerns/validatable"
120
116
  autoload :VERSION, "bridgetown-core/version"
121
117
  autoload :Watcher, "bridgetown-core/watcher"
118
+ autoload :YAMLParser, "bridgetown-core/yaml_parser"
122
119
 
123
120
  # extensions
124
121
  require "bridgetown-core/commands/registrations"
@@ -130,11 +127,13 @@ module Bridgetown
130
127
 
131
128
  require "bridgetown-core/drops/drop"
132
129
  require "bridgetown-core/drops/document_drop"
130
+ require "bridgetown-core/drops/resource_drop"
133
131
  require_all "bridgetown-core/converters"
134
132
  require_all "bridgetown-core/converters/markdown"
135
133
  require_all "bridgetown-core/drops"
136
134
  require_all "bridgetown-core/generators"
137
135
  require_all "bridgetown-core/tags"
136
+ require_all "bridgetown-core/core_ext"
138
137
 
139
138
  class << self
140
139
  # Tells you which Bridgetown environment you are building in so
@@ -193,11 +192,7 @@ module Bridgetown
193
192
  # @return [void]
194
193
  # rubocop:disable Naming/AccessorMethodName
195
194
  def set_timezone(timezone)
196
- ENV["TZ"] = if Utils::Platforms.really_windows?
197
- Utils::WinTZ.calculate(timezone)
198
- else
199
- timezone
200
- end
195
+ ENV["TZ"] = timezone
201
196
  end
202
197
  # rubocop:enable Naming/AccessorMethodName
203
198
 
@@ -218,11 +213,11 @@ module Bridgetown
218
213
  @logger = LogAdapter.new(writer, (ENV["BRIDGETOWN_LOG_LEVEL"] || :info).to_sym)
219
214
  end
220
215
 
221
- # An array of sites. Currently only ever a single entry.
216
+ # Deprecated. Now using the Current site.
222
217
  #
223
218
  # @return [Array<Bridgetown::Site>] the Bridgetown sites created.
224
219
  def sites
225
- @sites ||= []
220
+ [Bridgetown::Current.site].compact
226
221
  end
227
222
 
228
223
  # Ensures the questionable path is prefixed with the base directory
@@ -256,6 +251,19 @@ module Bridgetown
256
251
  end
257
252
 
258
253
  # Conditional optimizations
259
- Bridgetown::External.require_if_present("liquid/c")
254
+ Bridgetown::Utils::RequireGems.require_if_present("liquid/c")
260
255
  end
261
256
  end
257
+
258
+ module Bridgetown
259
+ module Model; end
260
+ module Resource; end
261
+ end
262
+
263
+ # This method is available in Ruby 3, monkey patching for older versions
264
+ Psych.extend Bridgetown::CoreExt::Psych::SafeLoadFile unless Psych.respond_to?(:safe_load_file)
265
+
266
+ loader = Zeitwerk::Loader.new
267
+ loader.push_dir File.join(__dir__, "bridgetown-core/model"), namespace: Bridgetown::Model
268
+ loader.push_dir File.join(__dir__, "bridgetown-core/resource"), namespace: Bridgetown::Resource
269
+ loader.setup # ready!
@@ -58,7 +58,13 @@ module Bridgetown
58
58
  # Returns a Set with the file paths
59
59
  def new_files
60
60
  @new_files ||= Set.new.tap do |files|
61
- site.each_site_file { |item| files << item.destination(site.dest) }
61
+ site.each_site_file do |item|
62
+ files << if item.method(:destination).arity == 1
63
+ item.destination(site.dest)
64
+ else
65
+ item.destination.output_path
66
+ end
67
+ end
62
68
  end
63
69
  end
64
70
 
@@ -95,7 +101,7 @@ module Bridgetown
95
101
  #
96
102
  # Returns a Set with the directory paths
97
103
  def keep_dirs
98
- site.keep_files.flat_map { |file| parent_dirs(site.in_dest_dir(file)) }.to_set
104
+ site.config.keep_files.flat_map { |file| parent_dirs(site.in_dest_dir(file)) }.to_set
99
105
  end
100
106
 
101
107
  # Private: Creates a regular expression from the config's keep_files array
@@ -106,7 +112,7 @@ module Bridgetown
106
112
  #
107
113
  # Returns the regular expression
108
114
  def keep_file_regex
109
- %r!\A#{Regexp.quote(site.dest)}\/(#{Regexp.union(site.keep_files).source})!
115
+ %r!\A#{Regexp.quote(site.dest)}\/(#{Regexp.union(site.config.keep_files).source})!
110
116
  end
111
117
  end
112
118
  end
@@ -8,82 +8,105 @@ module Bridgetown
8
8
  attr_reader :label, :metadata
9
9
  attr_writer :docs
10
10
 
11
+ attr_writer :resources
12
+
11
13
  # Create a new Collection.
12
14
  #
13
- # site - the site to which this collection belongs.
14
- # label - the name of the collection
15
- #
16
- # Returns nothing.
15
+ # @param site [Bridgetown::Site] the site to which this collection belongs
16
+ # @param label [String] the name of the collection
17
17
  def initialize(site, label)
18
18
  @site = site
19
19
  @label = sanitize_label(label)
20
20
  @metadata = extract_metadata
21
21
  end
22
22
 
23
+ def builtin?
24
+ label.in? %w(posts pages data).freeze
25
+ end
26
+
27
+ def legacy_reader?
28
+ label.in? %w(posts data).freeze
29
+ end
30
+
31
+ def data?
32
+ label == "data"
33
+ end
34
+
23
35
  # Fetch the Documents in this collection.
24
36
  # Defaults to an empty array if no documents have been read in.
25
37
  #
26
- # Returns an array of Bridgetown::Document objects.
38
+ # @return [Array<Bridgetown::Document>]
27
39
  def docs
28
40
  @docs ||= []
29
41
  end
30
42
 
31
- # Override of normal respond_to? to match method_missing's logic for
32
- # looking in @data.
33
- def respond_to_missing?(method, include_private = false)
34
- docs.respond_to?(method.to_sym, include_private) || super
43
+ # Fetch the Resources in this collection.
44
+ # Defaults to an empty array if no resources have been read in.
45
+ #
46
+ # @return [Array<Bridgetown::Resource::Base>]
47
+ def resources
48
+ @resources ||= []
35
49
  end
36
50
 
37
- # Override of method_missing to check in @data for the key.
38
- def method_missing(method, *args, &blck)
39
- if docs.respond_to?(method.to_sym)
40
- Bridgetown.logger.warn "Deprecation:",
41
- "#{label}.#{method} should be changed to #{label}.docs.#{method}."
42
- Bridgetown.logger.warn "", "Called by #{caller(0..0)}."
43
- docs.public_send(method.to_sym, *args, &blck)
44
- else
45
- super
46
- end
51
+ # Iterate over either Resources or Documents depending on how the site is
52
+ # configured
53
+ def each(&block)
54
+ site.uses_resource? ? resources.each(&block) : docs.each(&block)
47
55
  end
48
56
 
49
57
  # Fetch the static files in this collection.
50
58
  # Defaults to an empty array if no static files have been read in.
51
59
  #
52
- # Returns an array of Bridgetown::StaticFile objects.
60
+ # @return [Array<Bridgetown::StaticFile>]
61
+ def static_files
62
+ @static_files ||= []
63
+ end
64
+
53
65
  def files
54
- @files ||= []
66
+ Bridgetown::Deprecator.deprecation_message "Collection#files is now Collection#static_files"
67
+ static_files
55
68
  end
56
69
 
57
70
  # Read the allowed documents into the collection's array of docs.
58
71
  #
59
- # Returns the sorted array of docs.
60
- def read
72
+ # @return [Bridgetown::Collection] self
73
+ def read # rubocop:todo Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
61
74
  filtered_entries.each do |file_path|
62
75
  full_path = collection_dir(file_path)
63
76
  next if File.directory?(full_path)
64
77
 
65
- if Utils.has_yaml_header? full_path
78
+ if site.uses_resource?
79
+ next if File.basename(file_path).starts_with?("_")
80
+
81
+ if label == "data" || Utils.has_yaml_header?(full_path) ||
82
+ Utils.has_rbfm_header?(full_path)
83
+ read_resource(full_path)
84
+ else
85
+ read_static_file(file_path, full_path)
86
+ end
87
+ elsif Utils.has_yaml_header? full_path
66
88
  read_document(full_path)
67
89
  else
68
90
  read_static_file(file_path, full_path)
69
91
  end
70
92
  end
71
- site.static_files.concat(files)
93
+ site.static_files.concat(static_files)
72
94
  sort_docs!
95
+
96
+ self
73
97
  end
74
98
 
75
99
  # All the entries in this collection.
76
100
  #
77
- # Returns an Array of file paths to the documents in this collection
78
- # relative to the collection's directory
101
+ # @return [Array<String>] file paths to the documents in this collection
102
+ # relative to the collection's folder
79
103
  def entries
80
104
  return [] unless exists?
81
105
 
82
106
  @entries ||= begin
83
107
  collection_dir_slash = "#{collection_dir}/"
84
108
  Utils.safe_glob(collection_dir, ["**", "*"], File::FNM_DOTMATCH).map do |entry|
85
- entry[collection_dir_slash] = ""
86
- entry
109
+ entry.sub(collection_dir_slash, "")
87
110
  end
88
111
  end
89
112
  end
@@ -91,85 +114,87 @@ module Bridgetown
91
114
  # Filtered version of the entries in this collection.
92
115
  # See `Bridgetown::EntryFilter#filter` for more information.
93
116
  #
94
- # Returns a list of filtered entry paths.
117
+ # @return [Array<String>] list of filtered entry paths
95
118
  def filtered_entries
96
119
  return [] unless exists?
97
120
 
98
121
  @filtered_entries ||=
99
- Dir.chdir(directory) do
122
+ Dir.chdir(absolute_path) do
100
123
  entry_filter.filter(entries).reject do |f|
101
124
  path = collection_dir(f)
102
- File.directory?(path) || entry_filter.symlink?(f)
125
+ File.directory?(path)
103
126
  end
104
127
  end
105
128
  end
106
129
 
107
- # The directory for this Collection, relative to the site source or the directory
108
- # containing the collection.
130
+ # The folder name of this Collection, e.g. `_posts` or `_events`
109
131
  #
110
- # Returns a String containing the directory name where the collection
111
- # is stored on the filesystem.
112
- def relative_directory
113
- @relative_directory ||= "_#{label}"
132
+ # @return [String]
133
+ def folder_name
134
+ @folder_name ||= "_#{label}"
114
135
  end
136
+ alias_method :relative_directory, :folder_name
115
137
 
116
- # The relative path to the directory containing the collection.
138
+ # The relative path to the folder containing the collection.
117
139
  #
118
- # Returns a String containing the directory name where the collection
119
- # is stored relative to the source directory
140
+ # @return [String] folder where the collection is stored relative to the
141
+ # configured collections folder (usually the site source)
120
142
  def relative_path
121
- Pathname.new(container).join(relative_directory).to_s
143
+ Pathname.new(container).join(folder_name).to_s
122
144
  end
123
145
 
124
- # The full path to the directory containing the collection.
146
+ # The full path to the folder containing the collection.
125
147
  #
126
- # Returns a String containing the directory name where the collection
127
- # is stored on the filesystem.
128
- def directory
129
- @directory ||= site.in_source_dir(relative_path)
148
+ # @return [String] full path where the collection is stored on the filesystem
149
+ def absolute_path
150
+ @absolute_path ||= site.in_source_dir(relative_path)
130
151
  end
152
+ alias_method :directory, :absolute_path
131
153
 
132
- # The full path to the directory containing the collection, with
154
+ # The full path to the folder containing the collection, with
133
155
  # optional subpaths.
134
156
  #
135
- # *files - (optional) any other path pieces relative to the
136
- # directory to append to the path
137
- #
138
- # Returns a String containing th directory name where the collection
139
- # is stored on the filesystem.
157
+ # @param *files [Array<String>] any other path pieces relative to the
158
+ # folder to append to the path
159
+ # @return [String]
140
160
  def collection_dir(*files)
141
- return directory if files.empty?
161
+ return absolute_path if files.empty?
142
162
 
143
- site.in_source_dir(container, relative_directory, *files)
163
+ site.in_source_dir(relative_path, *files)
144
164
  end
145
165
 
146
- # Checks whether the directory "exists" for this collection.
166
+ # Checks whether the folder "exists" for this collection.
167
+ #
168
+ # @return [Boolean]
147
169
  def exists?
148
- File.directory?(directory)
170
+ File.directory?(absolute_path)
149
171
  end
150
172
 
151
173
  # The entry filter for this collection.
152
- # Creates an instance of Bridgetown::EntryFilter.
174
+ # Creates an instance of Bridgetown::EntryFilter if needed.
153
175
  #
154
- # Returns the instance of Bridgetown::EntryFilter for this collection.
176
+ # @return [Bridgetown::EntryFilter]
155
177
  def entry_filter
156
- @entry_filter ||= Bridgetown::EntryFilter.new(site, relative_directory)
178
+ @entry_filter ||= Bridgetown::EntryFilter.new(
179
+ site,
180
+ base_directory: folder_name,
181
+ include_underscores: site.uses_resource?
182
+ )
157
183
  end
158
184
 
159
185
  # An inspect string.
160
186
  #
161
- # Returns the inspect string
187
+ # @return [String]
162
188
  def inspect
163
- "#<#{self.class} @label=#{label} docs=#{docs}>"
189
+ "#<#{self.class} @label=#{label} docs=#{docs} resources=#{resources}>"
164
190
  end
165
191
 
166
192
  # Produce a sanitized label name
167
193
  # Label names may not contain anything but alphanumeric characters,
168
194
  # underscores, and hyphens.
169
195
  #
170
- # label - the possibly-unsafe label
171
- #
172
- # Returns a sanitized version of the label.
196
+ # @param label [String] the possibly-unsafe label
197
+ # @return [String] sanitized version of the label.
173
198
  def sanitize_label(label)
174
199
  label.gsub(%r![^a-z0-9_\-\.]!i, "")
175
200
  end
@@ -179,7 +204,8 @@ module Bridgetown
179
204
  # - label
180
205
  # - docs
181
206
  #
182
- # Returns a representation of this collection for use in Liquid.
207
+ # @return [Bridgetown::Drops::CollectionDrop] representation of this
208
+ # collection for use in Liquid
183
209
  def to_liquid
184
210
  Drops::CollectionDrop.new self
185
211
  end
@@ -187,14 +213,22 @@ module Bridgetown
187
213
  # Whether the collection's documents ought to be written as individual
188
214
  # files in the output.
189
215
  #
190
- # Returns true if the 'write' metadata is true, false otherwise.
216
+ # @return [Boolean] true if the 'write' metadata is true, false otherwise.
191
217
  def write?
192
218
  !!metadata.fetch("output", false)
193
219
  end
194
220
 
195
- # The URL template to render collection's documents at.
221
+ # Used by Resource's permalink processor
222
+ # @return [String]
223
+ def default_permalink
224
+ metadata.fetch("permalink") do
225
+ "/:collection/:path/"
226
+ end
227
+ end
228
+
229
+ # LEGACY: The URL template to render collection's documents at.
196
230
  #
197
- # Returns the URL template to render collection's documents at.
231
+ # @return [String]
198
232
  def url_template
199
233
  @url_template ||= metadata.fetch("permalink") do
200
234
  Utils.add_permalink_suffix("/:collection/:path", site.permalink_style)
@@ -203,13 +237,57 @@ module Bridgetown
203
237
 
204
238
  # Extract options for this collection from the site configuration.
205
239
  #
206
- # Returns the metadata for this collection
240
+ # @return [HashWithDotAccess::Hash]
207
241
  def extract_metadata
208
- if site.config["collections"].is_a?(Hash)
209
- site.config["collections"][label] || {}
210
- else
211
- {}
242
+ site.config.collections[label] || HashWithDotAccess::Hash.new
243
+ end
244
+
245
+ def merge_data_resources
246
+ data_contents = {}
247
+
248
+ sanitize_filename = ->(name) do
249
+ name.gsub(%r![^\w\s-]+|(?<=^|\b\s)\s+(?=$|\s?\b)!, "")
250
+ .gsub(%r!\s+!, "_")
212
251
  end
252
+
253
+ resources.each do |data_resource|
254
+ segments = data_resource.relative_path.each_filename.to_a[1..-1]
255
+ nested = []
256
+ segments.each_with_index do |segment, index|
257
+ sanitized_segment = sanitize_filename.(File.basename(segment, ".*"))
258
+ hsh = nested.empty? ? data_contents : data_contents.dig(*nested)
259
+ hsh[sanitized_segment] = if index == segments.length - 1
260
+ data_resource.data.rows || data_resource.data
261
+ else
262
+ {}
263
+ end
264
+ nested << sanitized_segment
265
+ end
266
+ end
267
+
268
+ merge_environment_specific_metadata(data_contents).with_dot_access
269
+ end
270
+
271
+ def merge_environment_specific_metadata(data_contents)
272
+ if data_contents["site_metadata"]
273
+ data_contents["site_metadata"][Bridgetown.environment]&.each_key do |k|
274
+ data_contents["site_metadata"][k] =
275
+ data_contents["site_metadata"][Bridgetown.environment][k]
276
+ end
277
+ data_contents["site_metadata"].delete(Bridgetown.environment)
278
+ end
279
+
280
+ data_contents
281
+ end
282
+
283
+ # Read in resource from repo path
284
+ # @param full_path [String]
285
+ def read_resource(full_path)
286
+ id = "repo://#{label}.collection/" + Addressable::URI.escape(
287
+ Pathname(full_path).relative_path_from(Pathname(site.source)).to_s
288
+ )
289
+ resource = Bridgetown::Model::Base.find(id).to_resource.read!
290
+ resources << resource if site.config.unpublished || resource.published?
213
291
  end
214
292
 
215
293
  private
@@ -219,17 +297,20 @@ module Bridgetown
219
297
  end
220
298
 
221
299
  def read_document(full_path)
222
- doc = Document.new(full_path, site: site, collection: self)
223
- doc.read
224
- docs << doc if site.unpublished || doc.published?
300
+ doc = Document.new(full_path, site: site, collection: self).tap(&:read)
301
+ docs << doc if site.config.unpublished || doc.published?
225
302
  end
226
303
 
227
304
  def sort_docs!
228
305
  if metadata["sort_by"].is_a?(String)
229
306
  sort_docs_by_key!
307
+ sort_resources_by_key!
230
308
  else
231
309
  docs.sort!
310
+ resources.sort!
232
311
  end
312
+ docs.reverse! if metadata.sort_direction == "descending"
313
+ resources.reverse! if metadata.sort_direction == "descending"
233
314
  end
234
315
 
235
316
  # A custom sort function based on Schwartzian transform
@@ -252,6 +333,24 @@ module Bridgetown
252
333
  end.map!(&:last)
253
334
  end
254
335
 
336
+ def sort_resources_by_key!
337
+ meta_key = metadata["sort_by"]
338
+ # Modify `docs` array to cache document's property along with the Document instance
339
+ resources.map! { |doc| [doc.data[meta_key], doc] }.sort! do |apples, olives|
340
+ order = determine_sort_order(meta_key, apples, olives)
341
+
342
+ # Fall back to `Document#<=>` if the properties were equal or were non-sortable
343
+ # Otherwise continue with current sort-order
344
+ if order.nil? || order.zero?
345
+ apples[-1] <=> olives[-1]
346
+ else
347
+ order
348
+ end
349
+
350
+ # Finally restore the `docs` array with just the Document objects themselves
351
+ end.map!(&:last)
352
+ end
353
+
255
354
  def determine_sort_order(sort_key, apples, olives)
256
355
  apple_property, apple_document = apples
257
356
  olive_property, olive_document = olives
@@ -277,7 +376,7 @@ module Bridgetown
277
376
  File.dirname(file_path)
278
377
  ).chomp("/.")
279
378
 
280
- files << StaticFile.new(
379
+ static_files << StaticFile.new(
281
380
  site,
282
381
  site.source,
283
382
  relative_dir,