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.
- checksums.yaml +4 -4
- data/History.md +46 -13
- data/README.md +27 -2
- data/lib/liquid/block.rb +85 -51
- data/lib/liquid/block_body.rb +123 -0
- data/lib/liquid/condition.rb +26 -15
- data/lib/liquid/context.rb +106 -140
- data/lib/liquid/document.rb +3 -3
- data/lib/liquid/drop.rb +17 -1
- data/lib/liquid/errors.rb +50 -2
- data/lib/liquid/expression.rb +33 -0
- data/lib/liquid/file_system.rb +17 -6
- data/lib/liquid/i18n.rb +39 -0
- data/lib/liquid/interrupts.rb +1 -1
- data/lib/liquid/lexer.rb +51 -0
- data/lib/liquid/locales/en.yml +22 -0
- data/lib/liquid/parser.rb +90 -0
- data/lib/liquid/parser_switching.rb +31 -0
- data/lib/liquid/profiler/hooks.rb +23 -0
- data/lib/liquid/profiler.rb +159 -0
- data/lib/liquid/range_lookup.rb +22 -0
- data/lib/liquid/standardfilters.rb +143 -55
- data/lib/liquid/strainer.rb +14 -4
- data/lib/liquid/tag.rb +25 -9
- data/lib/liquid/tags/assign.rb +12 -9
- data/lib/liquid/tags/break.rb +1 -1
- data/lib/liquid/tags/capture.rb +10 -8
- data/lib/liquid/tags/case.rb +13 -13
- data/lib/liquid/tags/comment.rb +9 -2
- data/lib/liquid/tags/continue.rb +1 -4
- data/lib/liquid/tags/cycle.rb +5 -7
- data/lib/liquid/tags/decrement.rb +3 -4
- data/lib/liquid/tags/for.rb +69 -36
- data/lib/liquid/tags/if.rb +52 -25
- data/lib/liquid/tags/ifchanged.rb +3 -3
- data/lib/liquid/tags/include.rb +19 -8
- data/lib/liquid/tags/increment.rb +4 -8
- data/lib/liquid/tags/raw.rb +4 -7
- data/lib/liquid/tags/table_row.rb +73 -0
- data/lib/liquid/tags/unless.rb +2 -4
- data/lib/liquid/template.rb +124 -14
- data/lib/liquid/token.rb +18 -0
- data/lib/liquid/utils.rb +13 -4
- data/lib/liquid/variable.rb +103 -25
- data/lib/liquid/variable_lookup.rb +78 -0
- data/lib/liquid/version.rb +1 -1
- data/lib/liquid.rb +19 -11
- data/test/fixtures/en_locale.yml +9 -0
- data/test/{liquid → integration}/assign_test.rb +18 -1
- data/test/integration/blank_test.rb +106 -0
- data/test/{liquid → integration}/capture_test.rb +3 -3
- data/test/integration/context_test.rb +32 -0
- data/test/integration/drop_test.rb +271 -0
- data/test/integration/error_handling_test.rb +207 -0
- data/test/{liquid → integration}/filter_test.rb +11 -11
- data/test/integration/hash_ordering_test.rb +23 -0
- data/test/{liquid → integration}/output_test.rb +13 -13
- data/test/integration/parsing_quirks_test.rb +116 -0
- data/test/integration/render_profiling_test.rb +154 -0
- data/test/{liquid → integration}/security_test.rb +10 -10
- data/test/{liquid → integration}/standard_filter_test.rb +148 -32
- data/test/{liquid → integration}/tags/break_tag_test.rb +1 -1
- data/test/{liquid → integration}/tags/continue_tag_test.rb +1 -1
- data/test/{liquid → integration}/tags/for_tag_test.rb +80 -2
- data/test/{liquid → integration}/tags/if_else_tag_test.rb +24 -21
- data/test/integration/tags/include_tag_test.rb +234 -0
- data/test/{liquid → integration}/tags/increment_tag_test.rb +1 -1
- data/test/{liquid → integration}/tags/raw_tag_test.rb +2 -1
- data/test/{liquid → integration}/tags/standard_tag_test.rb +28 -26
- data/test/integration/tags/statements_test.rb +113 -0
- data/test/{liquid/tags/html_tag_test.rb → integration/tags/table_row_test.rb} +5 -5
- data/test/{liquid → integration}/tags/unless_else_tag_test.rb +1 -1
- data/test/{liquid → integration}/template_test.rb +81 -45
- data/test/integration/variable_test.rb +82 -0
- data/test/test_helper.rb +73 -20
- data/test/{liquid/block_test.rb → unit/block_unit_test.rb} +2 -5
- data/test/{liquid/condition_test.rb → unit/condition_unit_test.rb} +23 -1
- data/test/{liquid/context_test.rb → unit/context_unit_test.rb} +39 -25
- data/test/{liquid/file_system_test.rb → unit/file_system_unit_test.rb} +11 -5
- data/test/unit/i18n_unit_test.rb +37 -0
- data/test/unit/lexer_unit_test.rb +48 -0
- data/test/{liquid/module_ex_test.rb → unit/module_ex_unit_test.rb} +7 -7
- data/test/unit/parser_unit_test.rb +82 -0
- data/test/{liquid/regexp_test.rb → unit/regexp_unit_test.rb} +3 -3
- data/test/{liquid/strainer_test.rb → unit/strainer_unit_test.rb} +20 -1
- data/test/unit/tag_unit_test.rb +16 -0
- data/test/unit/tags/case_tag_unit_test.rb +10 -0
- data/test/unit/tags/for_tag_unit_test.rb +13 -0
- data/test/unit/tags/if_tag_unit_test.rb +8 -0
- data/test/unit/template_unit_test.rb +69 -0
- data/test/unit/tokenizer_unit_test.rb +38 -0
- data/test/unit/variable_unit_test.rb +139 -0
- metadata +135 -67
- data/lib/extras/liquid_view.rb +0 -51
- data/lib/liquid/htmltags.rb +0 -73
- data/test/liquid/drop_test.rb +0 -180
- data/test/liquid/error_handling_test.rb +0 -81
- data/test/liquid/hash_ordering_test.rb +0 -25
- data/test/liquid/parsing_quirks_test.rb +0 -52
- data/test/liquid/tags/include_tag_test.rb +0 -166
- data/test/liquid/tags/statements_test.rb +0 -134
- data/test/liquid/variable_test.rb +0 -186
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15b38ad40486ed2c0f9af07847fd0328c1ebcd76
|
4
|
+
data.tar.gz: 1bf5887d011005c058d0ed524d03e40926c96d59
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
*
|
13
|
-
|
14
|
-
|
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.
|
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
|
+
[](http://travis-ci.org/Shopify/liquid)
|
2
|
+
[](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
|
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
|
-
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
31
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
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
|
59
|
-
|
60
|
-
|
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,
|
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
|
-
|
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("
|
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("
|
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
|
110
|
-
|
111
|
-
|
112
|
-
|
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
|
data/lib/liquid/condition.rb
CHANGED
@@ -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|
|
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
|
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
|
52
|
+
@child_relation = :or
|
53
|
+
@child_condition = condition
|
49
54
|
end
|
50
55
|
|
51
56
|
def and(condition)
|
52
|
-
@child_relation
|
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
|
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
|
-
|
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
|