liquid 1.9.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +0 -31
- data/Rakefile +1 -1
- data/lib/extras/liquid_view.rb +15 -2
- data/lib/liquid.rb +15 -15
- data/lib/liquid/block.rb +1 -2
- data/lib/liquid/context.rb +89 -99
- data/lib/liquid/drop.rb +6 -4
- data/lib/liquid/errors.rb +1 -0
- data/lib/liquid/standardfilters.rb +56 -11
- data/lib/liquid/strainer.rb +1 -1
- data/lib/liquid/tags/assign.rb +1 -1
- data/lib/liquid/tags/case.rb +2 -2
- data/lib/liquid/tags/cycle.rb +3 -4
- data/lib/liquid/tags/for.rb +53 -35
- data/lib/liquid/tags/if.rb +3 -3
- data/lib/liquid/template.rb +8 -7
- data/lib/liquid/variable.rb +10 -11
- metadata +5 -35
- data/example/server/example_servlet.rb +0 -37
- data/example/server/liquid_servlet.rb +0 -28
- data/example/server/server.rb +0 -12
- data/example/server/templates/index.liquid +0 -6
- data/example/server/templates/products.liquid +0 -45
- data/test/block_test.rb +0 -58
- data/test/condition_test.rb +0 -109
- data/test/context_test.rb +0 -418
- data/test/drop_test.rb +0 -141
- data/test/error_handling_test.rb +0 -78
- data/test/extra/breakpoint.rb +0 -547
- data/test/extra/caller.rb +0 -80
- data/test/file_system_test.rb +0 -30
- data/test/filter_test.rb +0 -98
- data/test/helper.rb +0 -20
- data/test/html_tag_test.rb +0 -31
- data/test/if_else_test.rb +0 -127
- data/test/include_tag_test.rb +0 -114
- data/test/module_ex_test.rb +0 -89
- data/test/output_test.rb +0 -121
- data/test/parsing_quirks_test.rb +0 -29
- data/test/regexp_test.rb +0 -40
- data/test/security_test.rb +0 -41
- data/test/standard_filter_test.rb +0 -126
- data/test/standard_tag_test.rb +0 -383
- data/test/statements_test.rb +0 -137
- data/test/strainer_test.rb +0 -16
- data/test/template_test.rb +0 -26
- data/test/unless_else_test.rb +0 -27
- data/test/variable_test.rb +0 -135
data/Manifest.txt
CHANGED
@@ -4,11 +4,6 @@ MIT-LICENSE
|
|
4
4
|
Manifest.txt
|
5
5
|
README.txt
|
6
6
|
Rakefile
|
7
|
-
example/server/example_servlet.rb
|
8
|
-
example/server/liquid_servlet.rb
|
9
|
-
example/server/server.rb
|
10
|
-
example/server/templates/index.liquid
|
11
|
-
example/server/templates/products.liquid
|
12
7
|
init.rb
|
13
8
|
lib/extras/liquid_view.rb
|
14
9
|
lib/liquid.rb
|
@@ -37,29 +32,3 @@ lib/liquid/tags/include.rb
|
|
37
32
|
lib/liquid/tags/unless.rb
|
38
33
|
lib/liquid/template.rb
|
39
34
|
lib/liquid/variable.rb
|
40
|
-
test/block_test.rb
|
41
|
-
test/condition_test.rb
|
42
|
-
test/context_test.rb
|
43
|
-
test/drop_test.rb
|
44
|
-
test/error_handling_test.rb
|
45
|
-
test/extra/breakpoint.rb
|
46
|
-
test/extra/caller.rb
|
47
|
-
test/file_system_test.rb
|
48
|
-
test/filter_test.rb
|
49
|
-
test/helper.rb
|
50
|
-
test/html_tag_test.rb
|
51
|
-
test/if_else_test.rb
|
52
|
-
test/include_tag_test.rb
|
53
|
-
test/module_ex_test.rb
|
54
|
-
test/output_test.rb
|
55
|
-
test/parsing_quirks_test.rb
|
56
|
-
test/regexp_test.rb
|
57
|
-
test/security_test.rb
|
58
|
-
test/standard_filter_test.rb
|
59
|
-
test/standard_tag_test.rb
|
60
|
-
test/statements_test.rb
|
61
|
-
test/strainer_test.rb
|
62
|
-
test/template_test.rb
|
63
|
-
test/test_helper.rb
|
64
|
-
test/unless_else_test.rb
|
65
|
-
test/variable_test.rb
|
data/Rakefile
CHANGED
data/lib/extras/liquid_view.rb
CHANGED
@@ -11,17 +11,30 @@ class LiquidView
|
|
11
11
|
end
|
12
12
|
|
13
13
|
|
14
|
-
def render(template,
|
14
|
+
def render(template, local_assigns_for_rails_less_than_2_1_0 = nil)
|
15
15
|
@action_view.controller.headers["Content-Type"] ||= 'text/html; charset=utf-8'
|
16
16
|
assigns = @action_view.assigns.dup
|
17
17
|
|
18
|
+
# template is a Template object in Rails >=2.1.0, a source string previously.
|
19
|
+
if template.respond_to? :source
|
20
|
+
source = template.source
|
21
|
+
local_assigns = template.locals
|
22
|
+
else
|
23
|
+
source = template
|
24
|
+
local_assigns = local_assigns_for_rails_less_than_2_1_0
|
25
|
+
end
|
26
|
+
|
18
27
|
if content_for_layout = @action_view.instance_variable_get("@content_for_layout")
|
19
28
|
assigns['content_for_layout'] = content_for_layout
|
20
29
|
end
|
21
30
|
assigns.merge!(local_assigns)
|
22
31
|
|
23
|
-
liquid = Liquid::Template.parse(
|
32
|
+
liquid = Liquid::Template.parse(source)
|
24
33
|
liquid.render(assigns, :filters => [@action_view.controller.master_helper_module], :registers => {:action_view => @action_view, :controller => @action_view.controller})
|
25
34
|
end
|
26
35
|
|
36
|
+
def compilable?
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
27
40
|
end
|
data/lib/liquid.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Copyright (c) 2005 Tobias Luetke
|
2
|
-
#
|
2
|
+
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
# a copy of this software and associated documentation files (the
|
5
5
|
# "Software"), to deal in the Software without restriction, including
|
@@ -7,10 +7,10 @@
|
|
7
7
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
8
|
# permit persons to whom the Software is furnished to do so, subject to
|
9
9
|
# the following conditions:
|
10
|
-
#
|
10
|
+
#
|
11
11
|
# The above copyright notice and this permission notice shall be
|
12
12
|
# included in all copies or substantial portions of the Software.
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
15
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
16
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
@@ -22,7 +22,7 @@
|
|
22
22
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
23
23
|
|
24
24
|
module Liquid
|
25
|
-
|
25
|
+
FilterSeparator = /\|/
|
26
26
|
ArgumentSeparator = ','
|
27
27
|
FilterArgumentSeparator = ':'
|
28
28
|
VariableAttributeSeparator = '.'
|
@@ -33,9 +33,17 @@ module Liquid
|
|
33
33
|
VariableStart = /\{\{/
|
34
34
|
VariableEnd = /\}\}/
|
35
35
|
VariableIncompleteEnd = /\}\}?/
|
36
|
-
|
36
|
+
QuotedString = /"[^"]+"|'[^']+'/
|
37
|
+
QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/
|
38
|
+
StrictQuotedFragment = /"[^"]+"|'[^']+'|[^\s,\|,\:,\,]+/
|
39
|
+
FirstFilterArgument = /#{FilterArgumentSeparator}(?:#{StrictQuotedFragment})/
|
40
|
+
OtherFilterArgument = /#{ArgumentSeparator}(?:#{StrictQuotedFragment})/
|
41
|
+
SpacelessFilter = /#{FilterSeparator}(?:#{StrictQuotedFragment})(?:#{FirstFilterArgument}(?:#{OtherFilterArgument})*)?/
|
42
|
+
Expression = /(?:#{QuotedFragment}(?:#{SpacelessFilter})*)/
|
37
43
|
TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/
|
38
|
-
|
44
|
+
AnyStartingTag = /\{\{|\{\%/
|
45
|
+
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/
|
46
|
+
TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/
|
39
47
|
VariableParser = /\[[^\]]+\]|#{VariableSegment}+/
|
40
48
|
end
|
41
49
|
|
@@ -55,14 +63,6 @@ require 'liquid/standardfilters'
|
|
55
63
|
require 'liquid/condition'
|
56
64
|
require 'liquid/module_ex'
|
57
65
|
|
58
|
-
# Load all the tags of the standard library
|
66
|
+
# Load all the tags of the standard library
|
59
67
|
#
|
60
68
|
Dir[File.dirname(__FILE__) + '/liquid/tags/*.rb'].each { |f| require f }
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
data/lib/liquid/block.rb
CHANGED
data/lib/liquid/context.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
module Liquid
|
2
|
-
|
2
|
+
|
3
3
|
# Context keeps the variable stack and resolves variables, as well as keywords
|
4
4
|
#
|
5
5
|
# context['variable'] = 'testing'
|
6
6
|
# context['variable'] #=> 'testing'
|
7
7
|
# context['true'] #=> true
|
8
8
|
# context['10.2232'] #=> 10.2232
|
9
|
-
#
|
10
|
-
# context.stack do
|
9
|
+
#
|
10
|
+
# context.stack do
|
11
11
|
# context['bob'] = 'bobsen'
|
12
12
|
# end
|
13
13
|
#
|
@@ -15,70 +15,73 @@ module Liquid
|
|
15
15
|
class Context
|
16
16
|
attr_reader :scopes
|
17
17
|
attr_reader :errors, :registers
|
18
|
-
|
18
|
+
|
19
19
|
def initialize(assigns = {}, registers = {}, rethrow_errors = false)
|
20
20
|
@scopes = [(assigns || {})]
|
21
21
|
@registers = registers
|
22
22
|
@errors = []
|
23
23
|
@rethrow_errors = rethrow_errors
|
24
|
-
end
|
25
|
-
|
24
|
+
end
|
25
|
+
|
26
26
|
def strainer
|
27
27
|
@strainer ||= Strainer.create(self)
|
28
|
-
end
|
29
|
-
|
30
|
-
# adds filters to this context.
|
31
|
-
# this does not register the filters with the main Template object. see <tt>Template.register_filter</tt>
|
28
|
+
end
|
29
|
+
|
30
|
+
# adds filters to this context.
|
31
|
+
# this does not register the filters with the main Template object. see <tt>Template.register_filter</tt>
|
32
32
|
# for that
|
33
33
|
def add_filters(filters)
|
34
34
|
filters = [filters].flatten.compact
|
35
|
-
|
36
|
-
filters.each do |f|
|
35
|
+
|
36
|
+
filters.each do |f|
|
37
37
|
raise ArgumentError, "Expected module but got: #{f.class}" unless f.is_a?(Module)
|
38
38
|
strainer.extend(f)
|
39
|
-
end
|
39
|
+
end
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
def handle_error(e)
|
43
43
|
errors.push(e)
|
44
44
|
raise if @rethrow_errors
|
45
|
-
|
45
|
+
|
46
46
|
case e
|
47
|
-
when SyntaxError
|
48
|
-
|
47
|
+
when SyntaxError
|
48
|
+
"Liquid syntax error: #{e.message}"
|
49
|
+
else
|
50
|
+
"Liquid error: #{e.message}"
|
49
51
|
end
|
50
52
|
end
|
51
|
-
|
52
|
-
|
53
|
+
|
54
|
+
|
53
55
|
def invoke(method, *args)
|
54
56
|
if strainer.respond_to?(method)
|
55
57
|
strainer.__send__(method, *args)
|
56
58
|
else
|
57
59
|
args.first
|
58
|
-
end
|
60
|
+
end
|
59
61
|
end
|
60
62
|
|
61
63
|
# push new local scope on the stack. use <tt>Context#stack</tt> instead
|
62
64
|
def push
|
65
|
+
raise StackLevelError, "Nesting too deep" if @scopes.length > 100
|
63
66
|
@scopes.unshift({})
|
64
67
|
end
|
65
|
-
|
68
|
+
|
66
69
|
# merge a hash of variables in the current local scope
|
67
70
|
def merge(new_scopes)
|
68
71
|
@scopes[0].merge!(new_scopes)
|
69
72
|
end
|
70
|
-
|
73
|
+
|
71
74
|
# pop from the stack. use <tt>Context#stack</tt> instead
|
72
75
|
def pop
|
73
|
-
raise ContextError if @scopes.size == 1
|
76
|
+
raise ContextError if @scopes.size == 1
|
74
77
|
@scopes.shift
|
75
78
|
end
|
76
|
-
|
79
|
+
|
77
80
|
# pushes a new local scope on the stack, pops it at the end of the block
|
78
81
|
#
|
79
82
|
# Example:
|
80
83
|
#
|
81
|
-
# context.stack do
|
84
|
+
# context.stack do
|
82
85
|
# context['var'] = 'hi'
|
83
86
|
# end
|
84
87
|
# context['var] #=> nil
|
@@ -88,33 +91,33 @@ module Liquid
|
|
88
91
|
push
|
89
92
|
begin
|
90
93
|
result = yield
|
91
|
-
ensure
|
94
|
+
ensure
|
92
95
|
pop
|
93
96
|
end
|
94
|
-
result
|
97
|
+
result
|
95
98
|
end
|
96
|
-
|
99
|
+
|
97
100
|
# Only allow String, Numeric, Hash, Array, Proc, Boolean or <tt>Liquid::Drop</tt>
|
98
101
|
def []=(key, value)
|
99
102
|
@scopes[0][key] = value
|
100
103
|
end
|
101
|
-
|
104
|
+
|
102
105
|
def [](key)
|
103
106
|
resolve(key)
|
104
107
|
end
|
105
|
-
|
108
|
+
|
106
109
|
def has_key?(key)
|
107
110
|
resolve(key) != nil
|
108
111
|
end
|
109
|
-
|
112
|
+
|
110
113
|
private
|
111
|
-
|
112
|
-
# Look up variable, either resolve directly after considering the name. We can directly handle
|
113
|
-
# Strings, digits, floats and booleans (true,false). If no match is made we lookup the variable in the current scope and
|
114
|
+
|
115
|
+
# Look up variable, either resolve directly after considering the name. We can directly handle
|
116
|
+
# Strings, digits, floats and booleans (true,false). If no match is made we lookup the variable in the current scope and
|
114
117
|
# later move up to the parent blocks to see if we can resolve the variable somewhere up the tree.
|
115
118
|
# Some special keywords return symbols. Those symbols are to be called on the rhs object in expressions
|
116
119
|
#
|
117
|
-
# Example:
|
120
|
+
# Example:
|
118
121
|
#
|
119
122
|
# products == empty #=> products.empty?
|
120
123
|
#
|
@@ -125,37 +128,40 @@ module Liquid
|
|
125
128
|
when 'true'
|
126
129
|
true
|
127
130
|
when 'false'
|
128
|
-
false
|
131
|
+
false
|
129
132
|
when 'blank'
|
130
|
-
:blank?
|
133
|
+
:blank?
|
131
134
|
when 'empty'
|
132
135
|
:empty?
|
136
|
+
# filtered variables
|
137
|
+
when SpacelessFilter
|
138
|
+
filtered_variable(key)
|
133
139
|
# Single quoted strings
|
134
140
|
when /^'(.*)'$/
|
135
141
|
$1.to_s
|
136
142
|
# Double quoted strings
|
137
143
|
when /^"(.*)"$/
|
138
|
-
$1.to_s
|
144
|
+
$1.to_s
|
139
145
|
# Integer and floats
|
140
|
-
when /^(\d+)$/
|
146
|
+
when /^(\d+)$/
|
141
147
|
$1.to_i
|
142
148
|
# Ranges
|
143
|
-
when /^\((\S+)\.\.(\S+)\)$/
|
149
|
+
when /^\((\S+)\.\.(\S+)\)$/
|
144
150
|
(resolve($1).to_i..resolve($2).to_i)
|
145
151
|
# Floats
|
146
|
-
when /^(\d[\d\.]+)$/
|
152
|
+
when /^(\d[\d\.]+)$/
|
147
153
|
$1.to_f
|
148
154
|
else
|
149
155
|
variable(key)
|
150
|
-
end
|
156
|
+
end
|
151
157
|
end
|
152
|
-
|
153
|
-
# fetches an object starting at the local scope and then moving up
|
154
|
-
# the hierachy
|
158
|
+
|
159
|
+
# fetches an object starting at the local scope and then moving up
|
160
|
+
# the hierachy
|
155
161
|
def find_variable(key)
|
156
|
-
@scopes.each do |scope|
|
162
|
+
@scopes.each do |scope|
|
157
163
|
if scope.has_key?(key)
|
158
|
-
variable = scope[key]
|
164
|
+
variable = scope[key]
|
159
165
|
variable = scope[key] = variable.call(self) if variable.is_a?(Proc)
|
160
166
|
variable = variable.to_liquid
|
161
167
|
variable.context = self if variable.respond_to?(:context=)
|
@@ -166,77 +172,61 @@ module Liquid
|
|
166
172
|
end
|
167
173
|
|
168
174
|
# resolves namespaced queries gracefully.
|
169
|
-
#
|
175
|
+
#
|
170
176
|
# Example
|
171
|
-
#
|
177
|
+
#
|
172
178
|
# @context['hash'] = {"name" => 'tobi'}
|
173
179
|
# assert_equal 'tobi', @context['hash.name']
|
174
|
-
# assert_equal 'tobi', @context['hash[name]']
|
180
|
+
# assert_equal 'tobi', @context['hash["name"]']
|
175
181
|
#
|
176
182
|
def variable(markup)
|
177
183
|
parts = markup.scan(VariableParser)
|
178
184
|
square_bracketed = /^\[(.*)\]$/
|
179
|
-
|
185
|
+
|
180
186
|
first_part = parts.shift
|
181
187
|
if first_part =~ square_bracketed
|
182
188
|
first_part = resolve($1)
|
183
189
|
end
|
184
|
-
|
190
|
+
|
185
191
|
if object = find_variable(first_part)
|
186
|
-
|
187
|
-
parts.each do |part|
|
188
|
-
|
189
|
-
# If object is a hash we look for the presence of the key and if its available
|
190
|
-
# we return it
|
191
|
-
|
192
|
-
if part =~ square_bracketed
|
193
|
-
part = resolve($1)
|
194
|
-
|
195
|
-
object[pos] = object[part].call(self) if object[part].is_a?(Proc) and object.respond_to?(:[]=)
|
196
|
-
object = object[part].to_liquid
|
197
|
-
|
198
|
-
else
|
199
192
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
end
|
193
|
+
parts.each do |part|
|
194
|
+
part = resolve($1) if part_resolved = (part =~ square_bracketed)
|
195
|
+
|
196
|
+
# If object is a hash- or array-like object we look for the
|
197
|
+
# presence of the key and if its available we return it
|
198
|
+
if object.respond_to?(:[]) and
|
199
|
+
((object.respond_to?(:has_key?) and object.has_key?(part)) or
|
200
|
+
(object.respond_to?(:fetch) and part.is_a?(Integer)))
|
201
|
+
|
202
|
+
# if its a proc we will replace the entry with the proc
|
203
|
+
res = object[part]
|
204
|
+
res = object[part] = res.call(self) if res.is_a?(Proc) and object.respond_to?(:[]=)
|
205
|
+
object = res.to_liquid
|
206
|
+
|
207
|
+
# Some special cases. If the part wasn't in square brackets and
|
208
|
+
# no key with the same name was found we interpret following calls
|
209
|
+
# as commands and call them on the current object
|
210
|
+
elsif !part_resolved and object.respond_to?(part) and ['size', 'first', 'last'].include?(part)
|
211
|
+
|
212
|
+
object = object.send(part.intern).to_liquid
|
213
|
+
|
214
|
+
# No key was present with the desired value and it wasn't one of the directly supported
|
215
|
+
# keywords either. The only thing we got left is to return nil
|
216
|
+
else
|
217
|
+
return nil
|
226
218
|
end
|
227
|
-
|
228
|
-
# If we are dealing with a drop here we have to
|
219
|
+
|
220
|
+
# If we are dealing with a drop here we have to
|
229
221
|
object.context = self if object.respond_to?(:context=)
|
230
222
|
end
|
231
223
|
end
|
232
|
-
|
224
|
+
|
233
225
|
object
|
234
|
-
end
|
235
|
-
|
236
|
-
private
|
226
|
+
end
|
237
227
|
|
238
|
-
def
|
239
|
-
|
228
|
+
def filtered_variable(markup)
|
229
|
+
Variable.new(markup).render(self)
|
240
230
|
end
|
241
231
|
end
|
242
232
|
end
|