liquid 2.6.3 → 5.4.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.
Files changed (100) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +272 -26
  3. data/README.md +67 -3
  4. data/lib/liquid/block.rb +62 -94
  5. data/lib/liquid/block_body.rb +255 -0
  6. data/lib/liquid/condition.rb +96 -38
  7. data/lib/liquid/context.rb +172 -154
  8. data/lib/liquid/document.rb +57 -9
  9. data/lib/liquid/drop.rb +33 -14
  10. data/lib/liquid/errors.rb +56 -10
  11. data/lib/liquid/expression.rb +45 -0
  12. data/lib/liquid/extensions.rb +21 -7
  13. data/lib/liquid/file_system.rb +27 -14
  14. data/lib/liquid/forloop_drop.rb +92 -0
  15. data/lib/liquid/i18n.rb +41 -0
  16. data/lib/liquid/interrupts.rb +3 -2
  17. data/lib/liquid/lexer.rb +62 -0
  18. data/lib/liquid/locales/en.yml +29 -0
  19. data/lib/liquid/parse_context.rb +54 -0
  20. data/lib/liquid/parse_tree_visitor.rb +42 -0
  21. data/lib/liquid/parser.rb +102 -0
  22. data/lib/liquid/parser_switching.rb +45 -0
  23. data/lib/liquid/partial_cache.rb +24 -0
  24. data/lib/liquid/profiler/hooks.rb +35 -0
  25. data/lib/liquid/profiler.rb +139 -0
  26. data/lib/liquid/range_lookup.rb +47 -0
  27. data/lib/liquid/registers.rb +51 -0
  28. data/lib/liquid/resource_limits.rb +62 -0
  29. data/lib/liquid/standardfilters.rb +789 -118
  30. data/lib/liquid/strainer_factory.rb +41 -0
  31. data/lib/liquid/strainer_template.rb +62 -0
  32. data/lib/liquid/tablerowloop_drop.rb +121 -0
  33. data/lib/liquid/tag/disableable.rb +22 -0
  34. data/lib/liquid/tag/disabler.rb +21 -0
  35. data/lib/liquid/tag.rb +49 -10
  36. data/lib/liquid/tags/assign.rb +61 -19
  37. data/lib/liquid/tags/break.rb +14 -4
  38. data/lib/liquid/tags/capture.rb +29 -21
  39. data/lib/liquid/tags/case.rb +80 -31
  40. data/lib/liquid/tags/comment.rb +24 -2
  41. data/lib/liquid/tags/continue.rb +14 -13
  42. data/lib/liquid/tags/cycle.rb +50 -32
  43. data/lib/liquid/tags/decrement.rb +24 -26
  44. data/lib/liquid/tags/echo.rb +41 -0
  45. data/lib/liquid/tags/for.rb +164 -100
  46. data/lib/liquid/tags/if.rb +105 -44
  47. data/lib/liquid/tags/ifchanged.rb +10 -11
  48. data/lib/liquid/tags/include.rb +85 -65
  49. data/lib/liquid/tags/increment.rb +24 -22
  50. data/lib/liquid/tags/inline_comment.rb +43 -0
  51. data/lib/liquid/tags/raw.rb +50 -11
  52. data/lib/liquid/tags/render.rb +109 -0
  53. data/lib/liquid/tags/table_row.rb +88 -0
  54. data/lib/liquid/tags/unless.rb +37 -21
  55. data/lib/liquid/template.rb +124 -46
  56. data/lib/liquid/template_factory.rb +9 -0
  57. data/lib/liquid/tokenizer.rb +39 -0
  58. data/lib/liquid/usage.rb +8 -0
  59. data/lib/liquid/utils.rb +68 -5
  60. data/lib/liquid/variable.rb +128 -32
  61. data/lib/liquid/variable_lookup.rb +96 -0
  62. data/lib/liquid/version.rb +3 -1
  63. data/lib/liquid.rb +36 -13
  64. metadata +69 -77
  65. data/lib/extras/liquid_view.rb +0 -51
  66. data/lib/liquid/htmltags.rb +0 -73
  67. data/lib/liquid/module_ex.rb +0 -62
  68. data/lib/liquid/strainer.rb +0 -53
  69. data/test/liquid/assign_test.rb +0 -21
  70. data/test/liquid/block_test.rb +0 -58
  71. data/test/liquid/capture_test.rb +0 -40
  72. data/test/liquid/condition_test.rb +0 -127
  73. data/test/liquid/context_test.rb +0 -478
  74. data/test/liquid/drop_test.rb +0 -180
  75. data/test/liquid/error_handling_test.rb +0 -81
  76. data/test/liquid/file_system_test.rb +0 -29
  77. data/test/liquid/filter_test.rb +0 -125
  78. data/test/liquid/hash_ordering_test.rb +0 -25
  79. data/test/liquid/module_ex_test.rb +0 -87
  80. data/test/liquid/output_test.rb +0 -116
  81. data/test/liquid/parsing_quirks_test.rb +0 -52
  82. data/test/liquid/regexp_test.rb +0 -44
  83. data/test/liquid/security_test.rb +0 -64
  84. data/test/liquid/standard_filter_test.rb +0 -263
  85. data/test/liquid/strainer_test.rb +0 -52
  86. data/test/liquid/tags/break_tag_test.rb +0 -16
  87. data/test/liquid/tags/continue_tag_test.rb +0 -16
  88. data/test/liquid/tags/for_tag_test.rb +0 -297
  89. data/test/liquid/tags/html_tag_test.rb +0 -63
  90. data/test/liquid/tags/if_else_tag_test.rb +0 -166
  91. data/test/liquid/tags/include_tag_test.rb +0 -166
  92. data/test/liquid/tags/increment_tag_test.rb +0 -24
  93. data/test/liquid/tags/raw_tag_test.rb +0 -24
  94. data/test/liquid/tags/standard_tag_test.rb +0 -295
  95. data/test/liquid/tags/statements_test.rb +0 -134
  96. data/test/liquid/tags/unless_else_tag_test.rb +0 -26
  97. data/test/liquid/template_test.rb +0 -146
  98. data/test/liquid/variable_test.rb +0 -186
  99. data/test/test_helper.rb +0 -29
  100. /data/{MIT-LICENSE → LICENSE} +0 -0
