jekyll 4.1.1 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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