jekyll 4.1.1 → 4.2.0

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.
@@ -35,13 +35,13 @@ module Jekyll
35
35
  # Returns nothing.
36
36
  # rubocop:disable Metrics/AbcSize
37
37
  def read_yaml(base, name, opts = {})
38
- filename = File.join(base, name)
38
+ filename = @path || site.in_source_dir(base, name)
39
+ Jekyll.logger.debug "Reading:", relative_path
39
40
 
40
41
  begin
41
- self.content = File.read(@path || site.in_source_dir(base, name),
42
- **Utils.merged_file_read_opts(site, opts))
42
+ self.content = File.read(filename, **Utils.merged_file_read_opts(site, opts))
43
43
  if content =~ Document::YAML_FRONT_MATTER_REGEXP
44
- self.content = $POSTMATCH
44
+ self.content = Regexp.last_match.post_match
45
45
  self.data = SafeYAML.load(Regexp.last_match(1))
46
46
  end
47
47
  rescue Psych::SyntaxError => e
@@ -69,7 +69,7 @@ module Jekyll
69
69
  end
70
70
 
71
71
  def validate_permalink!(filename)
72
- if self.data["permalink"]&.to_s&.empty?
72
+ if self.data["permalink"] == ""
73
73
  raise Errors::InvalidPermalinkError, "Invalid permalink in #{filename}"
74
74
  end
75
75
  end
@@ -112,12 +112,7 @@ module Jekyll
112
112
  #
113
113
  # Returns the Hash representation of this Convertible.
114
114
  def to_liquid(attrs = nil)
115
- further_data = \
116
- (attrs || self.class::ATTRIBUTES_FOR_LIQUID).each_with_object({}) do |attribute, hsh|
117
- hsh[attribute] = send(attribute)
118
- end
119
-
120
- defaults = site.frontmatter_defaults.all(relative_path, type)
115
+ further_data = attribute_hash(attrs || self.class::ATTRIBUTES_FOR_LIQUID)
121
116
  Utils.deep_merge_hashes defaults, Utils.deep_merge_hashes(data, further_data)
122
117
  end
123
118
 
@@ -247,6 +242,17 @@ module Jekyll
247
242
 
248
243
  private
249
244
 
245
+ def defaults
246
+ @defaults ||= site.frontmatter_defaults.all(relative_path, type)
247
+ end
248
+
249
+ def attribute_hash(attrs)
250
+ @attribute_hash ||= {}
251
+ @attribute_hash[attrs] ||= attrs.each_with_object({}) do |attribute, hsh|
252
+ hsh[attribute] = send(attribute)
253
+ end
254
+ end
255
+
250
256
  def no_layout?
251
257
  data["layout"] == "none"
252
258
  end
@@ -257,14 +257,16 @@ module Jekyll
257
257
  #
258
258
  # Returns the full path to the output file of this document.
259
259
  def destination(base_directory)
260
- dest = site.in_dest_dir(base_directory)
261
- path = site.in_dest_dir(dest, URL.unescape_path(url))
262
- if url.end_with? "/"
263
- path = File.join(path, "index.html")
264
- else
265
- path << output_ext unless path.end_with? output_ext
260
+ @destination ||= {}
261
+ @destination[base_directory] ||= begin
262
+ path = site.in_dest_dir(base_directory, URL.unescape_path(url))
263
+ if url.end_with? "/"
264
+ path = File.join(path, "index.html")
265
+ else
266
+ path << output_ext unless path.end_with? output_ext
267
+ end
268
+ path
266
269
  end
267
- path
268
270
  end
269
271
 
270
272
  # Write the generated Document file to the destination directory.
@@ -351,9 +353,14 @@ module Jekyll
351
353
  # True if the document has a collection and if that collection's #write?
352
354
  # method returns true, and if the site's Publisher will publish the document.
353
355
  # False otherwise.
356
+ #
357
+ # rubocop:disable Naming/MemoizedInstanceVariableName
354
358
  def write?
355
- collection&.write? && site.publisher.publish?(self)
359
+ return @write_p if defined?(@write_p)
360
+
361
+ @write_p = collection&.write? && site.publisher.publish?(self)
356
362
  end
363
+ # rubocop:enable Naming/MemoizedInstanceVariableName
357
364
 
358
365
  # The Document excerpt_separator, from the YAML Front-Matter or site
359
366
  # default excerpt_separator value
@@ -452,7 +459,10 @@ module Jekyll
452
459
  def merge_categories!(other)
453
460
  if other.key?("categories") && !other["categories"].nil?
454
461
  other["categories"] = other["categories"].split if other["categories"].is_a?(String)
