liquid 3.0.6 → 4.0.3

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 (103) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +154 -58
  3. data/{MIT-LICENSE → LICENSE} +0 -0
  4. data/README.md +33 -0
  5. data/lib/liquid/block.rb +42 -125
  6. data/lib/liquid/block_body.rb +99 -79
  7. data/lib/liquid/condition.rb +52 -32
  8. data/lib/liquid/context.rb +57 -51
  9. data/lib/liquid/document.rb +19 -9
  10. data/lib/liquid/drop.rb +17 -16
  11. data/lib/liquid/errors.rb +20 -24
  12. data/lib/liquid/expression.rb +26 -10
  13. data/lib/liquid/extensions.rb +19 -7
  14. data/lib/liquid/file_system.rb +11 -11
  15. data/lib/liquid/forloop_drop.rb +42 -0
  16. data/lib/liquid/i18n.rb +6 -6
  17. data/lib/liquid/interrupts.rb +1 -2
  18. data/lib/liquid/lexer.rb +12 -8
  19. data/lib/liquid/locales/en.yml +6 -2
  20. data/lib/liquid/parse_context.rb +38 -0
  21. data/lib/liquid/parse_tree_visitor.rb +42 -0
  22. data/lib/liquid/parser_switching.rb +4 -4
  23. data/lib/liquid/profiler/hooks.rb +7 -7
  24. data/lib/liquid/profiler.rb +18 -19
  25. data/lib/liquid/range_lookup.rb +16 -1
  26. data/lib/liquid/resource_limits.rb +23 -0
  27. data/lib/liquid/standardfilters.rb +207 -61
  28. data/lib/liquid/strainer.rb +15 -8
  29. data/lib/liquid/tablerowloop_drop.rb +62 -0
  30. data/lib/liquid/tag.rb +9 -8
  31. data/lib/liquid/tags/assign.rb +25 -4
  32. data/lib/liquid/tags/break.rb +0 -3
  33. data/lib/liquid/tags/capture.rb +1 -1
  34. data/lib/liquid/tags/case.rb +27 -12
  35. data/lib/liquid/tags/comment.rb +2 -2
  36. data/lib/liquid/tags/cycle.rb +16 -8
  37. data/lib/liquid/tags/decrement.rb +1 -4
  38. data/lib/liquid/tags/for.rb +103 -75
  39. data/lib/liquid/tags/if.rb +60 -44
  40. data/lib/liquid/tags/ifchanged.rb +0 -2
  41. data/lib/liquid/tags/include.rb +71 -51
  42. data/lib/liquid/tags/raw.rb +32 -4
  43. data/lib/liquid/tags/table_row.rb +21 -31
  44. data/lib/liquid/tags/unless.rb +3 -4
  45. data/lib/liquid/template.rb +42 -54
  46. data/lib/liquid/tokenizer.rb +31 -0
  47. data/lib/liquid/truffle.rb +5 -0
  48. data/lib/liquid/utils.rb +52 -8
  49. data/lib/liquid/variable.rb +59 -46
  50. data/lib/liquid/variable_lookup.rb +14 -6
  51. data/lib/liquid/version.rb +2 -1
  52. data/lib/liquid.rb +10 -7
  53. data/test/integration/assign_test.rb +8 -8
  54. data/test/integration/blank_test.rb +14 -14
  55. data/test/integration/block_test.rb +12 -0
  56. data/test/integration/context_test.rb +2 -2
  57. data/test/integration/document_test.rb +19 -0
  58. data/test/integration/drop_test.rb +42 -40
  59. data/test/integration/error_handling_test.rb +96 -43
  60. data/test/integration/filter_test.rb +60 -20
  61. data/test/integration/hash_ordering_test.rb +9 -9
  62. data/test/integration/output_test.rb +26 -27
  63. data/test/integration/parse_tree_visitor_test.rb +247 -0
  64. data/test/integration/parsing_quirks_test.rb +19 -13
  65. data/test/integration/render_profiling_test.rb +20 -20
  66. data/test/integration/security_test.rb +23 -7
  67. data/test/integration/standard_filter_test.rb +426 -46
  68. data/test/integration/tags/break_tag_test.rb +1 -2
  69. data/test/integration/tags/continue_tag_test.rb +0 -1
  70. data/test/integration/tags/for_tag_test.rb +135 -100
  71. data/test/integration/tags/if_else_tag_test.rb +75 -77
  72. data/test/integration/tags/include_tag_test.rb +50 -31
  73. data/test/integration/tags/increment_tag_test.rb +10 -11
  74. data/test/integration/tags/raw_tag_test.rb +7 -1
  75. data/test/integration/tags/standard_tag_test.rb +121 -122
  76. data/test/integration/tags/statements_test.rb +3 -5
  77. data/test/integration/tags/table_row_test.rb +20 -19
  78. data/test/integration/tags/unless_else_tag_test.rb +6 -6
  79. data/test/integration/template_test.rb +199 -49
  80. data/test/integration/trim_mode_test.rb +529 -0
  81. data/test/integration/variable_test.rb +27 -13
  82. data/test/test_helper.rb +33 -6
  83. data/test/truffle/truffle_test.rb +9 -0
  84. data/test/unit/block_unit_test.rb +8 -5
  85. data/test/unit/condition_unit_test.rb +94 -77
  86. data/test/unit/context_unit_test.rb +69 -72
  87. data/test/unit/file_system_unit_test.rb +3 -3
  88. data/test/unit/i18n_unit_test.rb +2 -2
  89. data/test/unit/lexer_unit_test.rb +12 -9
  90. data/test/unit/parser_unit_test.rb +2 -2
  91. data/test/unit/regexp_unit_test.rb +1 -1
  92. data/test/unit/strainer_unit_test.rb +96 -1
  93. data/test/unit/tag_unit_test.rb +7 -2
  94. data/test/unit/tags/case_tag_unit_test.rb +1 -1
  95. data/test/unit/tags/for_tag_unit_test.rb +2 -2
  96. data/test/unit/tags/if_tag_unit_test.rb +1 -1
  97. data/test/unit/template_unit_test.rb +14 -5
  98. data/test/unit/tokenizer_unit_test.rb +24 -7
  99. data/test/unit/variable_unit_test.rb +60 -43
  100. metadata +62 -50
  101. data/lib/liquid/module_ex.rb +0 -62
  102. data/lib/liquid/token.rb +0 -18
  103. data/test/unit/module_ex_unit_test.rb +0 -87
