jekyll 4.0.0.pre.alpha1 → 4.1.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 +58 -17
- data/lib/jekyll.rb +5 -1
- data/lib/jekyll/cache.rb +71 -64
- data/lib/jekyll/cleaner.rb +5 -5
- data/lib/jekyll/collection.rb +6 -4
- data/lib/jekyll/command.rb +4 -2
- data/lib/jekyll/commands/new.rb +4 -4
- data/lib/jekyll/commands/serve.rb +9 -1
- data/lib/jekyll/commands/serve/servlet.rb +13 -14
- data/lib/jekyll/commands/serve/websockets.rb +1 -1
- data/lib/jekyll/configuration.rb +40 -129
- data/lib/jekyll/converters/identity.rb +2 -2
- data/lib/jekyll/converters/markdown/kramdown_parser.rb +70 -9
- data/lib/jekyll/convertible.rb +21 -20
- data/lib/jekyll/document.rb +56 -31
- data/lib/jekyll/drops/document_drop.rb +12 -0
- data/lib/jekyll/drops/drop.rb +14 -8
- data/lib/jekyll/drops/site_drop.rb +11 -1
- data/lib/jekyll/drops/url_drop.rb +52 -1
- data/lib/jekyll/entry_filter.rb +38 -44
- data/lib/jekyll/excerpt.rb +3 -3
- data/lib/jekyll/filters.rb +140 -22
- data/lib/jekyll/filters/url_filters.rb +41 -14
- data/lib/jekyll/frontmatter_defaults.rb +15 -20
- data/lib/jekyll/hooks.rb +2 -5
- data/lib/jekyll/inclusion.rb +32 -0
- data/lib/jekyll/liquid_renderer.rb +18 -15
- data/lib/jekyll/liquid_renderer/file.rb +10 -0
- data/lib/jekyll/liquid_renderer/table.rb +18 -61
- data/lib/jekyll/mime.types +53 -11
- data/lib/jekyll/page.rb +26 -2
- data/lib/jekyll/page_excerpt.rb +25 -0
- data/lib/jekyll/path_manager.rb +31 -0
- data/lib/jekyll/profiler.rb +58 -0
- data/lib/jekyll/reader.rb +4 -1
- data/lib/jekyll/readers/collection_reader.rb +1 -0
- data/lib/jekyll/readers/data_reader.rb +1 -0
- data/lib/jekyll/readers/layout_reader.rb +1 -0
- data/lib/jekyll/readers/page_reader.rb +5 -5
- data/lib/jekyll/readers/post_reader.rb +2 -1
- data/lib/jekyll/readers/static_file_reader.rb +3 -3
- data/lib/jekyll/readers/theme_assets_reader.rb +1 -0
- data/lib/jekyll/renderer.rb +9 -15
- data/lib/jekyll/site.rb +21 -12
- data/lib/jekyll/static_file.rb +15 -10
- data/lib/jekyll/tags/highlight.rb +2 -4
- data/lib/jekyll/tags/include.rb +67 -11
- data/lib/jekyll/tags/post_url.rb +8 -5
- data/lib/jekyll/theme.rb +19 -10
- data/lib/jekyll/url.rb +7 -3
- data/lib/jekyll/utils.rb +14 -18
- data/lib/jekyll/utils/platforms.rb +1 -1
- data/lib/jekyll/utils/win_tz.rb +1 -1
- data/lib/jekyll/version.rb +1 -1
- data/lib/theme_template/theme.gemspec.erb +1 -4
- metadata +33 -36
data/lib/jekyll/drops/drop.rb
CHANGED
@@ -30,7 +30,6 @@ module Jekyll
|
|
30
30
|
# Returns nothing
|
31
31
|
def initialize(obj)
|
32
32
|
@obj = obj
|
33
|
-
@mutations = {} # only if mutable: true
|
34
33
|
end
|
35
34
|
|
36
35
|
# Access a method in the Drop or a field in the underlying hash data.
|
@@ -42,8 +41,8 @@ module Jekyll
|
|
42
41
|
#
|
43
42
|
# Returns the value for the given key, or nil if none exists
|
44
43
|
def [](key)
|
45
|
-
if self.class.mutable? &&
|
46
|
-
|
44
|
+
if self.class.mutable? && mutations.key?(key)
|
45
|
+
mutations[key]
|
47
46
|
elsif self.class.invokable? key
|
48
47
|
public_send key
|
49
48
|
else
|
@@ -66,11 +65,12 @@ module Jekyll
|
|
66
65
|
# and the key matches a method in which case it raises a
|
67
66
|
# DropMutationException.
|
68
67
|
def []=(key, val)
|
69
|
-
|
70
|
-
|
68
|
+
setter = "#{key}="
|
69
|
+
if respond_to?(setter)
|
70
|
+
public_send(setter, val)
|
71
71
|
elsif respond_to?(key.to_s)
|
72
72
|
if self.class.mutable?
|
73
|
-
|
73
|
+
mutations[key] = val
|
74
74
|
else
|
75
75
|
raise Errors::DropMutationException, "Key #{key} cannot be set in the drop."
|
76
76
|
end
|
@@ -100,7 +100,7 @@ module Jekyll
|
|
100
100
|
# Returns true if the given key is present
|
101
101
|
def key?(key)
|
102
102
|
return false if key.nil?
|
103
|
-
return true if self.class.mutable? &&
|
103
|
+
return true if self.class.mutable? && mutations.key?(key)
|
104
104
|
|
105
105
|
respond_to?(key) || fallback_data.key?(key)
|
106
106
|
end
|
@@ -113,7 +113,7 @@ module Jekyll
|
|
113
113
|
# Returns an Array of unique keys for content for the Drop.
|
114
114
|
def keys
|
115
115
|
(content_methods |
|
116
|
-
|
116
|
+
mutations.keys |
|
117
117
|
fallback_data.keys).flatten
|
118
118
|
end
|
119
119
|
|
@@ -204,6 +204,12 @@ module Jekyll
|
|
204
204
|
return yield(key) unless block.nil?
|
205
205
|
return default unless default.nil?
|
206
206
|
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
def mutations
|
211
|
+
@mutations ||= {}
|
212
|
+
end
|
207
213
|
end
|
208
214
|
end
|
209
215
|
end
|
@@ -8,7 +8,7 @@ module Jekyll
|
|
8
8
|
mutable false
|
9
9
|
|
10
10
|
def_delegator :@obj, :site_data, :data
|
11
|
-
def_delegators :@obj, :time, :pages, :static_files, :
|
11
|
+
def_delegators :@obj, :time, :pages, :static_files, :tags, :categories
|
12
12
|
|
13
13
|
private def_delegator :@obj, :config, :fallback_data
|
14
14
|
|
@@ -38,6 +38,16 @@ module Jekyll
|
|
38
38
|
@site_collections ||= @obj.collections.values.sort_by(&:label).map(&:to_liquid)
|
39
39
|
end
|
40
40
|
|
41
|
+
# `Site#documents` cannot be memoized so that `Site#docs_to_write` can access the
|
42
|
+
# latest state of the attribute.
|
43
|
+
#
|
44
|
+
# Since this method will be called after `Site#pre_render` hook, the `Site#documents`
|
45
|
+
# array shouldn't thereafter change and can therefore be safely memoized to prevent
|
46
|
+
# additional computation of `Site#documents`.
|
47
|
+
def documents
|
48
|
+
@documents ||= @obj.documents
|
49
|
+
end
|
50
|
+
|
41
51
|
# `{{ site.related_posts }}` is how posts can get posts related to
|
42
52
|
# them, either through LSI if it's enabled, or through the most
|
43
53
|
# recent posts.
|
@@ -35,46 +35,97 @@ module Jekyll
|
|
35
35
|
category_set.to_a.join("/")
|
36
36
|
end
|
37
37
|
|
38
|
+
# Similar to output from #categories, but each category will be downcased and
|
39
|
+
# all non-alphanumeric characters of the category replaced with a hyphen.
|
40
|
+
def slugified_categories
|
41
|
+
Array(@obj.data["categories"]).each_with_object(Set.new) do |category, set|
|
42
|
+
set << Utils.slugify(category.to_s)
|
43
|
+
end.to_a.join("/")
|
44
|
+
end
|
45
|
+
|
46
|
+
# CCYY
|
38
47
|
def year
|
39
48
|
@obj.date.strftime("%Y")
|
40
49
|
end
|
41
50
|
|
51
|
+
# MM: 01..12
|
42
52
|
def month
|
43
53
|
@obj.date.strftime("%m")
|
44
54
|
end
|
45
55
|
|
56
|
+
# DD: 01..31
|
46
57
|
def day
|
47
58
|
@obj.date.strftime("%d")
|
48
59
|
end
|
49
60
|
|
61
|
+
# hh: 00..23
|
50
62
|
def hour
|
51
63
|
@obj.date.strftime("%H")
|
52
64
|
end
|
53
65
|
|
66
|
+
# mm: 00..59
|
54
67
|
def minute
|
55
68
|
@obj.date.strftime("%M")
|
56
69
|
end
|
57
70
|
|
71
|
+
# ss: 00..59
|
58
72
|
def second
|
59
73
|
@obj.date.strftime("%S")
|
60
74
|
end
|
61
75
|
|
76
|
+
# D: 1..31
|
62
77
|
def i_day
|
63
78
|
@obj.date.strftime("%-d")
|
64
79
|
end
|
65
80
|
|
81
|
+
# M: 1..12
|
66
82
|
def i_month
|
67
83
|
@obj.date.strftime("%-m")
|
68
84
|
end
|
69
85
|
|
86
|
+
# MMM: Jan..Dec
|
70
87
|
def short_month
|
71
88
|
@obj.date.strftime("%b")
|
72
89
|
end
|
73
90
|
|
91
|
+
# MMMM: January..December
|
92
|
+
def long_month
|
93
|
+
@obj.date.strftime("%B")
|
94
|
+
end
|
95
|
+
|
96
|
+
# YY: 00..99
|
74
97
|
def short_year
|
75
98
|
@obj.date.strftime("%y")
|
76
99
|
end
|
77
100
|
|
101
|
+
# CCYYw, ISO week year
|
102
|
+
# may differ from CCYY for the first days of January and last days of December
|
103
|
+
def w_year
|
104
|
+
@obj.date.strftime("%G")
|
105
|
+
end
|
106
|
+
|
107
|
+
# WW: 01..53
|
108
|
+
# %W and %U do not comply with ISO 8601-1
|
109
|
+
def week
|
110
|
+
@obj.date.strftime("%V")
|
111
|
+
end
|
112
|
+
|
113
|
+
# d: 1..7 (Monday..Sunday)
|
114
|
+
def w_day
|
115
|
+
@obj.date.strftime("%u")
|
116
|
+
end
|
117
|
+
|
118
|
+
# dd: Mon..Sun
|
119
|
+
def short_day
|
120
|
+
@obj.date.strftime("%a")
|
121
|
+
end
|
122
|
+
|
123
|
+
# ddd: Monday..Sunday
|
124
|
+
def long_day
|
125
|
+
@obj.date.strftime("%A")
|
126
|
+
end
|
127
|
+
|
128
|
+
# DDD: 001..366
|
78
129
|
def y_day
|
79
130
|
@obj.date.strftime("%j")
|
80
131
|
end
|
@@ -82,7 +133,7 @@ module Jekyll
|
|
82
133
|
private
|
83
134
|
|
84
135
|
def fallback_data
|
85
|
-
{}
|
136
|
+
@fallback_data ||= {}
|
86
137
|
end
|
87
138
|
end
|
88
139
|
end
|
data/lib/jekyll/entry_filter.rb
CHANGED
@@ -3,9 +3,8 @@
|
|
3
3
|
module Jekyll
|
4
4
|
class EntryFilter
|
5
5
|
attr_reader :site
|
6
|
-
|
7
|
-
|
8
|
-
].freeze
|
6
|
+
|
7
|
+
SPECIAL_LEADING_CHAR_REGEX = %r!\A#{Regexp.union([".", "_", "#", "~"])}!o.freeze
|
9
8
|
|
10
9
|
def initialize(site, base_directory = nil)
|
11
10
|
@site = site
|
@@ -31,13 +30,24 @@ module Jekyll
|
|
31
30
|
|
32
31
|
def filter(entries)
|
33
32
|
entries.reject do |e|
|
34
|
-
# Reject this entry if it is a
|
33
|
+
# Reject this entry if it is just a "dot" representation.
|
34
|
+
# e.g.: '.', '..', '_movies/.', 'music/..', etc
|
35
|
+
next true if e.end_with?(".")
|
36
|
+
|
37
|
+
# Check if the current entry is explicitly included and cache the result
|
38
|
+
included = included?(e)
|
39
|
+
|
40
|
+
# Reject current entry if it is excluded but not explicitly included as well.
|
41
|
+
next true if excluded?(e) && !included
|
42
|
+
|
43
|
+
# Reject current entry if it is a symlink.
|
35
44
|
next true if symlink?(e)
|
36
|
-
# Do not reject this entry if it is included.
|
37
|
-
next false if included?(e)
|
38
45
|
|
39
|
-
#
|
40
|
-
|
46
|
+
# Do not reject current entry if it is explicitly included.
|
47
|
+
next false if included
|
48
|
+
|
49
|
+
# Reject current entry if it is special or a backup file.
|
50
|
+
special?(e) || backup?(e)
|
41
51
|
end
|
42
52
|
end
|
43
53
|
|
@@ -47,12 +57,12 @@ module Jekyll
|
|
47
57
|
end
|
48
58
|
|
49
59
|
def special?(entry)
|
50
|
-
|
51
|
-
|
60
|
+
SPECIAL_LEADING_CHAR_REGEX.match?(entry) ||
|
61
|
+
SPECIAL_LEADING_CHAR_REGEX.match?(File.basename(entry))
|
52
62
|
end
|
53
63
|
|
54
64
|
def backup?(entry)
|
55
|
-
entry
|
65
|
+
entry.end_with?("~")
|
56
66
|
end
|
57
67
|
|
58
68
|
def excluded?(entry)
|
@@ -86,40 +96,24 @@ module Jekyll
|
|
86
96
|
)
|
87
97
|
end
|
88
98
|
|
89
|
-
#
|
90
|
-
#
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
99
|
+
# Check if an entry matches a specific pattern.
|
100
|
+
# Returns true if path matches against any glob pattern, else false.
|
101
|
+
def glob_include?(enumerator, entry)
|
102
|
+
entry_with_source = PathManager.join(site.source, entry)
|
103
|
+
entry_is_directory = File.directory?(entry_with_source)
|
104
|
+
|
105
|
+
enumerator.any? do |pattern|
|
106
|
+
case pattern
|
107
|
+
when String
|
108
|
+
pattern_with_source = PathManager.join(site.source, pattern)
|
109
|
+
|
110
|
+
File.fnmatch?(pattern_with_source, entry_with_source) ||
|
111
|
+
entry_with_source.start_with?(pattern_with_source) ||
|
112
|
+
(pattern_with_source == "#{entry_with_source}/" if entry_is_directory)
|
113
|
+
when Regexp
|
114
|
+
pattern.match?(entry_with_source)
|
104
115
|
else
|
105
|
-
|
106
|
-
|
107
|
-
# If it's a directory they want to exclude, AKA
|
108
|
-
# ends with a "/" then we will go on to check and
|
109
|
-
# see if the entry falls within that path and
|
110
|
-
# exclude it if that's the case.
|
111
|
-
|
112
|
-
if entry.end_with?("/")
|
113
|
-
entry_path.in_path?(
|
114
|
-
item
|
115
|
-
)
|
116
|
-
|
117
|
-
else
|
118
|
-
File.fnmatch?(item, entry_path) ||
|
119
|
-
entry_path.to_path.start_with?(
|
120
|
-
item
|
121
|
-
)
|
122
|
-
end
|
116
|
+
false
|
123
117
|
end
|
124
118
|
end
|
125
119
|
end
|
data/lib/jekyll/excerpt.rb
CHANGED
@@ -10,7 +10,7 @@ module Jekyll
|
|
10
10
|
|
11
11
|
def_delegators :@doc,
|
12
12
|
:site, :name, :ext, :extname,
|
13
|
-
:collection, :related_posts,
|
13
|
+
:collection, :related_posts, :type,
|
14
14
|
:coffeescript_file?, :yaml_file?,
|
15
15
|
:url, :next_doc, :previous_doc
|
16
16
|
|
@@ -56,7 +56,7 @@ module Jekyll
|
|
56
56
|
#
|
57
57
|
# Returns true if the string passed in
|
58
58
|
def include?(something)
|
59
|
-
|
59
|
+
output&.include?(something) || content.include?(something)
|
60
60
|
end
|
61
61
|
|
62
62
|
# The UID for this doc (useful in feeds).
|
@@ -155,7 +155,7 @@ module Jekyll
|
|
155
155
|
tag_names.flatten!
|
156
156
|
tag_names.reverse_each do |tag_name|
|
157
157
|
next unless liquid_block?(tag_name)
|
158
|
-
next if
|
158
|
+
next if endtag_regex_stash(tag_name).match?(head)
|
159
159
|
|
160
160
|
modified = true
|
161
161
|
head << "\n{% end#{tag_name} %}"
|
data/lib/jekyll/filters.rb
CHANGED
@@ -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,13 +173,15 @@ 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)
|
172
186
|
|
173
187
|
input = input.values if input.is_a?(Hash)
|
@@ -182,8 +196,8 @@ module Jekyll
|
|
182
196
|
# stash or retrive results to return
|
183
197
|
@where_filter_cache[input_id][property][value] ||= begin
|
184
198
|
input.select do |object|
|
185
|
-
|
186
|
-
end
|
199
|
+
compare_property_vs_target(item_property(object, property), value)
|
200
|
+
end.to_a
|
187
201
|
end
|
188
202
|
end
|
189
203
|
|
@@ -208,6 +222,66 @@ module Jekyll
|
|
208
222
|
end || []
|
209
223
|
end
|
210
224
|
|
225
|
+
# Search an array of objects and returns the first object that has the queried attribute
|
226
|
+
# with the given value or returns nil otherwise.
|
227
|
+
#
|
228
|
+
# input - the object array.
|
229
|
+
# property - the property within each object to search by.
|
230
|
+
# value - the desired value.
|
231
|
+
# Cannot be an instance of Array nor Hash since calling #to_s on them returns
|
232
|
+
# their `#inspect` string object.
|
233
|
+
#
|
234
|
+
# Returns the found object or nil
|
235
|
+
#
|
236
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
237
|
+
def find(input, property, value)
|
238
|
+
return input if !property || value.is_a?(Array) || value.is_a?(Hash)
|
239
|
+
return input unless input.respond_to?(:find)
|
240
|
+
|
241
|
+
input = input.values if input.is_a?(Hash)
|
242
|
+
input_id = input.hash
|
243
|
+
|
244
|
+
# implement a hash based on method parameters to cache the end-result for given parameters.
|
245
|
+
@find_filter_cache ||= {}
|
246
|
+
@find_filter_cache[input_id] ||= {}
|
247
|
+
@find_filter_cache[input_id][property] ||= {}
|
248
|
+
|
249
|
+
# stash or retrive results to return
|
250
|
+
# Since `enum.find` can return nil or false, we use a placeholder string "<__NO MATCH__>"
|
251
|
+
# to validate caching.
|
252
|
+
result = @find_filter_cache[input_id][property][value] ||= begin
|
253
|
+
input.find do |object|
|
254
|
+
compare_property_vs_target(item_property(object, property), value)
|
255
|
+
end || "<__NO MATCH__>"
|
256
|
+
end
|
257
|
+
return nil if result == "<__NO MATCH__>"
|
258
|
+
|
259
|
+
result
|
260
|
+
end
|
261
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
262
|
+
|
263
|
+
# Searches an array of objects against an expression and returns the first object for which
|
264
|
+
# the expression evaluates to true, or returns nil otherwise.
|
265
|
+
#
|
266
|
+
# input - the object array
|
267
|
+
# variable - the variable to assign each item to in the expression
|
268
|
+
# expression - a Liquid comparison expression passed in as a string
|
269
|
+
#
|
270
|
+
# Returns the found object or nil
|
271
|
+
def find_exp(input, variable, expression)
|
272
|
+
return input unless input.respond_to?(:find)
|
273
|
+
|
274
|
+
input = input.values if input.is_a?(Hash)
|
275
|
+
|
276
|
+
condition = parse_condition(expression)
|
277
|
+
@context.stack do
|
278
|
+
input.find do |object|
|
279
|
+
@context[variable] = object
|
280
|
+
condition.evaluate(@context)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
211
285
|
# Convert the input into integer
|
212
286
|
#
|
213
287
|
# input - the object string
|
@@ -323,26 +397,66 @@ module Jekyll
|
|
323
397
|
.map!(&:last)
|
324
398
|
end
|
325
399
|
|
400
|
+
# `where` filter helper
|
401
|
+
#
|
402
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
403
|
+
def compare_property_vs_target(property, target)
|
404
|
+
case target
|
405
|
+
when NilClass
|
406
|
+
return true if property.nil?
|
407
|
+
when Liquid::Expression::MethodLiteral # `empty` or `blank`
|
408
|
+
target = target.to_s
|
409
|
+
return true if property == target || Array(property).join == target
|
410
|
+
else
|
411
|
+
target = target.to_s
|
412
|
+
if property.is_a? String
|
413
|
+
return true if property == target
|
414
|
+
else
|
415
|
+
Array(property).each do |prop|
|
416
|
+
return true if prop.to_s == target
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
false
|
422
|
+
end
|
423
|
+
|
424
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
425
|
+
|
326
426
|
def item_property(item, property)
|
327
427
|
@item_property_cache ||= {}
|
328
428
|
@item_property_cache[property] ||= {}
|
329
429
|
@item_property_cache[property][item] ||= begin
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
430
|
+
property = property.to_s
|
431
|
+
property = if item.respond_to?(:to_liquid)
|
432
|
+
read_liquid_attribute(item.to_liquid, property)
|
433
|
+
elsif item.respond_to?(:data)
|
434
|
+
item.data[property]
|
435
|
+
else
|
436
|
+
item[property]
|
437
|
+
end
|
438
|
+
|
439
|
+
parse_sort_input(property)
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
def read_liquid_attribute(liquid_data, property)
|
444
|
+
return liquid_data[property] unless property.include?(".")
|
445
|
+
|
446
|
+
property.split(".").reduce(liquid_data) do |data, key|
|
447
|
+
data.respond_to?(:[]) && data[key]
|
339
448
|
end
|
340
449
|
end
|
341
450
|
|
451
|
+
FLOAT_LIKE = %r!\A\s*-?(?:\d+\.?\d*|\.\d+)\s*\Z!.freeze
|
452
|
+
INTEGER_LIKE = %r!\A\s*-?\d+\s*\Z!.freeze
|
453
|
+
private_constant :FLOAT_LIKE, :INTEGER_LIKE
|
454
|
+
|
342
455
|
# return numeric values as numbers for proper sorting
|
343
456
|
def parse_sort_input(property)
|
344
|
-
|
345
|
-
return property.
|
457
|
+
stringified = property.to_s
|
458
|
+
return property.to_i if INTEGER_LIKE.match?(stringified)
|
459
|
+
return property.to_f if FLOAT_LIKE.match?(stringified)
|
346
460
|
|
347
461
|
property
|
348
462
|
end
|
@@ -389,10 +503,14 @@ module Jekyll
|
|
389
503
|
#
|
390
504
|
# Returns an instance of Liquid::Condition
|
391
505
|
def parse_binary_comparison(parser)
|
392
|
-
parse_comparison(parser)
|
393
|
-
|
394
|
-
|
506
|
+
condition = parse_comparison(parser)
|
507
|
+
first_condition = condition
|
508
|
+
while (binary_operator = parser.id?("and") || parser.id?("or"))
|
509
|
+
child_condition = parse_comparison(parser)
|
510
|
+
condition.send(binary_operator, child_condition)
|
511
|
+
condition = child_condition
|
395
512
|
end
|
513
|
+
first_condition
|
396
514
|
end
|
397
515
|
|
398
516
|
# Generates a Liquid::Condition object from a Liquid::Parser object based on whether the parsed
|