455
- other["categories"] = (data["categories"] || []) | other["categories"]
462
+
463
+ if data["categories"].is_a?(Array)
464
+ other["categories"] = data["categories"] | other["categories"]
465
+ end
456
466
  end
457
467
  end
458
468
 
@@ -473,7 +483,7 @@ module Jekyll
473
483
  def read_content(**opts)
474
484
  self.content = File.read(path, **Utils.merged_file_read_opts(site, opts))
475
485
  if content =~ YAML_FRONT_MATTER_REGEXP
476
- self.content = $POSTMATCH
486
+ self.content = Regexp.last_match.post_match
477
487
  data_file = SafeYAML.load(Regexp.last_match(1))
478
488
  merge_data!(data_file, :source => "YAML front matter") if data_file
479
489
  end
@@ -498,7 +508,6 @@ module Jekyll
498
508
  end
499
509
  end
500
510
 
501
- # rubocop:disable Metrics/AbcSize
502
511
  def populate_title
503
512
  if relative_path =~ DATE_FILENAME_MATCHER
504
513
  date, slug, ext = Regexp.last_match.captures
@@ -521,7 +530,6 @@ module Jekyll
521
530
  data["slug"] ||= slug
522
531
  data["ext"] ||= ext
523
532
  end
524
- # rubocop:enable Metrics/AbcSize
525
533
 
526
534
  def modify_date(date)
527
535
  if !data["date"] || data["date"].to_i == site.time.to_i
@@ -7,10 +7,10 @@ module Jekyll
7
7
 
8
8
  mutable false
9
9
 
10
- def_delegator :@obj, :write?, :output
11
- def_delegators :@obj, :label, :docs, :files, :directory, :relative_directory
10
+ delegate_method_as :write?, :output
11
+ delegate_methods :label, :docs, :files, :directory, :relative_directory
12
12
 
13
- private def_delegator :@obj, :metadata, :fallback_data
13
+ private delegate_method_as :metadata, :fallback_data
14
14
 
15
15
  def to_s
16
16
  docs.to_s
@@ -11,10 +11,11 @@ module Jekyll
11
11
 
12
12
  mutable false
13
13
 
14
- def_delegator :@obj, :relative_path, :path
15
- def_delegators :@obj, :id, :output, :content, :to_s, :relative_path, :url, :date
14
+ delegate_method_as :relative_path, :path
15
+ private delegate_method_as :data, :fallback_data
16
16
 
17
- private def_delegator :@obj, :data, :fallback_data
17
+ delegate_methods :id, :output, :content, :to_s, :relative_path, :url, :date
18
+ data_delegators "title", "categories", "tags"
18
19
 
19
20
  def collection
20
21
  @obj.collection.label
@@ -64,18 +65,6 @@ module Jekyll
64
65
  result[key] = doc[key] unless NESTED_OBJECT_FIELD_BLACKLIST.include?(key)
65
66
  end
66
67
  end
67
-
68
- def title
69
- @obj.data["title"]
70
- end
71
-
72
- def categories
73
- @obj.data["categories"]
74
- end
75
-
76
- def tags
77
- @obj.data["tags"]
78
- end
79
68
  end
80
69
  end
81
70
  end
@@ -6,20 +6,101 @@ module Jekyll
6
6
  include Enumerable
7
7
 
8
8
  NON_CONTENT_METHODS = [:fallback_data, :collapse_document].freeze
9
+ NON_CONTENT_METHOD_NAMES = NON_CONTENT_METHODS.map(&:to_s).freeze
10
+ private_constant :NON_CONTENT_METHOD_NAMES
9
11
 
10
- # Get or set whether the drop class is mutable.
11
- # Mutability determines whether or not pre-defined fields may be
12
- # overwritten.
13
- #
14
- # is_mutable - Boolean set mutability of the class (default: nil)
15
- #
16
- # Returns the mutability of the class
17
- def self.mutable(is_mutable = nil)
18
- @is_mutable = is_mutable || false
19
- end
12
+ # A private stash to avoid repeatedly generating the setter method name string for
13
+ # a call to `Drops::Drop#[]=`.
14
+ # The keys of the stash below have a very high probability of being called upon during
15
+ # the course of various `Jekyll::Renderer#run` calls.
16
+ SETTER_KEYS_STASH = {
17
+ "content" => "content=",
18
+ "layout" => "layout=",
19
+ "page" => "page=",
20
+ "paginator" => "paginator=",
21
+ "highlighter_prefix" => "highlighter_prefix=",
22
+ "highlighter_suffix" => "highlighter_suffix=",
23
+ }.freeze
24
+ private_constant :SETTER_KEYS_STASH
25
+
26
+ class << self
27
+ # Get or set whether the drop class is mutable.
28
+ # Mutability determines whether or not pre-defined fields may be
29
+ # overwritten.
30
+ #
31
+ # is_mutable - Boolean set mutability of the class (default: nil)
32
+ #
33
+ # Returns the mutability of the class
34
+ def mutable(is_mutable = nil)
35
+ @is_mutable = is_mutable || false
36
+ end
37
+
38
+ def mutable?
39
+ @is_mutable
40
+ end
41
+
42
+ # public delegation helper methods that calls onto Drop's instance
43
+ # variable `@obj`.
44
+
45
+ # Generate private Drop instance_methods for each symbol in the given list.
46
+ #
47
+ # Returns nothing.
48
+ def private_delegate_methods(*symbols)
49
+ symbols.each { |symbol| private delegate_method(symbol) }
50
+ nil
51
+ end
52
+
53
+ # Generate public Drop instance_methods for each symbol in the given list.
54
+ #
55
+ # Returns nothing.
56
+ def delegate_methods(*symbols)
57
+ symbols.each { |symbol| delegate_method(symbol) }
58
+ nil
59
+ end
20
60
 