@@ -1,73 +0,0 @@
1
- module Liquid
2
- class TableRow < Block
3
- Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
4
-
5
- def initialize(tag_name, markup, tokens)
6
- if markup =~ Syntax
7
- @variable_name = $1
8
- @collection_name = $2
9
- @attributes = {}
10
- markup.scan(TagAttributes) do |key, value|
11
- @attributes[key] = value
12
- end
13
- else
14
- raise SyntaxError.new("Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3")
15
- end
16
-
17
- super
18
- end
19
-
20
- def render(context)
21
- collection = context[@collection_name] or return ''
22
-
23
- from = @attributes['offset'] ? context[@attributes['offset']].to_i : 0
24
- to = @attributes['limit'] ? from + context[@attributes['limit']].to_i : nil
25
-
26
- collection = Utils.slice_collection_using_each(collection, from, to)
27
-
28
- length = collection.length
29
-
30
- cols = context[@attributes['cols']].to_i
31
-
32
- row = 1
33
- col = 0
34
-
35
- result = "<tr class=\"row1\">\n"
36
- context.stack do
37
-
38
- collection.each_with_index do |item, index|
39
- context[@variable_name] = item
40
- context['tablerowloop'] = {
41
- 'length' => length,
42
- 'index' => index + 1,
43
- 'index0' => index,
44
- 'col' => col + 1,
45
- 'col0' => col,
46
- 'rindex' => length - index,
47
- 'rindex0' => length - index - 1,
48
- 'first' => (index == 0),
49
- 'last' => (index == length - 1),
50
- 'col_first' => (col == 0),
51
- 'col_last' => (col == cols - 1)
52
- }
53
-
54
-
55
- col += 1
56
-
57
- result << "<td class=\"col#{col}\">" << render_all(@nodelist, context) << '</td>'
58
-
59
- if col == cols and (index != length - 1)
60
- col = 0
61
- row += 1
62
- result << "</tr>\n<tr class=\"row#{row}\">"
63
- end
64
-
65
- end
66
- end
67
- result << "</tr>\n"
68
- result
69
- end
70
- end
71
-
72
- Template.register_tag('tablerow', TableRow)
73
- end
@@ -1,62 +0,0 @@
1
- # Copyright 2007 by Domizio Demichelis
2
- # This library is free software. It may be used, redistributed and/or modified
3
- # under the same terms as Ruby itself
4
- #
5
- # This extension is used in order to expose the object of the implementing class
6
- # to liquid as it were a Drop. It also limits the liquid-callable methods of the instance
7
- # to the allowed method passed with the liquid_methods call
8
- # Example:
9
- #
10
- # class SomeClass
11
- # liquid_methods :an_allowed_method
12
- #
13
- # def an_allowed_method
14
- # 'this comes from an allowed method'
15
- # end
16
- # def unallowed_method
17
- # 'this will never be an output'
18
- # end
19
- # end
20
- #
21
- # if you want to extend the drop to other methods you can defines more methods
22
- # in the class <YourClass>::LiquidDropClass
23
- #
24
- # class SomeClass::LiquidDropClass
25
- # def another_allowed_method
26
- # 'and this from another allowed method'
27
- # end
28
- # end
29
- # end
30
- #
31
- # usage:
32
- # @something = SomeClass.new
33
- #
34
- # template:
35
- # {{something.an_allowed_method}}{{something.unallowed_method}} {{something.another_allowed_method}}
36
- #
37
- # output:
38
- # 'this comes from an allowed method and this from another allowed method'
39
- #
40
- # You can also chain associations, by adding the liquid_method call in the
41
- # association models.
42
- #
43
- class Module
44
-
45
- def liquid_methods(*allowed_methods)
46
- drop_class = eval "class #{self.to_s}::LiquidDropClass < Liquid::Drop; self; end"
47
- define_method :to_liquid do
48
- drop_class.new(self)
49
- end
50
- drop_class.class_eval do
51
- def initialize(object)
52
- @object = object
53
- end
54
- allowed_methods.each do |sym|
55
- define_method sym do
56
- @object.send sym
57
- end
58
- end
59
- end
60
- end
61
-
62
- end
@@ -1,53 +0,0 @@
1
- require 'set'
2
-
3
- module Liquid
4
-
5
- # Strainer is the parent class for the filters system.
6
- # New filters are mixed into the strainer class which is then instantiated for each liquid template render run.
7
- #
8
- # The Strainer only allows method calls defined in filters given to it via Strainer.global_filter,
9
- # Context#add_filters or Template.register_filter
10
- class Strainer #:nodoc:
11
- @@filters = []
12
- @@known_filters = Set.new
13
- @@known_methods = Set.new
14
-
15
- def initialize(context)
16
- @context = context
17
- end
18
-
19
- def self.global_filter(filter)
20
- raise ArgumentError, "Passed filter is not a module" unless filter.is_a?(Module)
21
- add_known_filter(filter)
22
- @@filters << filter unless @@filters.include?(filter)
23
- end
24
-
25
- def self.add_known_filter(filter)
26
- unless @@known_filters.include?(filter)
27
- @@method_blacklist ||= Set.new(Strainer.instance_methods.map(&:to_s))
28
- new_methods = filter.instance_methods.map(&:to_s)
29
- new_methods.reject!{ |m| @@method_blacklist.include?(m) }
30
- @@known_methods.merge(new_methods)
31
- @@known_filters.add(filter)
32
- end
33
- end
34
-
35
- def self.create(context)
36
- strainer = Strainer.new(context)
37
- @@filters.each { |m| strainer.extend(m) }
38
- strainer
39
- end
40
-
41
- def invoke(method, *args)
42
- if invokable?(method)
43
- send(method, *args)
44
- else
45
- args.first
46
- end
47
- end
48
-
49
- def invokable?(method)
50
- @@known_methods.include?(method.to_s) && respond_to?(method)
51
- end
52
- end
53
- end
@@ -1,21 +0,0 @@
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
@@ -1,58 +0,0 @@
1
- require 'test_helper'
2
-
3
- class BlockTest < 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
@@ -1,40 +0,0 @@
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
@@ -1,127 +0,0 @@
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