liquid 3.0.6 → 4.0.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.
- checksums.yaml +4 -4
- data/History.md +98 -58
- data/README.md +31 -0
- data/lib/liquid/block.rb +31 -124
- data/lib/liquid/block_body.rb +75 -59
- data/lib/liquid/condition.rb +23 -22
- data/lib/liquid/context.rb +50 -46
- data/lib/liquid/document.rb +19 -9
- data/lib/liquid/drop.rb +17 -16
- data/lib/liquid/errors.rb +20 -24
- data/lib/liquid/expression.rb +15 -3
- data/lib/liquid/extensions.rb +13 -7
- data/lib/liquid/file_system.rb +11 -11
- data/lib/liquid/forloop_drop.rb +42 -0
- data/lib/liquid/i18n.rb +5 -5
- data/lib/liquid/interrupts.rb +1 -2
- data/lib/liquid/lexer.rb +6 -4
- data/lib/liquid/locales/en.yml +5 -1
- data/lib/liquid/parse_context.rb +37 -0
- data/lib/liquid/parser_switching.rb +4 -4
- data/lib/liquid/profiler/hooks.rb +7 -7
- data/lib/liquid/profiler.rb +18 -19
- data/lib/liquid/range_lookup.rb +16 -1
- data/lib/liquid/resource_limits.rb +23 -0
- data/lib/liquid/standardfilters.rb +121 -61
- data/lib/liquid/strainer.rb +14 -7
- data/lib/liquid/tablerowloop_drop.rb +62 -0
- data/lib/liquid/tag.rb +9 -8
- data/lib/liquid/tags/assign.rb +17 -4
- data/lib/liquid/tags/break.rb +0 -3
- data/lib/liquid/tags/capture.rb +1 -1
- data/lib/liquid/tags/case.rb +19 -12
- data/lib/liquid/tags/comment.rb +2 -2
- data/lib/liquid/tags/cycle.rb +6 -6
- data/lib/liquid/tags/decrement.rb +1 -4
- data/lib/liquid/tags/for.rb +95 -75
- data/lib/liquid/tags/if.rb +49 -44
- data/lib/liquid/tags/ifchanged.rb +0 -2
- data/lib/liquid/tags/include.rb +61 -52
- data/lib/liquid/tags/raw.rb +32 -4
- data/lib/liquid/tags/table_row.rb +12 -30
- data/lib/liquid/tags/unless.rb +3 -4
- data/lib/liquid/template.rb +42 -54
- data/lib/liquid/tokenizer.rb +31 -0
- data/lib/liquid/utils.rb +52 -8
- data/lib/liquid/variable.rb +46 -45
- data/lib/liquid/variable_lookup.rb +7 -5
- data/lib/liquid/version.rb +1 -1
- data/lib/liquid.rb +9 -7
- data/test/integration/assign_test.rb +8 -8
- data/test/integration/blank_test.rb +14 -14
- data/test/integration/context_test.rb +2 -2
- data/test/integration/document_test.rb +19 -0
- data/test/integration/drop_test.rb +42 -40
- data/test/integration/error_handling_test.rb +99 -46
- data/test/integration/filter_test.rb +60 -20
- data/test/integration/hash_ordering_test.rb +9 -9
- data/test/integration/output_test.rb +26 -27
- data/test/integration/parsing_quirks_test.rb +15 -13
- data/test/integration/render_profiling_test.rb +20 -20
- data/test/integration/security_test.rb +9 -7
- data/test/integration/standard_filter_test.rb +179 -40
- data/test/integration/tags/break_tag_test.rb +1 -2
- data/test/integration/tags/continue_tag_test.rb +0 -1
- data/test/integration/tags/for_tag_test.rb +133 -98
- data/test/integration/tags/if_else_tag_test.rb +75 -77
- data/test/integration/tags/include_tag_test.rb +34 -30
- data/test/integration/tags/increment_tag_test.rb +10 -11
- data/test/integration/tags/raw_tag_test.rb +7 -1
- data/test/integration/tags/standard_tag_test.rb +121 -122
- data/test/integration/tags/statements_test.rb +3 -5
- data/test/integration/tags/table_row_test.rb +20 -19
- data/test/integration/tags/unless_else_tag_test.rb +6 -6
- data/test/integration/template_test.rb +190 -49
- data/test/integration/trim_mode_test.rb +525 -0
- data/test/integration/variable_test.rb +23 -13
- data/test/test_helper.rb +33 -5
- data/test/unit/block_unit_test.rb +8 -5
- data/test/unit/condition_unit_test.rb +86 -77
- data/test/unit/context_unit_test.rb +48 -57
- data/test/unit/file_system_unit_test.rb +3 -3
- data/test/unit/i18n_unit_test.rb +2 -2
- data/test/unit/lexer_unit_test.rb +11 -8
- data/test/unit/parser_unit_test.rb +2 -2
- data/test/unit/regexp_unit_test.rb +1 -1
- data/test/unit/strainer_unit_test.rb +80 -1
- data/test/unit/tag_unit_test.rb +7 -2
- data/test/unit/tags/case_tag_unit_test.rb +1 -1
- data/test/unit/tags/for_tag_unit_test.rb +2 -2
- data/test/unit/tags/if_tag_unit_test.rb +1 -1
- data/test/unit/template_unit_test.rb +14 -5
- data/test/unit/tokenizer_unit_test.rb +24 -7
- data/test/unit/variable_unit_test.rb +60 -43
- metadata +19 -14
- data/lib/liquid/module_ex.rb +0 -62
- data/lib/liquid/token.rb +0 -18
- data/test/unit/module_ex_unit_test.rb +0 -87
- /data/{MIT-LICENSE → LICENSE} +0 -0
data/lib/liquid/condition.rb
CHANGED
@@ -3,21 +3,26 @@ module Liquid
|
|
3
3
|
#
|
4
4
|
# Example:
|
5
5
|
#
|
6
|
-
# c = Condition.new(
|
6
|
+
# c = Condition.new(1, '==', 1)
|
7
7
|
# c.evaluate #=> true
|
8
8
|
#
|
9
9
|
class Condition #:nodoc:
|
10
10
|
@@operators = {
|
11
|
-
'=='.freeze =>
|
12
|
-
'!='.freeze =>
|
13
|
-
'<>'.freeze =>
|
11
|
+
'=='.freeze => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
|
12
|
+
'!='.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
|
13
|
+
'<>'.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
|
14
14
|
'<'.freeze => :<,
|
15
15
|
'>'.freeze => :>,
|
16
16
|
'>='.freeze => :>=,
|
17
17
|
'<='.freeze => :<=,
|
18
|
-
'contains'.freeze => lambda
|
19
|
-
left && right && left.respond_to?(:include?)
|
20
|
-
|
18
|
+
'contains'.freeze => lambda do |cond, left, right|
|
19
|
+
if left && right && left.respond_to?(:include?)
|
20
|
+
right = right.to_s if left.is_a?(String)
|
21
|
+
left.include?(right)
|
22
|
+
else
|
23
|
+
false
|
24
|
+
end
|
25
|
+
end
|
21
26
|
}
|
22
27
|
|
23
28
|
def self.operators
|
@@ -73,17 +78,17 @@ module Liquid
|
|
73
78
|
private
|
74
79
|
|
75
80
|
def equal_variables(left, right)
|
76
|
-
if left.is_a?(
|
77
|
-
if right.respond_to?(left)
|
78
|
-
return right.send(left.
|
81
|
+
if left.is_a?(Liquid::Expression::MethodLiteral)
|
82
|
+
if right.respond_to?(left.method_name)
|
83
|
+
return right.send(left.method_name)
|
79
84
|
else
|
80
85
|
return nil
|
81
86
|
end
|
82
87
|
end
|
83
88
|
|
84
|
-
if right.is_a?(
|
85
|
-
if left.respond_to?(right)
|
86
|
-
return left.send(right.
|
89
|
+
if right.is_a?(Liquid::Expression::MethodLiteral)
|
90
|
+
if left.respond_to?(right.method_name)
|
91
|
+
return left.send(right.method_name)
|
87
92
|
else
|
88
93
|
return nil
|
89
94
|
end
|
@@ -96,36 +101,32 @@ module Liquid
|
|
96
101
|
# If the operator is empty this means that the decision statement is just
|
97
102
|
# a single variable. We can just poll this variable from the context and
|
98
103
|
# return this as the result.
|
99
|
-
return context
|
104
|
+
return context.evaluate(left) if op.nil?
|
100
105
|
|
101
|
-
left = context
|
102
|
-
right = context
|
106
|
+
left = context.evaluate(left)
|
107
|
+
right = context.evaluate(right)
|
103
108
|
|
104
109
|
operation = self.class.operators[op] || raise(Liquid::ArgumentError.new("Unknown operator #{op}"))
|
105
110
|
|
106
111
|
if operation.respond_to?(:call)
|
107
112
|
operation.call(self, left, right)
|
108
|
-
elsif left.respond_to?(operation)
|
113
|
+
elsif left.respond_to?(operation) && right.respond_to?(operation)
|
109
114
|
begin
|
110
115
|
left.send(operation, right)
|
111
116
|
rescue ::ArgumentError => e
|
112
117
|
raise Liquid::ArgumentError.new(e.message)
|
113
118
|
end
|
114
|
-
else
|
115
|
-
nil
|
116
119
|
end
|
117
120
|
end
|
118
121
|
end
|
119
122
|
|
120
|
-
|
121
123
|
class ElseCondition < Condition
|
122
124
|
def else?
|
123
125
|
true
|
124
126
|
end
|
125
127
|
|
126
|
-
def evaluate(
|
128
|
+
def evaluate(_context)
|
127
129
|
true
|
128
130
|
end
|
129
131
|
end
|
130
|
-
|
131
132
|
end
|
data/lib/liquid/context.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Liquid
|
2
|
-
|
3
2
|
# Context keeps the variable stack and resolves variables, as well as keywords
|
4
3
|
#
|
5
4
|
# context['variable'] = 'testing'
|
@@ -14,41 +13,32 @@ module Liquid
|
|
14
13
|
# context['bob'] #=> nil class Context
|
15
14
|
class Context
|
16
15
|
attr_reader :scopes, :errors, :registers, :environments, :resource_limits
|
17
|
-
attr_accessor :
|
16
|
+
attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
|
18
17
|
|
19
18
|
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil)
|
20
19
|
@environments = [environments].flatten
|
21
20
|
@scopes = [(outer_scope || {})]
|
22
21
|
@registers = registers
|
23
22
|
@errors = []
|
24
|
-
@
|
25
|
-
@
|
26
|
-
@resource_limits
|
27
|
-
@parsed_expression = Hash.new{ |cache, markup| cache[markup] = Expression.parse(markup) }
|
23
|
+
@partial = false
|
24
|
+
@strict_variables = false
|
25
|
+
@resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits)
|
28
26
|
squash_instance_assigns_with_environments
|
29
27
|
|
30
28
|
@this_stack_used = false
|
31
29
|
|
30
|
+
self.exception_renderer = Template.default_exception_renderer
|
32
31
|
if rethrow_errors
|
33
|
-
self.
|
32
|
+
self.exception_renderer = ->(e) { raise }
|
34
33
|
end
|
35
34
|
|
36
35
|
@interrupts = []
|
37
36
|
@filters = []
|
37
|
+
@global_filter = nil
|
38
38
|
end
|
39
39
|
|
40
|
-
def
|
41
|
-
@
|
42
|
-
obj.length
|
43
|
-
else
|
44
|
-
1
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def resource_limits_reached?
|
49
|
-
(@resource_limits[:render_length_limit] && @resource_limits[:render_length_current] > @resource_limits[:render_length_limit]) ||
|
50
|
-
(@resource_limits[:render_score_limit] && @resource_limits[:render_score_current] > @resource_limits[:render_score_limit] ) ||
|
51
|
-
(@resource_limits[:assign_score_limit] && @resource_limits[:assign_score_current] > @resource_limits[:assign_score_limit] )
|
40
|
+
def warnings
|
41
|
+
@warnings ||= []
|
52
42
|
end
|
53
43
|
|
54
44
|
def strainer
|
@@ -65,8 +55,12 @@ module Liquid
|
|
65
55
|
@strainer = nil
|
66
56
|
end
|
67
57
|
|
58
|
+
def apply_global_filter(obj)
|
59
|
+
global_filter.nil? ? obj : global_filter.call(obj)
|
60
|
+
end
|
61
|
+
|
68
62
|
# are there any not handled interrupts?
|
69
|
-
def
|
63
|
+
def interrupt?
|
70
64
|
!@interrupts.empty?
|
71
65
|
end
|
72
66
|
|
@@ -80,15 +74,12 @@ module Liquid
|
|
80
74
|
@interrupts.pop
|
81
75
|
end
|
82
76
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
end
|
88
|
-
|
77
|
+
def handle_error(e, line_number = nil, raw_token = nil)
|
78
|
+
e = internal_error unless e.is_a?(Liquid::Error)
|
79
|
+
e.template_name ||= template_name
|
80
|
+
e.line_number ||= line_number
|
89
81
|
errors.push(e)
|
90
|
-
|
91
|
-
Liquid::Error.render(e)
|
82
|
+
exception_renderer.call(e).to_s
|
92
83
|
end
|
93
84
|
|
94
85
|
def invoke(method, *args)
|
@@ -96,7 +87,7 @@ module Liquid
|
|
96
87
|
end
|
97
88
|
|
98
89
|
# Push new local scope on the stack. use <tt>Context#stack</tt> instead
|
99
|
-
def push(new_scope={})
|
90
|
+
def push(new_scope = {})
|
100
91
|
@scopes.unshift(new_scope)
|
101
92
|
raise StackLevelError, "Nesting too deep".freeze if @scopes.length > 100
|
102
93
|
end
|
@@ -120,7 +111,7 @@ module Liquid
|
|
120
111
|
# end
|
121
112
|
#
|
122
113
|
# context['var] #=> nil
|
123
|
-
def stack(new_scope=nil)
|
114
|
+
def stack(new_scope = nil)
|
124
115
|
old_stack_used = @this_stack_used
|
125
116
|
if new_scope
|
126
117
|
push(new_scope)
|
@@ -157,10 +148,10 @@ module Liquid
|
|
157
148
|
# Example:
|
158
149
|
# products == empty #=> products.empty?
|
159
150
|
def [](expression)
|
160
|
-
evaluate(
|
151
|
+
evaluate(Expression.parse(expression))
|
161
152
|
end
|
162
153
|
|
163
|
-
def
|
154
|
+
def key?(key)
|
164
155
|
self[key] != nil
|
165
156
|
end
|
166
157
|
|
@@ -170,10 +161,9 @@ module Liquid
|
|
170
161
|
|
171
162
|
# Fetches an object starting at the local scope and then moving up the hierachy
|
172
163
|
def find_variable(key)
|
173
|
-
|
174
164
|
# This was changed from find() to find_index() because this is a very hot
|
175
165
|
# path and find_index() is optimized in MRI to reduce object allocation
|
176
|
-
index = @scopes.find_index { |s| s.
|
166
|
+
index = @scopes.find_index { |s| s.key?(key) }
|
177
167
|
scope = @scopes[index] if index
|
178
168
|
|
179
169
|
variable = nil
|
@@ -188,17 +178,23 @@ module Liquid
|
|
188
178
|
end
|
189
179
|
end
|
190
180
|
|
191
|
-
scope
|
192
|
-
variable
|
181
|
+
scope ||= @environments.last || @scopes.last
|
182
|
+
variable ||= lookup_and_evaluate(scope, key)
|
193
183
|
|
194
184
|
variable = variable.to_liquid
|
195
185
|
variable.context = self if variable.respond_to?(:context=)
|
196
186
|
|
197
|
-
|
187
|
+
variable
|
198
188
|
end
|
199
189
|
|
200
190
|
def lookup_and_evaluate(obj, key)
|
201
|
-
if
|
191
|
+
if @strict_variables && obj.respond_to?(:key?) && !obj.key?(key)
|
192
|
+
raise Liquid::UndefinedVariable, "undefined variable #{key}"
|
193
|
+
end
|
194
|
+
|
195
|
+
value = obj[key]
|
196
|
+
|
197
|
+
if value.is_a?(Proc) && obj.respond_to?(:[]=)
|
202
198
|
obj[key] = (value.arity == 0) ? value.call : value.call(self)
|
203
199
|
else
|
204
200
|
value
|
@@ -206,15 +202,23 @@ module Liquid
|
|
206
202
|
end
|
207
203
|
|
208
204
|
private
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
205
|
+
|
206
|
+
def internal_error
|
207
|
+
# raise and catch to set backtrace and cause on exception
|
208
|
+
raise Liquid::InternalError, 'internal'
|
209
|
+
rescue Liquid::InternalError => exc
|
210
|
+
exc
|
211
|
+
end
|
212
|
+
|
213
|
+
def squash_instance_assigns_with_environments
|
214
|
+
@scopes.last.each_key do |k|
|
215
|
+
@environments.each do |env|
|
216
|
+
if env.key?(k)
|
217
|
+
scopes.last[k] = lookup_and_evaluate(env, k)
|
218
|
+
break
|
216
219
|
end
|
217
220
|
end
|
218
|
-
end
|
221
|
+
end
|
222
|
+
end # squash_instance_assigns_with_environments
|
219
223
|
end # Context
|
220
224
|
end # Liquid
|
data/lib/liquid/document.rb
CHANGED
@@ -1,17 +1,27 @@
|
|
1
1
|
module Liquid
|
2
|
-
class Document <
|
3
|
-
def self.parse(tokens,
|
4
|
-
|
5
|
-
|
2
|
+
class Document < BlockBody
|
3
|
+
def self.parse(tokens, parse_context)
|
4
|
+
doc = new
|
5
|
+
doc.parse(tokens, parse_context)
|
6
|
+
doc
|
6
7
|
end
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
def parse(tokens, parse_context)
|
10
|
+
super do |end_tag_name, end_tag_params|
|
11
|
+
unknown_tag(end_tag_name, parse_context) if end_tag_name
|
12
|
+
end
|
13
|
+
rescue SyntaxError => e
|
14
|
+
e.line_number ||= parse_context.line_number
|
15
|
+
raise
|
11
16
|
end
|
12
17
|
|
13
|
-
|
14
|
-
|
18
|
+
def unknown_tag(tag, parse_context)
|
19
|
+
case tag
|
20
|
+
when 'else'.freeze, 'end'.freeze
|
21
|
+
raise SyntaxError.new(parse_context.locale.t("errors.syntax.unexpected_outer_tag".freeze, tag: tag))
|
22
|
+
else
|
23
|
+
raise SyntaxError.new(parse_context.locale.t("errors.syntax.unknown_tag".freeze, tag: tag))
|
24
|
+
end
|
15
25
|
end
|
16
26
|
end
|
17
27
|
end
|
data/lib/liquid/drop.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'set'
|
2
2
|
|
3
3
|
module Liquid
|
4
|
-
|
5
4
|
# A drop in liquid is a class which allows you to export DOM like things to liquid.
|
6
5
|
# Methods of drops are callable.
|
7
6
|
# The main use for liquid drops is to implement lazy loaded objects.
|
@@ -19,28 +18,27 @@ module Liquid
|
|
19
18
|
# tmpl = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {%endfor%} ' )
|
20
19
|
# tmpl.render('product' => ProductDrop.new ) # will invoke top_sales query.
|
21
20
|
#
|
22
|
-
# Your drop can either implement the methods sans any parameters
|
23
|
-
# catch all.
|
21
|
+
# Your drop can either implement the methods sans any parameters
|
22
|
+
# or implement the liquid_method_missing(name) method which is a catch all.
|
24
23
|
class Drop
|
25
24
|
attr_writer :context
|
26
25
|
|
27
|
-
EMPTY_STRING = ''.freeze
|
28
|
-
|
29
26
|
# Catch all for the method
|
30
|
-
def
|
31
|
-
nil
|
27
|
+
def liquid_method_missing(method)
|
28
|
+
return nil unless @context && @context.strict_variables
|
29
|
+
raise Liquid::UndefinedDropMethod, "undefined method #{method}"
|
32
30
|
end
|
33
31
|
|
34
32
|
# called by liquid to invoke a drop
|
35
33
|
def invoke_drop(method_or_key)
|
36
|
-
if
|
34
|
+
if self.class.invokable?(method_or_key)
|
37
35
|
send(method_or_key)
|
38
36
|
else
|
39
|
-
|
37
|
+
liquid_method_missing(method_or_key)
|
40
38
|
end
|
41
39
|
end
|
42
40
|
|
43
|
-
def
|
41
|
+
def key?(_name)
|
44
42
|
true
|
45
43
|
end
|
46
44
|
|
@@ -56,22 +54,25 @@ module Liquid
|
|
56
54
|
self.class.name
|
57
55
|
end
|
58
56
|
|
59
|
-
|
60
|
-
|
61
|
-
private
|
57
|
+
alias_method :[], :invoke_drop
|
62
58
|
|
63
59
|
# Check for method existence without invoking respond_to?, which creates symbols
|
64
60
|
def self.invokable?(method_name)
|
65
|
-
|
61
|
+
invokable_methods.include?(method_name.to_s)
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.invokable_methods
|
65
|
+
@invokable_methods ||= begin
|
66
66
|
blacklist = Liquid::Drop.public_instance_methods + [:each]
|
67
|
+
|
67
68
|
if include?(Enumerable)
|
68
69
|
blacklist += Enumerable.public_instance_methods
|
69
70
|
blacklist -= [:sort, :count, :first, :min, :max, :include?]
|
70
71
|
end
|
72
|
+
|
71
73
|
whitelist = [:to_liquid] + (public_instance_methods - blacklist)
|
72
|
-
|
74
|
+
Set.new(whitelist.map(&:to_s))
|
73
75
|
end
|
74
|
-
@invokable_methods.include?(method_name.to_s)
|
75
76
|
end
|
76
77
|
end
|
77
78
|
end
|
data/lib/liquid/errors.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
module Liquid
|
2
2
|
class Error < ::StandardError
|
3
3
|
attr_accessor :line_number
|
4
|
+
attr_accessor :template_name
|
4
5
|
attr_accessor :markup_context
|
5
6
|
|
6
|
-
def to_s(with_prefix=true)
|
7
|
+
def to_s(with_prefix = true)
|
7
8
|
str = ""
|
8
9
|
str << message_prefix if with_prefix
|
9
10
|
str << super()
|
@@ -16,20 +17,6 @@ module Liquid
|
|
16
17
|
str
|
17
18
|
end
|
18
19
|
|
19
|
-
def set_line_number_from_token(token)
|
20
|
-
return unless token.respond_to?(:line_number)
|
21
|
-
return if self.line_number
|
22
|
-
self.line_number = token.line_number
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.render(e)
|
26
|
-
if e.is_a?(Liquid::Error)
|
27
|
-
e.to_s
|
28
|
-
else
|
29
|
-
"Liquid error: #{e.to_s}"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
20
|
private
|
34
21
|
|
35
22
|
def message_prefix
|
@@ -41,7 +28,9 @@ module Liquid
|
|
41
28
|
end
|
42
29
|
|
43
30
|
if line_number
|
44
|
-
str << " (
|
31
|
+
str << " ("
|
32
|
+
str << template_name << " " if template_name
|
33
|
+
str << "line " << line_number.to_s << ")"
|
45
34
|
end
|
46
35
|
|
47
36
|
str << ": "
|
@@ -49,12 +38,19 @@ module Liquid
|
|
49
38
|
end
|
50
39
|
end
|
51
40
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
41
|
+
ArgumentError = Class.new(Error)
|
42
|
+
ContextError = Class.new(Error)
|
43
|
+
FileSystemError = Class.new(Error)
|
44
|
+
StandardError = Class.new(Error)
|
45
|
+
SyntaxError = Class.new(Error)
|
46
|
+
StackLevelError = Class.new(Error)
|
47
|
+
TaintedError = Class.new(Error)
|
48
|
+
MemoryError = Class.new(Error)
|
49
|
+
ZeroDivisionError = Class.new(Error)
|
50
|
+
FloatDomainError = Class.new(Error)
|
51
|
+
UndefinedVariable = Class.new(Error)
|
52
|
+
UndefinedDropMethod = Class.new(Error)
|
53
|
+
UndefinedFilter = Class.new(Error)
|
54
|
+
MethodOverrideError = Class.new(Error)
|
55
|
+
InternalError = Class.new(Error)
|
60
56
|
end
|
data/lib/liquid/expression.rb
CHANGED
@@ -1,11 +1,24 @@
|
|
1
1
|
module Liquid
|
2
2
|
class Expression
|
3
|
+
class MethodLiteral
|
4
|
+
attr_reader :method_name, :to_s
|
5
|
+
|
6
|
+
def initialize(method_name, to_s)
|
7
|
+
@method_name = method_name
|
8
|
+
@to_s = to_s
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_liquid
|
12
|
+
to_s
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
3
16
|
LITERALS = {
|
4
17
|
nil => nil, 'nil'.freeze => nil, 'null'.freeze => nil, ''.freeze => nil,
|
5
18
|
'true'.freeze => true,
|
6
19
|
'false'.freeze => false,
|
7
|
-
'blank'.freeze => :blank?,
|
8
|
-
'empty'.freeze => :empty
|
20
|
+
'blank'.freeze => MethodLiteral.new(:blank?, '').freeze,
|
21
|
+
'empty'.freeze => MethodLiteral.new(:empty?, '').freeze
|
9
22
|
}
|
10
23
|
|
11
24
|
def self.parse(markup)
|
@@ -28,6 +41,5 @@ module Liquid
|
|
28
41
|
end
|
29
42
|
end
|
30
43
|
end
|
31
|
-
|
32
44
|
end
|
33
45
|
end
|
data/lib/liquid/extensions.rb
CHANGED
@@ -7,44 +7,50 @@ class String # :nodoc:
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
class Array
|
10
|
+
class Array # :nodoc:
|
11
11
|
def to_liquid
|
12
12
|
self
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
class Hash
|
16
|
+
class Hash # :nodoc:
|
17
17
|
def to_liquid
|
18
18
|
self
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
class Numeric
|
22
|
+
class Numeric # :nodoc:
|
23
23
|
def to_liquid
|
24
24
|
self
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
class
|
28
|
+
class Range # :nodoc:
|
29
29
|
def to_liquid
|
30
30
|
self
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
class
|
34
|
+
class Time # :nodoc:
|
35
35
|
def to_liquid
|
36
36
|
self
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
class Date
|
40
|
+
class DateTime < Date # :nodoc:
|
41
|
+
def to_liquid
|
42
|
+
self
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Date # :nodoc:
|
41
47
|
def to_liquid
|
42
48
|
self
|
43
49
|
end
|
44
50
|
end
|
45
51
|
|
46
52
|
class TrueClass
|
47
|
-
def to_liquid
|
53
|
+
def to_liquid # :nodoc:
|
48
54
|
self
|
49
55
|
end
|
50
56
|
end
|
data/lib/liquid/file_system.rb
CHANGED
@@ -8,13 +8,13 @@ module Liquid
|
|
8
8
|
#
|
9
9
|
# Example:
|
10
10
|
#
|
11
|
-
#
|
12
|
-
#
|
11
|
+
# Liquid::Template.file_system = Liquid::LocalFileSystem.new(template_path)
|
12
|
+
# liquid = Liquid::Template.parse(template)
|
13
13
|
#
|
14
14
|
# This will parse the template with a LocalFileSystem implementation rooted at 'template_path'.
|
15
15
|
class BlankFileSystem
|
16
16
|
# Called by Liquid to retrieve a template file
|
17
|
-
def read_template_file(
|
17
|
+
def read_template_file(_template_path)
|
18
18
|
raise FileSystemError, "This liquid context does not allow includes."
|
19
19
|
end
|
20
20
|
end
|
@@ -26,10 +26,10 @@ module Liquid
|
|
26
26
|
#
|
27
27
|
# Example:
|
28
28
|
#
|
29
|
-
#
|
29
|
+
# file_system = Liquid::LocalFileSystem.new("/some/path")
|
30
30
|
#
|
31
|
-
#
|
32
|
-
#
|
31
|
+
# file_system.full_path("mypartial") # => "/some/path/_mypartial.liquid"
|
32
|
+
# file_system.full_path("dir/mypartial") # => "/some/path/dir/_mypartial.liquid"
|
33
33
|
#
|
34
34
|
# Optionally in the second argument you can specify a custom pattern for template filenames.
|
35
35
|
# The Kernel::sprintf format specification is used.
|
@@ -37,9 +37,9 @@ module Liquid
|
|
37
37
|
#
|
38
38
|
# Example:
|
39
39
|
#
|
40
|
-
#
|
40
|
+
# file_system = Liquid::LocalFileSystem.new("/some/path", "%s.html")
|
41
41
|
#
|
42
|
-
#
|
42
|
+
# file_system.full_path("index") # => "/some/path/index.html"
|
43
43
|
#
|
44
44
|
class LocalFileSystem
|
45
45
|
attr_accessor :root
|
@@ -49,9 +49,9 @@ module Liquid
|
|
49
49
|
@pattern = pattern
|
50
50
|
end
|
51
51
|
|
52
|
-
def read_template_file(template_path
|
52
|
+
def read_template_file(template_path)
|
53
53
|
full_path = full_path(template_path)
|
54
|
-
raise FileSystemError, "No such template '#{template_path}'" unless File.
|
54
|
+
raise FileSystemError, "No such template '#{template_path}'" unless File.exist?(full_path)
|
55
55
|
|
56
56
|
File.read(full_path)
|
57
57
|
end
|
@@ -65,7 +65,7 @@ module Liquid
|
|
65
65
|
File.join(root, @pattern % template_path)
|
66
66
|
end
|
67
67
|
|
68
|
-
raise FileSystemError, "Illegal template path '#{File.expand_path(full_path)}'" unless File.expand_path(full_path)
|
68
|
+
raise FileSystemError, "Illegal template path '#{File.expand_path(full_path)}'" unless File.expand_path(full_path).start_with?(File.expand_path(root))
|
69
69
|
|
70
70
|
full_path
|
71
71
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Liquid
|
2
|
+
class ForloopDrop < Drop
|
3
|
+
def initialize(name, length, parentloop)
|
4
|
+
@name = name
|
5
|
+
@length = length
|
6
|
+
@parentloop = parentloop
|
7
|
+
@index = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :name, :length, :parentloop
|
11
|
+
|
12
|
+
def index
|
13
|
+
@index + 1
|
14
|
+
end
|
15
|
+
|
16
|
+
def index0
|
17
|
+
@index
|
18
|
+
end
|
19
|
+
|
20
|
+
def rindex
|
21
|
+
@length - @index
|
22
|
+
end
|
23
|
+
|
24
|
+
def rindex0
|
25
|
+
@length - @index - 1
|
26
|
+
end
|
27
|
+
|
28
|
+
def first
|
29
|
+
@index == 0
|
30
|
+
end
|
31
|
+
|
32
|
+
def last
|
33
|
+
@index == @length - 1
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def increment!
|
39
|
+
@index += 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|