bridgetown-core 0.19.3 → 0.21.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) 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 +6 -6
  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/commands/webpack.rb +75 -0
  16. data/lib/bridgetown-core/commands/webpack/enable-postcss.rb +12 -0
  17. data/lib/bridgetown-core/commands/webpack/setup.rb +4 -0
  18. data/lib/bridgetown-core/commands/webpack/update.rb +3 -0
  19. data/lib/bridgetown-core/commands/webpack/webpack.config.js +18 -0
  20. data/lib/{site_template/webpack.config.js.erb → bridgetown-core/commands/webpack/webpack.defaults.js.erb} +32 -16
  21. data/lib/bridgetown-core/component.rb +183 -0
  22. data/lib/bridgetown-core/concerns/data_accessible.rb +1 -0
  23. data/lib/bridgetown-core/concerns/front_matter_importer.rb +52 -0
  24. data/lib/bridgetown-core/concerns/layout_placeable.rb +1 -1
  25. data/lib/bridgetown-core/concerns/site/configurable.rb +10 -10
  26. data/lib/bridgetown-core/concerns/site/content.rb +56 -15
  27. data/lib/bridgetown-core/concerns/site/localizable.rb +3 -5
  28. data/lib/bridgetown-core/concerns/site/processable.rb +6 -4
  29. data/lib/bridgetown-core/concerns/site/renderable.rb +26 -0
  30. data/lib/bridgetown-core/concerns/site/writable.rb +12 -2
  31. data/lib/bridgetown-core/concerns/validatable.rb +2 -5
  32. data/lib/bridgetown-core/configuration.rb +61 -33
  33. data/lib/bridgetown-core/configurations/bt-postcss.rb +6 -6
  34. data/lib/bridgetown-core/configurations/netlify.rb +1 -0
  35. data/lib/bridgetown-core/configurations/tailwindcss.rb +6 -6
  36. data/lib/bridgetown-core/converter.rb +23 -0
  37. data/lib/bridgetown-core/converters/erb_templates.rb +51 -35
  38. data/lib/bridgetown-core/converters/identity.rb +0 -9
  39. data/lib/bridgetown-core/converters/liquid_templates.rb +1 -1
  40. data/lib/bridgetown-core/converters/markdown.rb +14 -4
  41. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +5 -38
  42. data/lib/bridgetown-core/converters/ruby_templates.rb +17 -0
  43. data/lib/bridgetown-core/converters/smartypants.rb +3 -1
  44. data/lib/bridgetown-core/core_ext/psych.rb +19 -0
  45. data/lib/bridgetown-core/current.rb +10 -0
  46. data/lib/bridgetown-core/document.rb +9 -16
  47. data/lib/bridgetown-core/drops/collection_drop.rb +1 -1
  48. data/lib/bridgetown-core/drops/page_drop.rb +4 -0
  49. data/lib/bridgetown-core/drops/relations_drop.rb +23 -0
  50. data/lib/bridgetown-core/drops/resource_drop.rb +83 -0
  51. data/lib/bridgetown-core/drops/site_drop.rb +33 -8
  52. data/lib/bridgetown-core/drops/unified_payload_drop.rb +5 -0
  53. data/lib/bridgetown-core/entry_filter.rb +17 -28
  54. data/lib/bridgetown-core/errors.rb +0 -2
  55. data/lib/bridgetown-core/filters.rb +3 -26
  56. data/lib/bridgetown-core/filters/from_liquid.rb +23 -0
  57. data/lib/bridgetown-core/filters/url_filters.rb +12 -0
  58. data/lib/bridgetown-core/frontmatter_defaults.rb +1 -1
  59. data/lib/bridgetown-core/generators/prototype_generator.rb +59 -20
  60. data/lib/bridgetown-core/helpers.rb +48 -9
  61. data/lib/bridgetown-core/layout.rb +53 -21
  62. data/lib/bridgetown-core/liquid_renderer/file.rb +1 -0
  63. data/lib/bridgetown-core/liquid_renderer/table.rb +1 -0
  64. data/lib/bridgetown-core/model/base.rb +138 -0
  65. data/lib/bridgetown-core/model/builder_origin.rb +40 -0
  66. data/lib/bridgetown-core/model/origin.rb +38 -0
  67. data/lib/bridgetown-core/model/repo_origin.rb +126 -0
  68. data/lib/bridgetown-core/page.rb +11 -2
  69. data/lib/bridgetown-core/plugin_manager.rb +1 -3
  70. data/lib/bridgetown-core/publisher.rb +8 -2
  71. data/lib/bridgetown-core/reader.rb +37 -22
  72. data/lib/bridgetown-core/readers/data_reader.rb +5 -5
  73. data/lib/bridgetown-core/readers/defaults_reader.rb +1 -1
  74. data/lib/bridgetown-core/readers/layout_reader.rb +1 -1
  75. data/lib/bridgetown-core/readers/page_reader.rb +1 -0
  76. data/lib/bridgetown-core/readers/post_reader.rb +5 -4
  77. data/lib/bridgetown-core/regenerator.rb +9 -2
  78. data/lib/bridgetown-core/related_posts.rb +9 -6
  79. data/lib/bridgetown-core/renderer.rb +6 -13
  80. data/lib/bridgetown-core/resource/base.rb +329 -0
  81. data/lib/bridgetown-core/resource/destination.rb +49 -0
  82. data/lib/bridgetown-core/resource/permalink_processor.rb +179 -0
  83. data/lib/bridgetown-core/resource/relations.rb +132 -0
  84. data/lib/bridgetown-core/resource/taxonomy_term.rb +34 -0
  85. data/lib/bridgetown-core/resource/taxonomy_type.rb +56 -0
  86. data/lib/bridgetown-core/resource/transformer.rb +177 -0
  87. data/lib/bridgetown-core/ruby_template_view.rb +11 -11
  88. data/lib/bridgetown-core/site.rb +13 -6
  89. data/lib/bridgetown-core/static_file.rb +33 -10
  90. data/lib/bridgetown-core/tags/highlight.rb +2 -15
  91. data/lib/bridgetown-core/tags/include.rb +1 -1
  92. data/lib/bridgetown-core/tags/post_url.rb +2 -2
  93. data/lib/bridgetown-core/url.rb +1 -0
  94. data/lib/bridgetown-core/utils.rb +49 -43
  95. data/lib/bridgetown-core/utils/require_gems.rb +60 -0
  96. data/lib/bridgetown-core/utils/ruby_exec.rb +6 -9
  97. data/lib/bridgetown-core/utils/ruby_front_matter.rb +39 -0
  98. data/lib/bridgetown-core/version.rb +2 -2
  99. data/lib/bridgetown-core/watcher.rb +2 -1
  100. data/lib/bridgetown-core/yaml_parser.rb +22 -0
  101. data/lib/site_template/config/.keep +0 -0
  102. data/lib/site_template/package.json.erb +4 -4
  103. data/lib/site_template/plugins/site_builder.rb +1 -1
  104. data/lib/site_template/src/_posts/0000-00-00-welcome-to-bridgetown.md.erb +1 -1
  105. metadata +46 -41
  106. data/lib/bridgetown-core/external.rb +0 -58
  107. data/lib/bridgetown-core/page_without_a_file.rb +0 -17
  108. data/lib/bridgetown-core/path_manager.rb +0 -31
  109. data/lib/bridgetown-core/readers/collection_reader.rb +0 -23
  110. data/lib/bridgetown-core/readers/static_file_reader.rb +0 -25
  111. data/lib/bridgetown-core/utils/exec.rb +0 -26
  112. data/lib/bridgetown-core/utils/internet.rb +0 -37
  113. data/lib/bridgetown-core/utils/platforms.rb +0 -80
  114. data/lib/bridgetown-core/utils/thread_event.rb +0 -31
  115. 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: ad7d5025a99240586457a8afa4aa3e9a9aba0e289484ff855648bd772b89e7a9
