jekyll 3.9.3 → 4.4.1
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +511 -89
- data/LICENSE +1 -1
- data/README.markdown +48 -27
- data/lib/blank_template/_config.yml +3 -0
- data/lib/blank_template/_layouts/default.html +12 -0
- data/lib/blank_template/_sass/base.scss +9 -0
- data/lib/blank_template/assets/css/main.scss +4 -0
- data/lib/blank_template/index.md +8 -0
- data/lib/jekyll/cache.rb +186 -0
- data/lib/jekyll/cleaner.rb +8 -7
- data/lib/jekyll/collection.rb +84 -11
- data/lib/jekyll/command.rb +33 -6
- data/lib/jekyll/commands/build.rb +8 -28
- data/lib/jekyll/commands/clean.rb +3 -2
- data/lib/jekyll/commands/doctor.rb +46 -35
- data/lib/jekyll/commands/help.rb +1 -1
- data/lib/jekyll/commands/new.rb +44 -50
- data/lib/jekyll/commands/new_theme.rb +27 -28
- data/lib/jekyll/commands/serve/live_reload_reactor.rb +9 -16
- data/lib/jekyll/commands/serve/servlet.rb +21 -22
- data/lib/jekyll/commands/serve/websockets.rb +1 -1
- data/lib/jekyll/commands/serve.rb +75 -97
- data/lib/jekyll/configuration.rb +66 -158
- data/lib/jekyll/converters/identity.rb +18 -0
- data/lib/jekyll/converters/markdown/kramdown_parser.rb +83 -33
- data/lib/jekyll/converters/markdown.rb +49 -40
- data/lib/jekyll/converters/smartypants.rb +34 -14
- data/lib/jekyll/convertible.rb +36 -34
- data/lib/jekyll/deprecator.rb +2 -4
- data/lib/jekyll/document.rb +107 -72
- data/lib/jekyll/drops/collection_drop.rb +3 -4
- data/lib/jekyll/drops/document_drop.rb +9 -3
- data/lib/jekyll/drops/drop.rb +115 -33
- data/lib/jekyll/drops/excerpt_drop.rb +8 -0
- data/lib/jekyll/drops/site_drop.rb +9 -8
- data/lib/jekyll/drops/static_file_drop.rb +4 -4
- data/lib/jekyll/drops/theme_drop.rb +39 -0
- data/lib/jekyll/drops/unified_payload_drop.rb +7 -2
- data/lib/jekyll/drops/url_drop.rb +55 -3
- data/lib/jekyll/entry_filter.rb +42 -51
- data/lib/jekyll/excerpt.rb +48 -38
- data/lib/jekyll/external.rb +20 -19
- data/lib/jekyll/filters/date_filters.rb +6 -3
- data/lib/jekyll/filters/grouping_filters.rb +1 -2
- data/lib/jekyll/filters/url_filters.rb +50 -15
- data/lib/jekyll/filters.rb +211 -50
- data/lib/jekyll/frontmatter_defaults.rb +45 -36
- data/lib/jekyll/hooks.rb +26 -26
- data/lib/jekyll/inclusion.rb +32 -0
- data/lib/jekyll/layout.rb +12 -19
- data/lib/jekyll/liquid_extensions.rb +0 -2
- data/lib/jekyll/liquid_renderer/file.rb +24 -3
- data/lib/jekyll/liquid_renderer/table.rb +26 -77
- data/lib/jekyll/liquid_renderer.rb +31 -16
- data/lib/jekyll/log_adapter.rb +5 -1
- data/lib/jekyll/page.rb +51 -23
- data/lib/jekyll/page_excerpt.rb +25 -0
- data/lib/jekyll/page_without_a_file.rb +0 -4
- data/lib/jekyll/path_manager.rb +74 -0
- data/lib/jekyll/plugin.rb +5 -11
- data/lib/jekyll/plugin_manager.rb +15 -5
- data/lib/jekyll/profiler.rb +51 -0
- data/lib/jekyll/reader.rb +65 -10
- data/lib/jekyll/readers/collection_reader.rb +1 -0
- data/lib/jekyll/readers/data_reader.rb +48 -10
- data/lib/jekyll/readers/layout_reader.rb +3 -12
- data/lib/jekyll/readers/page_reader.rb +5 -5
- data/lib/jekyll/readers/post_reader.rb +32 -19
- data/lib/jekyll/readers/static_file_reader.rb +4 -4
- data/lib/jekyll/readers/theme_assets_reader.rb +8 -5
- data/lib/jekyll/regenerator.rb +4 -12
- data/lib/jekyll/related_posts.rb +1 -1
- data/lib/jekyll/renderer.rb +34 -49
- data/lib/jekyll/site.rb +151 -58
- data/lib/jekyll/static_file.rb +64 -28
- data/lib/jekyll/stevenson.rb +4 -8
- data/lib/jekyll/tags/highlight.rb +44 -57
- data/lib/jekyll/tags/include.rb +114 -80
- data/lib/jekyll/tags/link.rb +12 -7
- data/lib/jekyll/tags/post_url.rb +33 -30
- data/lib/jekyll/theme.rb +20 -18
- data/lib/jekyll/theme_builder.rb +91 -89
- data/lib/jekyll/url.rb +18 -10
- data/lib/jekyll/utils/ansi.rb +2 -2
- data/lib/jekyll/utils/exec.rb +0 -1
- data/lib/jekyll/utils/internet.rb +2 -4
- data/lib/jekyll/utils/platforms.rb +37 -52
- data/lib/jekyll/utils/thread_event.rb +1 -5
- data/lib/jekyll/utils.rb +29 -28
- data/lib/jekyll/version.rb +1 -1
- data/lib/jekyll.rb +9 -14
- data/lib/site_template/.gitignore +2 -0
- data/lib/site_template/404.html +2 -1
- data/lib/site_template/_config.yml +17 -5
- data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +5 -1
- data/lib/theme_template/README.md.erb +1 -3
- data/lib/theme_template/gitignore.erb +1 -0
- data/lib/theme_template/theme.gemspec.erb +1 -4
- data/rubocop/jekyll/assert_equal_literal_actual.rb +150 -0
- data/rubocop/jekyll/no_p_allowed.rb +5 -6
- data/rubocop/jekyll/no_puts_allowed.rb +5 -6
- metadata +149 -37
- data/lib/jekyll/converters/markdown/rdiscount_parser.rb +0 -37
- data/lib/jekyll/converters/markdown/redcarpet_parser.rb +0 -112
- data/lib/jekyll/utils/rouge.rb +0 -22
- /data/lib/site_template/{about.md → about.markdown} +0 -0
- /data/lib/site_template/{index.md → index.markdown} +0 -0
|
@@ -10,13 +10,17 @@ module Jekyll
|
|
|
10
10
|
# Returns the absolute URL as a String.
|
|
11
11
|
def absolute_url(input)
|
|
12
12
|
return if input.nil?
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
)
|
|
13
|
+
|
|
14
|
+
cache = if input.is_a?(String)
|
|
15
|
+
(@context.registers[:site].filter_cache[:absolute_url] ||= {})
|
|
16
|
+
else
|
|
17
|
+
(@context.registers[:cached_absolute_url] ||= {})
|
|
18
|
+
end
|
|
19
|
+
cache[input] ||= compute_absolute_url(input)
|
|
20
|
+
|
|
21
|
+
# Duplicate cached string so that the cached value is never mutated by
|
|
22
|
+
# a subsequent filter.
|
|
23
|
+
cache[input].dup
|
|
20
24
|
end
|
|
21
25
|
|
|
22
26
|
# Produces a URL relative to the domain root based on site.baseurl
|
|
@@ -27,13 +31,17 @@ module Jekyll
|
|
|
27
31
|
# Returns a URL relative to the domain root as a String.
|
|
28
32
|
def relative_url(input)
|
|
29
33
|
return if input.nil?
|
|
30
|
-
input = input.url if input.respond_to?(:url)
|
|
31
|
-
return input if Addressable::URI.parse(input.to_s).absolute?
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
cache = if input.is_a?(String)
|
|
36
|
+
(@context.registers[:site].filter_cache[:relative_url] ||= {})
|
|
37
|
+
else
|
|
38
|
+
(@context.registers[:cached_relative_url] ||= {})
|
|
39
|
+
end
|
|
40
|
+
cache[input] ||= compute_relative_url(input)
|
|
41
|
+
|
|
42
|
+
# Duplicate cached string so that the cached value is never mutated by
|
|
43
|
+
# a subsequent filter.
|
|
44
|
+
cache[input].dup
|
|
37
45
|
end
|
|
38
46
|
|
|
39
47
|
# Strips trailing `/index.html` from URLs to create pretty permalinks
|
|
@@ -43,21 +51,48 @@ module Jekyll
|
|
|
43
51
|
# Returns a URL with the trailing `/index.html` removed
|
|
44
52
|
def strip_index(input)
|
|
45
53
|
return if input.nil? || input.to_s.empty?
|
|
54
|
+
|
|
46
55
|
input.sub(%r!/index\.html?$!, "/")
|
|
47
56
|
end
|
|
48
57
|
|
|
49
58
|
private
|
|
50
59
|
|
|
60
|
+
def compute_absolute_url(input)
|
|
61
|
+
input = input.url if input.respond_to?(:url)
|
|
62
|
+
return input if Addressable::URI.parse(input.to_s).absolute?
|
|
63
|
+
|
|
64
|
+
site = @context.registers[:site]
|
|
65
|
+
site_url = site.config["url"]
|
|
66
|
+
return relative_url(input) if site_url.nil? || site_url == ""
|
|
67
|
+
|
|
68
|
+
Addressable::URI.parse(
|
|
69
|
+
site_url.to_s + relative_url(input)
|
|
70
|
+
).normalize.to_s
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def compute_relative_url(input)
|
|
74
|
+
input = input.url if input.respond_to?(:url)
|
|
75
|
+
return input if Addressable::URI.parse(input.to_s).absolute?
|
|
76
|
+
|
|
77
|
+
parts = [sanitized_baseurl, input]
|
|
78
|
+
Addressable::URI.parse(
|
|
79
|
+
parts.map! { |part| ensure_leading_slash(part.to_s) }.join
|
|
80
|
+
).normalize.to_s
|
|
81
|
+
end
|
|
82
|
+
|
|
51
83
|
def sanitized_baseurl
|
|
52
84
|
site = @context.registers[:site]
|
|
53
|
-
site.config["baseurl"]
|
|
85
|
+
baseurl = site.config["baseurl"]
|
|
86
|
+
return "" if baseurl.nil?
|
|
87
|
+
|
|
88
|
+
baseurl.to_s.chomp("/")
|
|
54
89
|
end
|
|
55
90
|
|
|
56
91
|
def ensure_leading_slash(input)
|
|
57
92
|
return input if input.nil? || input.empty? || input.start_with?("/")
|
|
93
|
+
|
|
58
94
|
"/#{input}"
|
|
59
95
|
end
|
|
60
|
-
|
|
61
96
|
end
|
|
62
97
|
end
|
|
63
98
|
end
|
data/lib/jekyll/filters.rb
CHANGED
|
@@ -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.
|
|
@@ -121,8 +121,20 @@ module Jekyll
|
|
|
121
121
|
# input - The String on which to operate.
|
|
122
122
|
#
|
|
123
123
|
# Returns the Integer word count.
|
|
124
|
-
def number_of_words(input)
|
|
125
|
-
|
|
124
|
+
def number_of_words(input, mode = nil)
|
|
125
|
+
cjk_charset = '\p{Han}\p{Katakana}\p{Hiragana}\p{Hangul}'
|
|
126
|
+
cjk_regex = %r![#{cjk_charset}]!o
|
|
127
|
+
word_regex = %r![^#{cjk_charset}\s]+!o
|
|
128
|
+
|
|
129
|
+
case mode
|
|
130
|
+
when "cjk"
|
|
131
|
+
input.scan(cjk_regex).length + input.scan(word_regex).length
|
|
132
|
+
when "auto"
|
|
133
|
+
cjk_count = input.scan(cjk_regex).length
|
|
134
|
+
cjk_count.zero? ? input.split.length : cjk_count + input.scan(word_regex).length
|
|
135
|
+
else
|
|
136
|
+
input.split.length
|
|
137
|
+
end
|
|
126
138
|
end
|
|
127
139
|
|
|
128
140
|
# Join an array of things into a string by separating with commas and the
|
|
@@ -161,14 +173,17 @@ module Jekyll
|
|
|
161
173
|
|
|
162
174
|
# Filter an array of objects
|
|
163
175
|
#
|
|
164
|
-
# input
|
|
165
|
-
# property - property within each object to filter by
|
|
166
|
-
# value
|
|
176
|
+
# input - the object array.
|
|
177
|
+
# property - the property within each object to filter by.
|
|
178
|
+
# value - the desired value.
|
|
179
|
+
# Cannot be an instance of Array nor Hash since calling #to_s on them returns
|
|
180
|
+
# their `#inspect` string object.
|
|
167
181
|
#
|
|
168
182
|
# Returns the filtered array of objects
|
|
169
183
|
def where(input, property, value)
|
|
170
|
-
return input if property.
|
|
184
|
+
return input if !property || value.is_a?(Array) || value.is_a?(Hash)
|
|
171
185
|
return input unless input.respond_to?(:select)
|
|
186
|
+
|
|
172
187
|
input = input.values if input.is_a?(Hash)
|
|
173
188
|
input_id = input.hash
|
|
174
189
|
|
|
@@ -178,12 +193,10 @@ module Jekyll
|
|
|
178
193
|
@where_filter_cache[input_id] ||= {}
|
|
179
194
|
@where_filter_cache[input_id][property] ||= {}
|
|
180
195
|
|
|
181
|
-
# stash or
|
|
182
|
-
@where_filter_cache[input_id][property][value] ||=
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
end || []
|
|
186
|
-
end
|
|
196
|
+
# stash or retrieve results to return
|
|
197
|
+
@where_filter_cache[input_id][property][value] ||= input.select do |object|
|
|
198
|
+
compare_property_vs_target(item_property(object, property), value)
|
|
199
|
+
end.to_a
|
|
187
200
|
end
|
|
188
201
|
|
|
189
202
|
# Filters an array of objects against an expression
|
|
@@ -195,6 +208,7 @@ module Jekyll
|
|
|
195
208
|
# Returns the filtered array of objects
|
|
196
209
|
def where_exp(input, variable, expression)
|
|
197
210
|
return input unless input.respond_to?(:select)
|
|
211
|
+
|
|
198
212
|
input = input.values if input.is_a?(Hash) # FIXME
|
|
199
213
|
|
|
200
214
|
condition = parse_condition(expression)
|
|
@@ -206,6 +220,65 @@ module Jekyll
|
|
|
206
220
|
end || []
|
|
207
221
|
end
|
|
208
222
|
|
|
223
|
+
# Search an array of objects and returns the first object that has the queried attribute
|
|
224
|
+
# with the given value or returns nil otherwise.
|
|
225
|
+
#
|
|
226
|
+
# input - the object array.
|
|
227
|
+
# property - the property within each object to search by.
|
|
228
|
+
# value - the desired value.
|
|
229
|
+
# Cannot be an instance of Array nor Hash since calling #to_s on them returns
|
|
230
|
+
# their `#inspect` string object.
|
|
231
|
+
#
|
|
232
|
+
# Returns the found object or nil
|
|
233
|
+
#
|
|
234
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
235
|
+
def find(input, property, value)
|
|
236
|
+
return input if !property || value.is_a?(Array) || value.is_a?(Hash)
|
|
237
|
+
return input unless input.respond_to?(:find)
|
|
238
|
+
|
|
239
|
+
input = input.values if input.is_a?(Hash)
|
|
240
|
+
input_id = input.hash
|
|
241
|
+
|
|
242
|
+
# implement a hash based on method parameters to cache the end-result for given parameters.
|
|
243
|
+
@find_filter_cache ||= {}
|
|
244
|
+
@find_filter_cache[input_id] ||= {}
|
|
245
|
+
@find_filter_cache[input_id][property] ||= {}
|
|
246
|
+
|
|
247
|
+
# stash or retrieve results to return
|
|
248
|
+
# Since `enum.find` can return nil or false, we use a placeholder string "<__NO MATCH__>"
|
|
249
|
+
# to validate caching.
|
|
250
|
+
result = @find_filter_cache[input_id][property][value] ||= input.find do |object|
|
|
251
|
+
compare_property_vs_target(item_property(object, property), value)
|
|
252
|
+
end || "<__NO MATCH__>"
|
|
253
|
+
|
|
254
|
+
return nil if result == "<__NO MATCH__>"
|
|
255
|
+
|
|
256
|
+
result
|
|
257
|
+
end
|
|
258
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
259
|
+
|
|
260
|
+
# Searches an array of objects against an expression and returns the first object for which
|
|
261
|
+
# the expression evaluates to true, or returns nil otherwise.
|
|
262
|
+
#
|
|
263
|
+
# input - the object array
|
|
264
|
+
# variable - the variable to assign each item to in the expression
|
|
265
|
+
# expression - a Liquid comparison expression passed in as a string
|
|
266
|
+
#
|
|
267
|
+
# Returns the found object or nil
|
|
268
|
+
def find_exp(input, variable, expression)
|
|
269
|
+
return input unless input.respond_to?(:find)
|
|
270
|
+
|
|
271
|
+
input = input.values if input.is_a?(Hash)
|
|
272
|
+
|
|
273
|
+
condition = parse_condition(expression)
|
|
274
|
+
@context.stack do
|
|
275
|
+
input.find do |object|
|
|
276
|
+
@context[variable] = object
|
|
277
|
+
condition.evaluate(@context)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
209
282
|
# Convert the input into integer
|
|
210
283
|
#
|
|
211
284
|
# input - the object string
|
|
@@ -214,6 +287,7 @@ module Jekyll
|
|
|
214
287
|
def to_integer(input)
|
|
215
288
|
return 1 if input == true
|
|
216
289
|
return 0 if input == false
|
|
290
|
+
|
|
217
291
|
input.to_i
|
|
218
292
|
end
|
|
219
293
|
|
|
@@ -225,19 +299,19 @@ module Jekyll
|
|
|
225
299
|
#
|
|
226
300
|
# Returns the filtered array of objects
|
|
227
301
|
def sort(input, property = nil, nils = "first")
|
|
228
|
-
if input.nil?
|
|
229
|
-
|
|
230
|
-
end
|
|
302
|
+
raise ArgumentError, "Cannot sort a null object." if input.nil?
|
|
303
|
+
|
|
231
304
|
if property.nil?
|
|
232
305
|
input.sort
|
|
233
306
|
else
|
|
234
|
-
|
|
307
|
+
case nils
|
|
308
|
+
when "first"
|
|
235
309
|
order = - 1
|
|
236
|
-
|
|
310
|
+
when "last"
|
|
237
311
|
order = + 1
|
|
238
312
|
else
|
|
239
313
|
raise ArgumentError, "Invalid nils order: " \
|
|
240
|
-
|
|
314
|
+
"'#{nils}' is not a valid nils order. It must be 'first' or 'last'."
|
|
241
315
|
end
|
|
242
316
|
|
|
243
317
|
sort_input(input, property, order)
|
|
@@ -246,6 +320,7 @@ module Jekyll
|
|
|
246
320
|
|
|
247
321
|
def pop(array, num = 1)
|
|
248
322
|
return array unless array.is_a?(Array)
|
|
323
|
+
|
|
249
324
|
num = Liquid::Utils.to_integer(num)
|
|
250
325
|
new_ary = array.dup
|
|
251
326
|
new_ary.pop(num)
|
|
@@ -254,6 +329,7 @@ module Jekyll
|
|
|
254
329
|
|
|
255
330
|
def push(array, input)
|
|
256
331
|
return array unless array.is_a?(Array)
|
|
332
|
+
|
|
257
333
|
new_ary = array.dup
|
|
258
334
|
new_ary.push(input)
|
|
259
335
|
new_ary
|
|
@@ -261,6 +337,7 @@ module Jekyll
|
|
|
261
337
|
|
|
262
338
|
def shift(array, num = 1)
|
|
263
339
|
return array unless array.is_a?(Array)
|
|
340
|
+
|
|
264
341
|
num = Liquid::Utils.to_integer(num)
|
|
265
342
|
new_ary = array.dup
|
|
266
343
|
new_ary.shift(num)
|
|
@@ -269,6 +346,7 @@ module Jekyll
|
|
|
269
346
|
|
|
270
347
|
def unshift(array, input)
|
|
271
348
|
return array unless array.is_a?(Array)
|
|
349
|
+
|
|
272
350
|
new_ary = array.dup
|
|
273
351
|
new_ary.unshift(input)
|
|
274
352
|
new_ary
|
|
@@ -276,6 +354,7 @@ module Jekyll
|
|
|
276
354
|
|
|
277
355
|
def sample(input, num = 1)
|
|
278
356
|
return input unless input.respond_to?(:sample)
|
|
357
|
+
|
|
279
358
|
num = Liquid::Utils.to_integer(num) rescue 1
|
|
280
359
|
if num == 1
|
|
281
360
|
input.sample
|
|
@@ -301,40 +380,94 @@ module Jekyll
|
|
|
301
380
|
# We also utilize the Schwartzian transform to make this more efficient.
|
|
302
381
|
def sort_input(input, property, order)
|
|
303
382
|
input.map { |item| [item_property(item, property), item] }
|
|
304
|
-
.sort! do |
|
|
305
|
-
|
|
306
|
-
|
|
383
|
+
.sort! do |a_info, b_info|
|
|
384
|
+
a_property = a_info.first
|
|
385
|
+
b_property = b_info.first
|
|
307
386
|
|
|
308
|
-
if !
|
|
387
|
+
if !a_property.nil? && b_property.nil?
|
|
309
388
|
- order
|
|
310
|
-
elsif
|
|
389
|
+
elsif a_property.nil? && !b_property.nil?
|
|
311
390
|
+ order
|
|
312
391
|
else
|
|
313
|
-
|
|
392
|
+
a_property <=> b_property || a_property.to_s <=> b_property.to_s
|
|
314
393
|
end
|
|
315
394
|
end
|
|
316
395
|
.map!(&:last)
|
|
317
396
|
end
|
|
318
397
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
398
|
+
# `where` filter helper
|
|
399
|
+
#
|
|
400
|
+
def compare_property_vs_target(property, target)
|
|
401
|
+
case target
|
|
402
|
+
when NilClass
|
|
403
|
+
return true if property.nil?
|
|
404
|
+
when Liquid::Expression::MethodLiteral # `empty` or `blank`
|
|
405
|
+
target = target.to_s
|
|
406
|
+
return true if property == target || Array(property).join == target
|
|
327
407
|
else
|
|
328
|
-
|
|
408
|
+
target = target.to_s
|
|
409
|
+
if property.is_a? String
|
|
410
|
+
return true if property == target
|
|
411
|
+
else
|
|
412
|
+
Array(property).each do |prop|
|
|
413
|
+
return true if prop.to_s == target
|
|
414
|
+
end
|
|
415
|
+
end
|
|
329
416
|
end
|
|
417
|
+
|
|
418
|
+
false
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def item_property(item, property)
|
|
422
|
+
@item_property_cache ||= @context.registers[:site].filter_cache[:item_property] ||= {}
|
|
423
|
+
@item_property_cache[property] ||= {}
|
|
424
|
+
@item_property_cache[property][item] ||= begin
|
|
425
|
+
property = property.to_s
|
|
426
|
+
property = if item.respond_to?(:to_liquid)
|
|
427
|
+
read_liquid_attribute(item.to_liquid, property)
|
|
428
|
+
elsif item.respond_to?(:data)
|
|
429
|
+
item.data[property]
|
|
430
|
+
else
|
|
431
|
+
item[property]
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
parse_sort_input(property)
|
|
435
|
+
end
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
def read_liquid_attribute(liquid_data, property)
|
|
439
|
+
return liquid_data[property] unless property.include?(".")
|
|
440
|
+
|
|
441
|
+
property.split(".").reduce(liquid_data) do |data, key|
|
|
442
|
+
data.respond_to?(:[]) && data[key]
|
|
443
|
+
end
|
|
444
|
+
rescue TypeError => e
|
|
445
|
+
msg = if liquid_data.is_a?(Array)
|
|
446
|
+
"Error accessing object (#{liquid_data.to_s[0...20]}) with given key. Expected an " \
|
|
447
|
+
"integer but got #{property.inspect} instead."
|
|
448
|
+
else
|
|
449
|
+
e.message
|
|
450
|
+
end
|
|
451
|
+
raise e, msg
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
FLOAT_LIKE = %r!\A\s*-?(?:\d+\.?\d*|\.\d+)\s*\Z!.freeze
|
|
455
|
+
INTEGER_LIKE = %r!\A\s*-?\d+\s*\Z!.freeze
|
|
456
|
+
private_constant :FLOAT_LIKE, :INTEGER_LIKE
|
|
457
|
+
|
|
458
|
+
# return numeric values as numbers for proper sorting
|
|
459
|
+
def parse_sort_input(property)
|
|
460
|
+
stringified = property.to_s
|
|
461
|
+
return property.to_i if INTEGER_LIKE.match?(stringified)
|
|
462
|
+
return property.to_f if FLOAT_LIKE.match?(stringified)
|
|
463
|
+
|
|
464
|
+
property
|
|
330
465
|
end
|
|
331
466
|
|
|
332
|
-
private
|
|
333
467
|
def as_liquid(item)
|
|
334
468
|
case item
|
|
335
469
|
when Hash
|
|
336
|
-
|
|
337
|
-
Hash[pairs]
|
|
470
|
+
item.each_with_object({}) { |(k, v), result| result[as_liquid(k)] = as_liquid(v) }
|
|
338
471
|
when Array
|
|
339
472
|
item.map { |i| as_liquid(i) }
|
|
340
473
|
else
|
|
@@ -352,25 +485,53 @@ module Jekyll
|
|
|
352
485
|
end
|
|
353
486
|
end
|
|
354
487
|
|
|
488
|
+
# ----------- The following set of code was *adapted* from Liquid::If
|
|
489
|
+
# ----------- ref: https://github.com/Shopify/liquid/blob/ffb0ace30315bbcf3548a0383fab531452060ae8/lib/liquid/tags/if.rb#L84-L107
|
|
490
|
+
|
|
355
491
|
# Parse a string to a Liquid Condition
|
|
356
|
-
private
|
|
357
492
|
def parse_condition(exp)
|
|
358
|
-
parser
|
|
359
|
-
|
|
360
|
-
operator = parser.consume?(:comparison)
|
|
361
|
-
condition =
|
|
362
|
-
if operator
|
|
363
|
-
Liquid::Condition.new(Liquid::Expression.parse(left_expr),
|
|
364
|
-
operator,
|
|
365
|
-
Liquid::Expression.parse(parser.expression))
|
|
366
|
-
else
|
|
367
|
-
Liquid::Condition.new(Liquid::Expression.parse(left_expr))
|
|
368
|
-
end
|
|
369
|
-
parser.consume(:end_of_string)
|
|
493
|
+
parser = Liquid::Parser.new(exp)
|
|
494
|
+
condition = parse_binary_comparison(parser)
|
|
370
495
|
|
|
496
|
+
parser.consume(:end_of_string)
|
|
371
497
|
condition
|
|
372
498
|
end
|
|
373
499
|
|
|
500
|
+
# Generate a Liquid::Condition object from a Liquid::Parser object additionally processing
|
|
501
|
+
# the parsed expression based on whether the expression consists of binary operations with
|
|
502
|
+
# Liquid operators `and` or `or`
|
|
503
|
+
#
|
|
504
|
+
# - parser: an instance of Liquid::Parser
|
|
505
|
+
#
|
|
506
|
+
# Returns an instance of Liquid::Condition
|
|
507
|
+
def parse_binary_comparison(parser)
|
|
508
|
+
condition = parse_comparison(parser)
|
|
509
|
+
first_condition = condition
|
|
510
|
+
while (binary_operator = parser.id?("and") || parser.id?("or"))
|
|
511
|
+
child_condition = parse_comparison(parser)
|
|
512
|
+
condition.send(binary_operator, child_condition)
|
|
513
|
+
condition = child_condition
|
|
514
|
+
end
|
|
515
|
+
first_condition
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
# Generates a Liquid::Condition object from a Liquid::Parser object based on whether the parsed
|
|
519
|
+
# expression involves a "comparison" operator (e.g. <, ==, >, !=, etc)
|
|
520
|
+
#
|
|
521
|
+
# - parser: an instance of Liquid::Parser
|
|
522
|
+
#
|
|
523
|
+
# Returns an instance of Liquid::Condition
|
|
524
|
+
def parse_comparison(parser)
|
|
525
|
+
left_operand = Liquid::Expression.parse(parser.expression)
|
|
526
|
+
operator = parser.consume?(:comparison)
|
|
527
|
+
|
|
528
|
+
# No comparison-operator detected. Initialize a Liquid::Condition using only left operand
|
|
529
|
+
return Liquid::Condition.new(left_operand) unless operator
|
|
530
|
+
|
|
531
|
+
# Parse what remained after extracting the left operand and the `:comparison` operator
|
|
532
|
+
# and initialize a Liquid::Condition object using the operands and the comparison-operator
|
|
533
|
+
Liquid::Condition.new(left_operand, operator, Liquid::Expression.parse(parser.expression))
|
|
534
|
+
end
|
|
374
535
|
end
|
|
375
536
|
end
|
|
376
537
|
|
|
@@ -12,6 +12,10 @@ module Jekyll
|
|
|
12
12
|
@site = site
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
def reset
|
|
16
|
+
@glob_cache = {} if @glob_cache
|
|
17
|
+
end
|
|
18
|
+
|
|
15
19
|
def update_deprecated_types(set)
|
|
16
20
|
return set unless set.key?("scope") && set["scope"].key?("type")
|
|
17
21
|
|
|
@@ -36,6 +40,7 @@ module Jekyll
|
|
|
36
40
|
def ensure_time!(set)
|
|
37
41
|
return set unless set.key?("values") && set["values"].key?("date")
|
|
38
42
|
return set if set["values"]["date"].is_a?(Time)
|
|
43
|
+
|
|
39
44
|
set["values"]["date"] = Utils.parse_date(
|
|
40
45
|
set["values"]["date"],
|
|
41
46
|
"An invalid date format was found in a front-matter default set: #{set}"
|
|
@@ -92,48 +97,51 @@ module Jekyll
|
|
|
92
97
|
# path - the path to check for
|
|
93
98
|
# type - the type (:post, :page or :draft) to check for
|
|
94
99
|
#
|
|
95
|
-
# Returns true if the scope applies to the given
|
|
100
|
+
# Returns true if the scope applies to the given type and path
|
|
96
101
|
def applies?(scope, path, type)
|
|
97
|
-
|
|
102
|
+
applies_type?(scope, type) && applies_path?(scope, path)
|
|
98
103
|
end
|
|
99
104
|
|
|
100
|
-
# rubocop:disable Metrics/AbcSize
|
|
101
105
|
def applies_path?(scope, path)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if scope["path"].to_s.include?("*")
|
|
110
|
-
Dir.glob(abs_scope_path).each do |scope_path|
|
|
111
|
-
scope_path = Pathname.new(scope_path).relative_path_from(site_path)
|
|
112
|
-
scope_path = strip_collections_dir(scope_path)
|
|
113
|
-
Jekyll.logger.debug "Globbed Scope Path:", scope_path
|
|
114
|
-
return true if path_is_subpath?(sanitized_path, scope_path)
|
|
115
|
-
end
|
|
116
|
-
false
|
|
106
|
+
rel_scope_path = scope["path"]
|
|
107
|
+
return true if !rel_scope_path.is_a?(String) || rel_scope_path.empty?
|
|
108
|
+
|
|
109
|
+
sanitized_path = sanitize_path(path)
|
|
110
|
+
|
|
111
|
+
if rel_scope_path.include?("*")
|
|
112
|
+
glob_scope(sanitized_path, rel_scope_path)
|
|
117
113
|
else
|
|
118
114
|
path_is_subpath?(sanitized_path, strip_collections_dir(rel_scope_path))
|
|
119
115
|
end
|
|
120
116
|
end
|
|
121
|
-
# rubocop:enable Metrics/AbcSize
|
|
122
117
|
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return true
|
|
127
|
-
end
|
|
128
|
-
end
|
|
118
|
+
def glob_scope(sanitized_path, rel_scope_path)
|
|
119
|
+
site_source = Pathname.new(@site.source)
|
|
120
|
+
abs_scope_path = site_source.join(rel_scope_path).to_s
|
|
129
121
|
|
|
122
|
+
glob_cache(abs_scope_path).each do |scope_path|
|
|
123
|
+
scope_path = Pathname.new(scope_path).relative_path_from(site_source).to_s
|
|
124
|
+
scope_path = strip_collections_dir(scope_path)
|
|
125
|
+
Jekyll.logger.debug "Globbed Scope Path:", scope_path
|
|
126
|
+
return true if path_is_subpath?(sanitized_path, scope_path)
|
|
127
|
+
end
|
|
130
128
|
false
|
|
131
129
|
end
|
|
132
130
|
|
|
131
|
+
def glob_cache(path)
|
|
132
|
+
@glob_cache ||= {}
|
|
133
|
+
@glob_cache[path] ||= Dir.glob(path)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def path_is_subpath?(path, parent_path)
|
|
137
|
+
path.start_with?(parent_path)
|
|
138
|
+
end
|
|
139
|
+
|
|
133
140
|
def strip_collections_dir(path)
|
|
134
141
|
collections_dir = @site.config["collections_dir"]
|
|
135
|
-
slashed_coll_dir = "#{collections_dir}/"
|
|
142
|
+
slashed_coll_dir = collections_dir.empty? ? "/" : "#{collections_dir}/"
|
|
136
143
|
return path if collections_dir.empty? || !path.to_s.start_with?(slashed_coll_dir)
|
|
144
|
+
|
|
137
145
|
path.sub(slashed_coll_dir, "")
|
|
138
146
|
end
|
|
139
147
|
|
|
@@ -149,7 +157,7 @@ module Jekyll
|
|
|
149
157
|
# Returns true if either of the above conditions are satisfied,
|
|
150
158
|
# otherwise returns false
|
|
151
159
|
def applies_type?(scope, type)
|
|
152
|
-
!scope.key?("type") || scope["type"].
|
|
160
|
+
!scope.key?("type") || type&.to_sym.eql?(scope["type"].to_sym)
|
|
153
161
|
end
|
|
154
162
|
|
|
155
163
|
# Checks if a given set of default values is valid
|
|
@@ -167,7 +175,7 @@ module Jekyll
|
|
|
167
175
|
# new_scope - the new scope hash
|
|
168
176
|
#
|
|
169
177
|
# Returns true if the new scope has precedence over the older
|
|
170
|
-
# rubocop: disable PredicateName
|
|
178
|
+
# rubocop: disable Naming/PredicateName
|
|
171
179
|
def has_precedence?(old_scope, new_scope)
|
|
172
180
|
return true if old_scope.nil?
|
|
173
181
|
|
|
@@ -182,13 +190,15 @@ module Jekyll
|
|
|
182
190
|
!old_scope.key? "type"
|
|
183
191
|
end
|
|
184
192
|
end
|
|
185
|
-
# rubocop: enable PredicateName
|
|
193
|
+
# rubocop: enable Naming/PredicateName
|
|
186
194
|
|
|
187
195
|
# Collects a list of sets that match the given path and type
|
|
188
196
|
#
|
|
189
197
|
# Returns an array of hashes
|
|
190
198
|
def matching_sets(path, type)
|
|
191
|
-
|
|
199
|
+
@matched_set_cache ||= {}
|
|
200
|
+
@matched_set_cache[path] ||= {}
|
|
201
|
+
@matched_set_cache[path][type] ||= valid_sets.select do |set|
|
|
192
202
|
!set.key?("scope") || applies?(set["scope"], path, type)
|
|
193
203
|
end
|
|
194
204
|
end
|
|
@@ -211,18 +221,17 @@ module Jekyll
|
|
|
211
221
|
Jekyll.logger.warn set.to_s
|
|
212
222
|
nil
|
|
213
223
|
end
|
|
214
|
-
end.compact
|
|
224
|
+
end.tap(&:compact!)
|
|
215
225
|
end
|
|
216
226
|
|
|
217
|
-
# Sanitizes the given path by removing a leading
|
|
218
|
-
|
|
219
|
-
SANITIZATION_REGEX = %r!\A/|(?<=[^/])\z!
|
|
220
|
-
|
|
227
|
+
# Sanitizes the given path by removing a leading slash
|
|
221
228
|
def sanitize_path(path)
|
|
222
229
|
if path.nil? || path.empty?
|
|
223
230
|
""
|
|
231
|
+
elsif path.start_with?("/")
|
|
232
|
+
path.gsub(%r!\A/|(?<=[^/])\z!, "")
|
|
224
233
|
else
|
|
225
|
-
path
|
|
234
|
+
path
|
|
226
235
|
end
|
|
227
236
|
end
|
|
228
237
|
end
|