liquid 1.9.0 → 2.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.
- 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
|