21
- def self.mutable?
22
- @is_mutable
61
+ # Generate public Drop instance_method for given symbol that calls `@obj.<sym>`.
62
+ #
63
+ # Returns delegated method symbol.
64
+ def delegate_method(symbol)
65
+ define_method(symbol) { @obj.send(symbol) }
66
+ end
67
+
68
+ # Generate public Drop instance_method named `delegate` that calls `@obj.<original>`.
69
+ #
70
+ # Returns delegated method symbol.
71
+ def delegate_method_as(original, delegate)
72
+ define_method(delegate) { @obj.send(original) }
73
+ end
74
+
75
+ # Generate public Drop instance_methods for each string entry in the given list.
76
+ # The generated method(s) access(es) `@obj`'s data hash.
77
+ #
78
+ # Returns nothing.
79
+ def data_delegators(*strings)
80
+ strings.each do |key|
81
+ data_delegator(key) if key.is_a?(String)
82
+ end
83
+ nil
84
+ end
85
+
86
+ # Generate public Drop instance_methods for given string `key`.
87
+ # The generated method access(es) `@obj`'s data hash.
88
+ #
89
+ # Returns method symbol.
90
+ def data_delegator(key)
91
+ define_method(key.to_sym) { @obj.data[key] }
92
+ end
93
+
94
+ # Array of stringified instance methods that do not end with the assignment operator.
95
+ #
96
+ # (<klass>.instance_methods always generates a new Array object so it can be mutated)
97
+ #
98
+ # Returns array of strings.
99
+ def getter_method_names
100
+ @getter_method_names ||= instance_methods.map!(&:to_s).tap do |list|
101
+ list.reject! { |item| item.end_with?("=") }
102
+ end
103
+ end
23
104
  end
24
105
 
25
106
  # Create a new Drop
@@ -65,7 +146,7 @@ module Jekyll
65
146
  # and the key matches a method in which case it raises a
66
147
  # DropMutationException.
67
148
  def []=(key, val)
68
- setter = "#{key}="
149
+ setter = SETTER_KEYS_STASH[key] || "#{key}="
69
150
  if respond_to?(setter)
70
151
  public_send(setter, val)
71
152
  elsif respond_to?(key.to_s)
@@ -84,13 +165,10 @@ module Jekyll
84
165
  #
85
166
  # Returns an Array of strings which represent method-specific keys.
86
167
  def content_methods
87
- @content_methods ||= (
88
- self.class.instance_methods \
89
- - Jekyll::Drops::Drop.instance_methods \
90
- - NON_CONTENT_METHODS
91
- ).map(&:to_s).reject do |method|
92
- method.end_with?("=")
93
- end
168
+ @content_methods ||= \
169
+ self.class.getter_method_names \
170
+ - Jekyll::Drops::Drop.getter_method_names \
171
+ - NON_CONTENT_METHOD_NAMES
94
172
  end
95
173
 
96
174
  # Check if key exists in Drop
@@ -7,10 +7,10 @@ module Jekyll
7
7
 
8
8
  mutable false
9
9
 
10
- def_delegator :@obj, :site_data, :data
11
- def_delegators :@obj, :time, :pages, :static_files, :tags, :categories
10
+ delegate_method_as :site_data, :data
11
+ delegate_methods :time, :pages, :static_files, :tags, :categories
12
12
 
13
- private def_delegator :@obj, :config, :fallback_data
13
+ private delegate_method_as :config, :fallback_data
14
14
 
15
15
  def [](key)
16
16
  if key != "posts" && @obj.collections.key?(key)