4
- data.tar.gz: 1ea0ad14dc78ca7465f7ff93bab6e178ec18b336e08641ac18bf75cf655b7246
3
+ metadata.gz: 2082873c6388655e983a22a2629e5d25ac322095c5bcc1dbbd6a4d7ee9b28957
4
+ data.tar.gz: b6952e9d9b9cee1470da8d3dd5e170b1ec331a3e6fe7ce41128943ff0db6f7a0
5
5
  SHA512:
6
- metadata.gz: 95b1542d9fa33741d18832a026aa43e720f99520b8367b12672e2689c7be19200e0cedd53b8070666c15e7594af43dec3c1885fa8570e6c7daf463afcae3405f
7
- data.tar.gz: '00048d6aa2ca15728544ecbf46ee05b4b813630a03a0f35154b45096a2f9720ea645e23faac13fcab18a85e395fa6782eee522cbd39248b88b0f2a80a7fcff41'
6
+ metadata.gz: 25d4e5d41be9a9b88e46d402726253fe400cd2d53d133e75337204ae3daafd7b41e275d3dbafa7bebf7d70f13de641371b3142251e746f4a2ba70238f9657d26
7
+ data.tar.gz: d51d1a5c5d6e9a714fac3f24b722b91e8a5fc9f1d42ab750060daa8f34999d460e55ac237f2a534e370795f978209c8249a7750d76f986e98aae84938293064e
@@ -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,