bridgetown-core 0.19.3 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/bridgetown-core.gemspec +1 -1
  3. data/lib/bridgetown-core.rb +30 -11
  4. data/lib/bridgetown-core/cleaner.rb +7 -1
  5. data/lib/bridgetown-core/collection.rb +173 -77
  6. data/lib/bridgetown-core/commands/base.rb +9 -0
  7. data/lib/bridgetown-core/commands/configure.rb +4 -0
  8. data/lib/bridgetown-core/commands/console.rb +4 -0
  9. data/lib/bridgetown-core/concerns/data_accessible.rb +1 -0
  10. data/lib/bridgetown-core/concerns/site/configurable.rb +7 -3
  11. data/lib/bridgetown-core/concerns/site/content.rb +57 -15
  12. data/lib/bridgetown-core/concerns/site/processable.rb +1 -0
  13. data/lib/bridgetown-core/concerns/site/renderable.rb +26 -0
  14. data/lib/bridgetown-core/concerns/site/writable.rb +11 -1
  15. data/lib/bridgetown-core/concerns/validatable.rb +1 -0
  16. data/lib/bridgetown-core/configuration.rb +39 -19
  17. data/lib/bridgetown-core/converter.rb +14 -0
  18. data/lib/bridgetown-core/converters/identity.rb +0 -9
  19. data/lib/bridgetown-core/converters/markdown.rb +14 -4
  20. data/lib/bridgetown-core/converters/markdown/kramdown_parser.rb +3 -0
  21. data/lib/bridgetown-core/current.rb +10 -0
  22. data/lib/bridgetown-core/document.rb +6 -14
  23. data/lib/bridgetown-core/drops/collection_drop.rb +1 -1
  24. data/lib/bridgetown-core/drops/page_drop.rb +4 -0
  25. data/lib/bridgetown-core/drops/resource_drop.rb +81 -0
  26. data/lib/bridgetown-core/drops/site_drop.rb +33 -8
  27. data/lib/bridgetown-core/drops/unified_payload_drop.rb +4 -0
  28. data/lib/bridgetown-core/entry_filter.rb +10 -23
  29. data/lib/bridgetown-core/errors.rb +0 -2
  30. data/lib/bridgetown-core/filters.rb +2 -1
  31. data/lib/bridgetown-core/generators/prototype_generator.rb +37 -19
  32. data/lib/bridgetown-core/layout.rb +2 -2
  33. data/lib/bridgetown-core/liquid_renderer/file.rb +1 -0
  34. data/lib/bridgetown-core/liquid_renderer/table.rb +1 -0
  35. data/lib/bridgetown-core/model/base.rb +138 -0
  36. data/lib/bridgetown-core/model/builder_origin.rb +40 -0
  37. data/lib/bridgetown-core/model/file_origin.rb +119 -0
  38. data/lib/bridgetown-core/model/origin.rb +38 -0
  39. data/lib/bridgetown-core/page.rb +9 -1
  40. data/lib/bridgetown-core/plugin_manager.rb +0 -2
  41. data/lib/bridgetown-core/publisher.rb +7 -1
  42. data/lib/bridgetown-core/reader.rb +25 -12
  43. data/lib/bridgetown-core/readers/data_reader.rb +3 -4
  44. data/lib/bridgetown-core/readers/post_reader.rb +1 -1
  45. data/lib/bridgetown-core/regenerator.rb +8 -1
  46. data/lib/bridgetown-core/related_posts.rb +1 -1
  47. data/lib/bridgetown-core/renderer.rb +5 -12
  48. data/lib/bridgetown-core/resource/base.rb +275 -0
  49. data/lib/bridgetown-core/resource/destination.rb +49 -0
  50. data/lib/bridgetown-core/resource/permalink_processor.rb +179 -0
  51. data/lib/bridgetown-core/resource/taxonomy_term.rb +25 -0
  52. data/lib/bridgetown-core/resource/taxonomy_type.rb +47 -0
  53. data/lib/bridgetown-core/resource/transformer.rb +173 -0
  54. data/lib/bridgetown-core/ruby_template_view.rb +4 -0
  55. data/lib/bridgetown-core/site.rb +9 -1
  56. data/lib/bridgetown-core/static_file.rb +33 -10
  57. data/lib/bridgetown-core/url.rb +1 -0
  58. data/lib/bridgetown-core/utils.rb +40 -40
  59. data/lib/bridgetown-core/utils/platforms.rb +1 -0
  60. data/lib/bridgetown-core/version.rb +2 -2
  61. data/lib/site_template/webpack.config.js.erb +8 -6
  62. metadata +28 -21
  63. data/lib/bridgetown-core/page_without_a_file.rb +0 -17
  64. data/lib/bridgetown-core/readers/collection_reader.rb +0 -23
  65. data/lib/bridgetown-core/utils/exec.rb +0 -26
  66. data/lib/bridgetown-core/utils/internet.rb +0 -37
  67. data/lib/bridgetown-core/utils/win_tz.rb +0 -75
