liquidoc 0.12.0.pre.rc5 → 0.12.0.pre.rc6
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/lib/liquid/filters/date_filters.rb +107 -0
- data/lib/liquid/filters/grouping_filters.rb +65 -0
- data/lib/liquid/filters/jekyll.rb +488 -0
- data/lib/liquid/filters/url_filters.rb +63 -0
- data/lib/liquid/tags/highlight.rb +131 -0
- data/lib/liquid/tags/jekyll.rb +219 -0
- data/lib/liquid/tags/link.rb +37 -0
- data/lib/liquid/tags/post_url.rb +103 -0
- data/lib/liquidoc/version.rb +1 -1
- data/lib/liquidoc.rb +378 -101
- metadata +38 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d01d4d6d0f2b4c6fb1c6cd9bbfff037d33db88674076835dfaa343df194d940
|
4
|
+
data.tar.gz: f3b7943d004fce1442644844bfb99ab7e94a54574ba4d6b4a4644ba1f14e14f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80e1b66595ecdf7b91b99759e355cb2053769c4ffa5f61d7380894dafa3e64009b8266299d8b8f84dbace1382423c28a7f617e8d1f2bb269c1d4084c7cf1d6dc
|
7
|
+
data.tar.gz: 5b90303b75f33fc389c8328a268b53168bc7e2f5afd4dbb1b8c339db468eaed73e0a8c7eda8900addb6d2429dffd1916596926d7bd2d6b353922982b18c1b523
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
module Filters
|
5
|
+
module DateFilters
|
6
|
+
# Format a date in short format e.g. "27 Jan 2011".
|
7
|
+
# Ordinal format is also supported, in both the UK
|
8
|
+
# (e.g. "27th Jan 2011") and US ("e.g. Jan 27th, 2011") formats.
|
9
|
+
# UK format is the default.
|
10
|
+
#
|
11
|
+
# date - the Time to format.
|
12
|
+
# type - if "ordinal" the returned String will be in ordinal format
|
13
|
+
# style - if "US" the returned String will be in US format.
|
14
|
+
# Otherwise it will be in UK format.
|
15
|
+
#
|
16
|
+
# Returns the formatting String.
|
17
|
+
def date_to_string(date, type = nil, style = nil)
|
18
|
+
stringify_date(date, "%b", type, style)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Format a date in long format e.g. "27 January 2011".
|
22
|
+
# Ordinal format is also supported, in both the UK
|
23
|
+
# (e.g. "27th January 2011") and US ("e.g. January 27th, 2011") formats.
|
24
|
+
# UK format is the default.
|
25
|
+
#
|
26
|
+
# date - the Time to format.
|
27
|
+
# type - if "ordinal" the returned String will be in ordinal format
|
28
|
+
# style - if "US" the returned String will be in US format.
|
29
|
+
# Otherwise it will be in UK format.
|
30
|
+
#
|
31
|
+
# Returns the formatted String.
|
32
|
+
def date_to_long_string(date, type = nil, style = nil)
|
33
|
+
stringify_date(date, "%B", type, style)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Format a date for use in XML.
|
37
|
+
#
|
38
|
+
# date - The Time to format.
|
39
|
+
#
|
40
|
+
# Examples
|
41
|
+
#
|
42
|
+
# date_to_xmlschema(Time.now)
|
43
|
+
# # => "2011-04-24T20:34:46+08:00"
|
44
|
+
#
|
45
|
+
# Returns the formatted String.
|
46
|
+
def date_to_xmlschema(date)
|
47
|
+
return date if date.to_s.empty?
|
48
|
+
time(date).xmlschema
|
49
|
+
end
|
50
|
+
|
51
|
+
# Format a date according to RFC-822
|
52
|
+
#
|
53
|
+
# date - The Time to format.
|
54
|
+
#
|
55
|
+
# Examples
|
56
|
+
#
|
57
|
+
# date_to_rfc822(Time.now)
|
58
|
+
# # => "Sun, 24 Apr 2011 12:34:46 +0000"
|
59
|
+
#
|
60
|
+
# Returns the formatted String.
|
61
|
+
def date_to_rfc822(date)
|
62
|
+
return date if date.to_s.empty?
|
63
|
+
time(date).rfc822
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
# month_type: Notations that evaluate to 'Month' via `Time#strftime` ("%b", "%B")
|
68
|
+
# type: nil (default) or "ordinal"
|
69
|
+
# style: nil (default) or "US"
|
70
|
+
#
|
71
|
+
# Returns a stringified date or the empty input.
|
72
|
+
def stringify_date(date, month_type, type = nil, style = nil)
|
73
|
+
return date if date.to_s.empty?
|
74
|
+
time = time(date)
|
75
|
+
if type == "ordinal"
|
76
|
+
day = time.day
|
77
|
+
ordinal_day = "#{day}#{ordinal(day)}"
|
78
|
+
return time.strftime("#{month_type} #{ordinal_day}, %Y") if style == "US"
|
79
|
+
return time.strftime("#{ordinal_day} #{month_type} %Y")
|
80
|
+
end
|
81
|
+
time.strftime("%d #{month_type} %Y")
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
def ordinal(number)
|
86
|
+
return "th" if (11..13).cover?(number)
|
87
|
+
|
88
|
+
case number % 10
|
89
|
+
when 1 then "st"
|
90
|
+
when 2 then "nd"
|
91
|
+
when 3 then "rd"
|
92
|
+
else "th"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
def time(input)
|
98
|
+
date = Liquid::Utils.to_date(input)
|
99
|
+
unless date.respond_to?(:to_time)
|
100
|
+
raise Errors::InvalidDateError,
|
101
|
+
"Invalid Date: '#{input.inspect}' is not a valid datetime."
|
102
|
+
end
|
103
|
+
date.to_time.dup.localtime
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
module Filters
|
5
|
+
module GroupingFilters
|
6
|
+
# Group an array of items by a property
|
7
|
+
#
|
8
|
+
# input - the inputted Enumerable
|
9
|
+
# property - the property
|
10
|
+
#
|
11
|
+
# Returns an array of Hashes, each looking something like this:
|
12
|
+
# {"name" => "larry"
|
13
|
+
# "items" => [...] } # all the items where `property` == "larry"
|
14
|
+
def group_by(input, property)
|
15
|
+
if groupable?(input)
|
16
|
+
groups = input.group_by { |item| item_property(item, property).to_s }
|
17
|
+
grouped_array(groups)
|
18
|
+
else
|
19
|
+
input
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Group an array of items by an expression
|
24
|
+
#
|
25
|
+
# input - the object array
|
26
|
+
# variable - the variable to assign each item to in the expression
|
27
|
+
# expression -a Liquid comparison expression passed in as a string
|
28
|
+
#
|
29
|
+
# Returns the filtered array of objects
|
30
|
+
def group_by_exp(input, variable, expression)
|
31
|
+
return input unless groupable?(input)
|
32
|
+
|
33
|
+
parsed_expr = parse_expression(expression)
|
34
|
+
@context.stack do
|
35
|
+
groups = input.group_by do |item|
|
36
|
+
@context[variable] = item
|
37
|
+
parsed_expr.render(@context)
|
38
|
+
end
|
39
|
+
grouped_array(groups)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def parse_expression(str)
|
45
|
+
Liquid::Variable.new(str, Liquid::ParseContext.new)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def groupable?(element)
|
50
|
+
element.respond_to?(:group_by)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def grouped_array(groups)
|
55
|
+
groups.each_with_object([]) do |item, array|
|
56
|
+
array << {
|
57
|
+
"name" => item.first,
|
58
|
+
"items" => item.last,
|
59
|
+
"size" => item.last.size,
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,488 @@
|
|
1
|
+
# These Liquid Filters are from the Jekyll project.
|
2
|
+
# They were copied here from Jekyll 4.0.0.pre.alpha1 source
|
3
|
+
# Modifications mostly consist of removals
|
4
|
+
# Would prefer keeping this in sync by using only select filters from a jekyll
|
5
|
+
# dependency gem, but I was unable to implement at this time. See
|
6
|
+
# ‘Forked’ at commit:
|
7
|
+
# https://github.com/jekyll/jekyll/tree/551014eb05f0c3eed3c8196b6d1e04e7ae433679
|
8
|
+
|
9
|
+
module Jekyll
|
10
|
+
module GroupingFilters
|
11
|
+
# Group an array of items by a property
|
12
|
+
#
|
13
|
+
# input - the inputted Enumerable
|
14
|
+
# property - the property
|
15
|
+
#
|
16
|
+
# Returns an array of Hashes, each looking something like this:
|
17
|
+
# {"name" => "larry"
|
18
|
+
# "items" => [...] } # all the items where `property` == "larry"
|
19
|
+
def group_by(input, property)
|
20
|
+
if groupable?(input)
|
21
|
+
groups = input.group_by { |item| item_property(item, property).to_s }
|
22
|
+
grouped_array(groups)
|
23
|
+
else
|
24
|
+
input
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Group an array of items by an expression
|
29
|
+
#
|
30
|
+
# input - the object array
|
31
|
+
# variable - the variable to assign each item to in the expression
|
32
|
+
# expression -a Liquid comparison expression passed in as a string
|
33
|
+
#
|
34
|
+
# Returns the filtered array of objects
|
35
|
+
def group_by_exp(input, variable, expression)
|
36
|
+
return input unless groupable?(input)
|
37
|
+
|
38
|
+
parsed_expr = parse_expression(expression)
|
39
|
+
@context.stack do
|
40
|
+
groups = input.group_by do |item|
|
41
|
+
@context[variable] = item
|
42
|
+
parsed_expr.render(@context)
|
43
|
+
end
|
44
|
+
grouped_array(groups)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def parse_expression(str)
|
50
|
+
Liquid::Variable.new(str, Liquid::ParseContext.new)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def groupable?(element)
|
55
|
+
element.respond_to?(:group_by)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def grouped_array(groups)
|
60
|
+
groups.each_with_object([]) do |item, array|
|
61
|
+
array << {
|
62
|
+
"name" => item.first,
|
63
|
+
"items" => item.last,
|
64
|
+
"size" => item.last.size,
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
module DateFilters
|
71
|
+
# Format a date in short format e.g. "27 Jan 2011".
|
72
|
+
# Ordinal format is also supported, in both the UK
|
73
|
+
# (e.g. "27th Jan 2011") and US ("e.g. Jan 27th, 2011") formats.
|
74
|
+
# UK format is the default.
|
75
|
+
#
|
76
|
+
# date - the Time to format.
|
77
|
+
# type - if "ordinal" the returned String will be in ordinal format
|
78
|
+
# style - if "US" the returned String will be in US format.
|
79
|
+
# Otherwise it will be in UK format.
|
80
|
+
#
|
81
|
+
# Returns the formatting String.
|
82
|
+
def date_to_string(date, type = nil, style = nil)
|
83
|
+
stringify_date(date, "%b", type, style)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Format a date in long format e.g. "27 January 2011".
|
87
|
+
# Ordinal format is also supported, in both the UK
|
88
|
+
# (e.g. "27th January 2011") and US ("e.g. January 27th, 2011") formats.
|
89
|
+
# UK format is the default.
|
90
|
+
#
|
91
|
+
# date - the Time to format.
|
92
|
+
# type - if "ordinal" the returned String will be in ordinal format
|
93
|
+
# style - if "US" the returned String will be in US format.
|
94
|
+
# Otherwise it will be in UK format.
|
95
|
+
#
|
96
|
+
# Returns the formatted String.
|
97
|
+
def date_to_long_string(date, type = nil, style = nil)
|
98
|
+
stringify_date(date, "%B", type, style)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Format a date for use in XML.
|
102
|
+
#
|
103
|
+
# date - The Time to format.
|
104
|
+
#
|
105
|
+
# Examples
|
106
|
+
#
|
107
|
+
# date_to_xmlschema(Time.now)
|
108
|
+
# # => "2011-04-24T20:34:46+08:00"
|
109
|
+
#
|
110
|
+
# Returns the formatted String.
|
111
|
+
def date_to_xmlschema(date)
|
112
|
+
return date if date.to_s.empty?
|
113
|
+
time(date).xmlschema
|
114
|
+
end
|
115
|
+
|
116
|
+
# Format a date according to RFC-822
|
117
|
+
#
|
118
|
+
# date - The Time to format.
|
119
|
+
#
|
120
|
+
# Examples
|
121
|
+
#
|
122
|
+
# date_to_rfc822(Time.now)
|
123
|
+
# # => "Sun, 24 Apr 2011 12:34:46 +0000"
|
124
|
+
#
|
125
|
+
# Returns the formatted String.
|
126
|
+
def date_to_rfc822(date)
|
127
|
+
return date if date.to_s.empty?
|
128
|
+
time(date).rfc822
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
# month_type: Notations that evaluate to 'Month' via `Time#strftime` ("%b", "%B")
|
133
|
+
# type: nil (default) or "ordinal"
|
134
|
+
# style: nil (default) or "US"
|
135
|
+
#
|
136
|
+
# Returns a stringified date or the empty input.
|
137
|
+
def stringify_date(date, month_type, type = nil, style = nil)
|
138
|
+
return date if date.to_s.empty?
|
139
|
+
time = time(date)
|
140
|
+
if type == "ordinal"
|
141
|
+
day = time.day
|
142
|
+
ordinal_day = "#{day}#{ordinal(day)}"
|
143
|
+
return time.strftime("#{month_type} #{ordinal_day}, %Y") if style == "US"
|
144
|
+
return time.strftime("#{ordinal_day} #{month_type} %Y")
|
145
|
+
end
|
146
|
+
time.strftime("%d #{month_type} %Y")
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
def ordinal(number)
|
151
|
+
return "th" if (11..13).cover?(number)
|
152
|
+
|
153
|
+
case number % 10
|
154
|
+
when 1 then "st"
|
155
|
+
when 2 then "nd"
|
156
|
+
when 3 then "rd"
|
157
|
+
else "th"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
def time(input)
|
163
|
+
date = Liquid::Utils.to_date(input)
|
164
|
+
unless date.respond_to?(:to_time)
|
165
|
+
raise "Invalid Date: '#{input.inspect}' is not a valid datetime."
|
166
|
+
end
|
167
|
+
date.to_time.dup.localtime
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
module Filters
|
172
|
+
|
173
|
+
# XML escape a string for use. Replaces any special characters with
|
174
|
+
# appropriate HTML entity replacements.
|
175
|
+
#
|
176
|
+
# input - The String to escape.
|
177
|
+
#
|
178
|
+
# Examples
|
179
|
+
#
|
180
|
+
# xml_escape('foo "bar" <baz>')
|
181
|
+
# # => "foo "bar" <baz>"
|
182
|
+
#
|
183
|
+
# Returns the escaped String.
|
184
|
+
def xml_escape(input)
|
185
|
+
input.to_s.encode(:xml => :attr).gsub(%r!\A"|"\Z!, "")
|
186
|
+
end
|
187
|
+
|
188
|
+
# CGI escape a string for use in a URL. Replaces any special characters
|
189
|
+
# with appropriate %XX replacements.
|
190
|
+
#
|
191
|
+
# input - The String to escape.
|
192
|
+
#
|
193
|
+
# Examples
|
194
|
+
#
|
195
|
+
# cgi_escape('foo,bar;baz?')
|
196
|
+
# # => "foo%2Cbar%3Bbaz%3F"
|
197
|
+
#
|
198
|
+
# Returns the escaped String.
|
199
|
+
def cgi_escape(input)
|
200
|
+
CGI.escape(input)
|
201
|
+
end
|
202
|
+
|
203
|
+
# URI escape a string.
|
204
|
+
#
|
205
|
+
# input - The String to escape.
|
206
|
+
#
|
207
|
+
# Examples
|
208
|
+
#
|
209
|
+
# uri_escape('foo, bar \\baz?')
|
210
|
+
# # => "foo,%20bar%20%5Cbaz?"
|
211
|
+
#
|
212
|
+
# Returns the escaped String.
|
213
|
+
def uri_escape(input)
|
214
|
+
Addressable::URI.normalize_component(input)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Replace any whitespace in the input string with a single space
|
218
|
+
#
|
219
|
+
# input - The String on which to operate.
|
220
|
+
#
|
221
|
+
# Returns the formatted String
|
222
|
+
def normalize_whitespace(input)
|
223
|
+
input.to_s.gsub(%r!\s+!, " ").strip
|
224
|
+
end
|
225
|
+
|
226
|
+
# Count the number of words in the input string.
|
227
|
+
#
|
228
|
+
# input - The String on which to operate.
|
229
|
+
#
|
230
|
+
# Returns the Integer word count.
|
231
|
+
def number_of_words(input)
|
232
|
+
input.split.length
|
233
|
+
end
|
234
|
+
|
235
|
+
# Join an array of things into a string by separating with commas and the
|
236
|
+
# word "and" for the last one.
|
237
|
+
#
|
238
|
+
# Based on but differs from array_to_sentence_string, not available to LiquiDoc
|
239
|
+
#
|
240
|
+
# array - The Array of Strings to join.
|
241
|
+
# connector - Word used to connect the last 2 items in the array
|
242
|
+
#
|
243
|
+
# Examples
|
244
|
+
#
|
245
|
+
# array_to_serial(["apples", "oranges", "grapes"])
|
246
|
+
# # => "apples, oranges, and grapes"
|
247
|
+
# array_to_serial(["apples", "oranges", "grapes"], "")
|
248
|
+
# # => "apples, oranges, grapes"
|
249
|
+
# Improved behavior::
|
250
|
+
# array_to_serial(["apples", "oranges"], "")
|
251
|
+
# # => "apples, oranges"
|
252
|
+
#
|
253
|
+
# Returns the formatted String.
|
254
|
+
def array_to_serial(array, connector="and", serializer=", ")
|
255
|
+
con = "#{connector} " unless connector.empty?
|
256
|
+
case array.length
|
257
|
+
when 0
|
258
|
+
out = ""
|
259
|
+
when 1
|
260
|
+
out = array[0].to_s
|
261
|
+
when 2
|
262
|
+
ser = serializer if connector.empty?
|
263
|
+
out = "#{array[0]}#{ser} #{con}#{array[1]}"
|
264
|
+
else
|
265
|
+
out = "#{array[0...-1].join(serializer)}#{serializer}#{con}#{array[-1]}"
|
266
|
+
end
|
267
|
+
out
|
268
|
+
end
|
269
|
+
|
270
|
+
# Convert the input into json string
|
271
|
+
#
|
272
|
+
# input - The Array or Hash to be converted
|
273
|
+
#
|
274
|
+
# Returns the converted json string
|
275
|
+
def jsonify(input)
|
276
|
+
as_liquid(input).to_json
|
277
|
+
end
|
278
|
+
|
279
|
+
# Filter an array of objects
|
280
|
+
#
|
281
|
+
# input - the object array
|
282
|
+
# property - property within each object to filter by
|
283
|
+
# value - desired value
|
284
|
+
#
|
285
|
+
# Returns the filtered array of objects
|
286
|
+
def where(input, property, value)
|
287
|
+
return input if property.nil? || value.nil?
|
288
|
+
return input unless input.respond_to?(:select)
|
289
|
+
input = input.values if input.is_a?(Hash)
|
290
|
+
input_id = input.hash
|
291
|
+
|
292
|
+
# implement a hash based on method parameters to cache the end-result
|
293
|
+
# for given parameters.
|
294
|
+
@where_filter_cache ||= {}
|
295
|
+
@where_filter_cache[input_id] ||= {}
|
296
|
+
@where_filter_cache[input_id][property] ||= {}
|
297
|
+
|
298
|
+
# stash or retrive results to return
|
299
|
+
@where_filter_cache[input_id][property][value] ||= begin
|
300
|
+
input.select do |object|
|
301
|
+
Array(item_property(object, property)).map!(&:to_s).include?(value.to_s)
|
302
|
+
end || []
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
# Filters an array of objects against an expression
|
307
|
+
#
|
308
|
+
# input - the object array
|
309
|
+
# variable - the variable to assign each item to in the expression
|
310
|
+
# expression - a Liquid comparison expression passed in as a string
|
311
|
+
#
|
312
|
+
# Returns the filtered array of objects
|
313
|
+
def where_exp(input, variable, expression)
|
314
|
+
return input unless input.respond_to?(:select)
|
315
|
+
input = input.values if input.is_a?(Hash) # FIXME
|
316
|
+
|
317
|
+
condition = parse_condition(expression)
|
318
|
+
@context.stack do
|
319
|
+
input.select do |object|
|
320
|
+
@context[variable] = object
|
321
|
+
condition.evaluate(@context)
|
322
|
+
end
|
323
|
+
end || []
|
324
|
+
end
|
325
|
+
|
326
|
+
# Convert the input into integer
|
327
|
+
#
|
328
|
+
# input - the object string
|
329
|
+
#
|
330
|
+
# Returns the integer value
|
331
|
+
def to_integer(input)
|
332
|
+
return 1 if input == true
|
333
|
+
return 0 if input == false
|
334
|
+
input.to_i
|
335
|
+
end
|
336
|
+
|
337
|
+
# Sort an array of objects
|
338
|
+
#
|
339
|
+
# input - the object array
|
340
|
+
# property - property within each object to filter by
|
341
|
+
# nils ('first' | 'last') - nils appear before or after non-nil values
|
342
|
+
#
|
343
|
+
# Returns the filtered array of objects
|
344
|
+
def sort(input, property = nil, nils = "first")
|
345
|
+
if input.nil?
|
346
|
+
raise ArgumentError, "Cannot sort a null object."
|
347
|
+
end
|
348
|
+
if property.nil?
|
349
|
+
input.sort
|
350
|
+
else
|
351
|
+
if nils == "first"
|
352
|
+
order = - 1
|
353
|
+
elsif nils == "last"
|
354
|
+
order = + 1
|
355
|
+
else
|
356
|
+
raise ArgumentError, "Invalid nils order: " \
|
357
|
+
"'#{nils}' is not a valid nils order. It must be 'first' or 'last'."
|
358
|
+
end
|
359
|
+
|
360
|
+
sort_input(input, property, order)
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
def pop(array, num=1)
|
365
|
+
return array unless array.is_a?(Array)
|
366
|
+
num = Liquid::Utils.to_integer(num)
|
367
|
+
new_ary = array.dup
|
368
|
+
new_ary.pop(num)
|
369
|
+
end
|
370
|
+
|
371
|
+
def push(array, add)
|
372
|
+
return array unless array.is_a?(Array)
|
373
|
+
new_ary = array.dup
|
374
|
+
new_ary.push(add)
|
375
|
+
end
|
376
|
+
|
377
|
+
def shift(array, num=1)
|
378
|
+
return array unless array.is_a?(Array)
|
379
|
+
num = Liquid::Utils.to_integer(num)
|
380
|
+
new_ary = array.dup
|
381
|
+
new_ary.shift(num)
|
382
|
+
end
|
383
|
+
|
384
|
+
def unshift(array, add)
|
385
|
+
return array unless array.is_a?(Array)
|
386
|
+
new_ary = array.dup
|
387
|
+
new_ary.unshift(add)
|
388
|
+
end
|
389
|
+
|
390
|
+
def sample(array, num=1)
|
391
|
+
return array unless array.respond_to?(:sample)
|
392
|
+
num = Liquid::Utils.to_integer(num)
|
393
|
+
array.sample(num)
|
394
|
+
end
|
395
|
+
|
396
|
+
# Convert an object into its String representation for debugging
|
397
|
+
#
|
398
|
+
# input - The Object to be converted
|
399
|
+
#
|
400
|
+
# Returns a String representation of the object.
|
401
|
+
def inspect(input)
|
402
|
+
xml_escape(input.inspect)
|
403
|
+
end
|
404
|
+
|
405
|
+
private
|
406
|
+
|
407
|
+
# Sort the input Enumerable by the given property.
|
408
|
+
# If the property doesn't exist, return the sort order respective of
|
409
|
+
# which item doesn't have the property.
|
410
|
+
# We also utilize the Schwartzian transform to make this more efficient.
|
411
|
+
def sort_input(input, property, order)
|
412
|
+
input.map { |item| [item_property(item, property), item] }
|
413
|
+
.sort! do |apple_info, orange_info|
|
414
|
+
apple_property = apple_info.first
|
415
|
+
orange_property = orange_info.first
|
416
|
+
|
417
|
+
if !apple_property.nil? && orange_property.nil?
|
418
|
+
- order
|
419
|
+
elsif apple_property.nil? && !orange_property.nil?
|
420
|
+
+ order
|
421
|
+
else
|
422
|
+
apple_property <=> orange_property
|
423
|
+
end
|
424
|
+
end
|
425
|
+
.map!(&:last)
|
426
|
+
end
|
427
|
+
|
428
|
+
private
|
429
|
+
def item_property(item, property)
|
430
|
+
if item.respond_to?(:to_liquid)
|
431
|
+
property.to_s.split(".").reduce(item.to_liquid) do |subvalue, attribute|
|
432
|
+
subvalue[attribute]
|
433
|
+
end
|
434
|
+
elsif item.respond_to?(:data)
|
435
|
+
item.data[property.to_s]
|
436
|
+
else
|
437
|
+
item[property.to_s]
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
private
|
442
|
+
def as_liquid(item)
|
443
|
+
case item
|
444
|
+
when Hash
|
445
|
+
pairs = item.map { |k, v| as_liquid([k, v]) }
|
446
|
+
Hash[pairs]
|
447
|
+
when Array
|
448
|
+
item.map { |i| as_liquid(i) }
|
449
|
+
else
|
450
|
+
if item.respond_to?(:to_liquid)
|
451
|
+
liquidated = item.to_liquid
|
452
|
+
# prevent infinite recursion for simple types (which return `self`)
|
453
|
+
if liquidated == item
|
454
|
+
item
|
455
|
+
else
|
456
|
+
as_liquid(liquidated)
|
457
|
+
end
|
458
|
+
else
|
459
|
+
item
|
460
|
+
end
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
# Parse a string to a Liquid Condition
|
465
|
+
private
|
466
|
+
def parse_condition(exp)
|
467
|
+
parser = Liquid::Parser.new(exp)
|
468
|
+
left_expr = parser.expression
|
469
|
+
operator = parser.consume?(:comparison)
|
470
|
+
condition =
|
471
|
+
if operator
|
472
|
+
Liquid::Condition.new(Liquid::Expression.parse(left_expr),
|
473
|
+
operator,
|
474
|
+
Liquid::Expression.parse(parser.expression))
|
475
|
+
else
|
476
|
+
Liquid::Condition.new(Liquid::Expression.parse(left_expr))
|
477
|
+
end
|
478
|
+
parser.consume(:end_of_string)
|
479
|
+
|
480
|
+
condition
|
481
|
+
end
|
482
|
+
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
Liquid::Template.register_filter(Jekyll::GroupingFilters)
|
487
|
+
Liquid::Template.register_filter(Jekyll::DateFilters)
|
488
|
+
Liquid::Template.register_filter(Jekyll::Filters)
|