spinto-liquid 2.3.0.1
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/History.md +56 -0
- data/MIT-LICENSE +20 -0
- data/README.md +44 -0
- data/lib/extras/liquid_view.rb +51 -0
- data/lib/liquid/block.rb +101 -0
- data/lib/liquid/condition.rb +120 -0
- data/lib/liquid/context.rb +245 -0
- data/lib/liquid/document.rb +17 -0
- data/lib/liquid/drop.rb +49 -0
- data/lib/liquid/errors.rb +11 -0
- data/lib/liquid/extensions.rb +62 -0
- data/lib/liquid/file_system.rb +62 -0
- data/lib/liquid/htmltags.rb +75 -0
- data/lib/liquid/module_ex.rb +62 -0
- data/lib/liquid/standardfilters.rb +241 -0
- data/lib/liquid/strainer.rb +54 -0
- data/lib/liquid/tag.rb +26 -0
- data/lib/liquid/tags/assign.rb +33 -0
- data/lib/liquid/tags/capture.rb +35 -0
- data/lib/liquid/tags/case.rb +79 -0
- data/lib/liquid/tags/comment.rb +9 -0
- data/lib/liquid/tags/cycle.rb +59 -0
- data/lib/liquid/tags/decrement.rb +39 -0
- data/lib/liquid/tags/for.rb +190 -0
- data/lib/liquid/tags/if.rb +79 -0
- data/lib/liquid/tags/ifchanged.rb +20 -0
- data/lib/liquid/tags/include.rb +65 -0
- data/lib/liquid/tags/increment.rb +35 -0
- data/lib/liquid/tags/raw.rb +21 -0
- data/lib/liquid/tags/unless.rb +33 -0
- data/lib/liquid/template.rb +150 -0
- data/lib/liquid/variable.rb +50 -0
- data/lib/liquid.rb +66 -0
- data/test/liquid/assign_test.rb +21 -0
- data/test/liquid/block_test.rb +58 -0
- data/test/liquid/capture_test.rb +40 -0
- data/test/liquid/condition_test.rb +127 -0
- data/test/liquid/context_test.rb +478 -0
- data/test/liquid/drop_test.rb +162 -0
- data/test/liquid/error_handling_test.rb +81 -0
- data/test/liquid/file_system_test.rb +29 -0
- data/test/liquid/filter_test.rb +106 -0
- data/test/liquid/module_ex_test.rb +87 -0
- data/test/liquid/output_test.rb +116 -0
- data/test/liquid/parsing_quirks_test.rb +52 -0
- data/test/liquid/regexp_test.rb +44 -0
- data/test/liquid/security_test.rb +41 -0
- data/test/liquid/standard_filter_test.rb +195 -0
- data/test/liquid/strainer_test.rb +25 -0
- data/test/liquid/tags/for_tag_test.rb +215 -0
- data/test/liquid/tags/html_tag_test.rb +39 -0
- data/test/liquid/tags/if_else_tag_test.rb +160 -0
- data/test/liquid/tags/include_tag_test.rb +139 -0
- data/test/liquid/tags/increment_tag_test.rb +24 -0
- data/test/liquid/tags/raw_tag_test.rb +15 -0
- data/test/liquid/tags/standard_tag_test.rb +295 -0
- data/test/liquid/tags/statements_test.rb +134 -0
- data/test/liquid/tags/unless_else_tag_test.rb +26 -0
- data/test/liquid/template_test.rb +74 -0
- data/test/liquid/variable_test.rb +170 -0
- data/test/test_helper.rb +29 -0
- metadata +136 -0
data/lib/liquid.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Copyright (c) 2005 Tobias Luetke
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
module Liquid
|
23
|
+
FilterSeparator = /\|/
|
24
|
+
ArgumentSeparator = ','
|
25
|
+
FilterArgumentSeparator = ':'
|
26
|
+
VariableAttributeSeparator = '.'
|
27
|
+
TagStart = /\{\%/
|
28
|
+
TagEnd = /\%\}/
|
29
|
+
VariableSignature = /\(?[\w\-\.\[\]]\)?/
|
30
|
+
VariableSegment = /[\w\-]/
|
31
|
+
VariableStart = /\{\{/
|
32
|
+
VariableEnd = /\}\}/
|
33
|
+
VariableIncompleteEnd = /\}\}?/
|
34
|
+
QuotedString = /"[^"]*"|'[^']*'/
|
35
|
+
QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/
|
36
|
+
StrictQuotedFragment = /"[^"]+"|'[^']+'|[^\s|:,]+/
|
37
|
+
FirstFilterArgument = /#{FilterArgumentSeparator}(?:#{StrictQuotedFragment})/
|
38
|
+
OtherFilterArgument = /#{ArgumentSeparator}(?:#{StrictQuotedFragment})/
|
39
|
+
SpacelessFilter = /^(?:'[^']+'|"[^"]+"|[^'"])*#{FilterSeparator}(?:#{StrictQuotedFragment})(?:#{FirstFilterArgument}(?:#{OtherFilterArgument})*)?/
|
40
|
+
Expression = /(?:#{QuotedFragment}(?:#{SpacelessFilter})*)/
|
41
|
+
TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/
|
42
|
+
AnyStartingTag = /\{\{|\{\%/
|
43
|
+
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/
|
44
|
+
TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/
|
45
|
+
VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/
|
46
|
+
end
|
47
|
+
|
48
|
+
require 'liquid/drop'
|
49
|
+
require 'liquid/extensions'
|
50
|
+
require 'liquid/errors'
|
51
|
+
require 'liquid/strainer'
|
52
|
+
require 'liquid/context'
|
53
|
+
require 'liquid/tag'
|
54
|
+
require 'liquid/block'
|
55
|
+
require 'liquid/document'
|
56
|
+
require 'liquid/variable'
|
57
|
+
require 'liquid/file_system'
|
58
|
+
require 'liquid/template'
|
59
|
+
require 'liquid/htmltags'
|
60
|
+
require 'liquid/standardfilters'
|
61
|
+
require 'liquid/condition'
|
62
|
+
require 'liquid/module_ex'
|
63
|
+
|
64
|
+
# Load all the tags of the standard library
|
65
|
+
#
|
66
|
+
Dir[File.dirname(__FILE__) + '/liquid/tags/*.rb'].each { |f| require f }
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class AssignTest < Test::Unit::TestCase
|
4
|
+
include Liquid
|
5
|
+
|
6
|
+
def test_assigned_variable
|
7
|
+
assert_template_result('.foo.',
|
8
|
+
'{% assign foo = values %}.{{ foo[0] }}.',
|
9
|
+
'values' => %w{foo bar baz})
|
10
|
+
|
11
|
+
assert_template_result('.bar.',
|
12
|
+
'{% assign foo = values %}.{{ foo[1] }}.',
|
13
|
+
'values' => %w{foo bar baz})
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_assign_with_filter
|
17
|
+
assert_template_result('.bar.',
|
18
|
+
'{% assign foo = values | split: "," %}.{{ foo[1] }}.',
|
19
|
+
'values' => "foo,bar,baz")
|
20
|
+
end
|
21
|
+
end # AssignTest
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class VariableTest < Test::Unit::TestCase
|
4
|
+
include Liquid
|
5
|
+
|
6
|
+
def test_blankspace
|
7
|
+
template = Liquid::Template.parse(" ")
|
8
|
+
assert_equal [" "], template.root.nodelist
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_variable_beginning
|
12
|
+
template = Liquid::Template.parse("{{funk}} ")
|
13
|
+
assert_equal 2, template.root.nodelist.size
|
14
|
+
assert_equal Variable, template.root.nodelist[0].class
|
15
|
+
assert_equal String, template.root.nodelist[1].class
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_variable_end
|
19
|
+
template = Liquid::Template.parse(" {{funk}}")
|
20
|
+
assert_equal 2, template.root.nodelist.size
|
21
|
+
assert_equal String, template.root.nodelist[0].class
|
22
|
+
assert_equal Variable, template.root.nodelist[1].class
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_variable_middle
|
26
|
+
template = Liquid::Template.parse(" {{funk}} ")
|
27
|
+
assert_equal 3, template.root.nodelist.size
|
28
|
+
assert_equal String, template.root.nodelist[0].class
|
29
|
+
assert_equal Variable, template.root.nodelist[1].class
|
30
|
+
assert_equal String, template.root.nodelist[2].class
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_variable_many_embedded_fragments
|
34
|
+
template = Liquid::Template.parse(" {{funk}} {{so}} {{brother}} ")
|
35
|
+
assert_equal 7, template.root.nodelist.size
|
36
|
+
assert_equal [String, Variable, String, Variable, String, Variable, String],
|
37
|
+
block_types(template.root.nodelist)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_with_block
|
41
|
+
template = Liquid::Template.parse(" {% comment %} {% endcomment %} ")
|
42
|
+
assert_equal [String, Comment, String], block_types(template.root.nodelist)
|
43
|
+
assert_equal 3, template.root.nodelist.size
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_with_custom_tag
|
47
|
+
Liquid::Template.register_tag("testtag", Block)
|
48
|
+
|
49
|
+
assert_nothing_thrown do
|
50
|
+
template = Liquid::Template.parse( "{% testtag %} {% endtesttag %}")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def block_types(nodelist)
|
56
|
+
nodelist.collect { |node| node.class }
|
57
|
+
end
|
58
|
+
end # VariableTest
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class CaptureTest < Test::Unit::TestCase
|
4
|
+
include Liquid
|
5
|
+
|
6
|
+
def test_captures_block_content_in_variable
|
7
|
+
assert_template_result("test string", "{% capture 'var' %}test string{% endcapture %}{{var}}", {})
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_capture_to_variable_from_outer_scope_if_existing
|
11
|
+
template_source = <<-END_TEMPLATE
|
12
|
+
{% assign var = '' %}
|
13
|
+
{% if true %}
|
14
|
+
{% capture var %}first-block-string{% endcapture %}
|
15
|
+
{% endif %}
|
16
|
+
{% if true %}
|
17
|
+
{% capture var %}test-string{% endcapture %}
|
18
|
+
{% endif %}
|
19
|
+
{{var}}
|
20
|
+
END_TEMPLATE
|
21
|
+
template = Template.parse(template_source)
|
22
|
+
rendered = template.render
|
23
|
+
assert_equal "test-string", rendered.gsub(/\s/, '')
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_assigning_from_capture
|
27
|
+
template_source = <<-END_TEMPLATE
|
28
|
+
{% assign first = '' %}
|
29
|
+
{% assign second = '' %}
|
30
|
+
{% for number in (1..3) %}
|
31
|
+
{% capture first %}{{number}}{% endcapture %}
|
32
|
+
{% assign second = first %}
|
33
|
+
{% endfor %}
|
34
|
+
{{ first }}-{{ second }}
|
35
|
+
END_TEMPLATE
|
36
|
+
template = Template.parse(template_source)
|
37
|
+
rendered = template.render
|
38
|
+
assert_equal "3-3", rendered.gsub(/\s/, '')
|
39
|
+
end
|
40
|
+
end # CaptureTest
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ConditionTest < Test::Unit::TestCase
|
4
|
+
include Liquid
|
5
|
+
|
6
|
+
def test_basic_condition
|
7
|
+
assert_equal false, Condition.new('1', '==', '2').evaluate
|
8
|
+
assert_equal true, Condition.new('1', '==', '1').evaluate
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_default_operators_evalute_true
|
12
|
+
assert_evalutes_true '1', '==', '1'
|
13
|
+
assert_evalutes_true '1', '!=', '2'
|
14
|
+
assert_evalutes_true '1', '<>', '2'
|
15
|
+
assert_evalutes_true '1', '<', '2'
|
16
|
+
assert_evalutes_true '2', '>', '1'
|
17
|
+
assert_evalutes_true '1', '>=', '1'
|
18
|
+
assert_evalutes_true '2', '>=', '1'
|
19
|
+
assert_evalutes_true '1', '<=', '2'
|
20
|
+
assert_evalutes_true '1', '<=', '1'
|
21
|
+
# negative numbers
|
22
|
+
assert_evalutes_true '1', '>', '-1'
|
23
|
+
assert_evalutes_true '-1', '<', '1'
|
24
|
+
assert_evalutes_true '1.0', '>', '-1.0'
|
25
|
+
assert_evalutes_true '-1.0', '<', '1.0'
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_default_operators_evalute_false
|
29
|
+
assert_evalutes_false '1', '==', '2'
|
30
|
+
assert_evalutes_false '1', '!=', '1'
|
31
|
+
assert_evalutes_false '1', '<>', '1'
|
32
|
+
assert_evalutes_false '1', '<', '0'
|
33
|
+
assert_evalutes_false '2', '>', '4'
|
34
|
+
assert_evalutes_false '1', '>=', '3'
|
35
|
+
assert_evalutes_false '2', '>=', '4'
|
36
|
+
assert_evalutes_false '1', '<=', '0'
|
37
|
+
assert_evalutes_false '1', '<=', '0'
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_contains_works_on_strings
|
41
|
+
assert_evalutes_true "'bob'", 'contains', "'o'"
|
42
|
+
assert_evalutes_true "'bob'", 'contains', "'b'"
|
43
|
+
assert_evalutes_true "'bob'", 'contains', "'bo'"
|
44
|
+
assert_evalutes_true "'bob'", 'contains', "'ob'"
|
45
|
+
assert_evalutes_true "'bob'", 'contains', "'bob'"
|
46
|
+
|
47
|
+
assert_evalutes_false "'bob'", 'contains', "'bob2'"
|
48
|
+
assert_evalutes_false "'bob'", 'contains', "'a'"
|
49
|
+
assert_evalutes_false "'bob'", 'contains', "'---'"
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_contains_works_on_arrays
|
53
|
+
@context = Liquid::Context.new
|
54
|
+
@context['array'] = [1,2,3,4,5]
|
55
|
+
|
56
|
+
assert_evalutes_false "array", 'contains', '0'
|
57
|
+
assert_evalutes_true "array", 'contains', '1'
|
58
|
+
assert_evalutes_true "array", 'contains', '2'
|
59
|
+
assert_evalutes_true "array", 'contains', '3'
|
60
|
+
assert_evalutes_true "array", 'contains', '4'
|
61
|
+
assert_evalutes_true "array", 'contains', '5'
|
62
|
+
assert_evalutes_false "array", 'contains', '6'
|
63
|
+
assert_evalutes_false "array", 'contains', '"1"'
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_contains_returns_false_for_nil_operands
|
67
|
+
@context = Liquid::Context.new
|
68
|
+
assert_evalutes_false "not_assigned", 'contains', '0'
|
69
|
+
assert_evalutes_false "0", 'contains', 'not_assigned'
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_or_condition
|
73
|
+
condition = Condition.new('1', '==', '2')
|
74
|
+
|
75
|
+
assert_equal false, condition.evaluate
|
76
|
+
|
77
|
+
condition.or Condition.new('2', '==', '1')
|
78
|
+
|
79
|
+
assert_equal false, condition.evaluate
|
80
|
+
|
81
|
+
condition.or Condition.new('1', '==', '1')
|
82
|
+
|
83
|
+
assert_equal true, condition.evaluate
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_and_condition
|
87
|
+
condition = Condition.new('1', '==', '1')
|
88
|
+
|
89
|
+
assert_equal true, condition.evaluate
|
90
|
+
|
91
|
+
condition.and Condition.new('2', '==', '2')
|
92
|
+
|
93
|
+
assert_equal true, condition.evaluate
|
94
|
+
|
95
|
+
condition.and Condition.new('2', '==', '1')
|
96
|
+
|
97
|
+
assert_equal false, condition.evaluate
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_should_allow_custom_proc_operator
|
101
|
+
Condition.operators['starts_with'] = Proc.new { |cond, left, right| left =~ %r{^#{right}} }
|
102
|
+
|
103
|
+
assert_evalutes_true "'bob'", 'starts_with', "'b'"
|
104
|
+
assert_evalutes_false "'bob'", 'starts_with', "'o'"
|
105
|
+
|
106
|
+
ensure
|
107
|
+
Condition.operators.delete 'starts_with'
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_left_or_right_may_contain_operators
|
111
|
+
@context = Liquid::Context.new
|
112
|
+
@context['one'] = @context['another'] = "gnomeslab-and-or-liquid"
|
113
|
+
|
114
|
+
assert_evalutes_true "one", '==', "another"
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
def assert_evalutes_true(left, op, right)
|
119
|
+
assert Condition.new(left, op, right).evaluate(@context || Liquid::Context.new),
|
120
|
+
"Evaluated false: #{left} #{op} #{right}"
|
121
|
+
end
|
122
|
+
|
123
|
+
def assert_evalutes_false(left, op, right)
|
124
|
+
assert !Condition.new(left, op, right).evaluate(@context || Liquid::Context.new),
|
125
|
+
"Evaluated true: #{left} #{op} #{right}"
|
126
|
+
end
|
127
|
+
end # ConditionTest
|
@@ -0,0 +1,478 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class HundredCentes
|
4
|
+
def to_liquid
|
5
|
+
100
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class CentsDrop < Liquid::Drop
|
10
|
+
def amount
|
11
|
+
HundredCentes.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def non_zero?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class ContextSensitiveDrop < Liquid::Drop
|
20
|
+
def test
|
21
|
+
@context['test']
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Category < Liquid::Drop
|
26
|
+
attr_accessor :name
|
27
|
+
|
28
|
+
def initialize(name)
|
29
|
+
@name = name
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_liquid
|
33
|
+
CategoryDrop.new(self)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class CategoryDrop
|
38
|
+
attr_accessor :category, :context
|
39
|
+
def initialize(category)
|
40
|
+
@category = category
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class CounterDrop < Liquid::Drop
|
45
|
+
def count
|
46
|
+
@count ||= 0
|
47
|
+
@count += 1
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class ArrayLike
|
52
|
+
def fetch(index)
|
53
|
+
end
|
54
|
+
|
55
|
+
def [](index)
|
56
|
+
@counts ||= []
|
57
|
+
@counts[index] ||= 0
|
58
|
+
@counts[index] += 1
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_liquid
|
62
|
+
self
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class ContextTest < Test::Unit::TestCase
|
67
|
+
include Liquid
|
68
|
+
|
69
|
+
def setup
|
70
|
+
@context = Liquid::Context.new
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_variables
|
74
|
+
@context['string'] = 'string'
|
75
|
+
assert_equal 'string', @context['string']
|
76
|
+
|
77
|
+
@context['num'] = 5
|
78
|
+
assert_equal 5, @context['num']
|
79
|
+
|
80
|
+
@context['time'] = Time.parse('2006-06-06 12:00:00')
|
81
|
+
assert_equal Time.parse('2006-06-06 12:00:00'), @context['time']
|
82
|
+
|
83
|
+
@context['date'] = Date.today
|
84
|
+
assert_equal Date.today, @context['date']
|
85
|
+
|
86
|
+
now = DateTime.now
|
87
|
+
@context['datetime'] = now
|
88
|
+
assert_equal now, @context['datetime']
|
89
|
+
|
90
|
+
@context['bool'] = true
|
91
|
+
assert_equal true, @context['bool']
|
92
|
+
|
93
|
+
@context['bool'] = false
|
94
|
+
assert_equal false, @context['bool']
|
95
|
+
|
96
|
+
@context['nil'] = nil
|
97
|
+
assert_equal nil, @context['nil']
|
98
|
+
assert_equal nil, @context['nil']
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_variables_not_existing
|
102
|
+
assert_equal nil, @context['does_not_exist']
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_scoping
|
106
|
+
assert_nothing_raised do
|
107
|
+
@context.push
|
108
|
+
@context.pop
|
109
|
+
end
|
110
|
+
|
111
|
+
assert_raise(Liquid::ContextError) do
|
112
|
+
@context.pop
|
113
|
+
end
|
114
|
+
|
115
|
+
assert_raise(Liquid::ContextError) do
|
116
|
+
@context.push
|
117
|
+
@context.pop
|
118
|
+
@context.pop
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_length_query
|
123
|
+
|
124
|
+
@context['numbers'] = [1,2,3,4]
|
125
|
+
|
126
|
+
assert_equal 4, @context['numbers.size']
|
127
|
+
|
128
|
+
@context['numbers'] = {1 => 1,2 => 2,3 => 3,4 => 4}
|
129
|
+
|
130
|
+
assert_equal 4, @context['numbers.size']
|
131
|
+
|
132
|
+
@context['numbers'] = {1 => 1,2 => 2,3 => 3,4 => 4, 'size' => 1000}
|
133
|
+
|
134
|
+
assert_equal 1000, @context['numbers.size']
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_hyphenated_variable
|
139
|
+
|
140
|
+
@context['oh-my'] = 'godz'
|
141
|
+
assert_equal 'godz', @context['oh-my']
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_add_filter
|
146
|
+
|
147
|
+
filter = Module.new do
|
148
|
+
def hi(output)
|
149
|
+
output + ' hi!'
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context = Context.new
|
154
|
+
context.add_filters(filter)
|
155
|
+
assert_equal 'hi? hi!', context.invoke(:hi, 'hi?')
|
156
|
+
|
157
|
+
context = Context.new
|
158
|
+
assert_equal 'hi?', context.invoke(:hi, 'hi?')
|
159
|
+
|
160
|
+
context.add_filters(filter)
|
161
|
+
assert_equal 'hi? hi!', context.invoke(:hi, 'hi?')
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_override_global_filter
|
166
|
+
global = Module.new do
|
167
|
+
def notice(output)
|
168
|
+
"Global #{output}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
local = Module.new do
|
173
|
+
def notice(output)
|
174
|
+
"Local #{output}"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
Template.register_filter(global)
|
179
|
+
assert_equal 'Global test', Template.parse("{{'test' | notice }}").render
|
180
|
+
assert_equal 'Local test', Template.parse("{{'test' | notice }}").render({}, :filters => [local])
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_only_intended_filters_make_it_there
|
184
|
+
|
185
|
+
filter = Module.new do
|
186
|
+
def hi(output)
|
187
|
+
output + ' hi!'
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context = Context.new
|
192
|
+
methods_before = context.strainer.methods.map { |method| method.to_s }
|
193
|
+
context.add_filters(filter)
|
194
|
+
methods_after = context.strainer.methods.map { |method| method.to_s }
|
195
|
+
assert_equal (methods_before + ["hi"]).sort, methods_after.sort
|
196
|
+
end
|
197
|
+
|
198
|
+
def test_add_item_in_outer_scope
|
199
|
+
@context['test'] = 'test'
|
200
|
+
@context.push
|
201
|
+
assert_equal 'test', @context['test']
|
202
|
+
@context.pop
|
203
|
+
assert_equal 'test', @context['test']
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_add_item_in_inner_scope
|
207
|
+
@context.push
|
208
|
+
@context['test'] = 'test'
|
209
|
+
assert_equal 'test', @context['test']
|
210
|
+
@context.pop
|
211
|
+
assert_equal nil, @context['test']
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_hierachical_data
|
215
|
+
@context['hash'] = {"name" => 'tobi'}
|
216
|
+
assert_equal 'tobi', @context['hash.name']
|
217
|
+
assert_equal 'tobi', @context['hash["name"]']
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_keywords
|
221
|
+
assert_equal true, @context['true']
|
222
|
+
assert_equal false, @context['false']
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_digits
|
226
|
+
assert_equal 100, @context['100']
|
227
|
+
assert_equal 100.00, @context['100.00']
|
228
|
+
end
|
229
|
+
|
230
|
+
def test_strings
|
231
|
+
assert_equal "hello!", @context['"hello!"']
|
232
|
+
assert_equal "hello!", @context["'hello!'"]
|
233
|
+
end
|
234
|
+
|
235
|
+
def test_merge
|
236
|
+
@context.merge({ "test" => "test" })
|
237
|
+
assert_equal 'test', @context['test']
|
238
|
+
@context.merge({ "test" => "newvalue", "foo" => "bar" })
|
239
|
+
assert_equal 'newvalue', @context['test']
|
240
|
+
assert_equal 'bar', @context['foo']
|
241
|
+
end
|
242
|
+
|
243
|
+
def test_array_notation
|
244
|
+
@context['test'] = [1,2,3,4,5]
|
245
|
+
|
246
|
+
assert_equal 1, @context['test[0]']
|
247
|
+
assert_equal 2, @context['test[1]']
|
248
|
+
assert_equal 3, @context['test[2]']
|
249
|
+
assert_equal 4, @context['test[3]']
|
250
|
+
assert_equal 5, @context['test[4]']
|
251
|
+
end
|
252
|
+
|
253
|
+
def test_recoursive_array_notation
|
254
|
+
@context['test'] = {'test' => [1,2,3,4,5]}
|
255
|
+
|
256
|
+
assert_equal 1, @context['test.test[0]']
|
257
|
+
|
258
|
+
@context['test'] = [{'test' => 'worked'}]
|
259
|
+
|
260
|
+
assert_equal 'worked', @context['test[0].test']
|
261
|
+
end
|
262
|
+
|
263
|
+
def test_hash_to_array_transition
|
264
|
+
@context['colors'] = {
|
265
|
+
'Blue' => ['003366','336699', '6699CC', '99CCFF'],
|
266
|
+
'Green' => ['003300','336633', '669966', '99CC99'],
|
267
|
+
'Yellow' => ['CC9900','FFCC00', 'FFFF99', 'FFFFCC'],
|
268
|
+
'Red' => ['660000','993333', 'CC6666', 'FF9999']
|
269
|
+
}
|
270
|
+
|
271
|
+
assert_equal '003366', @context['colors.Blue[0]']
|
272
|
+
assert_equal 'FF9999', @context['colors.Red[3]']
|
273
|
+
end
|
274
|
+
|
275
|
+
def test_try_first
|
276
|
+
@context['test'] = [1,2,3,4,5]
|
277
|
+
|
278
|
+
assert_equal 1, @context['test.first']
|
279
|
+
assert_equal 5, @context['test.last']
|
280
|
+
|
281
|
+
@context['test'] = {'test' => [1,2,3,4,5]}
|
282
|
+
|
283
|
+
assert_equal 1, @context['test.test.first']
|
284
|
+
assert_equal 5, @context['test.test.last']
|
285
|
+
|
286
|
+
@context['test'] = [1]
|
287
|
+
assert_equal 1, @context['test.first']
|
288
|
+
assert_equal 1, @context['test.last']
|
289
|
+
end
|
290
|
+
|
291
|
+
def test_access_hashes_with_hash_notation
|
292
|
+
@context['products'] = {'count' => 5, 'tags' => ['deepsnow', 'freestyle'] }
|
293
|
+
@context['product'] = {'variants' => [ {'title' => 'draft151cm'}, {'title' => 'element151cm'} ]}
|
294
|
+
|
295
|
+
assert_equal 5, @context['products["count"]']
|
296
|
+
assert_equal 'deepsnow', @context['products["tags"][0]']
|
297
|
+
assert_equal 'deepsnow', @context['products["tags"].first']
|
298
|
+
assert_equal 'draft151cm', @context['product["variants"][0]["title"]']
|
299
|
+
assert_equal 'element151cm', @context['product["variants"][1]["title"]']
|
300
|
+
assert_equal 'draft151cm', @context['product["variants"][0]["title"]']
|
301
|
+
assert_equal 'element151cm', @context['product["variants"].last["title"]']
|
302
|
+
end
|
303
|
+
|
304
|
+
def test_access_variable_with_hash_notation
|
305
|
+
@context['foo'] = 'baz'
|
306
|
+
@context['bar'] = 'foo'
|
307
|
+
|
308
|
+
assert_equal 'baz', @context['["foo"]']
|
309
|
+
assert_equal 'baz', @context['[bar]']
|
310
|
+
end
|
311
|
+
|
312
|
+
def test_access_hashes_with_hash_access_variables
|
313
|
+
|
314
|
+
@context['var'] = 'tags'
|
315
|
+
@context['nested'] = {'var' => 'tags'}
|
316
|
+
@context['products'] = {'count' => 5, 'tags' => ['deepsnow', 'freestyle'] }
|
317
|
+
|
318
|
+
assert_equal 'deepsnow', @context['products[var].first']
|
319
|
+
assert_equal 'freestyle', @context['products[nested.var].last']
|
320
|
+
end
|
321
|
+
|
322
|
+
def test_hash_notation_only_for_hash_access
|
323
|
+
@context['array'] = [1,2,3,4,5]
|
324
|
+
@context['hash'] = {'first' => 'Hello'}
|
325
|
+
|
326
|
+
assert_equal 1, @context['array.first']
|
327
|
+
assert_equal nil, @context['array["first"]']
|
328
|
+
assert_equal 'Hello', @context['hash["first"]']
|
329
|
+
end
|
330
|
+
|
331
|
+
def test_first_can_appear_in_middle_of_callchain
|
332
|
+
|
333
|
+
@context['product'] = {'variants' => [ {'title' => 'draft151cm'}, {'title' => 'element151cm'} ]}
|
334
|
+
|
335
|
+
assert_equal 'draft151cm', @context['product.variants[0].title']
|
336
|
+
assert_equal 'element151cm', @context['product.variants[1].title']
|
337
|
+
assert_equal 'draft151cm', @context['product.variants.first.title']
|
338
|
+
assert_equal 'element151cm', @context['product.variants.last.title']
|
339
|
+
|
340
|
+
end
|
341
|
+
|
342
|
+
def test_cents
|
343
|
+
@context.merge( "cents" => HundredCentes.new )
|
344
|
+
assert_equal 100, @context['cents']
|
345
|
+
end
|
346
|
+
|
347
|
+
def test_nested_cents
|
348
|
+
@context.merge( "cents" => { 'amount' => HundredCentes.new} )
|
349
|
+
assert_equal 100, @context['cents.amount']
|
350
|
+
|
351
|
+
@context.merge( "cents" => { 'cents' => { 'amount' => HundredCentes.new} } )
|
352
|
+
assert_equal 100, @context['cents.cents.amount']
|
353
|
+
end
|
354
|
+
|
355
|
+
def test_cents_through_drop
|
356
|
+
@context.merge( "cents" => CentsDrop.new )
|
357
|
+
assert_equal 100, @context['cents.amount']
|
358
|
+
end
|
359
|
+
|
360
|
+
def test_nested_cents_through_drop
|
361
|
+
@context.merge( "vars" => {"cents" => CentsDrop.new} )
|
362
|
+
assert_equal 100, @context['vars.cents.amount']
|
363
|
+
end
|
364
|
+
|
365
|
+
def test_drop_methods_with_question_marks
|
366
|
+
@context.merge( "cents" => CentsDrop.new )
|
367
|
+
assert @context['cents.non_zero?']
|
368
|
+
end
|
369
|
+
|
370
|
+
def test_context_from_within_drop
|
371
|
+
@context.merge( "test" => '123', "vars" => ContextSensitiveDrop.new )
|
372
|
+
assert_equal '123', @context['vars.test']
|
373
|
+
end
|
374
|
+
|
375
|
+
def test_nested_context_from_within_drop
|
376
|
+
@context.merge( "test" => '123', "vars" => {"local" => ContextSensitiveDrop.new } )
|
377
|
+
assert_equal '123', @context['vars.local.test']
|
378
|
+
end
|
379
|
+
|
380
|
+
def test_ranges
|
381
|
+
@context.merge( "test" => '5' )
|
382
|
+
assert_equal (1..5), @context['(1..5)']
|
383
|
+
assert_equal (1..5), @context['(1..test)']
|
384
|
+
assert_equal (5..5), @context['(test..test)']
|
385
|
+
end
|
386
|
+
|
387
|
+
def test_cents_through_drop_nestedly
|
388
|
+
@context.merge( "cents" => {"cents" => CentsDrop.new} )
|
389
|
+
assert_equal 100, @context['cents.cents.amount']
|
390
|
+
|
391
|
+
@context.merge( "cents" => { "cents" => {"cents" => CentsDrop.new}} )
|
392
|
+
assert_equal 100, @context['cents.cents.cents.amount']
|
393
|
+
end
|
394
|
+
|
395
|
+
def test_drop_with_variable_called_only_once
|
396
|
+
@context['counter'] = CounterDrop.new
|
397
|
+
|
398
|
+
assert_equal 1, @context['counter.count']
|
399
|
+
assert_equal 2, @context['counter.count']
|
400
|
+
assert_equal 3, @context['counter.count']
|
401
|
+
end
|
402
|
+
|
403
|
+
def test_drop_with_key_called_only_once
|
404
|
+
@context['counter'] = CounterDrop.new
|
405
|
+
|
406
|
+
assert_equal 1, @context['counter["count"]']
|
407
|
+
assert_equal 2, @context['counter["count"]']
|
408
|
+
assert_equal 3, @context['counter["count"]']
|
409
|
+
end
|
410
|
+
|
411
|
+
def test_proc_as_variable
|
412
|
+
@context['dynamic'] = Proc.new { 'Hello' }
|
413
|
+
|
414
|
+
assert_equal 'Hello', @context['dynamic']
|
415
|
+
end
|
416
|
+
|
417
|
+
def test_lambda_as_variable
|
418
|
+
@context['dynamic'] = proc { 'Hello' }
|
419
|
+
|
420
|
+
assert_equal 'Hello', @context['dynamic']
|
421
|
+
end
|
422
|
+
|
423
|
+
def test_nested_lambda_as_variable
|
424
|
+
@context['dynamic'] = { "lambda" => proc { 'Hello' } }
|
425
|
+
|
426
|
+
assert_equal 'Hello', @context['dynamic.lambda']
|
427
|
+
end
|
428
|
+
|
429
|
+
def test_array_containing_lambda_as_variable
|
430
|
+
@context['dynamic'] = [1,2, proc { 'Hello' } ,4,5]
|
431
|
+
|
432
|
+
assert_equal 'Hello', @context['dynamic[2]']
|
433
|
+
end
|
434
|
+
|
435
|
+
def test_lambda_is_called_once
|
436
|
+
@context['callcount'] = proc { @global ||= 0; @global += 1; @global.to_s }
|
437
|
+
|
438
|
+
assert_equal '1', @context['callcount']
|
439
|
+
assert_equal '1', @context['callcount']
|
440
|
+
assert_equal '1', @context['callcount']
|
441
|
+
|
442
|
+
@global = nil
|
443
|
+
end
|
444
|
+
|
445
|
+
def test_nested_lambda_is_called_once
|
446
|
+
@context['callcount'] = { "lambda" => proc { @global ||= 0; @global += 1; @global.to_s } }
|
447
|
+
|
448
|
+
assert_equal '1', @context['callcount.lambda']
|
449
|
+
assert_equal '1', @context['callcount.lambda']
|
450
|
+
assert_equal '1', @context['callcount.lambda']
|
451
|
+
|
452
|
+
@global = nil
|
453
|
+
end
|
454
|
+
|
455
|
+
def test_lambda_in_array_is_called_once
|
456
|
+
@context['callcount'] = [1,2, proc { @global ||= 0; @global += 1; @global.to_s } ,4,5]
|
457
|
+
|
458
|
+
assert_equal '1', @context['callcount[2]']
|
459
|
+
assert_equal '1', @context['callcount[2]']
|
460
|
+
assert_equal '1', @context['callcount[2]']
|
461
|
+
|
462
|
+
@global = nil
|
463
|
+
end
|
464
|
+
|
465
|
+
def test_access_to_context_from_proc
|
466
|
+
@context.registers[:magic] = 345392
|
467
|
+
|
468
|
+
@context['magic'] = proc { @context.registers[:magic] }
|
469
|
+
|
470
|
+
assert_equal 345392, @context['magic']
|
471
|
+
end
|
472
|
+
|
473
|
+
def test_to_liquid_and_context_at_first_level
|
474
|
+
@context['category'] = Category.new("foobar")
|
475
|
+
assert_kind_of CategoryDrop, @context['category']
|
476
|
+
assert_equal @context, @context['category'].context
|
477
|
+
end
|
478
|
+
end # ContextTest
|