liquid 2.6.1 → 4.0.3
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 +5 -5
- data/History.md +194 -29
- data/{MIT-LICENSE → LICENSE} +0 -0
- data/README.md +60 -2
- data/lib/liquid.rb +25 -14
- data/lib/liquid/block.rb +47 -96
- data/lib/liquid/block_body.rb +143 -0
- data/lib/liquid/condition.rb +70 -39
- data/lib/liquid/context.rb +116 -157
- data/lib/liquid/document.rb +19 -9
- data/lib/liquid/drop.rb +31 -14
- data/lib/liquid/errors.rb +54 -10
- data/lib/liquid/expression.rb +49 -0
- data/lib/liquid/extensions.rb +19 -7
- data/lib/liquid/file_system.rb +25 -14
- data/lib/liquid/forloop_drop.rb +42 -0
- data/lib/liquid/i18n.rb +39 -0
- data/lib/liquid/interrupts.rb +2 -3
- 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 +311 -77
- data/lib/liquid/strainer.rb +39 -26
- data/lib/liquid/tablerowloop_drop.rb +62 -0
- data/lib/liquid/tag.rb +28 -11
- data/lib/liquid/tags/assign.rb +34 -10
- data/lib/liquid/tags/break.rb +1 -4
- data/lib/liquid/tags/capture.rb +11 -9
- data/lib/liquid/tags/case.rb +37 -22
- data/lib/liquid/tags/comment.rb +10 -3
- data/lib/liquid/tags/continue.rb +1 -4
- data/lib/liquid/tags/cycle.rb +20 -14
- data/lib/liquid/tags/decrement.rb +4 -8
- data/lib/liquid/tags/for.rb +121 -60
- data/lib/liquid/tags/if.rb +73 -30
- data/lib/liquid/tags/ifchanged.rb +3 -5
- data/lib/liquid/tags/include.rb +77 -46
- data/lib/liquid/tags/increment.rb +4 -8
- data/lib/liquid/tags/raw.rb +35 -10
- data/lib/liquid/tags/table_row.rb +62 -0
- data/lib/liquid/tags/unless.rb +6 -9
- data/lib/liquid/template.rb +130 -32
- data/lib/liquid/tokenizer.rb +31 -0
- data/lib/liquid/truffle.rb +5 -0
- data/lib/liquid/utils.rb +57 -4
- data/lib/liquid/variable.rb +121 -30
- data/lib/liquid/variable_lookup.rb +88 -0
- data/lib/liquid/version.rb +2 -1
- 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/{liquid → integration}/capture_test.rb +13 -3
- 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 +776 -0
- data/test/{liquid → integration}/tags/break_tag_test.rb +2 -3
- data/test/{liquid → integration}/tags/continue_tag_test.rb +1 -2
- 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 +253 -0
- data/test/integration/tags/increment_tag_test.rb +23 -0
- data/test/{liquid → integration}/tags/raw_tag_test.rb +9 -2
- data/test/integration/tags/standard_tag_test.rb +296 -0
- data/test/integration/tags/statements_test.rb +111 -0
- data/test/{liquid/tags/html_tag_test.rb → integration/tags/table_row_test.rb} +25 -24
- 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 +106 -19
- data/test/truffle/truffle_test.rb +9 -0
- data/test/{liquid/block_test.rb → unit/block_unit_test.rb} +9 -9
- data/test/unit/condition_unit_test.rb +166 -0
- data/test/{liquid/context_test.rb → unit/context_unit_test.rb} +85 -74
- 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/{liquid/regexp_test.rb → unit/regexp_unit_test.rb} +4 -4
- 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 +157 -77
- data/lib/extras/liquid_view.rb +0 -51
- data/lib/liquid/htmltags.rb +0 -74
- data/lib/liquid/module_ex.rb +0 -62
- data/test/liquid/assign_test.rb +0 -21
- data/test/liquid/condition_test.rb +0 -127
- data/test/liquid/drop_test.rb +0 -180
- data/test/liquid/error_handling_test.rb +0 -81
- data/test/liquid/file_system_test.rb +0 -29
- data/test/liquid/filter_test.rb +0 -125
- data/test/liquid/hash_ordering_test.rb +0 -25
- data/test/liquid/module_ex_test.rb +0 -87
- data/test/liquid/output_test.rb +0 -116
- data/test/liquid/parsing_quirks_test.rb +0 -52
- data/test/liquid/security_test.rb +0 -64
- data/test/liquid/standard_filter_test.rb +0 -251
- data/test/liquid/strainer_test.rb +0 -52
- data/test/liquid/tags/for_tag_test.rb +0 -297
- data/test/liquid/tags/if_else_tag_test.rb +0 -166
- data/test/liquid/tags/include_tag_test.rb +0 -166
- data/test/liquid/tags/increment_tag_test.rb +0 -24
- data/test/liquid/tags/standard_tag_test.rb +0 -295
- data/test/liquid/tags/statements_test.rb +0 -134
- data/test/liquid/tags/unless_else_tag_test.rb +0 -26
- data/test/liquid/template_test.rb +0 -146
- data/test/liquid/variable_test.rb +0 -186
@@ -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
|
@@ -2,12 +2,24 @@ require 'cgi'
|
|
2
2
|
require 'bigdecimal'
|
3
3
|
|
4
4
|
module Liquid
|
5
|
-
|
6
5
|
module StandardFilters
|
6
|
+
HTML_ESCAPE = {
|
7
|
+
'&'.freeze => '&'.freeze,
|
8
|
+
'>'.freeze => '>'.freeze,
|
9
|
+
'<'.freeze => '<'.freeze,
|
10
|
+
'"'.freeze => '"'.freeze,
|
11
|
+
"'".freeze => '''.freeze
|
12
|
+
}.freeze
|
13
|
+
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
|
14
|
+
STRIP_HTML_BLOCKS = Regexp.union(
|
15
|
+
/<script.*?<\/script>/m,
|
16
|
+
/<!--.*?-->/m,
|
17
|
+
/<style.*?<\/style>/m
|
18
|
+
)
|
19
|
+
STRIP_HTML_TAGS = /<.*?>/m
|
7
20
|
|
8
21
|
# Return the size of an array or of an string
|
9
22
|
def size(input)
|
10
|
-
|
11
23
|
input.respond_to?(:size) ? input.size : 0
|
12
24
|
end
|
13
25
|
|
@@ -27,32 +39,56 @@ module Liquid
|
|
27
39
|
end
|
28
40
|
|
29
41
|
def escape(input)
|
30
|
-
CGI.escapeHTML(input)
|
42
|
+
CGI.escapeHTML(input.to_s).untaint unless input.nil?
|
31
43
|
end
|
44
|
+
alias_method :h, :escape
|
32
45
|
|
33
46
|
def escape_once(input)
|
34
|
-
|
35
|
-
rescue NameError
|
36
|
-
input
|
47
|
+
input.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
|
37
48
|
end
|
38
49
|
|
39
|
-
|
50
|
+
def url_encode(input)
|
51
|
+
CGI.escape(input.to_s) unless input.nil?
|
52
|
+
end
|
53
|
+
|
54
|
+
def url_decode(input)
|
55
|
+
return if input.nil?
|
56
|
+
|
57
|
+
result = CGI.unescape(input.to_s)
|
58
|
+
raise Liquid::ArgumentError, "invalid byte sequence in #{result.encoding}" unless result.valid_encoding?
|
59
|
+
|
60
|
+
result
|
61
|
+
end
|
62
|
+
|
63
|
+
def slice(input, offset, length = nil)
|
64
|
+
offset = Utils.to_integer(offset)
|
65
|
+
length = length ? Utils.to_integer(length) : 1
|
66
|
+
|
67
|
+
if input.is_a?(Array)
|
68
|
+
input.slice(offset, length) || []
|
69
|
+
else
|
70
|
+
input.to_s.slice(offset, length) || ''
|
71
|
+
end
|
72
|
+
end
|
40
73
|
|
41
74
|
# Truncate a string down to x characters
|
42
|
-
def truncate(input, length = 50, truncate_string = "...")
|
43
|
-
if input.nil?
|
44
|
-
|
75
|
+
def truncate(input, length = 50, truncate_string = "...".freeze)
|
76
|
+
return if input.nil?
|
77
|
+
input_str = input.to_s
|
78
|
+
length = Utils.to_integer(length)
|
79
|
+
truncate_string_str = truncate_string.to_s
|
80
|
+
l = length - truncate_string_str.length
|
45
81
|
l = 0 if l < 0
|
46
|
-
|
47
|
-
input.length > length.to_i ? truncated + truncate_string : input
|
82
|
+
input_str.length > length ? input_str[0...l] + truncate_string_str : input_str
|
48
83
|
end
|
49
84
|
|
50
|
-
def truncatewords(input, words = 15, truncate_string = "...")
|
51
|
-
if input.nil?
|
85
|
+
def truncatewords(input, words = 15, truncate_string = "...".freeze)
|
86
|
+
return if input.nil?
|
52
87
|
wordlist = input.to_s.split
|
53
|
-
|
88
|
+
words = Utils.to_integer(words)
|
89
|
+
l = words - 1
|
54
90
|
l = 0 if l < 0
|
55
|
-
wordlist.length > l ? wordlist[0..l].join(" ") + truncate_string : input
|
91
|
+
wordlist.length > l ? wordlist[0..l].join(" ".freeze) + truncate_string.to_s : input
|
56
92
|
end
|
57
93
|
|
58
94
|
# Split input string into an array of substrings separated by given pattern.
|
@@ -61,75 +97,176 @@ module Liquid
|
|
61
97
|
# <div class="summary">{{ post | split '//' | first }}</div>
|
62
98
|
#
|
63
99
|
def split(input, pattern)
|
64
|
-
input.split(pattern)
|
100
|
+
input.to_s.split(pattern.to_s)
|
101
|
+
end
|
102
|
+
|
103
|
+
def strip(input)
|
104
|
+
input.to_s.strip
|
105
|
+
end
|
106
|
+
|
107
|
+
def lstrip(input)
|
108
|
+
input.to_s.lstrip
|
109
|
+
end
|
110
|
+
|
111
|
+
def rstrip(input)
|
112
|
+
input.to_s.rstrip
|
65
113
|
end
|
66
114
|
|
67
115
|
def strip_html(input)
|
68
|
-
|
116
|
+
empty = ''.freeze
|
117
|
+
result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
|
118
|
+
result.gsub!(STRIP_HTML_TAGS, empty)
|
119
|
+
result
|
69
120
|
end
|
70
121
|
|
71
122
|
# Remove all newlines from the string
|
72
123
|
def strip_newlines(input)
|
73
|
-
input.to_s.gsub(/\r?\n/, '')
|
124
|
+
input.to_s.gsub(/\r?\n/, ''.freeze)
|
74
125
|
end
|
75
126
|
|
76
127
|
# Join elements of the array with certain character between them
|
77
|
-
def join(input, glue = ' ')
|
78
|
-
|
128
|
+
def join(input, glue = ' '.freeze)
|
129
|
+
InputIterator.new(input).join(glue)
|
79
130
|
end
|
80
131
|
|
81
132
|
# Sort elements of the array
|
82
133
|
# provide optional property with which to sort an array of hashes or drops
|
83
134
|
def sort(input, property = nil)
|
84
|
-
ary =
|
135
|
+
ary = InputIterator.new(input)
|
136
|
+
|
137
|
+
return [] if ary.empty?
|
138
|
+
|
85
139
|
if property.nil?
|
86
|
-
ary.sort
|
87
|
-
|
88
|
-
|
89
|
-
elsif ary.
|
90
|
-
|
140
|
+
ary.sort do |a, b|
|
141
|
+
nil_safe_compare(a, b)
|
142
|
+
end
|
143
|
+
elsif ary.all? { |el| el.respond_to?(:[]) }
|
144
|
+
begin
|
145
|
+
ary.sort { |a, b| nil_safe_compare(a[property], b[property]) }
|
146
|
+
rescue TypeError
|
147
|
+
raise_property_error(property)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Sort elements of an array ignoring case if strings
|
153
|
+
# provide optional property with which to sort an array of hashes or drops
|
154
|
+
def sort_natural(input, property = nil)
|
155
|
+
ary = InputIterator.new(input)
|
156
|
+
|
157
|
+
return [] if ary.empty?
|
158
|
+
|
159
|
+
if property.nil?
|
160
|
+
ary.sort do |a, b|
|
161
|
+
nil_safe_casecmp(a, b)
|
162
|
+
end
|
163
|
+
elsif ary.all? { |el| el.respond_to?(:[]) }
|
164
|
+
begin
|
165
|
+
ary.sort { |a, b| nil_safe_casecmp(a[property], b[property]) }
|
166
|
+
rescue TypeError
|
167
|
+
raise_property_error(property)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Filter the elements of an array to those with a certain property value.
|
173
|
+
# By default the target is any truthy value.
|
174
|
+
def where(input, property, target_value = nil)
|
175
|
+
ary = InputIterator.new(input)
|
176
|
+
|
177
|
+
if ary.empty?
|
178
|
+
[]
|
179
|
+
elsif ary.first.respond_to?(:[]) && target_value.nil?
|
180
|
+
begin
|
181
|
+
ary.select { |item| item[property] }
|
182
|
+
rescue TypeError
|
183
|
+
raise_property_error(property)
|
184
|
+
end
|
185
|
+
elsif ary.first.respond_to?(:[])
|
186
|
+
begin
|
187
|
+
ary.select { |item| item[property] == target_value }
|
188
|
+
rescue TypeError
|
189
|
+
raise_property_error(property)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Remove duplicate elements from an array
|
195
|
+
# provide optional property with which to determine uniqueness
|
196
|
+
def uniq(input, property = nil)
|
197
|
+
ary = InputIterator.new(input)
|
198
|
+
|
199
|
+
if property.nil?
|
200
|
+
ary.uniq
|
201
|
+
elsif ary.empty? # The next two cases assume a non-empty array.
|
202
|
+
[]
|
203
|
+
elsif ary.first.respond_to?(:[])
|
204
|
+
begin
|
205
|
+
ary.uniq { |a| a[property] }
|
206
|
+
rescue TypeError
|
207
|
+
raise_property_error(property)
|
208
|
+
end
|
91
209
|
end
|
92
210
|
end
|
93
211
|
|
94
212
|
# Reverse the elements of an array
|
95
213
|
def reverse(input)
|
96
|
-
ary =
|
214
|
+
ary = InputIterator.new(input)
|
97
215
|
ary.reverse
|
98
216
|
end
|
99
217
|
|
100
218
|
# map/collect on a given property
|
101
219
|
def map(input, property)
|
102
|
-
|
103
|
-
ary.map do |e|
|
220
|
+
InputIterator.new(input).map do |e|
|
104
221
|
e = e.call if e.is_a?(Proc)
|
105
|
-
e = e.to_liquid if e.respond_to?(:to_liquid)
|
106
222
|
|
107
|
-
if property == "to_liquid"
|
223
|
+
if property == "to_liquid".freeze
|
108
224
|
e
|
109
225
|
elsif e.respond_to?(:[])
|
110
|
-
e[property]
|
226
|
+
r = e[property]
|
227
|
+
r.is_a?(Proc) ? r.call : r
|
228
|
+
end
|
229
|
+
end
|
230
|
+
rescue TypeError
|
231
|
+
raise_property_error(property)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Remove nils within an array
|
235
|
+
# provide optional property with which to check for nil
|
236
|
+
def compact(input, property = nil)
|
237
|
+
ary = InputIterator.new(input)
|
238
|
+
|
239
|
+
if property.nil?
|
240
|
+
ary.compact
|
241
|
+
elsif ary.empty? # The next two cases assume a non-empty array.
|
242
|
+
[]
|
243
|
+
elsif ary.first.respond_to?(:[])
|
244
|
+
begin
|
245
|
+
ary.reject { |a| a[property].nil? }
|
246
|
+
rescue TypeError
|
247
|
+
raise_property_error(property)
|
111
248
|
end
|
112
249
|
end
|
113
250
|
end
|
114
251
|
|
115
252
|
# Replace occurrences of a string with another
|
116
|
-
def replace(input, string, replacement = '')
|
117
|
-
input.to_s.gsub(string, replacement.to_s)
|
253
|
+
def replace(input, string, replacement = ''.freeze)
|
254
|
+
input.to_s.gsub(string.to_s, replacement.to_s)
|
118
255
|
end
|
119
256
|
|
120
257
|
# Replace the first occurrences of a string with another
|
121
|
-
def replace_first(input, string, replacement = '')
|
122
|
-
input.to_s.sub(string, replacement.to_s)
|
258
|
+
def replace_first(input, string, replacement = ''.freeze)
|
259
|
+
input.to_s.sub(string.to_s, replacement.to_s)
|
123
260
|
end
|
124
261
|
|
125
262
|
# remove a substring
|
126
263
|
def remove(input, string)
|
127
|
-
input.to_s.gsub(string, '')
|
264
|
+
input.to_s.gsub(string.to_s, ''.freeze)
|
128
265
|
end
|
129
266
|
|
130
267
|
# remove the first occurrences of a substring
|
131
268
|
def remove_first(input, string)
|
132
|
-
input.to_s.sub(string, '')
|
269
|
+
input.to_s.sub(string.to_s, ''.freeze)
|
133
270
|
end
|
134
271
|
|
135
272
|
# add one string to another
|
@@ -137,6 +274,13 @@ module Liquid
|
|
137
274
|
input.to_s + string.to_s
|
138
275
|
end
|
139
276
|
|
277
|
+
def concat(input, array)
|
278
|
+
unless array.respond_to?(:to_ary)
|
279
|
+
raise ArgumentError.new("concat filter requires an array argument")
|
280
|
+
end
|
281
|
+
InputIterator.new(input).concat(array)
|
282
|
+
end
|
283
|
+
|
140
284
|
# prepend a string to another
|
141
285
|
def prepend(input, string)
|
142
286
|
string.to_s + input.to_s
|
@@ -144,10 +288,10 @@ module Liquid
|
|
144
288
|
|
145
289
|
# Add <br /> tags in front of all newlines in input string
|
146
290
|
def newline_to_br(input)
|
147
|
-
input.to_s.gsub(/\n/, "<br />\n")
|
291
|
+
input.to_s.gsub(/\n/, "<br />\n".freeze)
|
148
292
|
end
|
149
293
|
|
150
|
-
# Reformat a date
|
294
|
+
# Reformat a date using Ruby's core Time#strftime( string ) -> string
|
151
295
|
#
|
152
296
|
# %a - The abbreviated weekday name (``Sun'')
|
153
297
|
# %A - The full weekday name (``Sunday'')
|
@@ -161,6 +305,7 @@ module Liquid
|
|
161
305
|
# %m - Month of the year (01..12)
|
162
306
|
# %M - Minute of the hour (00..59)
|
163
307
|
# %p - Meridian indicator (``AM'' or ``PM'')
|
308
|
+
# %s - Number of seconds since 1970-01-01 00:00:00 UTC.
|
164
309
|
# %S - Second of the minute (00..60)
|
165
310
|
# %U - Week number of the current year,
|
166
311
|
# starting with the first Sunday as the first
|
@@ -175,34 +320,14 @@ module Liquid
|
|
175
320
|
# %Y - Year with century
|
176
321
|
# %Z - Time zone name
|
177
322
|
# %% - Literal ``%'' character
|
323
|
+
#
|
324
|
+
# See also: http://www.ruby-doc.org/core/Time.html#method-i-strftime
|
178
325
|
def date(input, format)
|
326
|
+
return input if format.to_s.empty?
|
179
327
|
|
180
|
-
|
181
|
-
return input.to_s
|
182
|
-
end
|
183
|
-
|
184
|
-
if ((input.is_a?(String) && !/^\d+$/.match(input.to_s).nil?) || input.is_a?(Integer)) && input.to_i > 0
|
185
|
-
input = Time.at(input.to_i)
|
186
|
-
end
|
187
|
-
|
188
|
-
date = if input.is_a?(String)
|
189
|
-
case input.downcase
|
190
|
-
when 'now', 'today'
|
191
|
-
Time.now
|
192
|
-
else
|
193
|
-
Time.parse(input)
|
194
|
-
end
|
195
|
-
else
|
196
|
-
input
|
197
|
-
end
|
328
|
+
return input unless date = Utils.to_date(input)
|
198
329
|
|
199
|
-
|
200
|
-
date.strftime(format.to_s)
|
201
|
-
else
|
202
|
-
input
|
203
|
-
end
|
204
|
-
rescue
|
205
|
-
input
|
330
|
+
date.strftime(format.to_s)
|
206
331
|
end
|
207
332
|
|
208
333
|
# Get the first element of the passed in array
|
@@ -223,6 +348,12 @@ module Liquid
|
|
223
348
|
array.last if array.respond_to?(:last)
|
224
349
|
end
|
225
350
|
|
351
|
+
# absolute value
|
352
|
+
def abs(input)
|
353
|
+
result = Utils.to_number(input).abs
|
354
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
355
|
+
end
|
356
|
+
|
226
357
|
# addition
|
227
358
|
def plus(input, operand)
|
228
359
|
apply_operation(input, operand, :+)
|
@@ -241,31 +372,134 @@ module Liquid
|
|
241
372
|
# division
|
242
373
|
def divided_by(input, operand)
|
243
374
|
apply_operation(input, operand, :/)
|
375
|
+
rescue ::ZeroDivisionError => e
|
376
|
+
raise Liquid::ZeroDivisionError, e.message
|
244
377
|
end
|
245
378
|
|
246
379
|
def modulo(input, operand)
|
247
380
|
apply_operation(input, operand, :%)
|
381
|
+
rescue ::ZeroDivisionError => e
|
382
|
+
raise Liquid::ZeroDivisionError, e.message
|
248
383
|
end
|
249
384
|
|
250
|
-
|
385
|
+
def round(input, n = 0)
|
386
|
+
result = Utils.to_number(input).round(Utils.to_number(n))
|
387
|
+
result = result.to_f if result.is_a?(BigDecimal)
|
388
|
+
result = result.to_i if n == 0
|
389
|
+
result
|
390
|
+
rescue ::FloatDomainError => e
|
391
|
+
raise Liquid::FloatDomainError, e.message
|
392
|
+
end
|
393
|
+
|
394
|
+
def ceil(input)
|
395
|
+
Utils.to_number(input).ceil.to_i
|
396
|
+
rescue ::FloatDomainError => e
|
397
|
+
raise Liquid::FloatDomainError, e.message
|
398
|
+
end
|
251
399
|
|
252
|
-
def
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
400
|
+
def floor(input)
|
401
|
+
Utils.to_number(input).floor.to_i
|
402
|
+
rescue ::FloatDomainError => e
|
403
|
+
raise Liquid::FloatDomainError, e.message
|
404
|
+
end
|
405
|
+
|
406
|
+
def at_least(input, n)
|
407
|
+
min_value = Utils.to_number(n)
|
408
|
+
|
409
|
+
result = Utils.to_number(input)
|
410
|
+
result = min_value if min_value > result
|
411
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
412
|
+
end
|
413
|
+
|
414
|
+
def at_most(input, n)
|
415
|
+
max_value = Utils.to_number(n)
|
416
|
+
|
417
|
+
result = Utils.to_number(input)
|
418
|
+
result = max_value if max_value < result
|
419
|
+
result.is_a?(BigDecimal) ? result.to_f : result
|
420
|
+
end
|
421
|
+
|
422
|
+
def default(input, default_value = ''.freeze)
|
423
|
+
if !input || input.respond_to?(:empty?) && input.empty?
|
424
|
+
default_value
|
260
425
|
else
|
261
|
-
|
426
|
+
input
|
262
427
|
end
|
263
428
|
end
|
264
429
|
|
430
|
+
private
|
431
|
+
|
432
|
+
def raise_property_error(property)
|
433
|
+
raise Liquid::ArgumentError.new("cannot select the property '#{property}'")
|
434
|
+
end
|
435
|
+
|
265
436
|
def apply_operation(input, operand, operation)
|
266
|
-
result = to_number(input).send(operation, to_number(operand))
|
437
|
+
result = Utils.to_number(input).send(operation, Utils.to_number(operand))
|
267
438
|
result.is_a?(BigDecimal) ? result.to_f : result
|
268
439
|
end
|
440
|
+
|
441
|
+
def nil_safe_compare(a, b)
|
442
|
+
if !a.nil? && !b.nil?
|
443
|
+
a <=> b
|
444
|
+
else
|
445
|
+
a.nil? ? 1 : -1
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
def nil_safe_casecmp(a, b)
|
450
|
+
if !a.nil? && !b.nil?
|
451
|
+
a.to_s.casecmp(b.to_s)
|
452
|
+
else
|
453
|
+
a.nil? ? 1 : -1
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
class InputIterator
|
458
|
+
include Enumerable
|
459
|
+
|
460
|
+
def initialize(input)
|
461
|
+
@input = if input.is_a?(Array)
|
462
|
+
input.flatten
|
463
|
+
elsif input.is_a?(Hash)
|
464
|
+
[input]
|
465
|
+
elsif input.is_a?(Enumerable)
|
466
|
+
input
|
467
|
+
else
|
468
|
+
Array(input)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
def join(glue)
|
473
|
+
to_a.join(glue.to_s)
|
474
|
+
end
|
475
|
+
|
476
|
+
def concat(args)
|
477
|
+
to_a.concat(args)
|
478
|
+
end
|
479
|
+
|
480
|
+
def reverse
|
481
|
+
reverse_each.to_a
|
482
|
+
end
|
483
|
+
|
484
|
+
def uniq(&block)
|
485
|
+
to_a.uniq(&block)
|
486
|
+
end
|
487
|
+
|
488
|
+
def compact
|
489
|
+
to_a.compact
|
490
|
+
end
|
491
|
+
|
492
|
+
def empty?
|
493
|
+
@input.each { return false }
|
494
|
+
true
|
495
|
+
end
|
496
|
+
|
497
|
+
def each
|
498
|
+
@input.each do |e|
|
499
|
+
yield(e.respond_to?(:to_liquid) ? e.to_liquid : e)
|
500
|
+
end
|
501
|
+
end
|
502
|
+
end
|
269
503
|
end
|
270
504
|
|
271
505
|
Template.register_filter(StandardFilters)
|