@@ -4,11 +4,11 @@ module Jekyll
4
4
  module Drops
5
5
  class StaticFileDrop < Drop
6
6
  extend Forwardable
7
- def_delegators :@obj, :name, :extname, :modified_time, :basename
8
- def_delegator :@obj, :relative_path, :path
9
- def_delegator :@obj, :type, :collection
7
+ delegate_methods :name, :extname, :modified_time, :basename
8
+ delegate_method_as :relative_path, :path
9
+ delegate_method_as :type, :collection
10
10
 
11
- private def_delegator :@obj, :data, :fallback_data
11
+ private delegate_method_as :data, :fallback_data
12
12
  end
13
13
  end
14
14
  end
@@ -7,8 +7,8 @@ module Jekyll
7
7
 
8
8
  mutable false
9
9
 
10
- def_delegator :@obj, :cleaned_relative_path, :path
11
- def_delegator :@obj, :output_ext, :output_ext
10
+ delegate_method :output_ext
11
+ delegate_method_as :cleaned_relative_path, :path
12
12
 
13
13
  def collection
14
14
  @obj.collection.label
@@ -113,7 +113,7 @@ module Jekyll
113
113
  #
114
114
  # Returns the formatted String
115
115
  def normalize_whitespace(input)
116
- input.to_s.gsub(%r!\s+!, " ").strip
116
+ input.to_s.gsub(%r!\s+!, " ").tap(&:strip!)
117
117
  end
118
118
 
119
119
  # Count the number of words in the input string.
@@ -307,9 +307,10 @@ module Jekyll
307
307
  if property.nil?
308
308
  input.sort
309
309
  else
310
- if nils == "first"
310
+ case nils
311
+ when "first"
311
312
  order = - 1
312
- elsif nils == "last"
313
+ when "last"
313
314
  order = + 1
314
315
  else
315
316
  raise ArgumentError, "Invalid nils order: " \
@@ -399,7 +400,6 @@ module Jekyll
399
400
 
400
401
  # `where` filter helper
401
402
  #
402
- # rubocop:disable Metrics/PerceivedComplexity
403
403
  def compare_property_vs_target(property, target)
404
404
  case target
405
405
  when NilClass
@@ -421,10 +421,8 @@ module Jekyll
421
421
  false
422
422
  end
423
423
 
424
- # rubocop:enable Metrics/PerceivedComplexity
425
-
426
424
  def item_property(item, property)
427
- @item_property_cache ||= {}
425
+ @item_property_cache ||= @context.registers[:site].filter_cache[:item_property] ||= {}
428
426
  @item_property_cache[property] ||= {}
429
427
  @item_property_cache[property][item] ||= begin
430
428
  property = property.to_s
@@ -464,8 +462,7 @@ module Jekyll
464
462
  def as_liquid(item)
465
463
  case item
466
464
  when Hash
467
- pairs = item.map { |k, v| as_liquid([k, v]) }
468
- Hash[pairs]
465
+ item.each_with_object({}) { |(k, v), result| result[as_liquid(k)] = as_liquid(v) }
469
466
  when Array
470
467
  item.map { |i| as_liquid(i) }
471
468
  else
@@ -76,13 +76,16 @@ module Jekyll
76
76
 
77
77
  parts = [sanitized_baseurl, input]
78
78
  Addressable::URI.parse(
79
- parts.compact.map { |part| ensure_leading_slash(part.to_s) }.join
79
+ parts.map! { |part| ensure_leading_slash(part.to_s) }.join
80
80
  ).normalize.to_s
81
81
  end
82
82
 
83
83
  def sanitized_baseurl
84
84
  site = @context.registers[:site]
85
- site.config["baseurl"].to_s.chomp("/")
85
+ baseurl = site.config["baseurl"]
86
+ return "" if baseurl.nil?
87
+
88
+ baseurl.to_s.chomp("/")
86
89
  end
87
90
 
88
91
  def ensure_leading_slash(input)
@@ -157,7 +157,7 @@ module Jekyll
157
157
  # Returns true if either of the above conditions are satisfied,
158
158
  # otherwise returns false
159
159
  def applies_type?(scope, type)
160
- !scope.key?("type") || scope["type"].eql?(type.to_s)
160
+ !scope.key?("type") || type&.to_sym.eql?(scope["type"].to_sym)
161
161
  end
162
162
 
163
163
  # Checks if a given set of default values is valid
@@ -223,7 +223,7 @@ module Jekyll
223
223
  Jekyll.logger.warn set.to_s
224
224
  nil
225
225
  end
226
- end.compact
226
+ end.tap(&:compact!)
227
227
  end
228
228
 
229
229
  # Sanitizes the given path by removing a leading slash