@@ -0,0 +1,31 @@
1
+ module Liquid
2
+ class Tokenizer
3
+ attr_reader :line_number
4
+
5
+ def initialize(source, line_numbers = false)
6
+ @source = source
7
+ @line_number = line_numbers ? 1 : nil
8
+ @tokens = tokenize
9
+ end
10
+
11
+ def shift
12
+ token = @tokens.shift
13
+ @line_number += token.count("\n") if @line_number && token
14
+ token
15
+ end
16
+
17
+ private
18
+
19
+ def tokenize
20
+ @source = @source.source if @source.respond_to?(:source)
21
+ return [] if @source.to_s.empty?
22
+
23
+ tokens = @source.split(TemplateParser)
24
+
25
+ # removes the rogue empty element at the beginning of the array
26
+ tokens.shift if tokens[0] && tokens[0].empty?
27
+
28
+ tokens
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,5 @@
1
+ module Liquid
2
+ module Truffle
3
+
4
+ end
5
+ end
data/lib/liquid/utils.rb CHANGED
@@ -1,27 +1,24 @@
1
1
  module Liquid
2
2
  module Utils
3
-
4
3
  def self.slice_collection(collection, from, to)
5
- if (from != 0 || to != nil) && collection.respond_to?(:load_slice)
4
+ if (from != 0 || !to.nil?) && collection.respond_to?(:load_slice)
6
5
  collection.load_slice(from, to)
7
6
  else
8
7
  slice_collection_using_each(collection, from, to)
9
8
  end
10
9
  end
11
10
 
12
- def self.non_blank_string?(collection)
13
- collection.is_a?(String) && collection != ''.freeze
14
- end
15
-
16
11
  def self.slice_collection_using_each(collection, from, to)
17
12
  segments = []
18
13
  index = 0
19
14
 
20
15
  # Maintains Ruby 1.8.7 String#each behaviour on 1.9
21
- return [collection] if non_blank_string?(collection)
16
+ if collection.is_a?(String)
17
+ return collection.empty? ? [] : [collection]
18
+ end
19
+ return [] unless collection.respond_to?(:each)
22
20
 
23
21
  collection.each do |item|