@@ -21,6 +21,8 @@ module Bridgetown
21
21
  # Gets/Sets the extension of this layout.
22
22
  attr_accessor :ext
23
23
 
24
+ alias_method :extname, :ext
25
+
24
26
  # Gets/Sets the Hash that holds the metadata for this layout.
25
27
  attr_accessor :data
26
28
 
@@ -53,8 +55,6 @@ module Bridgetown
53
55
  end
54
56
  @relative_path = @path.sub(@base_dir, "")
55
57
 
56
- self.data = {}
57
-
58
58
  process(name)
59
59
  read_yaml(base, name)
60
60
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Bridgetown
4
4
  class LiquidRenderer
5
+ # TODO: deprecate or move to a separate repo/plugin
5
6
  class File
6
7
  def initialize(renderer, filename)
7
8
  @renderer = renderer
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Bridgetown
4
4
  class LiquidRenderer
5
+ # TODO: deprecate or move to a separate repo/plugin
5
6
  class Table
6
7
  GAUGES = [:count, :bytes, :time].freeze
7
8
 
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_model"
4
+
5
+ module Bridgetown
6
+ module Model
7
+ class Base
8
+ include ActiveModel::Model
9
+ extend ActiveModel::Callbacks # also extends with DescendantsTracker
10
+ define_model_callbacks :load, :save, :destroy
11
+
12
+ def self.loads_id?(id)
13
+ name == ActiveSupport::Inflector.classify(
14
+ URI.parse(id).host.chomp(".collection")
15
+ )
16
+ end
17
+
18
+ def self.find(id)
19
+ unless Bridgetown::Current.site
20
+ raise "A Bridgetown site must be initialized and added to Current"
21
+ end
22
+
23
+ model_klass = klass_for_id(id)
24
+ model_klass.new(read_data_for_id(id))
25
+ end
26
+
27
+ def self.klass_for_id(id)
28
+ descendants.find do |klass|
29
+ klass.loads_id?(id)
30
+ end || self
31
+ end
32
+
33
+ def self.read_data_for_id(id)
34
+ origin_for_id(id).read
35
+ end
36
+
37
+ def self.origin_for_id(id)
38
+ scheme = URI.parse(id).scheme
39
+ origin_klass = Origin.descendants.find do |klass|
40
+ klass.handle_scheme?(scheme)
41
+ end
42
+
43
+ raise "No origin could be found for #{id}" unless origin_klass
44
+
45
+ origin_klass.new(id)
46
+ end
47
+
48
+ class << self
49
+ def build(collection_name, path, data)
50
+ data = Bridgetown::Model::BuilderOrigin.new("builder://#{path}").read do
51
+ data[:_collection_] = Bridgetown::Current.site.collections[collection_name]
52
+ data
53
+ end
54
+ new(data)
55
+ end
56
+ end
57
+
58
+ def initialize(attributes = {})
59
+ run_callbacks :load do
60
+ super
61
+ end
62
+ end
63
+
64
+ def id
65
+ attributes[:id] || attributes[:_id_]
66
+ end
67
+
68
+ # @return [Bridgetown::Model::Origin]
69
+ def origin
70
+ attributes[:_origin_]
71
+ end
72
+
73
+ def persisted?
74
+ id && origin.exists?
75
+ end
76
+
77
+ # @return [Bridgetown::Resource::Base]
78
+ def to_resource
79
+ Bridgetown::Resource::Base.new(model: self)
80
+ end
81
+
82
+ def as_resource_in_collection
83
+ collection.resources << to_resource.read!
84
+ collection.resources.last
85
+ end
86
+
87
+ # override if need be
88
+ # @return [Bridgetown::Site]
89
+ def site
90
+ Bridgetown::Current.site
91
+ end
92
+
93
+ # @return [Bridgetown::Collection]
94
+ def collection
95
+ attributes[:_collection_]
96
+ end
97
+
98
+ # @return [String]
99
+ def content
100
+ attributes[:_content_]
101
+ end
102
+
103
+ def attributes
104
+ @attributes ||= HashWithDotAccess::Hash.new
105
+ end
106
+
107
+ # Strip out keys like _origin_, _collection_, etc.
108
+ # @return [HashWithDotAccess::Hash]
109
+ def data_attributes
110
+ attributes.reject { |k| k.starts_with?("_") && k.ends_with?("_") }
111
+ end
112
+
113
+ def respond_to_missing?(method_name, include_private = false)
114
+ attributes.key?(method_name) || method_name.to_s.end_with?("=") || super
115
+ end
116
+
117
+ def method_missing(method_name, *args) # rubocop:disable Style/MethodMissingSuper
118
+ return attributes[method_name] if attributes.key?(method_name)
119
+
120
+ key = method_name.to_s
121
+ if key.end_with?("=")
122
+ key.chop!
123
+ # attribute_will_change!(key)
124
+ attributes[key] = args.first
125
+ return attributes[key]
126
+ end
127
+
128
+ Bridgetown.logger.warn "key `#{method_name}' not found in attributes for" \
129
+ " #{attributes[:id].presence || ("new " + self.class.to_s)}"
130
+ nil
131
+ end
132
+
133
+ def inspect
134
+ "#<#{self.class} #{data_attributes.inspect.delete_prefix("{").delete_suffix("}")}>"
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Model
5
+ # Abstract Superclass
6
+ class BuilderOrigin < Origin
7
+ # @return [Pathname]
8
+ attr_reader :relative_path
9
+
10
+ # Override in subclass
11
+ def self.handle_scheme?(scheme)
12
+ scheme == "builder"
13
+ end
14
+
15
+ def initialize(id)
16
+ self.id = id
17
+ @relative_path = Pathname.new(id.delete_prefix("builder://"))
18
+ end
19
+
20
+ def read
21
+ @data = if block_given?
22
+ yield
23
+ elsif defined?(SiteBuilder) && SiteBuilder.respond_to?(:data_for_id)
24
+ SiteBuilder.data_for_id(id)
25
+ else
26
+ raise "No builder exists which can read #{id}"
27
+ end
28
+ @data[:_id_] = id
29
+ @data[:_origin_] = self
30
+ @relative_path = Pathname.new(@data[:_relative_path_]) if @data[:_relative_path_]
31
+
32
+ @data
33
+ end
34
+
35
+ def exists?
36
+ false
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Model
5
+ class FileOrigin < Origin
6
+ YAML_FRONT_MATTER_REGEXP = %r!\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)!m.freeze
7
+
8
+ class << self
9
+ def handle_scheme?(scheme)
10
+ scheme == "file"
11
+ end
12
+
13
+ def data_file_extensions
14
+ %w(.yaml .yml .json .csv .tsv).freeze
15
+ end
16
+ end
17
+
18
+ def read
19
+ @data = (in_data_collection? ? read_file_data : read_frontmatter) || {}
20
+ @data[:_id_] = id
21
+ @data[:_origin_] = self
22
+ @data[:_collection_] = collection
23
+ @data[:_content_] = @content if @content
24
+
25
+ @data
26
+ rescue StandardError => e
27
+ handle_read_error(e)
28
+ end
29
+
30
+ def url
31
+ @url = URI.parse(id)
32
+ end
33
+
34
+ def relative_path
35
+ @relative_path ||= Pathname.new(
36
+ Addressable::URI.unescape(url.path.delete_prefix("/"))
37
+ )
38
+ end
39
+
40
+ def collection
41
+ return @collection if @collection
42
+
43
+ collection_name = if url.host.ends_with?(".collection")
44
+ url.host.chomp(".collection")
45
+ else
46
+ "pages"
47
+ end
48
+ @collection = Bridgetown::Current.site.collections[collection_name]
49
+ end
50
+
51
+ def original_path
52
+ @original_path ||= relative_path.expand_path(Bridgetown::Current.site.source)
53
+ end
54
+
55
+ def exists?
56
+ File.exist?(original_path)
57
+ end
58
+
59
+ private
60
+
61
+ def in_data_collection?
62
+ original_path.extname.downcase.in?(self.class.data_file_extensions) &&
63
+ collection.data?
64
+ end
65
+
66
+ def read_file_data
67
+ case original_path.extname.downcase
68
+ when ".csv"
69
+ {
70
+ array:
71
+ CSV.read(original_path,
72
+ headers: true,
73
+ encoding: Bridgetown::Current.site.config["encoding"]).map(&:to_hash),
74
+ }
75
+ when ".tsv"
76
+ {
77
+ array:
78
+ CSV.read(original_path,
79
+ col_sep: "\t",
80
+ headers: true,
81
+ encoding: Bridgetown::Current.site.config["encoding"]).map(&:to_hash),
82
+ }
83
+ else
84
+ yaml_data = SafeYAML.load_file(original_path)
85
+ yaml_data.is_a?(Array) ? { array: yaml_data } : yaml_data
86
+ end
87
+ end
88
+
89
+ def read_frontmatter
90
+ @content = File.read(
91
+ original_path, **Bridgetown::Utils.merged_file_read_opts(Bridgetown::Current.site, {})
92
+ )
93
+ content_match = @content.match(YAML_FRONT_MATTER_REGEXP)
94
+ if content_match
95
+ @content = content_match.post_match
96
+ SafeYAML.load(content_match[1])
97
+ else
98
+ yaml_data = SafeYAML.load_file(original_path)
99
+ yaml_data.is_a?(Array) ? { array: yaml_data } : yaml_data
100
+ end
101
+ end
102
+
103
+ def handle_read_error(error)
104
+ if error.is_a? Psych::SyntaxError
105
+ Bridgetown.logger.error "Error:",
106
+ "YAML Exception reading #{original_path}: #{error.message}"
107
+ else
108
+ Bridgetown.logger.error "Error:",
109
+ "could not read file #{original_path}: #{error.message}"
110
+ end
111
+
112
+ if Bridgetown::Current.site.config["strict_front_matter"] ||
113
+ error.is_a?(Bridgetown::Errors::FatalException)
114
+ raise error
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Model
5
+ # Abstract Superclass
6
+ class Origin
7
+ extend ActiveSupport::DescendantsTracker
8
+
9
+ # @return [String]
10
+ attr_accessor :id
11
+
12
+ # Override in subclass
13
+ def self.handle_scheme?(_scheme)
14
+ false
15
+ end
16
+
17
+ def initialize(id)
18
+ self.id = id
19
+ end
20
+
21
+ def read
22
+ raise "Implement #read in a subclass of Bridgetown::Model::Origin"
23
+ end
24
+
25
+ # @return [Pathname]
26
+ def relative_path
27
+ raise "Implement #relative_path in a subclass of Bridgetown::Model::Origin"
28
+ end
29
+
30
+ def exists?
31
+ raise "Implement #exists? in a subclass of Bridgetown::Model::Origin"
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ require "bridgetown-core/model/builder_origin"
38
+ require "bridgetown-core/model/file_origin"
@@ -47,7 +47,7 @@ module Bridgetown
47
47
  read_yaml(PathManager.join(base, dir), name)
