liquid-4-0-2 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +235 -0
  3. data/LICENSE +20 -0
  4. data/README.md +108 -0
  5. data/lib/liquid.rb +80 -0
  6. data/lib/liquid/block.rb +77 -0
  7. data/lib/liquid/block_body.rb +142 -0
  8. data/lib/liquid/condition.rb +151 -0
  9. data/lib/liquid/context.rb +226 -0
  10. data/lib/liquid/document.rb +27 -0
  11. data/lib/liquid/drop.rb +78 -0
  12. data/lib/liquid/errors.rb +56 -0
  13. data/lib/liquid/expression.rb +49 -0
  14. data/lib/liquid/extensions.rb +74 -0
  15. data/lib/liquid/file_system.rb +73 -0
  16. data/lib/liquid/forloop_drop.rb +42 -0
  17. data/lib/liquid/i18n.rb +39 -0
  18. data/lib/liquid/interrupts.rb +16 -0
  19. data/lib/liquid/lexer.rb +55 -0
  20. data/lib/liquid/locales/en.yml +26 -0
  21. data/lib/liquid/parse_context.rb +38 -0
  22. data/lib/liquid/parse_tree_visitor.rb +42 -0
  23. data/lib/liquid/parser.rb +90 -0
  24. data/lib/liquid/parser_switching.rb +31 -0
  25. data/lib/liquid/profiler.rb +158 -0
  26. data/lib/liquid/profiler/hooks.rb +23 -0
  27. data/lib/liquid/range_lookup.rb +37 -0
  28. data/lib/liquid/resource_limits.rb +23 -0
  29. data/lib/liquid/standardfilters.rb +485 -0
  30. data/lib/liquid/strainer.rb +66 -0
  31. data/lib/liquid/tablerowloop_drop.rb +62 -0
  32. data/lib/liquid/tag.rb +43 -0
  33. data/lib/liquid/tags/assign.rb +59 -0
  34. data/lib/liquid/tags/break.rb +18 -0
  35. data/lib/liquid/tags/capture.rb +38 -0
  36. data/lib/liquid/tags/case.rb +94 -0
  37. data/lib/liquid/tags/comment.rb +16 -0
  38. data/lib/liquid/tags/continue.rb +18 -0
  39. data/lib/liquid/tags/cycle.rb +65 -0
  40. data/lib/liquid/tags/decrement.rb +35 -0
  41. data/lib/liquid/tags/for.rb +203 -0
  42. data/lib/liquid/tags/if.rb +122 -0
  43. data/lib/liquid/tags/ifchanged.rb +18 -0
  44. data/lib/liquid/tags/include.rb +124 -0
  45. data/lib/liquid/tags/increment.rb +31 -0
  46. data/lib/liquid/tags/raw.rb +47 -0
  47. data/lib/liquid/tags/table_row.rb +62 -0
  48. data/lib/liquid/tags/unless.rb +30 -0
  49. data/lib/liquid/template.rb +254 -0
  50. data/lib/liquid/tokenizer.rb +31 -0
  51. data/lib/liquid/utils.rb +83 -0
  52. data/lib/liquid/variable.rb +148 -0
  53. data/lib/liquid/variable_lookup.rb +88 -0
  54. data/lib/liquid/version.rb +4 -0
  55. data/test/fixtures/en_locale.yml +9 -0
  56. data/test/integration/assign_test.rb +48 -0
  57. data/test/integration/blank_test.rb +106 -0
  58. data/test/integration/block_test.rb +12 -0
  59. data/test/integration/capture_test.rb +50 -0
  60. data/test/integration/context_test.rb +32 -0
  61. data/test/integration/document_test.rb +19 -0
  62. data/test/integration/drop_test.rb +273 -0
  63. data/test/integration/error_handling_test.rb +260 -0
  64. data/test/integration/filter_test.rb +178 -0
  65. data/test/integration/hash_ordering_test.rb +23 -0
  66. data/test/integration/output_test.rb +123 -0
  67. data/test/integration/parse_tree_visitor_test.rb +247 -0
  68. data/test/integration/parsing_quirks_test.rb +122 -0
  69. data/test/integration/render_profiling_test.rb +154 -0
  70. data/test/integration/security_test.rb +80 -0
  71. data/test/integration/standard_filter_test.rb +698 -0
  72. data/test/integration/tags/break_tag_test.rb +15 -0
  73. data/test/integration/tags/continue_tag_test.rb +15 -0
  74. data/test/integration/tags/for_tag_test.rb +410 -0
  75. data/test/integration/tags/if_else_tag_test.rb +188 -0
  76. data/test/integration/tags/include_tag_test.rb +245 -0
  77. data/test/integration/tags/increment_tag_test.rb +23 -0
  78. data/test/integration/tags/raw_tag_test.rb +31 -0
  79. data/test/integration/tags/standard_tag_test.rb +296 -0
  80. data/test/integration/tags/statements_test.rb +111 -0
  81. data/test/integration/tags/table_row_test.rb +64 -0
  82. data/test/integration/tags/unless_else_tag_test.rb +26 -0
  83. data/test/integration/template_test.rb +332 -0
  84. data/test/integration/trim_mode_test.rb +529 -0
  85. data/test/integration/variable_test.rb +96 -0
  86. data/test/test_helper.rb +116 -0
  87. data/test/unit/block_unit_test.rb +58 -0
  88. data/test/unit/condition_unit_test.rb +166 -0
  89. data/test/unit/context_unit_test.rb +489 -0
  90. data/test/unit/file_system_unit_test.rb +35 -0
  91. data/test/unit/i18n_unit_test.rb +37 -0
  92. data/test/unit/lexer_unit_test.rb +51 -0
  93. data/test/unit/parser_unit_test.rb +82 -0
  94. data/test/unit/regexp_unit_test.rb +44 -0
  95. data/test/unit/strainer_unit_test.rb +164 -0
  96. data/test/unit/tag_unit_test.rb +21 -0
  97. data/test/unit/tags/case_tag_unit_test.rb +10 -0
  98. data/test/unit/tags/for_tag_unit_test.rb +13 -0
  99. data/test/unit/tags/if_tag_unit_test.rb +8 -0
  100. data/test/unit/template_unit_test.rb +78 -0
  101. data/test/unit/tokenizer_unit_test.rb +55 -0
  102. data/test/unit/variable_unit_test.rb +162 -0
  103. metadata +224 -0