24
-
25
22
  if to && to <= index
26
23
  break
27
24
  end
@@ -35,5 +32,52 @@ module Liquid
35
32
 
36
33
  segments
37
34
  end
35
+
36
+ def self.to_integer(num)
37
+ return num if num.is_a?(Integer)
38
+ num = num.to_s
39
+ begin
40
+ Integer(num)
41
+ rescue ::ArgumentError
42
+ raise Liquid::ArgumentError, "invalid integer"
43
+ end
44
+ end
45
+
46
+ def self.to_number(obj)
47
+ case obj
48
+ when Float
49
+ BigDecimal(obj.to_s)
50
+ when Numeric
51
+ obj
52
+ when String
53
+ (obj.strip =~ /\A-?\d+\.\d+\z/) ? BigDecimal(obj) : obj.to_i
54
+ else
55
+ if obj.respond_to?(:to_number)
56
+ obj.to_number
57
+ else
58
+ 0
59
+ end
60
+ end
61
+ end
62
+
63
+ def self.to_date(obj)
64
+ return obj if obj.respond_to?(:strftime)
65
+
66
+ if obj.is_a?(String)
67
+ return nil if obj.empty?
68
+ obj = obj.downcase
69
+ end
70
+
71
+ case obj
72
+ when 'now'.freeze, 'today'.freeze
73
+ Time.now
74
+ when /\A\d+\z/, Integer
75
+ Time.at(obj.to_i)
76
+ when String
77
+ Time.parse(obj)
78
+ end
79
+ rescue ::ArgumentError
80
+ nil
81
+ end
38
82
  end
39
83
  end
@@ -1,5 +1,4 @@
1
1
  module Liquid
2
-
3
2
  # Holds variables. Variables are only loaded "just in time"
4
3
  # and are not evaluated as part of the render stage
5
4
  #
@@ -11,16 +10,23 @@ module Liquid
11
10
  # {{ user | link }}
12
11
  #
13
12
  class Variable