48
48
 
49
49
  data.default_proc = proc do |_, key|
50
- site.frontmatter_defaults.find(relative_path, type, key)
50
+ site.frontmatter_defaults.find(relative_path, type, key.to_s)
51
51
  end
52
52
 
53
53
  Bridgetown::Hooks.trigger :pages, :post_init, self
@@ -116,6 +116,7 @@ module Bridgetown
116
116
  permalink: permalink
117
117
  ).to_s
118
118
  end
119
+ alias_method :relative_url, :url
119
120
 
120
121
  # Returns a hash of URL placeholder names (as symbols) mapping to the
121
122
  # desired placeholder replacements. For details see "url.rb"
@@ -218,4 +219,11 @@ module Bridgetown
218
219
  true
219
220
  end
220
221
  end
222
+
223
+ # set up virtual page class for future compatibility
224
+ class GeneratedPage < Page
225
+ def read_yaml(_base, _name, _opts = {})
226
+ self.data ||= HashWithDotAccess::Hash.new
227
+ end
228
+ end
221
229
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "zeitwerk"
4
-
5
3
  module Bridgetown
6
4
  class PluginManager
7
5
  PLUGINS_GROUP = :bridgetown_plugins
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Bridgetown
4
4
  class Publisher
5
+ # @param site [Bridgetown::Site]
5
6
  def initialize(site)
