liquid 1.7.0 → 1.9.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/CHANGELOG +17 -15
- data/History.txt +44 -0
- data/MIT-LICENSE +2 -2
- data/Manifest.txt +6 -1
- data/{README → README.txt} +0 -0
- data/Rakefile +3 -3
- data/init.rb +5 -3
- data/lib/liquid.rb +8 -6
- data/lib/liquid/block.rb +6 -9
- data/lib/liquid/condition.rb +49 -17
- data/lib/liquid/context.rb +67 -41
- data/lib/liquid/errors.rb +8 -5
- data/lib/liquid/htmltags.rb +17 -7
- data/lib/liquid/module_ex.rb +62 -0
- data/lib/liquid/standardfilters.rb +39 -0
- data/lib/liquid/strainer.rb +20 -11
- data/lib/liquid/tag.rb +4 -3
- data/lib/liquid/tags/assign.rb +15 -4
- data/lib/liquid/tags/capture.rb +15 -2
- data/lib/liquid/tags/case.rb +51 -36
- data/lib/liquid/tags/cycle.rb +16 -2
- data/lib/liquid/tags/for.rb +45 -8
- data/lib/liquid/tags/if.rb +35 -7
- data/lib/liquid/tags/include.rb +2 -3
- data/lib/liquid/tags/unless.rb +6 -2
- data/lib/liquid/template.rb +13 -18
- data/lib/liquid/variable.rb +25 -12
- data/test/block_test.rb +8 -0
- data/test/condition_test.rb +109 -0
- data/test/context_test.rb +88 -10
- data/test/drop_test.rb +3 -1
- data/test/error_handling_test.rb +16 -3
- data/test/extra/breakpoint.rb +0 -0
- data/test/extra/caller.rb +0 -0
- data/test/filter_test.rb +3 -3
- data/test/html_tag_test.rb +7 -0
- data/test/if_else_test.rb +32 -0
- data/test/include_tag_test.rb +24 -1
- data/test/module_ex_test.rb +89 -0
- data/test/parsing_quirks_test.rb +15 -0
- data/test/regexp_test.rb +4 -3
- data/test/standard_filter_test.rb +27 -2
- data/test/standard_tag_test.rb +67 -20
- data/test/test_helper.rb +20 -0
- data/test/unless_else_test.rb +8 -0
- metadata +60 -46
data/CHANGELOG
CHANGED
@@ -1,30 +1,32 @@
|
|
1
|
-
|
1
|
+
* Fixed gem install rake task
|
2
|
+
* Improve Error encapsulation in liquid by maintaining a own set of exceptions instead of relying on ruby build ins
|
2
3
|
|
3
|
-
|
4
|
-
To export new objects to liquid just implement .to_liquid on them and return objects which themselves have .to_liquid methods.
|
4
|
+
* Added If with or / and expressions
|
5
5
|
|
6
|
-
|
6
|
+
* Implemented .to_liquid for all objects which can be passed to liquid like Strings Arrays Hashes Numerics and Booleans. To export new objects to liquid just implement .to_liquid on them and return objects which themselves have .to_liquid methods.
|
7
7
|
|
8
|
-
Added
|
8
|
+
* Added more tags to standard library
|
9
9
|
|
10
|
-
|
10
|
+
* Added include tag ( like partials in rails )
|
11
11
|
|
12
|
-
|
12
|
+
* [...] Gazillion of detail improvements
|
13
13
|
|
14
|
-
|
14
|
+
* Added strainers as filter hosts for better security [Tobias Luetke]
|
15
15
|
|
16
|
-
Fixed
|
17
|
-
filter which was obviously misleading [Tobias Luetke]
|
16
|
+
* Fixed that rails integration would call filter with the wrong "self" [Michael Geary]
|
18
17
|
|
19
|
-
|
18
|
+
* Fixed bad error reporting when a filter called a method which doesn't exist. Liquid told you that it couldn't find the filter which was obviously misleading [Tobias Luetke]
|
20
19
|
|
21
|
-
|
20
|
+
* Removed count helper from standard lib. use size [Tobias Luetke]
|
22
21
|
|
23
|
-
|
22
|
+
* Fixed bug with string filter parameters failing to tolerate commas in strings. [Paul Hammond]
|
23
|
+
|
24
|
+
* Improved filter parameters. Filter parameters are now context sensitive; Types are resolved according to the rules of the context. Multiple parameters are now separated by the Liquid::ArgumentSeparator: , by default [Paul Hammond]
|
25
|
+
|
24
26
|
{{ 'Typo' | link_to: 'http://typo.leetsoft.com', 'Typo - a modern weblog engine' }}
|
25
27
|
|
26
28
|
|
27
|
-
Added Liquid::Drop. A base class which you can use for exporting proxy objects to liquid which can acquire more data when used in liquid. [Tobias Luetke]
|
29
|
+
* Added Liquid::Drop. A base class which you can use for exporting proxy objects to liquid which can acquire more data when used in liquid. [Tobias Luetke]
|
28
30
|
|
29
31
|
class ProductDrop < Liquid::Drop
|
30
32
|
def top_sales
|
@@ -35,4 +37,4 @@ Added Liquid::Drop. A base class which you can use for exporting proxy objects t
|
|
35
37
|
t.render('product' => ProductDrop.new )
|
36
38
|
|
37
39
|
|
38
|
-
Added filter parameters support. Example: {{ date | format_date: "%Y" }} [Paul Hammond]
|
40
|
+
* Added filter parameters support. Example: {{ date | format_date: "%Y" }} [Paul Hammond]
|
data/History.txt
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
1.9.0 / 2008-03-04
|
2
|
+
|
3
|
+
* Fixed gem install rake task
|
4
|
+
* Improve Error encapsulation in liquid by maintaining a own set of exceptions instead of relying on ruby build ins
|
5
|
+
|
6
|
+
Before 1.9.0
|
7
|
+
|
8
|
+
* Added If with or / and expressions
|
9
|
+
|
10
|
+
* Implemented .to_liquid for all objects which can be passed to liquid like Strings Arrays Hashes Numerics and Booleans. To export new objects to liquid just implement .to_liquid on them and return objects which themselves have .to_liquid methods.
|
11
|
+
|
12
|
+
* Added more tags to standard library
|
13
|
+
|
14
|
+
* Added include tag ( like partials in rails )
|
15
|
+
|
16
|
+
* [...] Gazillion of detail improvements
|
17
|
+
|
18
|
+
* Added strainers as filter hosts for better security [Tobias Luetke]
|
19
|
+
|
20
|
+
* Fixed that rails integration would call filter with the wrong "self" [Michael Geary]
|
21
|
+
|
22
|
+
* Fixed bad error reporting when a filter called a method which doesn't exist. Liquid told you that it couldn't find the filter which was obviously misleading [Tobias Luetke]
|
23
|
+
|
24
|
+
* Removed count helper from standard lib. use size [Tobias Luetke]
|
25
|
+
|
26
|
+
* Fixed bug with string filter parameters failing to tolerate commas in strings. [Paul Hammond]
|
27
|
+
|
28
|
+
* Improved filter parameters. Filter parameters are now context sensitive; Types are resolved according to the rules of the context. Multiple parameters are now separated by the Liquid::ArgumentSeparator: , by default [Paul Hammond]
|
29
|
+
|
30
|
+
{{ 'Typo' | link_to: 'http://typo.leetsoft.com', 'Typo - a modern weblog engine' }}
|
31
|
+
|
32
|
+
|
33
|
+
* Added Liquid::Drop. A base class which you can use for exporting proxy objects to liquid which can acquire more data when used in liquid. [Tobias Luetke]
|
34
|
+
|
35
|
+
class ProductDrop < Liquid::Drop
|
36
|
+
def top_sales
|
37
|
+
Shop.current.products.find(:all, :order => 'sales', :limit => 10 )
|
38
|
+
end
|
39
|
+
end
|
40
|
+
t = Liquid::Template.parse( ' {% for product in product.top_sales %} {{ product.name }} {% endfor %} ' )
|
41
|
+
t.render('product' => ProductDrop.new )
|
42
|
+
|
43
|
+
|
44
|
+
* Added filter parameters support. Example: {{ date | format_date: "%Y" }} [Paul Hammond]
|
data/MIT-LICENSE
CHANGED
@@ -13,8 +13,8 @@ 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
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
17
|
-
NONINFRINGEMENT. IN NO EVENT
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
CHANGELOG
|
2
|
+
History.txt
|
2
3
|
MIT-LICENSE
|
3
4
|
Manifest.txt
|
4
|
-
README
|
5
|
+
README.txt
|
5
6
|
Rakefile
|
6
7
|
example/server/example_servlet.rb
|
7
8
|
example/server/liquid_servlet.rb
|
@@ -20,6 +21,7 @@ lib/liquid/errors.rb
|
|
20
21
|
lib/liquid/extensions.rb
|
21
22
|
lib/liquid/file_system.rb
|
22
23
|
lib/liquid/htmltags.rb
|
24
|
+
lib/liquid/module_ex.rb
|
23
25
|
lib/liquid/standardfilters.rb
|
24
26
|
lib/liquid/strainer.rb
|
25
27
|
lib/liquid/tag.rb
|
@@ -36,6 +38,7 @@ lib/liquid/tags/unless.rb
|
|
36
38
|
lib/liquid/template.rb
|
37
39
|
lib/liquid/variable.rb
|
38
40
|
test/block_test.rb
|
41
|
+
test/condition_test.rb
|
39
42
|
test/context_test.rb
|
40
43
|
test/drop_test.rb
|
41
44
|
test/error_handling_test.rb
|
@@ -47,6 +50,7 @@ test/helper.rb
|
|
47
50
|
test/html_tag_test.rb
|
48
51
|
test/if_else_test.rb
|
49
52
|
test/include_tag_test.rb
|
53
|
+
test/module_ex_test.rb
|
50
54
|
test/output_test.rb
|
51
55
|
test/parsing_quirks_test.rb
|
52
56
|
test/regexp_test.rb
|
@@ -56,5 +60,6 @@ test/standard_tag_test.rb
|
|
56
60
|
test/statements_test.rb
|
57
61
|
test/strainer_test.rb
|
58
62
|
test/template_test.rb
|
63
|
+
test/test_helper.rb
|
59
64
|
test/unless_else_test.rb
|
60
65
|
test/variable_test.rb
|
data/{README → README.txt}
RENAMED
File without changes
|
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@ require 'rubygems'
|
|
3
3
|
require 'rake'
|
4
4
|
require 'hoe'
|
5
5
|
|
6
|
-
PKG_VERSION = "1.
|
6
|
+
PKG_VERSION = "1.9.0"
|
7
7
|
PKG_NAME = "liquid"
|
8
8
|
PKG_DESC = "A secure non evaling end user template engine with aesthetic markup."
|
9
9
|
|
@@ -17,8 +17,8 @@ end
|
|
17
17
|
Hoe.new(PKG_NAME, PKG_VERSION) do |p|
|
18
18
|
p.rubyforge_name = PKG_NAME
|
19
19
|
p.summary = PKG_DESC
|
20
|
-
p.description =
|
20
|
+
p.description = PKG_DESC
|
21
21
|
p.author = "Tobias Luetke"
|
22
22
|
p.email = "tobi@leetsoft.com"
|
23
|
-
p.url = "http://
|
23
|
+
p.url = "http://www.liquidmarkup.org"
|
24
24
|
end
|
data/init.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'liquid'
|
2
2
|
require 'extras/liquid_view'
|
3
3
|
|
4
|
-
ActionView::
|
5
|
-
|
6
|
-
|
4
|
+
if defined? ActionView::Template and ActionView::Template.respond_to? :register_template_handler
|
5
|
+
ActionView::Template
|
6
|
+
else
|
7
|
+
ActionView::Base
|
8
|
+
end.register_template_handler(:liquid, LiquidView)
|
data/lib/liquid.rb
CHANGED
@@ -13,8 +13,8 @@
|
|
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
|
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
17
|
-
# NONINFRINGEMENT. IN NO EVENT
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
18
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -28,14 +28,15 @@ module Liquid
|
|
28
28
|
VariableAttributeSeparator = '.'
|
29
29
|
TagStart = /\{\%/
|
30
30
|
TagEnd = /\%\}/
|
31
|
-
VariableSignature =
|
32
|
-
VariableSegment = /[\w\-]
|
31
|
+
VariableSignature = /\(?[\w\-\.\[\]]\)?/
|
32
|
+
VariableSegment = /[\w\-]\??/
|
33
33
|
VariableStart = /\{\{/
|
34
34
|
VariableEnd = /\}\}/
|
35
|
+
VariableIncompleteEnd = /\}\}?/
|
35
36
|
QuotedFragment = /"[^"]+"|'[^']+'|[^\s,|]+/
|
36
37
|
TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/
|
37
|
-
TemplateParser = /(#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{
|
38
|
-
VariableParser =
|
38
|
+
TemplateParser = /(#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd})/
|
39
|
+
VariableParser = /\[[^\]]+\]|#{VariableSegment}+/
|
39
40
|
end
|
40
41
|
|
41
42
|
require 'liquid/drop'
|
@@ -52,6 +53,7 @@ require 'liquid/template'
|
|
52
53
|
require 'liquid/htmltags'
|
53
54
|
require 'liquid/standardfilters'
|
54
55
|
require 'liquid/condition'
|
56
|
+
require 'liquid/module_ex'
|
55
57
|
|
56
58
|
# Load all the tags of the standard library
|
57
59
|
#
|
data/lib/liquid/block.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Liquid
|
2
2
|
|
3
3
|
class Block < Tag
|
4
|
+
|
4
5
|
def parse(tokens)
|
5
6
|
@nodelist ||= []
|
6
7
|
@nodelist.clear
|
@@ -20,7 +21,7 @@ module Liquid
|
|
20
21
|
|
21
22
|
# fetch the tag from registered blocks
|
22
23
|
if tag = Template.tags[$1]
|
23
|
-
@nodelist << tag.new($2, tokens)
|
24
|
+
@nodelist << tag.new($1, $2, tokens)
|
24
25
|
else
|
25
26
|
# this tag is not registered with the system
|
26
27
|
# pass it to the current block for special handling or error reporting
|
@@ -63,7 +64,7 @@ module Liquid
|
|
63
64
|
end
|
64
65
|
|
65
66
|
def block_name
|
66
|
-
|
67
|
+
@tag_name
|
67
68
|
end
|
68
69
|
|
69
70
|
def create_variable(token)
|
@@ -76,7 +77,7 @@ module Liquid
|
|
76
77
|
def render(context)
|
77
78
|
render_all(@nodelist, context)
|
78
79
|
end
|
79
|
-
|
80
|
+
|
80
81
|
protected
|
81
82
|
|
82
83
|
def assert_missing_delimitation!
|
@@ -86,13 +87,9 @@ module Liquid
|
|
86
87
|
def render_all(list, context)
|
87
88
|
list.collect do |token|
|
88
89
|
begin
|
89
|
-
|
90
|
-
token.render(context)
|
91
|
-
else
|
92
|
-
token.to_s
|
93
|
-
end
|
90
|
+
token.respond_to?(:render) ? token.render(context) : token
|
94
91
|
rescue Exception => e
|
95
|
-
context.
|
92
|
+
context.handle_error(e)
|
96
93
|
end
|
97
94
|
|
98
95
|
end
|
data/lib/liquid/condition.rb
CHANGED
@@ -1,21 +1,55 @@
|
|
1
1
|
module Liquid
|
2
|
-
# Container for liquid nodes which conveniently
|
2
|
+
# Container for liquid nodes which conveniently wraps decision making logic
|
3
3
|
#
|
4
4
|
# Example:
|
5
5
|
#
|
6
6
|
# c = Condition.new('1', '==', '1')
|
7
7
|
# c.evaluate #=> true
|
8
8
|
#
|
9
|
-
class Condition
|
9
|
+
class Condition #:nodoc:
|
10
|
+
@@operators = {
|
11
|
+
'==' => lambda { |cond, left, right| cond.send(:equal_variables, left, right) },
|
12
|
+
'!=' => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) },
|
13
|
+
'<>' => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) },
|
14
|
+
'<' => :<,
|
15
|
+
'>' => :>,
|
16
|
+
'>=' => :>=,
|
17
|
+
'<=' => :<=,
|
18
|
+
'contains' => lambda { |cond, left, right| left.include?(right) },
|
19
|
+
}
|
20
|
+
|
21
|
+
def self.operators
|
22
|
+
@@operators
|
23
|
+
end
|
24
|
+
|
10
25
|
attr_reader :attachment
|
11
26
|
attr_accessor :left, :operator, :right
|
12
27
|
|
13
28
|
def initialize(left = nil, operator = nil, right = nil)
|
14
29
|
@left, @operator, @right = left, operator, right
|
30
|
+
@child_relation = nil
|
31
|
+
@child_condition = nil
|
15
32
|
end
|
16
33
|
|
17
34
|
def evaluate(context = Context.new)
|
18
|
-
interpret_condition(left, right, operator, context)
|
35
|
+
result = interpret_condition(left, right, operator, context)
|
36
|
+
|
37
|
+
case @child_relation
|
38
|
+
when :or
|
39
|
+
result || @child_condition.evaluate(context)
|
40
|
+
when :and
|
41
|
+
result && @child_condition.evaluate(context)
|
42
|
+
else
|
43
|
+
result
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def or(condition)
|
48
|
+
@child_relation, @child_condition = :or, condition
|
49
|
+
end
|
50
|
+
|
51
|
+
def and(condition)
|
52
|
+
@child_relation, @child_condition = :and, condition
|
19
53
|
end
|
20
54
|
|
21
55
|
def attach(attachment)
|
@@ -24,6 +58,10 @@ module Liquid
|
|
24
58
|
|
25
59
|
def else?
|
26
60
|
false
|
61
|
+
end
|
62
|
+
|
63
|
+
def inspect
|
64
|
+
"#<Condition #{[@left, @operator, @right].compact.join(' ')}>"
|
27
65
|
end
|
28
66
|
|
29
67
|
private
|
@@ -56,26 +94,20 @@ module Liquid
|
|
56
94
|
return context[left] if op == nil
|
57
95
|
|
58
96
|
left, right = context[left], context[right]
|
97
|
+
|
59
98
|
|
60
|
-
operation =
|
61
|
-
when '==' then return equal_variables(left, right)
|
62
|
-
when '!=' then return !equal_variables(left, right)
|
63
|
-
when '<>' then return !equal_variables(left, right)
|
64
|
-
when '>' then :>
|
65
|
-
when '<' then :<
|
66
|
-
when '>=' then :>=
|
67
|
-
when '<=' then :<=
|
68
|
-
else
|
69
|
-
raise ArgumentError.new("Error in tag '#{name}' - Unknown operator #{op}")
|
70
|
-
end
|
99
|
+
operation = self.class.operators[op] || raise(ArgumentError.new("Unknown operator #{op}"))
|
71
100
|
|
72
|
-
if
|
73
|
-
|
101
|
+
if operation.respond_to?(:call)
|
102
|
+
operation.call(self, left, right)
|
103
|
+
elsif left.respond_to?(operation) and right.respond_to?(operation)
|
104
|
+
left.send(operation, right)
|
74
105
|
else
|
75
106
|
nil
|
76
107
|
end
|
77
108
|
end
|
78
|
-
end
|
109
|
+
end
|
110
|
+
|
79
111
|
|
80
112
|
class ElseCondition < Condition
|
81
113
|
|
data/lib/liquid/context.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
module Liquid
|
2
2
|
|
3
|
-
class ContextError < StandardError
|
4
|
-
end
|
5
|
-
|
6
3
|
# Context keeps the variable stack and resolves variables, as well as keywords
|
7
4
|
#
|
8
5
|
# context['variable'] = 'testing'
|
@@ -17,20 +14,18 @@ module Liquid
|
|
17
14
|
# context['bob'] #=> nil class Context
|
18
15
|
class Context
|
19
16
|
attr_reader :scopes
|
20
|
-
attr_reader :
|
17
|
+
attr_reader :errors, :registers
|
21
18
|
|
22
|
-
def initialize(
|
23
|
-
@
|
24
|
-
@
|
25
|
-
|
19
|
+
def initialize(assigns = {}, registers = {}, rethrow_errors = false)
|
20
|
+
@scopes = [(assigns || {})]
|
21
|
+
@registers = registers
|
22
|
+
@errors = []
|
23
|
+
@rethrow_errors = rethrow_errors
|
24
|
+
end
|
26
25
|
|
27
26
|
def strainer
|
28
27
|
@strainer ||= Strainer.create(self)
|
29
|
-
end
|
30
|
-
|
31
|
-
def registers
|
32
|
-
@template.registers
|
33
|
-
end
|
28
|
+
end
|
34
29
|
|
35
30
|
# adds filters to this context.
|
36
31
|
# this does not register the filters with the main Template object. see <tt>Template.register_filter</tt>
|
@@ -38,12 +33,22 @@ module Liquid
|
|
38
33
|
def add_filters(filters)
|
39
34
|
filters = [filters].flatten.compact
|
40
35
|
|
41
|
-
raise ArgumentError, "Expected module but got: #{filter_module.class}" unless filters.all? { |f| f.is_a?(Module)}
|
42
|
-
|
43
36
|
filters.each do |f|
|
37
|
+
raise ArgumentError, "Expected module but got: #{f.class}" unless f.is_a?(Module)
|
44
38
|
strainer.extend(f)
|
45
39
|
end
|
46
40
|
end
|
41
|
+
|
42
|
+
def handle_error(e)
|
43
|
+
errors.push(e)
|
44
|
+
raise if @rethrow_errors
|
45
|
+
|
46
|
+
case e
|
47
|
+
when SyntaxError then "Liquid syntax error: #{e.message}"
|
48
|
+
else "Liquid error: #{e.message}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
47
52
|
|
48
53
|
def invoke(method, *args)
|
49
54
|
if strainer.respond_to?(method)
|
@@ -115,16 +120,16 @@ module Liquid
|
|
115
120
|
#
|
116
121
|
def resolve(key)
|
117
122
|
case key
|
118
|
-
when nil
|
123
|
+
when nil, 'nil', 'null', ''
|
119
124
|
nil
|
120
125
|
when 'true'
|
121
126
|
true
|
122
127
|
when 'false'
|
123
|
-
false
|
128
|
+
false
|
129
|
+
when 'blank'
|
130
|
+
:blank?
|
124
131
|
when 'empty'
|
125
132
|
:empty?
|
126
|
-
when 'nil', 'null'
|
127
|
-
nil
|
128
133
|
# Single quoted strings
|
129
134
|
when /^'(.*)'$/
|
130
135
|
$1.to_s
|
@@ -134,11 +139,15 @@ module Liquid
|
|
134
139
|
# Integer and floats
|
135
140
|
when /^(\d+)$/
|
136
141
|
$1.to_i
|
142
|
+
# Ranges
|
143
|
+
when /^\((\S+)\.\.(\S+)\)$/
|
144
|
+
(resolve($1).to_i..resolve($2).to_i)
|
145
|
+
# Floats
|
137
146
|
when /^(\d[\d\.]+)$/
|
138
147
|
$1.to_f
|
139
148
|
else
|
140
149
|
variable(key)
|
141
|
-
end
|
150
|
+
end
|
142
151
|
end
|
143
152
|
|
144
153
|
# fetches an object starting at the local scope and then moving up
|
@@ -148,7 +157,8 @@ module Liquid
|
|
148
157
|
if scope.has_key?(key)
|
149
158
|
variable = scope[key]
|
150
159
|
variable = scope[key] = variable.call(self) if variable.is_a?(Proc)
|
151
|
-
variable
|
160
|
+
variable = variable.to_liquid
|
161
|
+
variable.context = self if variable.respond_to?(:context=)
|
152
162
|
return variable
|
153
163
|
end
|
154
164
|
end
|
@@ -164,39 +174,55 @@ module Liquid
|
|
164
174
|
# assert_equal 'tobi', @context['hash[name]']
|
165
175
|
#
|
166
176
|
def variable(markup)
|
167
|
-
parts
|
177
|
+
parts = markup.scan(VariableParser)
|
178
|
+
square_bracketed = /^\[(.*)\]$/
|
179
|
+
|
180
|
+
first_part = parts.shift
|
181
|
+
if first_part =~ square_bracketed
|
182
|
+
first_part = resolve($1)
|
183
|
+
end
|
168
184
|
|
169
|
-
if object = find_variable(
|
185
|
+
if object = find_variable(first_part)
|
170
186
|
|
171
187
|
parts.each do |part|
|
172
188
|
|
173
189
|
# If object is a hash we look for the presence of the key and if its available
|
174
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
|
175
199
|
|
176
|
-
|
177
|
-
|
200
|
+
# Hash
|
201
|
+
if object.respond_to?(:has_key?) and object.has_key?(part)
|
178
202
|
|
179
|
-
|
180
|
-
|
181
|
-
|
203
|
+
# if its a proc we will replace the entry in the hash table with the proc
|
204
|
+
res = object[part]
|
205
|
+
res = object[part] = res.call(self) if res.is_a?(Proc) and object.respond_to?(:[]=)
|
206
|
+
object = res.to_liquid
|
182
207
|
|
183
|
-
|
184
|
-
|
185
|
-
|
208
|
+
# Array
|
209
|
+
elsif object.respond_to?(:fetch) and part =~ /^\d+$/
|
210
|
+
pos = part.to_i
|
186
211
|
|
187
|
-
|
188
|
-
|
212
|
+
object[pos] = object[pos].call(self) if object[pos].is_a?(Proc) and object.respond_to?(:[]=)
|
213
|
+
object = object[pos].to_liquid
|
189
214
|
|
190
|
-
|
191
|
-
|
192
|
-
|
215
|
+
# Some special cases. If no key with the same name was found we interpret following calls
|
216
|
+
# as commands and call them on the current object
|
217
|
+
elsif object.respond_to?(part) and ['size', 'first', 'last'].include?(part)
|
193
218
|
|
194
|
-
|
219
|
+
object = object.send(part.intern).to_liquid
|
195
220
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
221
|
+
# No key was present with the desired value and it wasn't one of the directly supported
|
222
|
+
# keywords either. The only thing we got left is to return nil
|
223
|
+
else
|
224
|
+
return nil
|
225
|
+
end
|
200
226
|
end
|
201
227
|
|
202
228
|
# If we are dealing with a drop here we have to
|