13
+ FilterMarkupRegex = /#{FilterSeparator}\s*(.*)/om
14
14
  FilterParser = /(?:\s+|#{QuotedFragment}|#{ArgumentSeparator})+/o
15
- EasyParse = /\A *(\w+(?:\.\w+)*) *\z/
16
- attr_accessor :filters, :name, :warnings
17
- attr_accessor :line_number
15
+ FilterArgsRegex = /(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o
16
+ JustTagAttributes = /\A#{TagAttributes}\z/o
17
+ MarkupWithQuotedFragment = /(#{QuotedFragment})(.*)/om
18
+
19
+ attr_accessor :filters, :name, :line_number
20
+ attr_reader :parse_context
21
+ alias_method :options, :parse_context
22
+
18
23
  include ParserSwitching
19
24
 
20
- def initialize(markup, options = {})
25
+ def initialize(markup, parse_context)
21
26
  @markup = markup
22
27
  @name = nil
23
- @options = options || {}
28
+ @parse_context = parse_context
29
+ @line_number = parse_context.line_number
24
30
 
25
31
  parse_with_selected_parser(markup)
26
32
  end
@@ -35,35 +41,27 @@ module Liquid
35
41
 
36
42
  def lax_parse(markup)
37
43
  @filters = []
38
- if markup =~ /(#{QuotedFragment})(.*)/om
39
- name_markup = $1
40
- filter_markup = $2
41
- @name = Expression.parse(name_markup)
42
- if filter_markup =~ /#{FilterSeparator}\s*(.*)/om
43
- filters = $1.scan(FilterParser)
44
- filters.each do |f|
45
- if f =~ /\w+/
46
- filtername = Regexp.last_match(0)
47
- filterargs = f.scan(/(?:#{FilterArgumentSeparator}|#{ArgumentSeparator})\s*((?:\w+\s*\:\s*)?#{QuotedFragment})/o).flatten
48
- @filters << parse_filter_expressions(filtername, filterargs)
49
- end
50
- end
44
+ return unless markup =~ MarkupWithQuotedFragment
45
+
46
+ name_markup = $1
47
+ filter_markup = $2
48
+ @name = Expression.parse(name_markup)
49
+ if filter_markup =~ FilterMarkupRegex
50
+ filters = $1.scan(FilterParser)
51
+ filters.each do |f|
52
+ next unless f =~ /\w+/
53
+ filtername = Regexp.last_match(0)
54
+ filterargs = f.scan(FilterArgsRegex).flatten
55
+ @filters << parse_filter_expressions(filtername, filterargs)
51
56
  end
52
57
  end
53
58
  end
54
59
 
55
60
  def strict_parse(markup)
56
- # Very simple valid cases
57
- if markup =~ EasyParse
58
- @name = Expression.parse($1)
59
- @filters = []
60
- return
61
- end
62
-
63
61
  @filters = []
64
62
  p = Parser.new(markup)
65
- # Could be just filters with no input
66
- @name = p.look(:pipe) ? nil : Expression.parse(p.expression)
63
+
64
+ @name = Expression.parse(p.expression)
67
65
  while p.consume?(:pipe)
68
66
  filtername = p.consume(:id)
69
67
  filterargs = p.consume?(:colon) ? parse_filterargs(p) : []
@@ -76,17 +74,21 @@ module Liquid
76
74
  # first argument
77
75
  filterargs = [p.argument]
78
76
  # followed by comma separated others
79
- while p.consume?(:comma)
80
- filterargs << p.argument
81
- end
77
+ filterargs << p.argument while p.consume?(:comma)
82
78
  filterargs
83
79
  end
84
80
 
85
81
  def render(context)
86
- @filters.inject(context.evaluate(@name)) do |output, (filter_name, filter_args, filter_kwargs)|
82
+ obj = @filters.inject(context.evaluate(@name)) do |output, (filter_name, filter_args, filter_kwargs)|
87
83
  filter_args = evaluate_filter_expressions(context, filter_args, filter_kwargs)
88
- output = context.invoke(filter_name, output, *filter_args)
89
- end.tap{ |obj| taint_check(obj) }
84
+ context.invoke(filter_name, output, *filter_args)
85
+ end
86
+
87
+ obj = context.apply_global_filter(obj)
88
+
89
+ taint_check(context, obj)
90
+
91
+ obj
90
92
  end
91
93
 
92
94
  private
@@ -95,7 +97,7 @@ module Liquid
95
97
  filter_args = []
96
98
  keyword_args = {}
97
99
  unparsed_args.each do |a|
98
- if matches = a.match(/\A#{TagAttributes}\z/o)
100
+ if matches = a.match(JustTagAttributes)
99
101
  keyword_args[matches[1]] = Expression.parse(matches[2])
100
102
  else
101
103
  filter_args << Expression.parse(a)
@@ -118,17 +120,28 @@ module Liquid
118
120
  parsed_args
119
121
  end
120
122
 
121
- def taint_check(obj)
122
- if obj.tainted?
123
- @markup =~ QuotedFragment
124
- name = Regexp.last_match(0)
125
- case Template.taint_mode
126
- when :warn
127
- @warnings ||= []
128
- @warnings << "variable '#{name}' is tainted and was not escaped"
129
- when :error
130
- raise TaintedError, "Error - variable '#{name}' is tainted and was not escaped"
131
- end
123
+ def taint_check(context, obj)
124
+ return unless obj.tainted?
125
+ return if Template.taint_mode == :lax
126
+
127
+ @markup =~ QuotedFragment
128
+ name = Regexp.last_match(0)
129
+
130
+ error = TaintedError.new("variable '#{name}' is tainted and was not escaped")
131
+ error.line_number = line_number
132
+ error.template_name = context.template_name
133
+
134
+ case Template.taint_mode
135
+ when :warn
136
+ context.warnings << error
137
+ when :error
138
+ raise error
139
+ end
140
+ end
141
+
142
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
143
+ def children
144
+ [@node.name] + @node.filters.flatten
132
145
  end
133
146
  end
134
147
  end
@@ -1,7 +1,7 @@
1
1
  module Liquid
2
2
  class VariableLookup
3
3
  SQUARE_BRACKETED = /\A\[(.*)\]\z/m
4
- COMMAND_METHODS = ['size'.freeze, 'first'.freeze, 'last'.freeze]
4
+ COMMAND_METHODS = ['size'.freeze, 'first'.freeze, 'last'.freeze].freeze
5
5
 
6
6
  attr_reader :name, :lookups
7
7
 
@@ -41,8 +41,8 @@ module Liquid
41
41
  # If object is a hash- or array-like object we look for the
42
42
  # presence of the key and if its available we return it
43
43
  if object.respond_to?(:[]) &&
44
- ((object.respond_to?(:has_key?) && object.has_key?(key)) ||
45
- (object.respond_to?(:fetch) && key.is_a?(Integer)))
44
+ ((object.respond_to?(:key?) && object.key?(key)) ||
45
+ (object.respond_to?(:fetch) && key.is_a?(Integer)))
46
46
 
47
47
  # if its a proc we will replace the entry with the proc
48
48
  res = context.lookup_and_evaluate(object, key)
@@ -55,9 +55,11 @@ module Liquid
55
55
  object = object.send(key).to_liquid
56
56
 
57
57
  # No key was present with the desired value and it wasn't one of the directly supported
58
- # keywords either. The only thing we got left is to return nil
58
+ # keywords either. The only thing we got left is to return nil or
59
+ # raise an exception if `strict_variables` option is set to true
59
60
  else
60
- return nil
61
+ return nil unless context.strict_variables
62
+ raise Liquid::UndefinedVariable, "undefined variable #{key}"
61
63
  end
62
64
 
63
65
  # If we are dealing with a drop here we have to
@@ -68,7 +70,7 @@ module Liquid
68
70
  end
69
71
 
70
72
  def ==(other)
71
- self.class == other.class && self.state == other.state
73
+ self.class == other.class && state == other.state
72
74
  end
73
75
 
74
76
  protected
@@ -76,5 +78,11 @@ module Liquid
76
78
  def state
77
79
  [@name, @lookups, @command_flags]
78
80
  end
81
+
82
+ class ParseTreeVisitor < Liquid::ParseTreeVisitor
83
+ def children
84
+ @node.lookups
85
+ end
86
+ end
79
87
  end
80
88
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+
2
3
  module Liquid
3
- VERSION = "3.0.6"
4
+ VERSION = "4.0.3".freeze
4
5
  end
data/lib/liquid.rb CHANGED
@@ -24,6 +24,7 @@ module Liquid
24
24
  ArgumentSeparator = ','.freeze
25
25
  FilterArgumentSeparator = ':'.freeze
26
26
  VariableAttributeSeparator = '.'.freeze
27
+ WhitespaceControl = '-'.freeze
27
28
  TagStart = /\{\%/
28
29
  TagEnd = /\%\}/
29
30
  VariableSignature = /\(?[\w\-\.\[\]]\)?/
@@ -34,7 +35,7 @@ module Liquid
34
35
  QuotedString = /"[^"]*"|'[^']*'/
35
36
  QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/o
36
37
  TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/o
37
- AnyStartingTag = /\{\{|\{\%/
38
+ AnyStartingTag = /#{TagStart}|#{VariableStart}/o
38
39
  PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om
39
40
  TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
40
41
  VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/o
@@ -44,10 +45,13 @@ module Liquid
44
45
  end
45
46
 
46
47
  require "liquid/version"
48
+ require 'liquid/parse_tree_visitor'
47
49
  require 'liquid/lexer'
48
50
  require 'liquid/parser'
49
51
  require 'liquid/i18n'
50
52
  require 'liquid/drop'
53
+ require 'liquid/tablerowloop_drop'
54
+ require 'liquid/forloop_drop'
51
55
  require 'liquid/extensions'
52
56
  require 'liquid/errors'
53
57
  require 'liquid/interrupts'
@@ -57,21 +61,20 @@ require 'liquid/context'
57
61
  require 'liquid/parser_switching'
58
62
  require 'liquid/tag'
59
63
  require 'liquid/block'
64
+ require 'liquid/block_body'
60
65
  require 'liquid/document'
61
66
  require 'liquid/variable'
62
67
  require 'liquid/variable_lookup'
63
68
  require 'liquid/range_lookup'
64
69
  require 'liquid/file_system'
70
+ require 'liquid/resource_limits'
65
71
  require 'liquid/template'
66
72
  require 'liquid/standardfilters'
67
73
  require 'liquid/condition'
68
- require 'liquid/module_ex'
69
74
  require 'liquid/utils'
70
- require 'liquid/token'
75
+ require 'liquid/tokenizer'
76
+ require 'liquid/parse_context'
71
77
 
72
78
  # Load all the tags of the standard library
73
79
  #
74
- Dir[File.dirname(__FILE__) + '/liquid/tags/*.rb'].each { |f| require f }
75
-
76
- require 'liquid/profiler'
77
- require 'liquid/profiler/hooks'
80
+ Dir["#{__dir__}/liquid/tags/*.rb"].each { |f| require f }
@@ -15,24 +15,24 @@ class AssignTest < Minitest::Test
15
15
 
16
16
  def test_assigned_variable
17
17
  assert_template_result('.foo.',
18
- '{% assign foo = values %}.{{ foo[0] }}.',
19
- 'values' => %w{foo bar baz})
18
+ '{% assign foo = values %}.{{ foo[0] }}.',
19
+ 'values' => %w(foo bar baz))
20
20
 
21
21
  assert_template_result('.bar.',
22
- '{% assign foo = values %}.{{ foo[1] }}.',
23
- 'values' => %w{foo bar baz})
22
+ '{% assign foo = values %}.{{ foo[1] }}.',
23
+ 'values' => %w(foo bar baz))
24
24
  end
25
25
 
26
26
  def test_assign_with_filter
27
27
  assert_template_result('.bar.',
28
- '{% assign foo = values | split: "," %}.{{ foo[1] }}.',
29
- 'values' => "foo,bar,baz")
28
+ '{% assign foo = values | split: "," %}.{{ foo[1] }}.',
29
+ 'values' => "foo,bar,baz")
30
30
  end
31
31
 
32
32
  def test_assign_syntax_error
33
33
  assert_match_syntax_error(/assign/,
34
- '{% assign foo not values %}.',
35
- 'values' => "foo,bar,baz")
34
+ '{% assign foo not values %}.',
35
+ 'values' => "foo,bar,baz")
36
36
  end
37
37
 
38
38
  def test_assign_uses_error_mode
@@ -9,7 +9,7 @@ class FoobarTag < Liquid::Tag
9
9
  end
10
10
 
11
11
  class BlankTestFileSystem
12
- def read_template_file(template_path, context)
12
+ def read_template_file(template_path)
13
13
  template_path
14
14
  end
15
15
  end
@@ -31,7 +31,7 @@ class BlankTest < Minitest::Test
31
31
  end
32
32
 
33
33
  def test_new_tags_are_not_blank_by_default
34
- assert_template_result(" "*N, wrap_in_for("{% foobar %}"))
34
+ assert_template_result(" " * N, wrap_in_for("{% foobar %}"))
35
35
  end
36
36
 
37
37
  def test_loops_are_blank
@@ -47,7 +47,7 @@ class BlankTest < Minitest::Test
47
47
  end
48
48
 
49
49
  def test_mark_as_blank_only_during_parsing
50
- assert_template_result(" "*(N+1), wrap(" {% if false %} this never happens, but still, this block is not blank {% endif %}"))
50
+ assert_template_result(" " * (N + 1), wrap(" {% if false %} this never happens, but still, this block is not blank {% endif %}"))
51
51
  end
52
52
 
53
53
  def test_comments_are_blank
@@ -60,9 +60,9 @@ class BlankTest < Minitest::Test
60
60
 
61
61
  def test_nested_blocks_are_blank_but_only_if_all_children_are
62
62
  assert_template_result("", wrap(wrap(" ")))
63
- assert_template_result("\n but this is not "*(N+1),
64
- wrap(%q{{% if true %} {% comment %} this is blank {% endcomment %} {% endif %}
65
- {% if true %} but this is not {% endif %}}))
63
+ assert_template_result("\n but this is not " * (N + 1),
64
+ wrap('{% if true %} {% comment %} this is blank {% endcomment %} {% endif %}
65
+ {% if true %} but this is not {% endif %}'))
66
66
  end
67
67
 
68
68
  def test_assigns_are_blank
@@ -76,31 +76,31 @@ class BlankTest < Minitest::Test
76
76
 
77
77
  def test_whitespace_is_not_blank_if_other_stuff_is_present
78
78
  body = " x "
79
- assert_template_result(body*(N+1), wrap(body))
79
+ assert_template_result(body * (N + 1), wrap(body))
80
80
  end
81
81
 
82
82
  def test_increment_is_not_blank
83
- assert_template_result(" 0"*2*(N+1), wrap("{% assign foo = 0 %} {% increment foo %} {% decrement foo %}"))
83
+ assert_template_result(" 0" * 2 * (N + 1), wrap("{% assign foo = 0 %} {% increment foo %} {% decrement foo %}"))
84
84
  end
85
85
 
86
86
  def test_cycle_is_not_blank
87
- assert_template_result(" "*((N+1)/2)+" ", wrap("{% cycle ' ', ' ' %}"))
87
+ assert_template_result(" " * ((N + 1) / 2) + " ", wrap("{% cycle ' ', ' ' %}"))
88
88
  end
89
89
 
90
90
  def test_raw_is_not_blank
91
- assert_template_result(" "*(N+1), wrap(" {% raw %} {% endraw %}"))
91
+ assert_template_result(" " * (N + 1), wrap(" {% raw %} {% endraw %}"))
92
92
  end
93
93
 
94
94
  def test_include_is_blank
95
95
  Liquid::Template.file_system = BlankTestFileSystem.new
96
- assert_template_result "foobar"*(N+1), wrap("{% include 'foobar' %}")
97
- assert_template_result " foobar "*(N+1), wrap("{% include ' foobar ' %}")
98
- assert_template_result " "*(N+1), wrap(" {% include ' ' %} ")
96
+ assert_template_result "foobar" * (N + 1), wrap("{% include 'foobar' %}")
97
+ assert_template_result " foobar " * (N + 1), wrap("{% include ' foobar ' %}")
98
+ assert_template_result " " * (N + 1), wrap(" {% include ' ' %} ")
99
99
  end
100
100
 
101
101
  def test_case_is_blank
102
102
  assert_template_result("", wrap(" {% assign foo = 'bar' %} {% case foo %} {% when 'bar' %} {% when 'whatever' %} {% else %} {% endcase %} "))
103
103
  assert_template_result("", wrap(" {% assign foo = 'else' %} {% case foo %} {% when 'bar' %} {% when 'whatever' %} {% else %} {% endcase %} "))
104
- assert_template_result(" x "*(N+1), wrap(" {% assign foo = 'else' %} {% case foo %} {% when 'bar' %} {% when 'whatever' %} {% else %} x {% endcase %} "))
104
+ assert_template_result(" x " * (N + 1), wrap(" {% assign foo = 'else' %} {% case foo %} {% when 'bar' %} {% when 'whatever' %} {% else %} x {% endcase %} "))
105
105
  end
106
106
  end
@@ -0,0 +1,12 @@
1
+ require 'test_helper'
2
+
3
+ class BlockTest < Minitest::Test
4
+ include Liquid
5
+
6
+ def test_unexpected_end_tag
7
+ exc = assert_raises(SyntaxError) do
8
+ Template.parse("{% if true %}{% endunless %}")
9
+ end
10
+ assert_equal exc.message, "Liquid syntax error: 'endunless' is not a valid delimiter for if tags. use endif"
11
+ end
12
+ end
@@ -18,14 +18,14 @@ class ContextTest < Minitest::Test
18
18
 
19
19
  with_global_filter(global) do
20
20
  assert_equal 'Global test', Template.parse("{{'test' | notice }}").render!
21
- assert_equal 'Local test', Template.parse("{{'test' | notice }}").render!({}, :filters => [local])
21
+ assert_equal 'Local test', Template.parse("{{'test' | notice }}").render!({}, filters: [local])
22
22
  end
23
23
  end
24
24
 
25
25
  def test_has_key_will_not_add_an_error_for_missing_keys
26
26
  with_error_mode :strict do
27
27
  context = Context.new
28
- context.has_key?('unknown')
28
+ context.key?('unknown')
29
29
  assert_empty context.errors
30
30
  end
31
31
  end
@@ -0,0 +1,19 @@
1
+ require 'test_helper'
2
+
3
+ class DocumentTest < Minitest::Test
4
+ include Liquid
5
+
6
+ def test_unexpected_outer_tag
7
+ exc = assert_raises(SyntaxError) do
8
+ Template.parse("{% else %}")
9
+ end
10
+ assert_equal exc.message, "Liquid syntax error: Unexpected outer 'else' tag"
11
+ end
12
+
13
+ def test_unknown_tag
14
+ exc = assert_raises(SyntaxError) do
15
+ Template.parse("{% foo %}")
16
+ end
17
+ assert_equal exc.message, "Liquid syntax error: Unknown tag 'foo'"
18
+ end
19
+ end