liquid 2.1.3 → 2.2.0
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.
- data/Rakefile +10 -11
- data/lib/liquid.rb +1 -1
- data/lib/liquid/context.rb +122 -127
- data/lib/liquid/standardfilters.rb +66 -62
- data/lib/liquid/strainer.rb +3 -0
- metadata +5 -5
data/Rakefile
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
$:.unshift File.join(File.dirname(__FILE__), 'test') unless $:.include? File.join(File.dirname(__FILE__), 'test')
|
3
|
+
|
2
4
|
require 'rubygems'
|
3
5
|
require 'rake'
|
4
6
|
require 'rake/testtask'
|
@@ -7,9 +9,8 @@ require 'rake/gempackagetask'
|
|
7
9
|
task :default => 'test'
|
8
10
|
|
9
11
|
Rake::TestTask.new(:test) do |t|
|
10
|
-
t.libs <<
|
11
|
-
t.
|
12
|
-
t.pattern = 'test/*_test.rb'
|
12
|
+
t.libs << '.' << 'lib' << 'test'
|
13
|
+
t.pattern = 'test/lib/**/*_test.rb'
|
13
14
|
t.verbose = false
|
14
15
|
end
|
15
16
|
|
@@ -25,20 +26,18 @@ end
|
|
25
26
|
|
26
27
|
namespace :profile do
|
27
28
|
|
28
|
-
|
29
29
|
task :default => [:run]
|
30
|
-
|
30
|
+
|
31
31
|
desc "Run the liquid profile/perforamce coverage"
|
32
32
|
task :run do
|
33
|
-
|
33
|
+
|
34
34
|
ruby "performance/shopify.rb"
|
35
|
-
|
35
|
+
|
36
36
|
end
|
37
|
-
|
38
|
-
desc "Run KCacheGrind"
|
37
|
+
|
38
|
+
desc "Run KCacheGrind"
|
39
39
|
task :grind => :run do
|
40
40
|
system "kcachegrind /tmp/liquid.rubyprof_calltreeprinter.txt"
|
41
41
|
end
|
42
|
+
|
42
43
|
end
|
43
|
-
|
44
|
-
|
data/lib/liquid.rb
CHANGED
@@ -38,7 +38,7 @@ module Liquid
|
|
38
38
|
StrictQuotedFragment = /"[^"]+"|'[^']+'|[^\s,\|,\:,\,]+/
|
39
39
|
FirstFilterArgument = /#{FilterArgumentSeparator}(?:#{StrictQuotedFragment})/
|
40
40
|
OtherFilterArgument = /#{ArgumentSeparator}(?:#{StrictQuotedFragment})/
|
41
|
-
SpacelessFilter =
|
41
|
+
SpacelessFilter = /^(?:'[^']+'|"[^"]+"|[^'"])*#{FilterSeparator}(?:#{StrictQuotedFragment})(?:#{FirstFilterArgument}(?:#{OtherFilterArgument})*)?/
|
42
42
|
Expression = /(?:#{QuotedFragment}(?:#{SpacelessFilter})*)/
|
43
43
|
TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/
|
44
44
|
AnyStartingTag = /\{\{|\{\%/
|
data/lib/liquid/context.rb
CHANGED
@@ -28,8 +28,9 @@ module Liquid
|
|
28
28
|
@strainer ||= Strainer.create(self)
|
29
29
|
end
|
30
30
|
|
31
|
-
#
|
32
|
-
#
|
31
|
+
# Adds filters to this context.
|
32
|
+
#
|
33
|
+
# Note that this does not register the filters with the main Template object. see <tt>Template.register_filter</tt>
|
33
34
|
# for that
|
34
35
|
def add_filters(filters)
|
35
36
|
filters = [filters].flatten.compact
|
@@ -52,7 +53,6 @@ module Liquid
|
|
52
53
|
end
|
53
54
|
end
|
54
55
|
|
55
|
-
|
56
56
|
def invoke(method, *args)
|
57
57
|
if strainer.respond_to?(method)
|
58
58
|
strainer.__send__(method, *args)
|
@@ -61,43 +61,44 @@ module Liquid
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
-
#
|
64
|
+
# Push new local scope on the stack. use <tt>Context#stack</tt> instead
|
65
65
|
def push(new_scope={})
|
66
66
|
raise StackLevelError, "Nesting too deep" if @scopes.length > 100
|
67
67
|
@scopes.unshift(new_scope)
|
68
68
|
end
|
69
69
|
|
70
|
-
#
|
70
|
+
# Merge a hash of variables in the current local scope
|
71
71
|
def merge(new_scopes)
|
72
72
|
@scopes[0].merge!(new_scopes)
|
73
73
|
end
|
74
74
|
|
75
|
-
#
|
75
|
+
# Pop from the stack. use <tt>Context#stack</tt> instead
|
76
76
|
def pop
|
77
77
|
raise ContextError if @scopes.size == 1
|
78
78
|
@scopes.shift
|
79
79
|
end
|
80
80
|
|
81
|
-
#
|
81
|
+
# Pushes a new local scope on the stack, pops it at the end of the block
|
82
82
|
#
|
83
83
|
# Example:
|
84
|
-
#
|
85
84
|
# context.stack do
|
86
85
|
# context['var'] = 'hi'
|
87
86
|
# end
|
88
|
-
# context['var] #=> nil
|
89
87
|
#
|
88
|
+
# context['var] #=> nil
|
90
89
|
def stack(new_scope={},&block)
|
91
90
|
result = nil
|
92
91
|
push(new_scope)
|
92
|
+
|
93
93
|
begin
|
94
94
|
result = yield
|
95
95
|
ensure
|
96
96
|
pop
|
97
97
|
end
|
98
|
+
|
98
99
|
result
|
99
100
|
end
|
100
|
-
|
101
|
+
|
101
102
|
def clear_instance_assigns
|
102
103
|
@scopes[0] = {}
|
103
104
|
end
|
@@ -116,139 +117,133 @@ module Liquid
|
|
116
117
|
end
|
117
118
|
|
118
119
|
private
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
(resolve($1).to_i..resolve($2).to_i)
|
153
|
-
# Floats
|
154
|
-
when /^(\d[\d\.]+)$/
|
155
|
-
$1.to_f
|
156
|
-
else
|
157
|
-
variable(key)
|
120
|
+
# Look up variable, either resolve directly after considering the name. We can directly handle
|
121
|
+
# Strings, digits, floats and booleans (true,false).
|
122
|
+
# If no match is made we lookup the variable in the current scope and
|
123
|
+
# later move up to the parent blocks to see if we can resolve the variable somewhere up the tree.
|
124
|
+
# Some special keywords return symbols. Those symbols are to be called on the rhs object in expressions
|
125
|
+
#
|
126
|
+
# Example:
|
127
|
+
# products == empty #=> products.empty?
|
128
|
+
def resolve(key)
|
129
|
+
case key
|
130
|
+
when nil, 'nil', 'null', ''
|
131
|
+
nil
|
132
|
+
when 'true'
|
133
|
+
true
|
134
|
+
when 'false'
|
135
|
+
false
|
136
|
+
when 'blank'
|
137
|
+
:blank?
|
138
|
+
when 'empty' # Single quoted strings
|
139
|
+
:empty?
|
140
|
+
when /^'(.*)'$/ # Double quoted strings
|
141
|
+
$1.to_s
|
142
|
+
when /^"(.*)"$/ # Integer and floats
|
143
|
+
$1.to_s
|
144
|
+
when /^(\d+)$/ # Ranges
|
145
|
+
$1.to_i
|
146
|
+
when /^\((\S+)\.\.(\S+)\)$/ # Floats
|
147
|
+
(resolve($1).to_i..resolve($2).to_i)
|
148
|
+
when /^(\d[\d\.]+)$/
|
149
|
+
$1.to_f
|
150
|
+
else
|
151
|
+
variable(key)
|
152
|
+
end
|
158
153
|
end
|
159
|
-
end
|
160
154
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
155
|
+
# Fetches an object starting at the local scope and then moving up the hierachy
|
156
|
+
def find_variable(key)
|
157
|
+
scope = @scopes.find { |s| s.has_key?(key) }
|
158
|
+
|
159
|
+
if scope.nil?
|
160
|
+
@environments.each do |e|
|
161
|
+
if variable = lookup_and_evaluate(e, key)
|
162
|
+
scope = e
|
163
|
+
break
|
164
|
+
end
|
170
165
|
end
|
171
166
|
end
|
172
|
-
end
|
173
|
-
scope ||= @environments.last || @scopes.last
|
174
|
-
variable ||= lookup_and_evaluate(scope, key)
|
175
|
-
|
176
|
-
variable = variable.to_liquid
|
177
|
-
variable.context = self if variable.respond_to?(:context=)
|
178
|
-
return variable
|
179
|
-
end
|
180
167
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
# assert_equal 'tobi', @context['hash.name']
|
187
|
-
# assert_equal 'tobi', @context['hash["name"]']
|
188
|
-
#
|
189
|
-
def variable(markup)
|
190
|
-
parts = markup.scan(VariableParser)
|
191
|
-
square_bracketed = /^\[(.*)\]$/
|
168
|
+
scope ||= @environments.last || @scopes.last
|
169
|
+
variable ||= lookup_and_evaluate(scope, key)
|
170
|
+
|
171
|
+
variable = variable.to_liquid
|
172
|
+
variable.context = self if variable.respond_to?(:context=)
|
192
173
|
|
193
|
-
|
194
|
-
if first_part =~ square_bracketed
|
195
|
-
first_part = resolve($1)
|
174
|
+
return variable
|
196
175
|
end
|
197
176
|
|
198
|
-
|
177
|
+
# Resolves namespaced queries gracefully.
|
178
|
+
#
|
179
|
+
# Example
|
180
|
+
# @context['hash'] = {"name" => 'tobi'}
|
181
|
+
# assert_equal 'tobi', @context['hash.name']
|
182
|
+
# assert_equal 'tobi', @context['hash["name"]']
|
183
|
+
def variable(markup)
|
184
|
+
parts = markup.scan(VariableParser)
|
185
|
+
square_bracketed = /^\[(.*)\]$/
|
199
186
|
|
200
|
-
parts.
|
201
|
-
part = resolve($1) if part_resolved = (part =~ square_bracketed)
|
187
|
+
first_part = parts.shift
|
202
188
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
((object.respond_to?(:has_key?) and object.has_key?(part)) or
|
207
|
-
(object.respond_to?(:fetch) and part.is_a?(Integer)))
|
189
|
+
if first_part =~ square_bracketed
|
190
|
+
first_part = resolve($1)
|
191
|
+
end
|
208
192
|
|
209
|
-
|
210
|
-
res = lookup_and_evaluate(object, part)
|
211
|
-
object = res.to_liquid
|
193
|
+
if object = find_variable(first_part)
|
212
194
|
|
213
|
-
|
214
|
-
|
215
|
-
# as commands and call them on the current object
|
216
|
-
elsif !part_resolved and object.respond_to?(part) and ['size', 'first', 'last'].include?(part)
|
195
|
+
parts.each do |part|
|
196
|
+
part = resolve($1) if part_resolved = (part =~ square_bracketed)
|
217
197
|
|
218
|
-
object
|
198
|
+
# If object is a hash- or array-like object we look for the
|
199
|
+
# presence of the key and if its available we return it
|
200
|
+
if object.respond_to?(:[]) and
|
201
|
+
((object.respond_to?(:has_key?) and object.has_key?(part)) or
|
202
|
+
(object.respond_to?(:fetch) and part.is_a?(Integer)))
|
219
203
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
return nil
|
224
|
-
end
|
204
|
+
# if its a proc we will replace the entry with the proc
|
205
|
+
res = lookup_and_evaluate(object, part)
|
206
|
+
object = res.to_liquid
|
225
207
|
|
226
|
-
|
227
|
-
|
208
|
+
# Some special cases. If the part wasn't in square brackets and
|
209
|
+
# no key with the same name was found we interpret following calls
|
210
|
+
# as commands and call them on the current object
|
211
|
+
elsif !part_resolved and object.respond_to?(part) and ['size', 'first', 'last'].include?(part)
|
212
|
+
|
213
|
+
object = object.send(part.intern).to_liquid
|
214
|
+
|
215
|
+
# No key was present with the desired value and it wasn't one of the directly supported
|
216
|
+
# keywords either. The only thing we got left is to return nil
|
217
|
+
else
|
218
|
+
return nil
|
219
|
+
end
|
220
|
+
|
221
|
+
# If we are dealing with a drop here we have to
|
222
|
+
object.context = self if object.respond_to?(:context=)
|
223
|
+
end
|
228
224
|
end
|
229
|
-
end
|
230
225
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
226
|
+
object
|
227
|
+
end # variable
|
228
|
+
|
229
|
+
def lookup_and_evaluate(obj, key)
|
230
|
+
if (value = obj[key]).is_a?(Proc) && obj.respond_to?(:[]=)
|
231
|
+
obj[key] = (value.arity == 0) ? value.call : value.call(self)
|
232
|
+
else
|
233
|
+
value
|
234
|
+
end
|
235
|
+
end # lookup_and_evaluate
|
236
|
+
|
237
|
+
def squash_instance_assigns_with_environments
|
238
|
+
@scopes.last.each_key do |k|
|
239
|
+
@environments.each do |env|
|
240
|
+
if env.has_key?(k)
|
241
|
+
scopes.last[k] = lookup_and_evaluate(env, k)
|
242
|
+
break
|
243
|
+
end
|
248
244
|
end
|
249
245
|
end
|
250
|
-
end
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
end
|
246
|
+
end # squash_instance_assigns_with_environments
|
247
|
+
end # Context
|
248
|
+
|
249
|
+
end # Liquid
|
@@ -1,36 +1,40 @@
|
|
1
1
|
require 'cgi'
|
2
2
|
|
3
3
|
module Liquid
|
4
|
-
|
4
|
+
|
5
5
|
module StandardFilters
|
6
|
-
|
6
|
+
|
7
7
|
# Return the size of an array or of an string
|
8
8
|
def size(input)
|
9
|
-
|
9
|
+
|
10
10
|
input.respond_to?(:size) ? input.size : 0
|
11
|
-
end
|
12
|
-
|
11
|
+
end
|
12
|
+
|
13
13
|
# convert a input string to DOWNCASE
|
14
14
|
def downcase(input)
|
15
15
|
input.to_s.downcase
|
16
|
-
end
|
16
|
+
end
|
17
17
|
|
18
18
|
# convert a input string to UPCASE
|
19
19
|
def upcase(input)
|
20
20
|
input.to_s.upcase
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
# capitalize words in the input centence
|
24
24
|
def capitalize(input)
|
25
25
|
input.to_s.capitalize
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
def escape(input)
|
29
29
|
CGI.escapeHTML(input) rescue input
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
|
+
def escape_once(input)
|
33
|
+
ActionView::Helpers::TagHelper.escape_once(input) rescue input
|
34
|
+
end
|
35
|
+
|
32
36
|
alias_method :h, :escape
|
33
|
-
|
37
|
+
|
34
38
|
# Truncate a string down to x characters
|
35
39
|
def truncate(input, length = 50, truncate_string = "...")
|
36
40
|
if input.nil? then return end
|
@@ -44,19 +48,19 @@ module Liquid
|
|
44
48
|
wordlist = input.to_s.split
|
45
49
|
l = words.to_i - 1
|
46
50
|
l = 0 if l < 0
|
47
|
-
wordlist.length > l ? wordlist[0..l].join(" ") + truncate_string : input
|
51
|
+
wordlist.length > l ? wordlist[0..l].join(" ") + truncate_string : input
|
48
52
|
end
|
49
|
-
|
53
|
+
|
50
54
|
def strip_html(input)
|
51
55
|
input.to_s.gsub(/<script.*?<\/script>/, '').gsub(/<.*?>/, '')
|
52
|
-
end
|
53
|
-
|
56
|
+
end
|
57
|
+
|
54
58
|
# Remove all newlines from the string
|
55
|
-
def strip_newlines(input)
|
56
|
-
input.to_s.gsub(/\n/, '')
|
59
|
+
def strip_newlines(input)
|
60
|
+
input.to_s.gsub(/\n/, '')
|
57
61
|
end
|
58
|
-
|
59
|
-
|
62
|
+
|
63
|
+
|
60
64
|
# Join elements of the array with certain character between them
|
61
65
|
def join(input, glue = ' ')
|
62
66
|
[input].flatten.join(glue)
|
@@ -73,8 +77,8 @@ module Liquid
|
|
73
77
|
elsif ary.first.respond_to?(property)
|
74
78
|
ary.sort {|a,b| a.send(property) <=> b.send(property) }
|
75
79
|
end
|
76
|
-
end
|
77
|
-
|
80
|
+
end
|
81
|
+
|
78
82
|
# map/collect on a given property
|
79
83
|
def map(input, property)
|
80
84
|
ary = [input].flatten
|
@@ -84,42 +88,42 @@ module Liquid
|
|
84
88
|
ary.map {|e| e.send(property) }
|
85
89
|
end
|
86
90
|
end
|
87
|
-
|
91
|
+
|
88
92
|
# Replace occurrences of a string with another
|
89
93
|
def replace(input, string, replacement = '')
|
90
94
|
input.to_s.gsub(string, replacement)
|
91
95
|
end
|
92
|
-
|
96
|
+
|
93
97
|
# Replace the first occurrences of a string with another
|
94
98
|
def replace_first(input, string, replacement = '')
|
95
99
|
input.to_s.sub(string, replacement)
|
96
|
-
end
|
97
|
-
|
100
|
+
end
|
101
|
+
|
98
102
|
# remove a substring
|
99
103
|
def remove(input, string)
|
100
|
-
input.to_s.gsub(string, '')
|
104
|
+
input.to_s.gsub(string, '')
|
101
105
|
end
|
102
|
-
|
106
|
+
|
103
107
|
# remove the first occurrences of a substring
|
104
108
|
def remove_first(input, string)
|
105
|
-
input.to_s.sub(string, '')
|
106
|
-
end
|
107
|
-
|
109
|
+
input.to_s.sub(string, '')
|
110
|
+
end
|
111
|
+
|
108
112
|
# add one string to another
|
109
113
|
def append(input, string)
|
110
114
|
input.to_s + string.to_s
|
111
115
|
end
|
112
|
-
|
116
|
+
|
113
117
|
# prepend a string to another
|
114
118
|
def prepend(input, string)
|
115
119
|
string.to_s + input.to_s
|
116
120
|
end
|
117
|
-
|
121
|
+
|
118
122
|
# Add <br /> tags in front of all newlines in input string
|
119
|
-
def newline_to_br(input)
|
120
|
-
input.to_s.gsub(/\n/, "<br />\n")
|
123
|
+
def newline_to_br(input)
|
124
|
+
input.to_s.gsub(/\n/, "<br />\n")
|
121
125
|
end
|
122
|
-
|
126
|
+
|
123
127
|
# Reformat a date
|
124
128
|
#
|
125
129
|
# %a - The abbreviated weekday name (``Sun'')
|
@@ -149,74 +153,74 @@ module Liquid
|
|
149
153
|
# %Z - Time zone name
|
150
154
|
# %% - Literal ``%'' character
|
151
155
|
def date(input, format)
|
152
|
-
|
156
|
+
|
153
157
|
if format.to_s.empty?
|
154
158
|
return input.to_s
|
155
159
|
end
|
156
|
-
|
160
|
+
|
157
161
|
date = input.is_a?(String) ? Time.parse(input) : input
|
158
|
-
|
162
|
+
|
159
163
|
if date.respond_to?(:strftime)
|
160
164
|
date.strftime(format.to_s)
|
161
165
|
else
|
162
166
|
input
|
163
167
|
end
|
164
|
-
rescue => e
|
168
|
+
rescue => e
|
165
169
|
input
|
166
170
|
end
|
167
|
-
|
168
|
-
# Get the first element of the passed in array
|
169
|
-
#
|
171
|
+
|
172
|
+
# Get the first element of the passed in array
|
173
|
+
#
|
170
174
|
# Example:
|
171
175
|
# {{ product.images | first | to_img }}
|
172
|
-
#
|
176
|
+
#
|
173
177
|
def first(array)
|
174
178
|
array.first if array.respond_to?(:first)
|
175
179
|
end
|
176
180
|
|
177
|
-
# Get the last element of the passed in array
|
178
|
-
#
|
181
|
+
# Get the last element of the passed in array
|
182
|
+
#
|
179
183
|
# Example:
|
180
184
|
# {{ product.images | last | to_img }}
|
181
|
-
#
|
185
|
+
#
|
182
186
|
def last(array)
|
183
187
|
array.last if array.respond_to?(:last)
|
184
188
|
end
|
185
|
-
|
189
|
+
|
186
190
|
# addition
|
187
191
|
def plus(input, operand)
|
188
192
|
to_number(input) + to_number(operand)
|
189
193
|
end
|
190
|
-
|
194
|
+
|
191
195
|
# subtraction
|
192
196
|
def minus(input, operand)
|
193
197
|
to_number(input) - to_number(operand)
|
194
198
|
end
|
195
|
-
|
199
|
+
|
196
200
|
# multiplication
|
197
201
|
def times(input, operand)
|
198
202
|
to_number(input) * to_number(operand)
|
199
203
|
end
|
200
|
-
|
204
|
+
|
201
205
|
# division
|
202
206
|
def divided_by(input, operand)
|
203
207
|
to_number(input) / to_number(operand)
|
204
208
|
end
|
205
|
-
|
209
|
+
|
206
210
|
private
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
211
|
+
|
212
|
+
def to_number(obj)
|
213
|
+
case obj
|
214
|
+
when Numeric
|
215
|
+
obj
|
216
|
+
when String
|
217
|
+
(obj.strip =~ /^\d+\.\d+$/) ? obj.to_f : obj.to_i
|
218
|
+
else
|
219
|
+
0
|
220
|
+
end
|
216
221
|
end
|
217
|
-
|
218
|
-
|
222
|
+
|
219
223
|
end
|
220
|
-
|
224
|
+
|
221
225
|
Template.register_filter(StandardFilters)
|
222
226
|
end
|
data/lib/liquid/strainer.rb
CHANGED
@@ -16,6 +16,9 @@ module Liquid
|
|
16
16
|
INTERNAL_METHOD = /^__/
|
17
17
|
@@required_methods = Set.new([:__id__, :__send__, :respond_to?, :extend, :methods, :class, :object_id])
|
18
18
|
|
19
|
+
# Ruby 1.9.2 introduces Object#respond_to_missing?, which is invoked by Object#respond_to?
|
20
|
+
@@required_methods << :respond_to_missing? if Object.respond_to? :respond_to_missing?
|
21
|
+
|
19
22
|
@@filters = {}
|
20
23
|
|
21
24
|
def initialize(context)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: liquid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 2.
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 2.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Tobias Luetke
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-08-
|
18
|
+
date: 2010-08-22 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|