liquid 2.6.3 → 3.0.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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +46 -13
  3. data/README.md +27 -2
  4. data/lib/liquid/block.rb +85 -51
  5. data/lib/liquid/block_body.rb +123 -0
  6. data/lib/liquid/condition.rb +26 -15
  7. data/lib/liquid/context.rb +106 -140
  8. data/lib/liquid/document.rb +3 -3
  9. data/lib/liquid/drop.rb +17 -1
  10. data/lib/liquid/errors.rb +50 -2
  11. data/lib/liquid/expression.rb +33 -0
  12. data/lib/liquid/file_system.rb +17 -6
  13. data/lib/liquid/i18n.rb +39 -0
  14. data/lib/liquid/interrupts.rb +1 -1
  15. data/lib/liquid/lexer.rb +51 -0
  16. data/lib/liquid/locales/en.yml +22 -0
  17. data/lib/liquid/parser.rb +90 -0
  18. data/lib/liquid/parser_switching.rb +31 -0
  19. data/lib/liquid/profiler/hooks.rb +23 -0
  20. data/lib/liquid/profiler.rb +159 -0
  21. data/lib/liquid/range_lookup.rb +22 -0
  22. data/lib/liquid/standardfilters.rb +143 -55
  23. data/lib/liquid/strainer.rb +14 -4
  24. data/lib/liquid/tag.rb +25 -9
  25. data/lib/liquid/tags/assign.rb +12 -9
  26. data/lib/liquid/tags/break.rb +1 -1
  27. data/lib/liquid/tags/capture.rb +10 -8
  28. data/lib/liquid/tags/case.rb +13 -13
  29. data/lib/liquid/tags/comment.rb +9 -2
  30. data/lib/liquid/tags/continue.rb +1 -4
  31. data/lib/liquid/tags/cycle.rb +5 -7
  32. data/lib/liquid/tags/decrement.rb +3 -4
  33. data/lib/liquid/tags/for.rb +69 -36
  34. data/lib/liquid/tags/if.rb +52 -25
  35. data/lib/liquid/tags/ifchanged.rb +3 -3
  36. data/lib/liquid/tags/include.rb +19 -8
  37. data/lib/liquid/tags/increment.rb +4 -8
  38. data/lib/liquid/tags/raw.rb +4 -7
  39. data/lib/liquid/tags/table_row.rb +73 -0
  40. data/lib/liquid/tags/unless.rb +2 -4
  41. data/lib/liquid/template.rb +124 -14
  42. data/lib/liquid/token.rb +18 -0
  43. data/lib/liquid/utils.rb +13 -4
  44. data/lib/liquid/variable.rb +103 -25
  45. data/lib/liquid/variable_lookup.rb +78 -0
  46. data/lib/liquid/version.rb +1 -1
  47. data/lib/liquid.rb +19 -11
  48. data/test/fixtures/en_locale.yml +9 -0
  49. data/test/{liquid → integration}/assign_test.rb +18 -1
  50. data/test/integration/blank_test.rb +106 -0
  51. data/test/{liquid → integration}/capture_test.rb +3 -3
  52. data/test/integration/context_test.rb +32 -0
  53. data/test/integration/drop_test.rb +271 -0
  54. data/test/integration/error_handling_test.rb +207 -0
  55. data/test/{liquid → integration}/filter_test.rb +11 -11
  56. data/test/integration/hash_ordering_test.rb +23 -0
  57. data/test/{liquid → integration}/output_test.rb +13 -13
  58. data/test/integration/parsing_quirks_test.rb +116 -0
  59. data/test/integration/render_profiling_test.rb +154 -0
  60. data/test/{liquid → integration}/security_test.rb +10 -10
  61. data/test/{liquid → integration}/standard_filter_test.rb +148 -32
  62. data/test/{liquid → integration}/tags/break_tag_test.rb +1 -1
  63. data/test/{liquid → integration}/tags/continue_tag_test.rb +1 -1
  64. data/test/{liquid → integration}/tags/for_tag_test.rb +80 -2
  65. data/test/{liquid → integration}/tags/if_else_tag_test.rb +24 -21
  66. data/test/integration/tags/include_tag_test.rb +234 -0
  67. data/test/{liquid → integration}/tags/increment_tag_test.rb +1 -1
  68. data/test/{liquid → integration}/tags/raw_tag_test.rb +2 -1
  69. data/test/{liquid → integration}/tags/standard_tag_test.rb +28 -26
  70. data/test/integration/tags/statements_test.rb +113 -0
  71. data/test/{liquid/tags/html_tag_test.rb → integration/tags/table_row_test.rb} +5 -5
  72. data/test/{liquid → integration}/tags/unless_else_tag_test.rb +1 -1
  73. data/test/{liquid → integration}/template_test.rb +81 -45
  74. data/test/integration/variable_test.rb +82 -0
  75. data/test/test_helper.rb +73 -20
  76. data/test/{liquid/block_test.rb → unit/block_unit_test.rb} +2 -5
  77. data/test/{liquid/condition_test.rb → unit/condition_unit_test.rb} +23 -1
  78. data/test/{liquid/context_test.rb → unit/context_unit_test.rb} +39 -25
  79. data/test/{liquid/file_system_test.rb → unit/file_system_unit_test.rb} +11 -5
  80. data/test/unit/i18n_unit_test.rb +37 -0
  81. data/test/unit/lexer_unit_test.rb +48 -0
  82. data/test/{liquid/module_ex_test.rb → unit/module_ex_unit_test.rb} +7 -7
  83. data/test/unit/parser_unit_test.rb +82 -0
  84. data/test/{liquid/regexp_test.rb → unit/regexp_unit_test.rb} +3 -3
  85. data/test/{liquid/strainer_test.rb → unit/strainer_unit_test.rb} +20 -1
  86. data/test/unit/tag_unit_test.rb +16 -0
  87. data/test/unit/tags/case_tag_unit_test.rb +10 -0
  88. data/test/unit/tags/for_tag_unit_test.rb +13 -0
  89. data/test/unit/tags/if_tag_unit_test.rb +8 -0
  90. data/test/unit/template_unit_test.rb +69 -0
  91. data/test/unit/tokenizer_unit_test.rb +38 -0
  92. data/test/unit/variable_unit_test.rb +139 -0
  93. metadata +135 -67
  94. data/lib/extras/liquid_view.rb +0 -51
  95. data/lib/liquid/htmltags.rb +0 -73
  96. data/test/liquid/drop_test.rb +0 -180
  97. data/test/liquid/error_handling_test.rb +0 -81
  98. data/test/liquid/hash_ordering_test.rb +0 -25
  99. data/test/liquid/parsing_quirks_test.rb +0 -52
  100. data/test/liquid/tags/include_tag_test.rb +0 -166
  101. data/test/liquid/tags/statements_test.rb +0 -134
  102. data/test/liquid/variable_test.rb +0 -186
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 556c422717dbe398a9c414ac93d2d7fa6cf824d9
4
- data.tar.gz: 1b63c67741c2e9c1bb0fa61c0e84856ecd17a6a7
3
+ metadata.gz: 15b38ad40486ed2c0f9af07847fd0328c1ebcd76
4
+ data.tar.gz: 1bf5887d011005c058d0ed524d03e40926c96d59
5
5
  SHA512:
6
- metadata.gz: fefb1fb53e150321193372e6f75dac1b860454a214a16a3cdb526e13d524b00761d50450b986b0ddab7297d253e2db46025e3a23144362b188a974834adfd08d
7
- data.tar.gz: 8e1449c8d9923df9ca9ea64c52848725106d84b39e16311fee5c7a8e006f9b8c78f663efa2574f15d1f32c6bfe7acb1001ac79881fe87435dbd273f5ea7eb213
6
+ metadata.gz: 176f0aa1184d56a180447e3353d90dc31a24b7c203c107dd80446520667b4f87a9f91eed07c7f0cfbd53c9f69572f2d818201d98d11b3ba1c21e4635f657a8ca
7
+ data.tar.gz: 81da9c0069bccfc02de2e5da4e78d1b8dddf2b382018c5e50d7c32a5a56c6df0b1f06187c78274b7e6f170d8d5d475e776f31f903663a861df636565c946e567
data/History.md CHANGED
@@ -1,17 +1,42 @@
1
1
  # Liquid Version History
2
2
 
3
- IMPORTANT: Liquid 2.6 is going to be the last version of Liquid which maintains explicit Ruby 1.8 compatability.
4
- The following releases will only be tested against Ruby 1.9 and Ruby 2.0 and are likely to break on Ruby 1.8.
5
-
6
- ## 2.6.3 / 2015-07-23 / branch "2-6-stable"
7
-
8
- * Fix test failure under certain timezones [Dylan Thacker-Smith]
9
-
10
- ## 2.6.2 / 2015-01-23
3
+ ## 3.0.0 / not yet released / branch "master"
11
4
 