@@ -0,0 +1,35 @@
1
+ require 'test_helper'
2
+
3
+ class FileSystemUnitTest < Minitest::Test
4
+ include Liquid
5
+
6
+ def test_default
7
+ assert_raises(FileSystemError) do
8
+ BlankFileSystem.new.read_template_file("dummy")
9
+ end
10
+ end
11
+
12
+ def test_local
13
+ file_system = Liquid::LocalFileSystem.new("/some/path")
14
+ assert_equal "/some/path/_mypartial.liquid", file_system.full_path("mypartial")
15
+ assert_equal "/some/path/dir/_mypartial.liquid", file_system.full_path("dir/mypartial")
16
+
17
+ assert_raises(FileSystemError) do
18
+ file_system.full_path("../dir/mypartial")
19
+ end
20
+
21
+ assert_raises(FileSystemError) do
22
+ file_system.full_path("/dir/../../dir/mypartial")
23
+ end
24
+
25
+ assert_raises(FileSystemError) do
26
+ file_system.full_path("/etc/passwd")
27
+ end
28
+ end
29
+
30
+ def test_custom_template_filename_patterns
31
+ file_system = Liquid::LocalFileSystem.new("/some/path", "%s.html")
32
+ assert_equal "/some/path/mypartial.html", file_system.full_path("mypartial")
33
+ assert_equal "/some/path/dir/mypartial.html", file_system.full_path("dir/mypartial")
34
+ end
35
+ end # FileSystemTest
@@ -0,0 +1,37 @@
1
+ require 'test_helper'
2
+
3
+ class I18nUnitTest < Minitest::Test
4
+ include Liquid
5
+
6
+ def setup
7
+ @i18n = I18n.new(fixture("en_locale.yml"))
8
+ end
9
+
10
+ def test_simple_translate_string
11
+ assert_equal "less is more", @i18n.translate("simple")
12
+ end
13
+
14
+ def test_nested_translate_string
15
+ assert_equal "something wasn't right", @i18n.translate("errors.syntax.oops")
16
+ end
17
+
18
+ def test_single_string_interpolation
19
+ assert_equal "something different", @i18n.translate("whatever", something: "different")
20
+ end
21
+
22
+ # def test_raises_translation_error_on_undefined_interpolation_key
23
+ # assert_raises I18n::TranslationError do
24
+ # @i18n.translate("whatever", :oopstypos => "yes")
25
+ # end
26
+ # end
27
+
28
+ def test_raises_unknown_translation
29
+ assert_raises I18n::TranslationError do
30
+ @i18n.translate("doesnt_exist")
31
+ end
32
+ end
33
+
34
+ def test_sets_default_path_to_en
35
+ assert_equal I18n::DEFAULT_LOCALE, I18n.new.path
36
+ end
37
+ end
@@ -0,0 +1,51 @@
1
+ require 'test_helper'
2
+
3
+ class LexerUnitTest < Minitest::Test
4
+ include Liquid
5
+
6
+ def test_strings
7
+ tokens = Lexer.new(%( 'this is a test""' "wat 'lol'")).tokenize
8
+ assert_equal [[:string, %('this is a test""')], [:string, %("wat 'lol'")], [:end_of_string]], tokens
9
+ end
10
+
11
+ def test_integer
12
+ tokens = Lexer.new('hi 50').tokenize
13
+ assert_equal [[:id, 'hi'], [:number, '50'], [:end_of_string]], tokens
14
+ end
15
+
16
+ def test_float
17
+ tokens = Lexer.new('hi 5.0').tokenize
18
+ assert_equal [[:id, 'hi'], [:number, '5.0'], [:end_of_string]], tokens
19
+ end
20
+
21
+ def test_comparison
22
+ tokens = Lexer.new('== <> contains ').tokenize
23
+ assert_equal [[:comparison, '=='], [:comparison, '<>'], [:comparison, 'contains'], [:end_of_string]], tokens
24
+ end
25
+
26
+ def test_specials
27
+ tokens = Lexer.new('| .:').tokenize
28
+ assert_equal [[:pipe, '|'], [:dot, '.'], [:colon, ':'], [:end_of_string]], tokens
29
+ tokens = Lexer.new('[,]').tokenize
30
+ assert_equal [[:open_square, '['], [:comma, ','], [:close_square, ']'], [:end_of_string]], tokens
31
+ end
32
+
33
+ def test_fancy_identifiers
34
+ tokens = Lexer.new('hi five?').tokenize
35
+ assert_equal [[:id, 'hi'], [:id, 'five?'], [:end_of_string]], tokens
36
+
37
+ tokens = Lexer.new('2foo').tokenize
38
+ assert_equal [[:number, '2'], [:id, 'foo'], [:end_of_string]], tokens
39
+ end
40
+
41
+ def test_whitespace
42
+ tokens = Lexer.new("five|\n\t ==").tokenize
43
+ assert_equal [[:id, 'five'], [:pipe, '|'], [:comparison, '=='], [:end_of_string]], tokens
44
+ end
45
+
46
+ def test_unexpected_character
47
+ assert_raises(SyntaxError) do
48
+ Lexer.new("%").tokenize
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,82 @@
1
+ require 'test_helper'
2
+
3
+ class ParserUnitTest < Minitest::Test
4
+ include Liquid
5
+
6
+ def test_consume
7
+ p = Parser.new("wat: 7")
8
+ assert_equal 'wat', p.consume(:id)
9
+ assert_equal ':', p.consume(:colon)
10
+ assert_equal '7', p.consume(:number)
11
+ end
12
+
13
+ def test_jump
14
+ p = Parser.new("wat: 7")
15
+ p.jump(2)
16
+ assert_equal '7', p.consume(:number)
17
+ end
18
+
19
+ def test_consume?
20
+ p = Parser.new("wat: 7")
21
+ assert_equal 'wat', p.consume?(:id)
22
+ assert_equal false, p.consume?(:dot)
23
+ assert_equal ':', p.consume(:colon)
24
+ assert_equal '7', p.consume?(:number)
25
+ end
26
+
27
+ def test_id?
28
+ p = Parser.new("wat 6 Peter Hegemon")
29
+ assert_equal 'wat', p.id?('wat')
30
+ assert_equal false, p.id?('endgame')
31
+ assert_equal '6', p.consume(:number)
32
+ assert_equal 'Peter', p.id?('Peter')
33
+ assert_equal false, p.id?('Achilles')
34
+ end
35
+
36
+ def test_look
37
+ p = Parser.new("wat 6 Peter Hegemon")
38
+ assert_equal true, p.look(:id)
39
+ assert_equal 'wat', p.consume(:id)
40
+ assert_equal false, p.look(:comparison)
41
+ assert_equal true, p.look(:number)
42
+ assert_equal true, p.look(:id, 1)
43
+ assert_equal false, p.look(:number, 1)
44
+ end
45
+
46
+ def test_expressions
47
+ p = Parser.new("hi.there hi?[5].there? hi.there.bob")
48
+ assert_equal 'hi.there', p.expression
49
+ assert_equal 'hi?[5].there?', p.expression
50
+ assert_equal 'hi.there.bob', p.expression
51
+
52
+ p = Parser.new("567 6.0 'lol' \"wut\"")
53
+ assert_equal '567', p.expression
54
+ assert_equal '6.0', p.expression
55
+ assert_equal "'lol'", p.expression
56
+ assert_equal '"wut"', p.expression
57
+ end
58
+
59
+ def test_ranges
60
+ p = Parser.new("(5..7) (1.5..9.6) (young..old) (hi[5].wat..old)")
61
+ assert_equal '(5..7)', p.expression
62
+ assert_equal '(1.5..9.6)', p.expression
63
+ assert_equal '(young..old)', p.expression
64
+ assert_equal '(hi[5].wat..old)', p.expression
65
+ end
66
+
67
+ def test_arguments
68
+ p = Parser.new("filter: hi.there[5], keyarg: 7")
69
+ assert_equal 'filter', p.consume(:id)
70
+ assert_equal ':', p.consume(:colon)
71
+ assert_equal 'hi.there[5]', p.argument
72
+ assert_equal ',', p.consume(:comma)
73
+ assert_equal 'keyarg: 7', p.argument
74
+ end
75
+
76
+ def test_invalid_expression
77
+ assert_raises(SyntaxError) do
78
+ p = Parser.new("==")
79
+ p.expression
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,44 @@
1
+ require 'test_helper'
2
+
3
+ class RegexpUnitTest < Minitest::Test
4
+ include Liquid
5
+
6
+ def test_empty
7
+ assert_equal [], ''.scan(QuotedFragment)
8
+ end
9
+
10
+ def test_quote
11
+ assert_equal ['"arg 1"'], '"arg 1"'.scan(QuotedFragment)
12
+ end
13
+
14
+ def test_words
15
+ assert_equal ['arg1', 'arg2'], 'arg1 arg2'.scan(QuotedFragment)
16
+ end
17
+
18
+ def test_tags
19
+ assert_equal ['<tr>', '</tr>'], '<tr> </tr>'.scan(QuotedFragment)
20
+ assert_equal ['<tr></tr>'], '<tr></tr>'.scan(QuotedFragment)
21
+ assert_equal ['<style', 'class="hello">', '</style>'], %(<style class="hello">' </style>).scan(QuotedFragment)
22
+ end
23
+
24
+ def test_double_quoted_words
25
+ assert_equal ['arg1', 'arg2', '"arg 3"'], 'arg1 arg2 "arg 3"'.scan(QuotedFragment)
26
+ end
27
+
28
+ def test_single_quoted_words
29
+ assert_equal ['arg1', 'arg2', "'arg 3'"], 'arg1 arg2 \'arg 3\''.scan(QuotedFragment)
30
+ end
31
+
32
+ def test_quoted_words_in_the_middle
33
+ assert_equal ['arg1', 'arg2', '"arg 3"', 'arg4'], 'arg1 arg2 "arg 3" arg4 '.scan(QuotedFragment)
34
+ end
35
+
36
+ def test_variable_parser
37
+ assert_equal ['var'], 'var'.scan(VariableParser)
38
+ assert_equal ['var', 'method'], 'var.method'.scan(VariableParser)
39
+ assert_equal ['var', '[method]'], 'var[method]'.scan(VariableParser)
40
+ assert_equal ['var', '[method]', '[0]'], 'var[method][0]'.scan(VariableParser)
41
+ assert_equal ['var', '["method"]', '[0]'], 'var["method"][0]'.scan(VariableParser)
42
+ assert_equal ['var', '[method]', '[0]', 'method'], 'var[method][0].method'.scan(VariableParser)
43
+ end
44
+ end # RegexpTest
@@ -0,0 +1,164 @@
1
+ require 'test_helper'
2
+
3
+ class StrainerUnitTest < Minitest::Test
4
+ include Liquid
5
+
6
+ module AccessScopeFilters
7
+ def public_filter
8
+ "public"
9
+ end
10
+
11
+ def private_filter
12
+ "private"
13
+ end
14
+ private :private_filter
15
+ end
16
+
17
+ Strainer.global_filter(AccessScopeFilters)
18
+
19
+ def test_strainer
20
+ strainer = Strainer.create(nil)
21
+ assert_equal 5, strainer.invoke('size', 'input')
22
+ assert_equal "public", strainer.invoke("public_filter")
23
+ end
24
+
25
+ def test_stainer_raises_argument_error
26
+ strainer = Strainer.create(nil)
27
+ assert_raises(Liquid::ArgumentError) do
28
+ strainer.invoke("public_filter", 1)
29
+ end
30
+ end
31
+
32
+ def test_stainer_argument_error_contains_backtrace
33
+ strainer = Strainer.create(nil)
34
+ begin
35
+ strainer.invoke("public_filter", 1)
36
+ rescue Liquid::ArgumentError => e
37
+ assert_match(
38
+ /\ALiquid error: wrong number of arguments \((1 for 0|given 1, expected 0)\)\z/,
39
+ e.message)
40
+ assert_equal e.backtrace[0].split(':')[0], __FILE__
41
+ end
42
+ end
43
+
44
+ def test_strainer_only_invokes_public_filter_methods
45
+ strainer = Strainer.create(nil)
46
+ assert_equal false, strainer.class.invokable?('__test__')
47
+ assert_equal false, strainer.class.invokable?('test')
48
+ assert_equal false, strainer.class.invokable?('instance_eval')
49
+ assert_equal false, strainer.class.invokable?('__send__')
50
+ assert_equal true, strainer.class.invokable?('size') # from the standard lib
51
+ end
52
+
53
+ def test_strainer_returns_nil_if_no_filter_method_found
54
+ strainer = Strainer.create(nil)
55
+ assert_nil strainer.invoke("private_filter")
56
+ assert_nil strainer.invoke("undef_the_filter")
57
+ end
58
+
59
+ def test_strainer_returns_first_argument_if_no_method_and_arguments_given
60
+ strainer = Strainer.create(nil)
61
+ assert_equal "password", strainer.invoke("undef_the_method", "password")
62
+ end
63
+
64
+ def test_strainer_only_allows_methods_defined_in_filters
65
+ strainer = Strainer.create(nil)
66
+ assert_equal "1 + 1", strainer.invoke("instance_eval", "1 + 1")
67
+ assert_equal "puts", strainer.invoke("__send__", "puts", "Hi Mom")
68
+ assert_equal "has_method?", strainer.invoke("invoke", "has_method?", "invoke")
69
+ end
70
+
71
+ def test_strainer_uses_a_class_cache_to_avoid_method_cache_invalidation
72
+ a = Module.new
73
+ b = Module.new
74
+ strainer = Strainer.create(nil, [a, b])
75
+ assert_kind_of Strainer, strainer
76
+ assert_kind_of a, strainer
77
+ assert_kind_of b, strainer
78
+ assert_kind_of Liquid::StandardFilters, strainer
79
+ end
80
+
81
+ def test_add_filter_when_wrong_filter_class
82
+ c = Context.new
83
+ s = c.strainer
84
+ wrong_filter = ->(v) { v.reverse }
85
+
86
+ assert_raises ArgumentError do
87
+ s.class.add_filter(wrong_filter)
88
+ end
89
+ end
90
+
91
+ module PrivateMethodOverrideFilter
92
+ private
93
+
94
+ def public_filter
95
+ "overriden as private"
96
+ end
97
+ end
98
+
99
+ def test_add_filter_raises_when_module_privately_overrides_registered_public_methods
100
+ strainer = Context.new.strainer
101
+
102
+ error = assert_raises(Liquid::MethodOverrideError) do
103
+ strainer.class.add_filter(PrivateMethodOverrideFilter)
104
+ end
105
+ assert_equal 'Liquid error: Filter overrides registered public methods as non public: public_filter', error.message
106
+ end
107
+
108
+ module ProtectedMethodOverrideFilter
109
+ protected
110
+
111
+ def public_filter
112
+ "overriden as protected"
113
+ end
114
+ end
115
+
116
+ def test_add_filter_raises_when_module_overrides_registered_public_method_as_protected
117
+ strainer = Context.new.strainer
118
+
119
+ error = assert_raises(Liquid::MethodOverrideError) do
120
+ strainer.class.add_filter(ProtectedMethodOverrideFilter)
121
+ end
122
+ assert_equal 'Liquid error: Filter overrides registered public methods as non public: public_filter', error.message
123
+ end
124
+
125
+ module PublicMethodOverrideFilter
126
+ def public_filter
127
+ "public"
128
+ end
129
+ end
130
+
131
+ def test_add_filter_does_not_raise_when_module_overrides_previously_registered_method
132
+ strainer = Context.new.strainer
133
+ strainer.class.add_filter(PublicMethodOverrideFilter)
134
+ assert strainer.class.filter_methods.include?('public_filter')
135
+ end
136
+
137
+ module LateAddedFilter
138
+ def late_added_filter(input)
139
+ "filtered"
140
+ end
141
+ end
142
+
143
+ def test_global_filter_clears_cache
144
+ assert_equal 'input', Strainer.create(nil).invoke('late_added_filter', 'input')
145
+ Strainer.global_filter(LateAddedFilter)
146
+ assert_equal 'filtered', Strainer.create(nil).invoke('late_added_filter', 'input')
147
+ end
148
+
149
+ def test_add_filter_does_not_include_already_included_module
150
+ mod = Module.new do
151
+ class << self
152
+ attr_accessor :include_count
153
+ def included(mod)
154
+ self.include_count += 1
155
+ end
156
+ end
157
+ self.include_count = 0
158
+ end
159
+ strainer = Context.new.strainer
160
+ strainer.class.add_filter(mod)
161
+ strainer.class.add_filter(mod)
162
+ assert_equal 1, mod.include_count
163
+ end
164
+ end # StrainerTest
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+
3
+ class TagUnitTest < Minitest::Test
4
+ include Liquid
5
+
6
+ def test_tag
7
+ tag = Tag.parse('tag', "", Tokenizer.new(""), ParseContext.new)
8
+ assert_equal 'liquid::tag', tag.name
9
+ assert_equal '', tag.render(Context.new)
10
+ end
11
+
12
+ def test_return_raw_text_of_tag
13
+ tag = Tag.parse("long_tag", "param1, param2, param3", Tokenizer.new(""), ParseContext.new)
14
+ assert_equal("long_tag param1, param2, param3", tag.raw)
15
+ end
16
+
17
+ def test_tag_name_should_return_name_of_the_tag
18
+ tag = Tag.parse("some_tag", "", Tokenizer.new(""), ParseContext.new)
19
+ assert_equal 'some_tag', tag.tag_name
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ require 'test_helper'
2
+
3
+ class CaseTagUnitTest < Minitest::Test
4
+ include Liquid
5
+
6
+ def test_case_nodelist
7
+ template = Liquid::Template.parse('{% case var %}{% when true %}WHEN{% else %}ELSE{% endcase %}')
8
+ assert_equal ['WHEN', 'ELSE'], template.root.nodelist[0].nodelist.map(&:nodelist).flatten
9
+ end
10
+ end