6
7
  @site = site
7
8
  end
@@ -11,7 +12,12 @@ module Bridgetown
11
12
  end
12
13
 
13
14
  def hidden_in_the_future?(thing)
14
- thing.respond_to?(:date) && !@site.future && thing.date.to_i > @site.time.to_i
15
+ return false unless thing.respond_to?(:date)
16
+
17
+ future_allowed =
18
+ thing.respond_to?(:collection) && thing.collection.metadata.future || @site.future
19
+ thing_time = thing.date.is_a?(Date) ? thing.date.to_time.to_i : thing.date.to_i
20
+ !future_allowed && thing_time > @site.time.to_i
15
21
  end
16
22
 
17
23
  private
@@ -11,20 +11,29 @@ module Bridgetown
11
11
  # Read Site data from disk and load it into internal data structures.
12
12
  #
13
13
  # Returns nothing.
14
- # rubocop:disable Metrics/AbcSize
15
- def read
16
- @site.defaults_reader.read
17
- @site.layouts = LayoutReader.new(site).read
14
+ def read # rubocop:todo Metrics/AbcSize
15
+ site.defaults_reader.read
16
+ site.layouts = LayoutReader.new(site).read
18
17
  read_directories
19
18
  read_included_excludes
20
19
  sort_files!
21
- @site.data = DataReader.new(site).read(site.config["data_dir"])
22
- CollectionReader.new(site).read
20
+ read_collections
21
+ site.data = if site.uses_resource?
22
+ site.collections.data.merge_data_resources
23
+ else
24
+ DataReader.new(site).read
25
+ end
23
26
  Bridgetown::PluginManager.source_manifests.map(&:content).compact.each do |plugin_content_dir|