12
- * Remove duplicate hash key [Parker Moore]
13
-
14
- ## 2.6.1 / 2014-01-10
5
+ * ...
6
+ * Removed Block#end_tag. Instead, override parse with `super` followed by your code. See #446 [Dylan Thacker-Smith, dylanahsmith]
7
+ * Fixed condition with wrong data types, see #423 [Bogdan Gusiev]
8
+ * Add url_encode to standard filters, see #421 [Derrick Reimer, djreimer]
9
+ * Add uniq to standard filters [Florian Weingarten, fw42]
10
+ * Add exception_handler feature, see #397 and #254 [Bogdan Gusiev, bogdan and Florian Weingarten, fw42]
11
+ * Optimize variable parsing to avoid repeated regex evaluation during template rendering #383 [Jason Hiltz-Laforge, jasonhl]
12
+ * Optimize checking for block interrupts to reduce object allocation #380 [Jason Hiltz-Laforge, jasonhl]
13
+ * Properly set context rethrow_errors on render! #349 [Thierry Joyal, tjoyal]
14
+ * Fix broken rendering of variables which are equal to false, see #345 [Florian Weingarten, fw42]
15
+ * Remove ActionView template handler [Dylan Thacker-Smith, dylanahsmith]
16
+ * Freeze lots of string literals for new Ruby 2.1 optimization, see #297 [Florian Weingarten, fw42]
17
+ * Allow newlines in tags and variables, see #324 [Dylan Thacker-Smith, dylanahsmith]
18
+ * Tag#parse is called after initialize, which now takes options instead of tokens as the 3rd argument. See #321 [Dylan Thacker-Smith, dylanahsmith]
19
+ * Raise `Liquid::ArgumentError` instead of `::ArgumentError` when filter has wrong number of arguments #309 [Bogdan Gusiev, bogdan]
20
+ * Add a to_s default for liquid drops, see #306 [Adam Doeler, releod]
21
+ * Add strip, lstrip, and rstrip to standard filters [Florian Weingarten, fw42]
22
+ * Make if, for & case tags return complete and consistent nodelists, see #250 [Nick Jones, dntj]
23
+ * Prevent arbitrary method invocation on condition objects, see #274 [Dylan Thacker-Smith, dylanahsmith]
24
+ * Don't call to_sym when creating conditions for security reasons, see #273 [Bouke van der Bijl, bouk]
25
+ * Fix resource counting bug with respond_to?(:length), see #263 [Florian Weingarten, fw42]
26
+ * Allow specifying custom patterns for template filenames, see #284 [Andrei Gladkyi, agladkyi]
27
+ * Allow drops to optimize loading a slice of elements, see #282 [Tom Burns, boourns]
28
+ * Support for passing variables to snippets in subdirs, see #271 [Joost Hietbrink, joost]
29
+ * Add a class cache to avoid runtime extend calls, see #249 [James Tucker, raggi]
30
+ * Remove some legacy Ruby 1.8 compatibility code, see #276 [Florian Weingarten, fw42]
31
+ * Add default filter to standard filters, see #267 [Derrick Reimer, djreimer]
32
+ * Add optional strict parsing and warn parsing, see #235 [Tristan Hume, trishume]
33
+ * Add I18n syntax error translation, see #241 [Simon Hørup Eskildsen, Sirupsen]
34
+ * Make sort filter work on enumerable drops, see #239 [Florian Weingarten, fw42]
35
+ * Fix clashing method names in enumerable drops, see #238 [Florian Weingarten, fw42]
36
+ * Make map filter work on enumerable drops, see #233 [Florian Weingarten, fw42]
37
+ * Improved whitespace stripping for blank blocks, related to #216 [Florian Weingarten, fw42]
38
+
39
+ ## 2.6.1 / 2014-01-10 / branch "2-6-stable"
15
40
 
