liquid-4-0-2 4.0.2
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 +7 -0
- data/History.md +235 -0
- data/LICENSE +20 -0
- data/README.md +108 -0
- data/lib/liquid.rb +80 -0
- data/lib/liquid/block.rb +77 -0
- data/lib/liquid/block_body.rb +142 -0
- data/lib/liquid/condition.rb +151 -0
- data/lib/liquid/context.rb +226 -0
- data/lib/liquid/document.rb +27 -0
- data/lib/liquid/drop.rb +78 -0
- data/lib/liquid/errors.rb +56 -0
- data/lib/liquid/expression.rb +49 -0
- data/lib/liquid/extensions.rb +74 -0
- data/lib/liquid/file_system.rb +73 -0
- data/lib/liquid/forloop_drop.rb +42 -0
- data/lib/liquid/i18n.rb +39 -0
- data/lib/liquid/interrupts.rb +16 -0
- data/lib/liquid/lexer.rb +55 -0
- data/lib/liquid/locales/en.yml +26 -0
- data/lib/liquid/parse_context.rb +38 -0
- data/lib/liquid/parse_tree_visitor.rb +42 -0
- data/lib/liquid/parser.rb +90 -0
- data/lib/liquid/parser_switching.rb +31 -0
- data/lib/liquid/profiler.rb +158 -0
- data/lib/liquid/profiler/hooks.rb +23 -0
- data/lib/liquid/range_lookup.rb +37 -0
- data/lib/liquid/resource_limits.rb +23 -0
- data/lib/liquid/standardfilters.rb +485 -0
- data/lib/liquid/strainer.rb +66 -0
- data/lib/liquid/tablerowloop_drop.rb +62 -0
- data/lib/liquid/tag.rb +43 -0
- data/lib/liquid/tags/assign.rb +59 -0
- data/lib/liquid/tags/break.rb +18 -0
- data/lib/liquid/tags/capture.rb +38 -0
- data/lib/liquid/tags/case.rb +94 -0
- data/lib/liquid/tags/comment.rb +16 -0
- data/lib/liquid/tags/continue.rb +18 -0
- data/lib/liquid/tags/cycle.rb +65 -0
- data/lib/liquid/tags/decrement.rb +35 -0
- data/lib/liquid/tags/for.rb +203 -0
- data/lib/liquid/tags/if.rb +122 -0
- data/lib/liquid/tags/ifchanged.rb +18 -0
- data/lib/liquid/tags/include.rb +124 -0
- data/lib/liquid/tags/increment.rb +31 -0
- data/lib/liquid/tags/raw.rb +47 -0
- data/lib/liquid/tags/table_row.rb +62 -0
- data/lib/liquid/tags/unless.rb +30 -0
- data/lib/liquid/template.rb +254 -0
- data/lib/liquid/tokenizer.rb +31 -0
- data/lib/liquid/utils.rb +83 -0
- data/lib/liquid/variable.rb +148 -0
- data/lib/liquid/variable_lookup.rb +88 -0
- data/lib/liquid/version.rb +4 -0
- data/test/fixtures/en_locale.yml +9 -0
- data/test/integration/assign_test.rb +48 -0
- data/test/integration/blank_test.rb +106 -0
- data/test/integration/block_test.rb +12 -0
- data/test/integration/capture_test.rb +50 -0
- data/test/integration/context_test.rb +32 -0
- data/test/integration/document_test.rb +19 -0
- data/test/integration/drop_test.rb +273 -0
- data/test/integration/error_handling_test.rb +260 -0
- data/test/integration/filter_test.rb +178 -0
- data/test/integration/hash_ordering_test.rb +23 -0
- data/test/integration/output_test.rb +123 -0
- data/test/integration/parse_tree_visitor_test.rb +247 -0
- data/test/integration/parsing_quirks_test.rb +122 -0
- data/test/integration/render_profiling_test.rb +154 -0
- data/test/integration/security_test.rb +80 -0
- data/test/integration/standard_filter_test.rb +698 -0
- data/test/integration/tags/break_tag_test.rb +15 -0
- data/test/integration/tags/continue_tag_test.rb +15 -0
- data/test/integration/tags/for_tag_test.rb +410 -0
- data/test/integration/tags/if_else_tag_test.rb +188 -0
- data/test/integration/tags/include_tag_test.rb +245 -0
- data/test/integration/tags/increment_tag_test.rb +23 -0
- data/test/integration/tags/raw_tag_test.rb +31 -0
- data/test/integration/tags/standard_tag_test.rb +296 -0
- data/test/integration/tags/statements_test.rb +111 -0
- data/test/integration/tags/table_row_test.rb +64 -0
- data/test/integration/tags/unless_else_tag_test.rb +26 -0
- data/test/integration/template_test.rb +332 -0
- data/test/integration/trim_mode_test.rb +529 -0
- data/test/integration/variable_test.rb +96 -0
- data/test/test_helper.rb +116 -0
- data/test/unit/block_unit_test.rb +58 -0
- data/test/unit/condition_unit_test.rb +166 -0
- data/test/unit/context_unit_test.rb +489 -0
- data/test/unit/file_system_unit_test.rb +35 -0
- data/test/unit/i18n_unit_test.rb +37 -0
- data/test/unit/lexer_unit_test.rb +51 -0
- data/test/unit/parser_unit_test.rb +82 -0
- data/test/unit/regexp_unit_test.rb +44 -0
- data/test/unit/strainer_unit_test.rb +164 -0
- data/test/unit/tag_unit_test.rb +21 -0
- data/test/unit/tags/case_tag_unit_test.rb +10 -0
- data/test/unit/tags/for_tag_unit_test.rb +13 -0
- data/test/unit/tags/if_tag_unit_test.rb +8 -0
- data/test/unit/template_unit_test.rb +78 -0
- data/test/unit/tokenizer_unit_test.rb +55 -0
- data/test/unit/variable_unit_test.rb +162 -0
- metadata +224 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module Liquid
|
2
|
+
class BlockBody
|
3
|
+
def render_node_with_profiling(node, output, context, skip_output = false)
|
4
|
+
Profiler.profile_node_render(node) do
|
5
|
+
render_node_without_profiling(node, output, context, skip_output)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
alias_method :render_node_without_profiling, :render_node_to_output
|
10
|
+
alias_method :render_node_to_output, :render_node_with_profiling
|
11
|
+
end
|
12
|
+
|
13
|
+
class Include < Tag
|
14
|
+
def render_with_profiling(context)
|
15
|
+
Profiler.profile_children(context.evaluate(@template_name_expr).to_s) do
|
16
|
+
render_without_profiling(context)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
alias_method :render_without_profiling, :render
|
21
|
+
alias_method :render, :render_with_profiling
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Liquid
|
2
|
+
class RangeLookup
|
3
|
+
def self.parse(start_markup, end_markup)
|
4
|
+
start_obj = Expression.parse(start_markup)
|
5
|
+
end_obj = Expression.parse(end_markup)
|
6
|
+
if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
|
7
|
+
new(start_obj, end_obj)
|
8
|
+
else
|
9
|
+
start_obj.to_i..end_obj.to_i
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(start_obj, end_obj)
|
14
|
+
@start_obj = start_obj
|
15
|
+
@end_obj = end_obj
|
16
|
+
end
|
17
|
+
|
18
|
+
def evaluate(context)
|
19
|
+
start_int = to_integer(context.evaluate(@start_obj))
|
20
|
+
end_int = to_integer(context.evaluate(@end_obj))
|
21
|
+
start_int..end_int
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def to_integer(input)
|
27
|
+
case input
|
28
|
+
when Integer
|
29
|
+
input
|
30
|
+
when NilClass, String
|
31
|
+
input.to_i
|
32
|
+
else
|
33
|
+
Utils.to_integer(input)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Liquid
|
2
|
+
class ResourceLimits
|
3
|
+
attr_accessor :render_length, :render_score, :assign_score,
|
4
|
+
:render_length_limit, :render_score_limit, :assign_score_limit
|
5
|
+
|
6
|
+
def initialize(limits)
|
7
|
+
@render_length_limit = limits[:render_length_limit]
|
8
|
+
@render_score_limit = limits[:render_score_limit]
|
9
|
+
@assign_score_limit = limits[:assign_score_limit]
|
10
|
+
reset
|
11
|
+
end
|
12
|
+
|
13
|
+
def reached?
|
14
|
+
(@render_length_limit && @render_length > @render_length_limit) ||
|
15
|
+
(@render_score_limit && @render_score > @render_score_limit) ||
|
16
|
+
(@assign_score_limit && @assign_score > @assign_score_limit)
|
17
|
+
end
|
18
|
+
|
19
|
+
def reset
|
20
|
+
@render_length = @render_score = @assign_score = 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,485 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'bigdecimal'
|
3
|
+
|
4
|
+
module Liquid
|
5
|
+
module StandardFilters
|
6
|
+
HTML_ESCAPE = {
|
7
|
+
'&'.freeze => '&'.freeze,
|
8
|
+
'>'.freeze => '>'.freeze,
|
9
|
+
'<'.freeze => '<'.freeze,
|
10
|
+
'"'.freeze => '"'.freeze,
|
11
|
+
"'".freeze => '''.freeze
|
12
|
+
}
|
13
|
+
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
|
14
|
+
|
15
|
+
# Return the size of an array or of an string
|
16
|
+
def size(input)
|
17
|
+
input.respond_to?(:size) ? input.size : 0
|
18
|
+
end
|
19
|
+
|
20
|
+
# convert an input string to DOWNCASE
|
21
|
+
def downcase(input)
|
22
|
+
input.to_s.downcase
|
23
|
+
end
|
24
|
+
|
25
|
+
# convert an input string to UPCASE
|
26
|
+
def upcase(input)
|
27
|
+
input.to_s.upcase
|
28
|
+
end
|
29
|
+
|
30
|
+
# capitalize words in the input centence
|
31
|
+
def capitalize(input)
|
32
|
+
input.to_s.capitalize
|
33
|
+
end
|
34
|
+
|
35
|
+
def escape(input)
|
36
|
+
CGI.escapeHTML(input.to_s).untaint unless input.nil?
|
37
|
+
end
|
38
|
+
alias_method :h, :escape
|
39
|
+
|
40
|
+
def escape_once(input)
|
41
|
+
input.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
|
42
|
+
end
|
43
|
+
|
44
|
+
def url_encode(input)
|
45
|
+
CGI.escape(input.to_s) unless input.nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
def url_decode(input)
|
49
|
+
CGI.unescape(input.to_s) unless input.nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
def slice(input, offset, length = nil)
|
53
|
+
offset = Utils.to_integer(offset)
|
54
|
+
length = length ? Utils.to_integer(length) : 1
|
55
|
+
|
56
|
+
if input.is_a?(Array)
|
57
|
+
input.slice(offset, length) || []
|
58
|
+
else
|
59
|
+
input.to_s.slice(offset, length) || ''
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Truncate a string down to x characters
|
64
|
+
def truncate(input, length = 50, truncate_string = "...".freeze)
|
65
|
+
return if input.nil?
|
66
|
+
input_str = input.to_s
|
67
|
+
length = Utils.to_integer(length)
|
68
|
+
truncate_string_str = truncate_string.to_s
|
69
|
+
l = length - truncate_string_str.length
|
70
|
+
l = 0 if l < 0
|
71
|
+
input_str.length > length ? input_str[0...l] + truncate_string_str : input_str
|
72
|
+
end
|
73
|
+
|
74
|
+
def truncatewords(input, words = 15, truncate_string = "...".freeze)
|
75
|
+
return if input.nil?
|
76
|
+
wordlist = input.to_s.split
|
77
|
+
words = Utils.to_integer(words)
|
78
|
+
l = words - 1
|
79
|
+
l = 0 if l < 0
|
80
|
+
wordlist.length > l ? wordlist[0..l].join(" ".freeze) + truncate_string.to_s : input
|
81
|
+
end
|
82
|
+
|
83
|
+
# Split input string into an array of substrings separated by given pattern.
|
84
|
+
#
|
85
|
+
# Example:
|
86
|
+
# <div class="summary">{{ post | split '//' | first }}</div>
|
87
|
+
#
|
88
|
+
def split(input, pattern)
|
89
|
+
input.to_s.split(pattern.to_s)
|
90
|
+
end
|
91
|
+
|
92
|
+
def strip(input)
|
93
|
+
input.to_s.strip
|
94
|
+
end
|
95
|
+
|
96
|
+
def lstrip(input)
|
97
|
+
input.to_s.lstrip
|
98
|
+
end
|
99
|
+
|
100
|
+
def rstrip(input)
|
101
|
+
input.to_s.rstrip
|
102
|
+
end
|
103
|
+
|
104
|
+
def strip_html(input)
|
105
|
+
empty = ''.freeze
|
106
|
+
input.to_s.gsub(/<script.*?<\/script>/m, empty).gsub(/<!--.*?-->/m, empty).gsub(/<style.*?<\/style>/m, empty).gsub(/<.*?>/m, empty)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Remove all newlines from the string
|
110
|
+
def strip_newlines(input)
|
111
|
+
input.to_s.gsub(/\r?\n/, ''.freeze)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Join elements of the array with certain character between them
|
115
|
+
def join(input, glue = ' '.freeze)
|
116
|
+
InputIterator.new(input).join(glue)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Sort elements of the array
|
120
|
+
# provide optional property with which to sort an array of hashes or drops
|
121
|
+
def sort(input, property = nil)
|
122
|
+
ary = InputIterator.new(input)
|
123
|
+
|
124
|
+
return [] if ary.empty?
|
125
|
+
|
126
|
+
if property.nil?
|
127
|
+
ary.sort do |a, b|
|
128
|
+
nil_safe_compare(a, b)
|
129
|
+
end
|
130
|
+
elsif ary.all? { |el| el.respond_to?(:[]) }
|
131
|
+
ary.sort do |a, b|
|
132
|
+
nil_safe_compare(a[property], b[property])
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Sort elements of an array ignoring case if strings
|
138
|
+
# provide optional property with which to sort an array of hashes or drops
|
139
|
+
def sort_natural(input, property = nil)
|
140
|
+
ary = InputIterator.new(input)
|
141
|
+
|
142
|
+
return [] if ary.empty?
|
143
|
+
|
144
|
+
if property.nil?
|
145
|
+
ary.sort do |a, b|
|
146
|
+
nil_safe_casecmp(a, b)
|
147
|
+
end
|
148
|
+
elsif ary.all? { |el| el.respond_to?(:[]) }
|
149
|
+
ary.sort do |a, b|
|
150
|
+
nil_safe_casecmp(a[property], b[property])
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Filter the elements of an array to those with a certain property value.
|
156
|
+
# By default the target is any truthy value.
|
157
|
+
def where(input, property, target_value = nil)
|
158
|
+
ary = InputIterator.new(input)
|
159
|
+
|
160
|
+
if ary.empty?
|
161
|
+
[]
|
162
|
+
elsif ary.first.respond_to?(:[]) && target_value.nil?
|
163
|
+
ary.where_present(property)
|
164
|
+
elsif ary.first.respond_to?(:[])
|
165
|
+
ary.where(property, target_value)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Remove duplicate elements from an array
|
170
|
+
# provide optional property with which to determine uniqueness
|
171
|
+
def uniq(input, property = nil)
|
172
|
+
ary = InputIterator.new(input)
|
173
|
+
|
174
|
+
if property.nil?
|
175
|
+
ary.uniq
|
176
|
+
elsif ary.empty? # The next two cases assume a non-empty array.
|
177
|
+
[]
|
178
|
+
elsif ary.first.respond_to?(:[])
|
179
|
+
ary.uniq{ |a| a[property] }
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Reverse the elements of an array
|
184
|
+
def reverse(input)
|
185
|
+
ary = InputIterator.new(input)
|
186
|
+
ary.reverse
|
187
|
+
end
|
188
|
+
|
189
|
+
# map/collect on a given property
|
190
|
+
def map(input, property)
|
191
|
+
InputIterator.new(input).map do |e|
|
192
|
+
e = e.call if e.is_a?(Proc)
|
193
|
+
|
194
|
+
if property == "to_liquid".freeze
|
195
|
+
e
|
196
|
+
elsif e.respond_to?(:[])
|
197
|
+
r = e[property]
|
198
|
+
r.is_a?(Proc) ? r.call : r
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Remove nils within an array
|
204
|
+
# provide optional property with which to check for nil
|
205
|
+
def compact(input, property = nil)
|
206
|
+
ary = InputIterator.new(input)
|
207
|
+
|
208
|
+
if property.nil?
|
209
|
+
ary.compact
|
210
|
+
elsif ary.empty? # The next two cases assume a non-empty array.
|
211
|
+
[]
|
212
|
+
elsif ary.first.respond_to?(:[])
|
213
|
+
ary.reject{ |a| a[property].nil? }
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# Replace occurrences of a string with another
|
218
|
+
def replace(input, string, replacement = ''.freeze)
|
219
|
+
input.to_s.gsub(string.to_s, replacement.to_s)
|
220
|
+
end
|
221
|
+
|
222
|
+
# Replace the first occurrences of a string with another
|
223
|
+
def replace_first(input, string, replacement = ''.freeze)
|
224
|
+
input.to_s.sub(string.to_s, replacement.to_s)
|
225
|
+
end
|
226
|
+
|
227
|
+
# remove a substring
|
228
|
+
def remove(input, string)
|
229
|
+
input.to_s.gsub(string.to_s, ''.freeze)
|
230
|
+
end
|
231
|
+
|
232
|
+
# remove the first occurrences of a substring
|
233
|
+
def remove_first(input, string)
|
234
|
+
input.to_s.sub(string.to_s, ''.freeze)
|
235
|
+
end
|
236
|
+
|
237
|
+
# add one string to another
|
238
|
+
def append(input, string)
|
239
|
+
input.to_s + string.to_s
|
240
|
+
end
|
241
|
+
|
242
|
+
def concat(input, array)
|
243
|
+
unless array.respond_to?(:to_ary)
|
244
|
+
raise ArgumentError.new("concat filter requires an array argument")
|
245
|
+
end
|
246
|
+
InputIterator.new(input).concat(array)
|
247
|
+
end
|
248
|
+
|
249
|
+
# prepend a string to another
|
250
|
+
def prepend(input, string)
|
251
|
+
string.to_s + input.to_s
|
252
|
+
end
|
253
|
+
|
254
|
+
# Add <br /> tags in front of all newlines in input string
|
255
|
+
def newline_to_br(input)
|
256
|
+
input.to_s.gsub(/\n/, "<br />\n".freeze)
|
257
|
+
end
|
258
|
+
|
259
|
+
# Reformat a date using Ruby's core Time#strftime( string ) -> string
|
260
|
+
#
|
261
|
+
# %a - The abbreviated weekday name (``Sun'')
|
262
|
+
# %A - The full weekday name (``Sunday'')
|
263
|
+
# %b - The abbreviated month name (``Jan'')
|
264
|
+
# %B - The full month name (``January'')
|
265
|
+
# %c - The preferred local date and time representation
|
266
|
+
# %d - Day of the month (01..31)
|
267
|
+
# %H - Hour of the day, 24-hour clock (00..23)
|
268
|
+
# %I - Hour of the day, 12-hour clock (01..12)
|
269
|
+
# %j - Day of the year (001..366)
|
270
|
+
# %m - Month of the year (01..12)
|
271
|
+
# %M - Minute of the hour (00..59)
|
272
|
+
# %p - Meridian indicator (``AM'' or ``PM'')
|
273
|
+
# %s - Number of seconds since 1970-01-01 00:00:00 UTC.
|
274
|
+
# %S - Second of the minute (00..60)
|
275
|
+
# %U - Week number of the current year,
|
276
|
+
# starting with the first Sunday as the first
|
277
|
+
# day of the first week (00..53)
|
278
|
+
# %W - Week number of the current year,
|
279
|
+
# starting with the first Monday as the first
|
280
|
+
# day of the first week (00..53)
|
281
|
+
# %w - Day of the week (Sunday is 0, 0..6)
|
282
|
+
# %x - Preferred representation for the date alone, no time
|
283
|
+
# %X - Preferred representation for the time alone, no date
|
284
|
+
# %y - Year without a century (00..99)
|
285
|
+
# %Y - Year with century
|
286
|
+
# %Z - Time zone name
|
287
|
+
# %% - Literal ``%'' character
|
288
|
+
#
|
289
|
+
# See also: http://www.ruby-doc.org/core/Time.html#method-i-strftime
|
290
|
+
def date(input, format)
|
291
|
+
return input if format.to_s.empty?
|
292
|
+
|
293
|
+
return input unless date = Utils.to_date(input)
|
294
|
+
|
295
|
+
date.strftime(format.to_s)
|
296
|
+
end
|
297
|
+
|
298
|
+
# Get the first element of the passed in array
|
299
|
+
#
|
300
|
+
# Example:
|
301
|
+
# {{ product.images | first | to_img }}
|
302
|
+
#
|
303
|
+
def first(array)
|
304
|
+
array.first if array.respond_to?(:first)
|
305
|
+
end
|
306
|
+
|
307
|
+
# Get the last element of the passed in array
|
308
|
+
#
|
309
|
+
# Example:
|
310
|
+
# {{ product.images | last | to_img }}
|
311
|
+
#
|
312
|
+
def last(array)
|
313
|
+
array.last if array.respond_to?(:last)
|
314
|
+
end
|
315
|
+
|
316
|
+
# absolute value
|
317
|
+
def abs(input)
|
318
|
+
result = Utils.to_number(input).abs
|
319
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
320
|
+
end
|
321
|
+
|
322
|
+
# addition
|
323
|
+
def plus(input, operand)
|
324
|
+
apply_operation(input, operand, :+)
|
325
|
+
end
|
326
|
+
|
327
|
+
# subtraction
|
328
|
+
def minus(input, operand)
|
329
|
+
apply_operation(input, operand, :-)
|
330
|
+
end
|
331
|
+
|
332
|
+
# multiplication
|
333
|
+
def times(input, operand)
|
334
|
+
apply_operation(input, operand, :*)
|
335
|
+
end
|
336
|
+
|
337
|
+
# division
|
338
|
+
def divided_by(input, operand)
|
339
|
+
apply_operation(input, operand, :/)
|
340
|
+
rescue ::ZeroDivisionError => e
|
341
|
+
raise Liquid::ZeroDivisionError, e.message
|
342
|
+
end
|
343
|
+
|
344
|
+
def modulo(input, operand)
|
345
|
+
apply_operation(input, operand, :%)
|
346
|
+
rescue ::ZeroDivisionError => e
|
347
|
+
raise Liquid::ZeroDivisionError, e.message
|
348
|
+
end
|
349
|
+
|
350
|
+
def round(input, n = 0)
|
351
|
+
result = Utils.to_number(input).round(Utils.to_number(n))
|
352
|
+
result = result.to_f if result.is_a?(BigDecimal)
|
353
|
+
result = result.to_i if n == 0
|
354
|
+
result
|
355
|
+
rescue ::FloatDomainError => e
|
356
|
+
raise Liquid::FloatDomainError, e.message
|
357
|
+
end
|
358
|
+
|
359
|
+
def ceil(input)
|
360
|
+
Utils.to_number(input).ceil.to_i
|
361
|
+
rescue ::FloatDomainError => e
|
362
|
+
raise Liquid::FloatDomainError, e.message
|
363
|
+
end
|
364
|
+
|
365
|
+
def floor(input)
|
366
|
+
Utils.to_number(input).floor.to_i
|
367
|
+
rescue ::FloatDomainError => e
|
368
|
+
raise Liquid::FloatDomainError, e.message
|
369
|
+
end
|
370
|
+
|
371
|
+
def at_least(input, n)
|
372
|
+
min_value = Utils.to_number(n)
|
373
|
+
|
374
|
+
result = Utils.to_number(input)
|
375
|
+
result = min_value if min_value > result
|
376
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
377
|
+
end
|
378
|
+
|
379
|
+
def at_most(input, n)
|
380
|
+
max_value = Utils.to_number(n)
|
381
|
+
|
382
|
+
result = Utils.to_number(input)
|
383
|
+
result = max_value if max_value < result
|
384
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
385
|
+
end
|
386
|
+
|
387
|
+
def default(input, default_value = ''.freeze)
|
388
|
+
if !input || input.respond_to?(:empty?) && input.empty?
|
389
|
+
default_value
|
390
|
+
else
|
391
|
+
input
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
private
|
396
|
+
|
397
|
+
def apply_operation(input, operand, operation)
|
398
|
+
result = Utils.to_number(input).send(operation, Utils.to_number(operand))
|
399
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
400
|
+
end
|
401
|
+
|
402
|
+
def nil_safe_compare(a, b)
|
403
|
+
if !a.nil? && !b.nil?
|
404
|
+
a <=> b
|
405
|
+
else
|
406
|
+
a.nil? ? 1 : -1
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
def nil_safe_casecmp(a, b)
|
411
|
+
if !a.nil? && !b.nil?
|
412
|
+
a.to_s.casecmp(b.to_s)
|
413
|
+
else
|
414
|
+
a.nil? ? 1 : -1
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
class InputIterator
|
419
|
+
include Enumerable
|
420
|
+
|
421
|
+
def initialize(input)
|
422
|
+
@input = if input.is_a?(Array)
|
423
|
+
input.flatten
|
424
|
+
elsif input.is_a?(Hash)
|
425
|
+
[input]
|
426
|
+
elsif input.is_a?(Enumerable)
|
427
|
+
input
|
428
|
+
else
|
429
|
+
Array(input)
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
def join(glue)
|
434
|
+
to_a.join(glue.to_s)
|
435
|
+
end
|
436
|
+
|
437
|
+
def concat(args)
|
438
|
+
to_a.concat(args)
|
439
|
+
end
|
440
|
+
|
441
|
+
def reverse
|
442
|
+
reverse_each.to_a
|
443
|
+
end
|
444
|
+
|
445
|
+
def uniq(&block)
|
446
|
+
to_a.uniq(&block)
|
447
|
+
end
|
448
|
+
|
449
|
+
def compact
|
450
|
+
to_a.compact
|
451
|
+
end
|
452
|
+
|
453
|
+
def empty?
|
454
|
+
@input.each { return false }
|
455
|
+
true
|
456
|
+
end
|
457
|
+
|
458
|
+
def each
|
459
|
+
@input.each do |e|
|
460
|
+
yield(e.respond_to?(:to_liquid) ? e.to_liquid : e)
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
def where(property, target_value)
|
465
|
+
select do |item|
|
466
|
+
item[property] == target_value
|
467
|
+
end
|
468
|
+
rescue TypeError
|
469
|
+
# Cannot index with the given property type (eg. indexing integers with strings
|
470
|
+
# which are only allowed to be indexed by other integers).
|
471
|
+
raise ArgumentError.new("cannot select the property `#{property}`")
|
472
|
+
end
|
473
|
+
|
474
|
+
def where_present(property)
|
475
|
+
select { |item| item[property] }
|
476
|
+
rescue TypeError
|
477
|
+
# Cannot index with the given property type (eg. indexing integers with strings
|
478
|
+
# which are only allowed to be indexed by other integers).
|
479
|
+
raise ArgumentError.new("cannot select the property `#{property}`")
|
480
|
+
end
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
Template.register_filter(StandardFilters)
|
485
|
+
end
|