24
27
  PluginContentReader.new(site, plugin_content_dir).read
25
28
  end
26
29
  end
27
- # rubocop:enable Metrics/AbcSize
30
+
31
+ def read_collections
32
+ site.collections.each_value do |collection|
33
+ collection.read unless !site.uses_resource? &&
34
+ collection.legacy_reader?
35
+ end
36
+ end
28
37
 
29
38
  # Sorts posts, pages, and static files.
30
39
  def sort_files!
@@ -61,7 +70,7 @@ module Bridgetown
61
70
  end
62
71
  end
63
72
 
64
- retrieve_posts(dir)
73
+ retrieve_posts(dir) unless site.uses_resource?
65
74
  retrieve_dirs(base, dir, dot_dirs)
66
75
  retrieve_pages(dir, dot_pages)
67
76
  retrieve_static_files(dir, dot_static_files)
@@ -102,6 +111,13 @@ module Bridgetown
102
111
  #
103
112
  # Returns nothing.
104
113
  def retrieve_pages(dir, dot_pages)
114
+ if site.uses_resource?
115
+ dot_pages.each do |page_path|
116
+ site.collections.pages.send(:read_resource, site.in_source_dir(dir, page_path))
117
+ end
118
+ return
119
+ end
120
+
105
121
  site.pages.concat(PageReader.new(site, dir).read(dot_pages))
106
122
  end
107
123
 
@@ -126,7 +142,7 @@ module Bridgetown
126
142
  #
127
143
  # Returns the Array of filtered entries.
128
144
  def filter_entries(entries, base_directory = nil)
129
- EntryFilter.new(site, base_directory).filter(entries)
145
+ EntryFilter.new(site, base_directory: base_directory).filter(entries)
130
146
  end
131
147
 
132
148
  # Read the entries from a particular directory for processing
@@ -165,14 +181,11 @@ module Bridgetown
165
181
  end
166
182
 
167
183
  def read_included_excludes
168
- entry_filter = EntryFilter.new(site)
169
-
170
184
  site.include.each do |entry|
171
185
  next if entry == ".htaccess"
172
186
 
173
187
  entry_path = site.in_source_dir(entry)
174
188
  next if File.directory?(entry_path)
175
- next if entry_filter.symlink?(entry_path)
176
189
 
177
190
  read_included_file(entry_path) if File.file?(entry_path)
178
191
  end