16
41
  Security fix, cherry-picked from master (4e14a65):
17
42
  * Don't call to_sym when creating conditions for security reasons, see #273 [Bouke van der Bijl, bouk]
@@ -19,7 +44,9 @@ Security fix, cherry-picked from master (4e14a65):
19
44
 
20
45
  ## 2.6.0 / 2013-11-25
21
46
 
22
- * ...
47
+ IMPORTANT: Liquid 2.6 is going to be the last version of Liquid which maintains explicit Ruby 1.8 compatability.
48
+ The following releases will only be tested against Ruby 1.9 and Ruby 2.0 and are likely to break on Ruby 1.8.
49
+
23
50
  * Bugfix for #106: fix example servlet [gnowoel]
24
51
  * Bugfix for #97: strip_html filter supports multi-line tags [Jo Liss, joliss]
25
52
  * Bugfix for #114: strip_html filter supports style tags [James Allardice, jamesallardice]
@@ -39,7 +66,13 @@ Security fix, cherry-picked from master (4e14a65):
39
66
  * Better documentation for 'include' tag (closes #163) [Peter Schröder, phoet]
40
67
  * Use of BigDecimal on filters to have better precision (closes #155) [Arthur Nogueira Neves, arthurnn]
41
68
 
42
- ## 2.5.4 / 2013-11-11 / branch "2.5-stable"
69
+ ## 2.5.5 / 2014-01-10 / branch "2-5-stable"
70
+
71
+ Security fix, cherry-picked from master (4e14a65):
72
+ * Don't call to_sym when creating conditions for security reasons, see #273 [Bouke van der Bijl, bouk]
73
+ * Prevent arbitrary method invocation on condition objects, see #274 [Dylan Thacker-Smith, dylanahsmith]
74
+
75
+ ## 2.5.4 / 2013-11-11
43
76
 
44
77
  * Fix "can't convert Fixnum into String" for "replace", see #173, [wǒ_is神仙, jsw0528]
45
78
 
data/README.md CHANGED
@@ -1,9 +1,12 @@
1
+ [![Build Status](https://api.travis-ci.org/Shopify/liquid.svg?branch=master)](http://travis-ci.org/Shopify/liquid)
2
+ [![Inline docs](http://inch-ci.org/github/Shopify/liquid.svg?branch=master)](http://inch-ci.org/github/Shopify/liquid)
3
+
1
4
  # Liquid template engine
2
5
 
3
6
  * [Contributing guidelines](CONTRIBUTING.md)
4
7
  * [Version history](History.md)
5
8
  * [Liquid documentation from Shopify](http://docs.shopify.com/themes/liquid-basics)
6
- * [Liquid Wiki from Shopify](http://wiki.shopify.com/Liquid)
9
+ * [Liquid Wiki at GitHub](https://github.com/Shopify/liquid/wiki)
7
10
  * [Website](http://liquidmarkup.org/)
8
11
 
9
12
  ## Introduction
@@ -47,4 +50,26 @@ For standard use you can just pass it the content of a file and call render with
47
50
  @template.render('name' => 'tobi') # => "hi tobi"
48
51
  ```
49
52
 
50
- [![Build Status](https://secure.travis-ci.org/Shopify/liquid.png)](http://travis-ci.org/Shopify/liquid)
53
+ ### Error Modes
54
+
55
+ Setting the error mode of Liquid lets you specify how strictly you want your templates to be interpreted.
56
+ Normally the parser is very lax and will accept almost anything without error. Unfortunately this can make
57
+ it very hard to debug and can lead to unexpected behaviour.
58
+
59
+ Liquid also comes with a stricter parser that can be used when editing templates to give better error messages
60
+ when templates are invalid. You can enable this new parser like this:
61
+
62
+ ```ruby
63
+ Liquid::Template.error_mode = :strict # Raises a SyntaxError when invalid syntax is used
64
+ Liquid::Template.error_mode = :warn # Adds errors to template.errors but continues as normal
65
+ Liquid::Template.error_mode = :lax # The default mode, accepts almost anything.
66
+ ```
67
+
68
+ If you want to set the error mode only on specific templates you can pass `:error_mode` as an option to `parse`:
69
+ ```ruby
70
+ Liquid::Template.parse(source, :error_mode => :strict)
71
+ ```
72
+ This is useful for doing things like enabling strict mode only in the theme editor.
73
+
74
+ It is recommended that you enable `:strict` or `:warn` mode on new apps to stop invalid templates from being created.
75
+ It is also recommended that you use it in the template editors of existing apps to give editors better error messages.
data/lib/liquid/block.rb CHANGED
@@ -1,45 +1,58 @@
1
1
  module Liquid
2
-
3
2
  class Block < Tag
4
- IsTag = /^#{TagStart}/o
5
- IsVariable = /^#{VariableStart}/o
6
- FullToken = /^#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}$/o
7
- ContentOfVariable = /^#{VariableStart}(.*)#{VariableEnd}$/o
3
+ FullToken = /\A#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om
4
+ ContentOfVariable = /\A#{VariableStart}(.*)#{VariableEnd}\z/om
5
+ TAGSTART = "{%".freeze
6
+ VARSTART = "{{".freeze
7
+
8
+ def blank?
9
+ @blank
10
+ end
8
11
 
9
12
  def parse(tokens)
13
+ @blank = true
10
14
  @nodelist ||= []
11
15
  @nodelist.clear
12
16
 
13
17
  while token = tokens.shift
14
-
15
- case token
16
- when IsTag
17
- if token =~ FullToken
18
-
19
- # if we found the proper block delimiter just end parsing here and let the outer block
20
- # proceed
21
- if block_delimiter == $1
22
- end_tag
23
- return
24
- end
25
-
26
- # fetch the tag from registered blocks
27
- if tag = Template.tags[$1]
28
- @nodelist << tag.new($1, $2, tokens)
18
+ begin
19
+ unless token.empty?
20
+ case
21
+ when token.start_with?(TAGSTART)
22
+ if token =~ FullToken
23
+
24
+ # if we found the proper block delimiter just end parsing here and let the outer block
25
+ # proceed
26
+ return if block_delimiter == $1
27
+
28
+ # fetch the tag from registered blocks
29
+ if tag = Template.tags[$1]
30
+ markup = token.is_a?(Token) ? token.child($2) : $2
31
+ new_tag = tag.parse($1, markup, tokens, @options)
32
+ new_tag.line_number = token.line_number if token.is_a?(Token)
33
+ @blank &&= new_tag.blank?
34
+ @nodelist << new_tag
35
+ else
36
+ # this tag is not registered with the system
37
+ # pass it to the current block for special handling or error reporting
38
+ unknown_tag($1, $2, tokens)
39
+ end
40
+ else
41
+ raise SyntaxError.new(options[:locale].t("errors.syntax.tag_termination".freeze, :token => token, :tag_end => TagEnd.inspect))
42
+ end
43
+ when token.start_with?(VARSTART)
44
+ new_var = create_variable(token)
45
+ new_var.line_number = token.line_number if token.is_a?(Token)
46
+ @nodelist << new_var
47
+ @blank = false
29
48
  else
30
- # this tag is not registered with the system
31
- # pass it to the current block for special handling or error reporting
32
- unknown_tag($1, $2, tokens)
49
+ @nodelist << token
50
+ @blank &&= (token =~ /\A\s*\z/)
33
51
  end
34
- else
35
- raise SyntaxError, "Tag '#{token}' was not properly terminated with regexp: #{TagEnd.inspect} "
36
52
  end
37
- when IsVariable
38
- @nodelist << create_variable(token)
39
- when ''
40
- # pass
41
- else
42
- @nodelist << token
53
+ rescue SyntaxError => e
54
+ e.set_line_number_from_token(token)
55
+ raise
43
56
  end
44
57
  end
45
58
 
@@ -49,33 +62,46 @@ module Liquid
49
62
  assert_missing_delimitation!
50
63
  end
51
64
 
52
- def end_tag
65
+ # warnings of this block and all sub-tags
66
+ def warnings
67
+ all_warnings = []
68
+ all_warnings.concat(@warnings) if @warnings
69
+
70
+ (nodelist || []).each do |node|
71
+ all_warnings.concat(node.warnings || []) if node.respond_to?(:warnings)
72
+ end
73
+
74
+ all_warnings
53
75
  end
54
76
 
55
77
  def unknown_tag(tag, params, tokens)
56
78
  case tag
57
- when 'else'
58
- raise SyntaxError, "#{block_name} tag does not expect else tag"
59
- when 'end'
60
- raise SyntaxError, "'end' is not a valid delimiter for #{block_name} tags. use #{block_delimiter}"
79
+ when 'else'.freeze
80
+ raise SyntaxError.new(options[:locale].t("errors.syntax.unexpected_else".freeze,
81
+ :block_name => block_name))
82
+ when 'end'.freeze
83
+ raise SyntaxError.new(options[:locale].t("errors.syntax.invalid_delimiter".freeze,
84
+ :block_name => block_name,
85
+ :block_delimiter => block_delimiter))
61
86
  else
62
- raise SyntaxError, "Unknown tag '#{tag}'"
87
+ raise SyntaxError.new(options[:locale].t("errors.syntax.unknown_tag".freeze, :tag => tag))
63
88
  end
64
89
  end
65
90
 
66
- def block_delimiter
67
- "end#{block_name}"
68
- end
69
-
70
91
  def block_name
71
92
  @tag_name
72
93
  end
73
94
 
95
+ def block_delimiter
96
+ @block_delimiter ||= "end#{block_name}"
97
+ end
98
+
74
99
  def create_variable(token)
75
100
  token.scan(ContentOfVariable) do |content|
76
- return Variable.new(content.first)
101
+ markup = token.is_a?(Token) ? token.child(content.first) : content.first
102
+ return Variable.new(markup, @options)
77
103
  end
78
- raise SyntaxError.new("Variable '#{token}' was not properly terminated with regexp: #{VariableEnd.inspect} ")
104
+ raise SyntaxError.new(options[:locale].t("errors.syntax.variable_termination".freeze, :token => token, :tag_end => VariableEnd.inspect))
79
105
  end
80
106
 
81
107
  def render(context)
@@ -85,7 +111,7 @@ module Liquid
85
111
  protected
86
112
 
87
113
  def assert_missing_delimitation!
88
- raise SyntaxError.new("#{block_name} tag was never closed")
114
+ raise SyntaxError.new(options[:locale].t("errors.syntax.tag_never_closed".freeze, :block_name => block_name))
89
115
  end
90
116
 
91
117
  def render_all(list, context)
@@ -106,21 +132,29 @@ module Liquid
106
132
  break
107
133
  end
108
134
 
109
- token_output = (token.respond_to?(:render) ? token.render(context) : token)
110
- context.resource_limits[:render_length_current] += (token_output.respond_to?(:length) ? token_output.length : 1)
111
- if context.resource_limits_reached?
112
- context.resource_limits[:reached] = true
113
- raise MemoryError.new("Memory limits exceeded")
135
+ token_output = render_token(token, context)
136
+
137
+ unless token.is_a?(Block) && token.blank?
138
+ output << token_output
114
139
  end
115
- output << token_output
116
140
  rescue MemoryError => e
117
141
  raise e
118
142
  rescue ::StandardError => e
119
- output << (context.handle_error(e))
143
+ output << (context.handle_error(e, token))
120
144
  end
121
145
  end
122
146
 
123
147
  output.join
124
148
  end
149
+
150
+ def render_token(token, context)
151
+ token_output = (token.respond_to?(:render) ? token.render(context) : token)
152
+ context.increment_used_resources(:render_length_current, token_output)
153
+ if context.resource_limits_reached?
154
+ context.resource_limits[:reached] = true
155
+ raise MemoryError.new("Memory limits exceeded".freeze)
156
+ end
157
+ token_output
158
+ end
125
159
  end
126
160
  end
@@ -0,0 +1,123 @@
1
+ module Liquid
2
+ class BlockBody
3
+ FullToken = /\A#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om
4
+ ContentOfVariable = /\A#{VariableStart}(.*)#{VariableEnd}\z/om
5
+ TAGSTART = "{%".freeze
6
+ VARSTART = "{{".freeze
7
+
8
+ attr_reader :nodelist
9
+
10
+ def initialize
11
+ @nodelist = []
12
+ @blank = true
13
+ end
14
+
15
+ def parse(tokens, options)
16
+ while token = tokens.shift
17
+ begin
18
+ unless token.empty?
19
+ case
20
+ when token.start_with?(TAGSTART)
21
+ if token =~ FullToken
22
+ tag_name = $1
23
+ markup = $2
24
+ # fetch the tag from registered blocks
25
+ if tag = Template.tags[tag_name]
26
+ markup = token.child(markup) if token.is_a?(Token)
27
+ new_tag = tag.parse(tag_name, markup, tokens, options)
28
+ new_tag.line_number = token.line_number if token.is_a?(Token)
29
+ @blank &&= new_tag.blank?
30
+ @nodelist << new_tag
31
+ else
32
+ # end parsing if we reach an unknown tag and let the caller decide
33
+ # determine how to proceed
34
+ return yield tag_name, markup
35
+ end
36
+ else
37
+ raise SyntaxError.new(options[:locale].t("errors.syntax.tag_termination".freeze, :token => token, :tag_end => TagEnd.inspect))
38
+ end
39
+ when token.start_with?(VARSTART)
40
+ new_var = create_variable(token, options)
41
+ new_var.line_number = token.line_number if token.is_a?(Token)
42
+ @nodelist << new_var
43
+ @blank = false
44
+ else
45
+ @nodelist << token
46
+ @blank &&= !!(token =~ /\A\s*\z/)
47
+ end
48
+ end
49
+ rescue SyntaxError => e
50
+ e.set_line_number_from_token(token)
51
+ raise
52
+ end
53
+ end
54
+
55
+ yield nil, nil
56
+ end
57
+
58
+ def blank?
59
+ @blank
60
+ end
61
+
62
+ def warnings
63
+ all_warnings = []
64
+ nodelist.each do |node|
65
+ all_warnings.concat(node.warnings) if node.respond_to?(:warnings) && node.warnings
66
+ end
67
+ all_warnings
68
+ end
69
+
70
+ def render(context)
71
+ output = []
72
+ context.resource_limits[:render_length_current] = 0
73
+ context.resource_limits[:render_score_current] += @nodelist.length
74
+
75
+ @nodelist.each do |token|
76
+ # Break out if we have any unhanded interrupts.
77
+ break if context.has_interrupt?
78
+
79
+ begin
80
+ # If we get an Interrupt that means the block must stop processing. An
81
+ # Interrupt is any command that stops block execution such as {% break %}
82
+ # or {% continue %}
83
+ if token.is_a?(Continue) or token.is_a?(Break)
84
+ context.push_interrupt(token.interrupt)
85
+ break
86
+ end
87
+
88
+ token_output = render_token(token, context)
89
+
90
+ unless token.is_a?(Block) && token.blank?
91
+ output << token_output
92
+ end
93
+ rescue MemoryError => e
94
+ raise e
95
+ rescue ::StandardError => e
96
+ output << context.handle_error(e, token)
97
+ end
98
+ end
99
+
100
+ output.join
101
+ end
102
+
103
+ private
104
+
105
+ def render_token(token, context)
106
+ token_output = (token.respond_to?(:render) ? token.render(context) : token)
107
+ context.increment_used_resources(:render_length_current, token_output)
108
+ if context.resource_limits_reached?
109
+ context.resource_limits[:reached] = true
110
+ raise MemoryError.new("Memory limits exceeded".freeze)
111
+ end
112
+ token_output
113
+ end
114
+
115
+ def create_variable(token, options)
116
+ token.scan(ContentOfVariable) do |content|
117
+ markup = token.is_a?(Token) ? token.child(content.first) : content.first
118
+ return Variable.new(markup, options)
119
+ end
120
+ raise SyntaxError.new(options[:locale].t("errors.syntax.variable_termination".freeze, :token => token, :tag_end => VariableEnd.inspect))
121
+ end
122
+ end
123
+ end
@@ -8,14 +8,16 @@ module Liquid
8
8
  #
9
9
  class Condition #:nodoc:
10
10
  @@operators = {
11
- '==' => lambda { |cond, left, right| cond.send(:equal_variables, left, right) },
12
- '!=' => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) },
13
- '<>' => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) },
14
- '<' => :<,
15
- '>' => :>,
16
- '>=' => :>=,
17
- '<=' => :<=,
18
- 'contains' => lambda { |cond, left, right| left && right ? left.include?(right) : false }
11
+ '=='.freeze => lambda { |cond, left, right| cond.send(:equal_variables, left, right) },
12
+ '!='.freeze => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) },
13
+ '<>'.freeze => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) },
14
+ '<'.freeze => :<,
15
+ '>'.freeze => :>,
16
+ '>='.freeze => :>=,
17
+ '<='.freeze => :<=,
18
+ 'contains'.freeze => lambda { |cond, left, right|
19
+ left && right && left.respond_to?(:include?) ? left.include?(right) : false
20
+ }
19
21
  }
20
22
 
21
23
  def self.operators
@@ -26,7 +28,9 @@ module Liquid
26
28
  attr_accessor :left, :operator, :right
27
29
 
28
30
  def initialize(left = nil, operator = nil, right = nil)
29
- @left, @operator, @right = left, operator, right
31
+ @left = left
32
+ @operator = operator
33
+ @right = right
30
34
  @child_relation = nil
31
35
  @child_condition = nil
32
36
  end
@@ -45,11 +49,13 @@ module Liquid
45
49
  end
46
50
 
47
51
  def or(condition)
48
- @child_relation, @child_condition = :or, condition
52
+ @child_relation = :or
53
+ @child_condition = condition
49
54
  end
50
55
 
51
56
  def and(condition)
52
- @child_relation, @child_condition = :and, condition
57
+ @child_relation = :and
58
+ @child_condition = condition
53
59
  end
54
60
 
55
61
  def attach(attachment)
@@ -61,7 +67,7 @@ module Liquid
61
67
  end
62
68
 
63
69
  def inspect
64
- "#<Condition #{[@left, @operator, @right].compact.join(' ')}>"
70
+ "#<Condition #{[@left, @operator, @right].compact.join(' '.freeze)}>"
65
71
  end
66
72
 
67
73
  private
@@ -92,14 +98,19 @@ module Liquid
92
98
  # return this as the result.
93
99
  return context[left] if op == nil
94
100
 
95
- left, right = context[left], context[right]
101
+ left = context[left]
102
+ right = context[right]
96
103
 
97
- operation = self.class.operators[op] || raise(ArgumentError.new("Unknown operator #{op}"))
104
+ operation = self.class.operators[op] || raise(Liquid::ArgumentError.new("Unknown operator #{op}"))
98
105
 
99
106
  if operation.respond_to?(:call)
100
107
  operation.call(self, left, right)
101
108
  elsif left.respond_to?(operation) and right.respond_to?(operation)
102
- left.send(operation, right)
109
+ begin
110
+ left.send(operation, right)
111
+ rescue ::ArgumentError => e
112
+ raise Liquid::ArgumentError.new(e.message)
113
+ end
103
114
  else
104
115
  nil
105
116
  end