liquid 5.3.0 → 5.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.md +20 -1
- data/README.md +5 -5
- data/lib/liquid/block.rb +8 -4
- data/lib/liquid/block_body.rb +21 -6
- data/lib/liquid/condition.rb +9 -4
- data/lib/liquid/context.rb +13 -5
- data/lib/liquid/drop.rb +4 -0
- data/lib/liquid/errors.rb +16 -15
- data/lib/liquid/expression.rb +4 -1
- data/lib/liquid/forloop_drop.rb +45 -5
- data/lib/liquid/lexer.rb +2 -3
- data/lib/liquid/locales/en.yml +7 -5
- data/lib/liquid/partial_cache.rb +15 -6
- data/lib/liquid/range_lookup.rb +11 -1
- data/lib/liquid/{static_registers.rb → registers.rb} +13 -10
- data/lib/liquid/standardfilters.rb +480 -68
- data/lib/liquid/strainer_factory.rb +4 -0
- data/lib/liquid/strainer_template.rb +4 -0
- data/lib/liquid/tablerowloop_drop.rb +58 -1
- data/lib/liquid/tag/disabler.rb +0 -8
- data/lib/liquid/tag.rb +10 -3
- data/lib/liquid/tags/assign.rb +12 -8
- data/lib/liquid/tags/break.rb +8 -0
- data/lib/liquid/tags/capture.rb +13 -10
- data/lib/liquid/tags/case.rb +22 -1
- data/lib/liquid/tags/comment.rb +72 -0
- data/lib/liquid/tags/continue.rb +8 -9
- data/lib/liquid/tags/cycle.rb +12 -11
- data/lib/liquid/tags/decrement.rb +22 -20
- data/lib/liquid/tags/echo.rb +16 -9
- data/lib/liquid/tags/for.rb +25 -46
- data/lib/liquid/tags/if.rb +12 -10
- data/lib/liquid/tags/include.rb +21 -17
- data/lib/liquid/tags/increment.rb +22 -17
- data/lib/liquid/tags/inline_comment.rb +30 -0
- data/lib/liquid/tags/raw.rb +13 -2
- data/lib/liquid/tags/render.rb +37 -8
- data/lib/liquid/tags/table_row.rb +33 -3
- data/lib/liquid/tags/unless.rb +17 -6
- data/lib/liquid/template.rb +11 -4
- data/lib/liquid/tokenizer.rb +9 -3
- data/lib/liquid/variable.rb +4 -4
- data/lib/liquid/variable_lookup.rb +10 -7
- data/lib/liquid/version.rb +1 -1
- data/lib/liquid.rb +3 -3
- metadata +7 -123
- data/lib/liquid/register.rb +0 -6
- data/test/fixtures/en_locale.yml +0 -9
- data/test/integration/assign_test.rb +0 -117
- data/test/integration/blank_test.rb +0 -109
- data/test/integration/block_test.rb +0 -58
- data/test/integration/capture_test.rb +0 -58
- data/test/integration/context_test.rb +0 -634
- data/test/integration/document_test.rb +0 -21
- data/test/integration/drop_test.rb +0 -257
- data/test/integration/error_handling_test.rb +0 -272
- data/test/integration/expression_test.rb +0 -46
- data/test/integration/filter_kwarg_test.rb +0 -24
- data/test/integration/filter_test.rb +0 -189
- data/test/integration/hash_ordering_test.rb +0 -25
- data/test/integration/output_test.rb +0 -125
- data/test/integration/parsing_quirks_test.rb +0 -134
- data/test/integration/profiler_test.rb +0 -240
- data/test/integration/security_test.rb +0 -89
- data/test/integration/standard_filter_test.rb +0 -925
- data/test/integration/tag/disableable_test.rb +0 -59
- data/test/integration/tag_test.rb +0 -45
- data/test/integration/tags/break_tag_test.rb +0 -17
- data/test/integration/tags/continue_tag_test.rb +0 -17
- data/test/integration/tags/echo_test.rb +0 -13
- data/test/integration/tags/for_tag_test.rb +0 -466
- data/test/integration/tags/if_else_tag_test.rb +0 -190
- data/test/integration/tags/include_tag_test.rb +0 -269
- data/test/integration/tags/increment_tag_test.rb +0 -25
- data/test/integration/tags/liquid_tag_test.rb +0 -116
- data/test/integration/tags/raw_tag_test.rb +0 -34
- data/test/integration/tags/render_tag_test.rb +0 -213
- data/test/integration/tags/standard_tag_test.rb +0 -303
- data/test/integration/tags/statements_test.rb +0 -113
- data/test/integration/tags/table_row_test.rb +0 -66
- data/test/integration/tags/unless_else_tag_test.rb +0 -28
- data/test/integration/template_test.rb +0 -340
- data/test/integration/trim_mode_test.rb +0 -563
- data/test/integration/variable_test.rb +0 -138
- data/test/test_helper.rb +0 -207
- data/test/unit/block_unit_test.rb +0 -53
- data/test/unit/condition_unit_test.rb +0 -181
- data/test/unit/file_system_unit_test.rb +0 -37
- data/test/unit/i18n_unit_test.rb +0 -39
- data/test/unit/lexer_unit_test.rb +0 -53
- data/test/unit/parse_tree_visitor_test.rb +0 -261
- data/test/unit/parser_unit_test.rb +0 -84
- data/test/unit/partial_cache_unit_test.rb +0 -128
- data/test/unit/regexp_unit_test.rb +0 -46
- data/test/unit/static_registers_unit_test.rb +0 -156
- data/test/unit/strainer_factory_unit_test.rb +0 -101
- data/test/unit/strainer_template_unit_test.rb +0 -82
- data/test/unit/tag_unit_test.rb +0 -23
- data/test/unit/tags/case_tag_unit_test.rb +0 -12
- data/test/unit/tags/for_tag_unit_test.rb +0 -15
- data/test/unit/tags/if_tag_unit_test.rb +0 -10
- data/test/unit/template_factory_unit_test.rb +0 -12
- data/test/unit/template_unit_test.rb +0 -87
- data/test/unit/tokenizer_unit_test.rb +0 -62
- data/test/unit/variable_unit_test.rb +0 -164
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d5699469b4a46eb15532bf2d5000925eeeff4f2eb5fef683d53bf09f0ec8825
|
4
|
+
data.tar.gz: df2bebc1a371dc250c768d46996c861fc22db9a1e2afc77f2f12247baa9a2e71
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e953d07e402f2325e56953a1cce6f4d334a183548fd70db1a472695b491f3f2efb6318625c1582e708362f5d2cff81f932d9c0d30c3da23d63fdfdab12b50725
|
7
|
+
data.tar.gz: d6abf81f05029169f17da65b9c93af90c96c93fccd6179cff3a02d050f5736e177c2686348b1d62af4caa432e6332ffc19c37c86004d139e422876049856fc97
|
data/History.md
CHANGED
@@ -1,10 +1,29 @@
|
|
1
1
|
# Liquid Change Log
|
2
2
|
|
3
|
+
## 5.5.0 2024-03-21
|
4
|
+
|
5
|
+
Please reference the GitHub release for more information.
|
6
|
+
|
7
|
+
## 5.4.0 2022-07-29
|
8
|
+
|
9
|
+
### Breaking Changes
|
10
|
+
* Drop support for end-of-life Ruby versions (2.5 and 2.6) (#1578) [Andy Waite]
|
11
|
+
|
12
|
+
### Features
|
13
|
+
* Allow `#` to be used as an inline comment tag (#1498) [CP Clermont]
|
14
|
+
|
15
|
+
### Fixes
|
16
|
+
* `PartialCache` now shares snippet cache with subcontexts by default (#1553) [Chris AtLee]
|
17
|
+
* Hash registers no longer leak into subcontexts as static registers (#1564) [Chris AtLee]
|
18
|
+
* Fix `ParseTreeVisitor` for `with` variable expressions in `Render` tag (#1596) [CP Clermont]
|
19
|
+
|
20
|
+
### Changed
|
21
|
+
* Liquid::Context#registers now always returns a Liquid::Registers object, though supports the most used Hash functions for compatibility (#1553)
|
22
|
+
|
3
23
|
## 5.3.0 2022-03-22
|
4
24
|
|
5
25
|
### Fixes
|
6
26
|
* StandardFilter: Fix missing @context on iterations (#1525) [Thierry Joyal]
|
7
|
-
* Test under Ruby 3.1 (#1533) [petergoldstein]
|
8
27
|
* Fix warning about block and default value in `static_registers.rb` (#1531) [Peter Zhu]
|
9
28
|
|
10
29
|
### Deprecation
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
[![Build
|
1
|
+
[![Build status](https://github.com/Shopify/liquid/actions/workflows/liquid.yml/badge.svg)](https://github.com/Shopify/liquid/actions/workflows/liquid.yml)
|
2
2
|
[![Inline docs](http://inch-ci.org/github/Shopify/liquid.svg?branch=master)](http://inch-ci.org/github/Shopify/liquid)
|
3
3
|
|
4
4
|
# Liquid template engine
|
5
5
|
|
6
6
|
* [Contributing guidelines](CONTRIBUTING.md)
|
7
7
|
* [Version history](History.md)
|
8
|
-
* [Liquid documentation from Shopify](https://shopify.dev/api/liquid)
|
8
|
+
* [Liquid documentation from Shopify](https://shopify.dev/docs/api/liquid)
|
9
9
|
* [Liquid Wiki at GitHub](https://github.com/Shopify/liquid/wiki)
|
10
10
|
* [Website](http://liquidmarkup.org/)
|
11
11
|
|
@@ -63,13 +63,13 @@ when templates are invalid. You can enable this new parser like this:
|
|
63
63
|
|
64
64
|
```ruby
|
65
65
|
Liquid::Template.error_mode = :strict # Raises a SyntaxError when invalid syntax is used
|
66
|
-
Liquid::Template.error_mode = :warn # Adds errors to template.errors but continues as normal
|
66
|
+
Liquid::Template.error_mode = :warn # Adds strict errors to template.errors but continues as normal
|
67
67
|
Liquid::Template.error_mode = :lax # The default mode, accepts almost anything.
|
68
68
|
```
|
69
69
|
|
70
70
|
If you want to set the error mode only on specific templates you can pass `:error_mode` as an option to `parse`:
|
71
71
|
```ruby
|
72
|
-
Liquid::Template.parse(source, :
|
72
|
+
Liquid::Template.parse(source, error_mode: :strict)
|
73
73
|
```
|
74
74
|
This is useful for doing things like enabling strict mode only in the theme editor.
|
75
75
|
|
@@ -111,4 +111,4 @@ template.render!({ 'x' => 1}, { strict_variables: true })
|
|
111
111
|
|
112
112
|
To help track usages of a feature or code path in production, we have released opt-in usage tracking. To enable this, we provide an empty `Liquid:: Usage.increment` method which you can customize to your needs. The feature is well suited to https://github.com/Shopify/statsd-instrument. However, the choice of implementation is up to you.
|
113
113
|
|
114
|
-
Once you have enabled usage tracking, we recommend reporting any events through Github Issues that your system may be logging. It is highly likely this event has been added to consider deprecating or improving code specific to this event, so please raise any concerns.
|
114
|
+
Once you have enabled usage tracking, we recommend reporting any events through Github Issues that your system may be logging. It is highly likely this event has been added to consider deprecating or improving code specific to this event, so please raise any concerns.
|
data/lib/liquid/block.rb
CHANGED
@@ -36,13 +36,17 @@ module Liquid
|
|
36
36
|
# @api private
|
37
37
|
def self.raise_unknown_tag(tag, block_name, block_delimiter, parse_context)
|
38
38
|
if tag == 'else'
|
39
|
-
raise SyntaxError, parse_context.locale.t(
|
40
|
-
|
39
|
+
raise SyntaxError, parse_context.locale.t(
|
40
|
+
"errors.syntax.unexpected_else",
|
41
|
+
block_name: block_name,
|
42
|
+
)
|
41
43
|
elsif tag.start_with?('end')
|
42
|
-
raise SyntaxError, parse_context.locale.t(
|
44
|
+
raise SyntaxError, parse_context.locale.t(
|
45
|
+
"errors.syntax.invalid_delimiter",
|
43
46
|
tag: tag,
|
44
47
|
block_name: block_name,
|
45
|
-
block_delimiter: block_delimiter
|
48
|
+
block_delimiter: block_delimiter,
|
49
|
+
)
|
46
50
|
else
|
47
51
|
raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag)
|
48
52
|
end
|
data/lib/liquid/block_body.rb
CHANGED
@@ -4,8 +4,9 @@ require 'English'
|
|
4
4
|
|
5
5
|
module Liquid
|
6
6
|
class BlockBody
|
7
|
-
LiquidTagToken = /\A\s*(
|
8
|
-
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(
|
7
|
+
LiquidTagToken = /\A\s*(#{TagName})\s*(.*?)\z/o
|
8
|
+
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(#{TagName})(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
9
|
+
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}#{WhitespaceControl}?\s*(\w+)\s*(.*)?#{WhitespaceControl}?#{TagEnd}\z/om
|
9
10
|
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
|
10
11
|
WhitespaceOrNothing = /\A\s*\z/
|
11
12
|
TAGSTART = "{%"
|
@@ -37,7 +38,7 @@ module Liquid
|
|
37
38
|
|
38
39
|
private def parse_for_liquid_tag(tokenizer, parse_context)
|
39
40
|
while (token = tokenizer.shift)
|
40
|
-
unless token.empty? || token
|
41
|
+
unless token.empty? || token.match?(WhitespaceOrNothing)
|
41
42
|
unless token =~ LiquidTagToken
|
42
43
|
# line isn't empty but didn't match tag syntax, yield and let the
|
43
44
|
# caller raise a syntax error
|
@@ -45,6 +46,12 @@ module Liquid
|
|
45
46
|
end
|
46
47
|
tag_name = Regexp.last_match(1)
|
47
48
|
markup = Regexp.last_match(2)
|
49
|
+
|
50
|
+
if tag_name == 'liquid'
|
51
|
+
parse_context.line_number -= 1
|
52
|
+
next parse_liquid_tag(markup, parse_context)
|
53
|
+
end
|
54
|
+
|
48
55
|
unless (tag = registered_tags[tag_name])
|
49
56
|
# end parsing if we reach an unknown tag and let the caller decide
|
50
57
|
# determine how to proceed
|
@@ -109,14 +116,22 @@ module Liquid
|
|
109
116
|
end
|
110
117
|
end
|
111
118
|
|
112
|
-
private def
|
119
|
+
private def handle_invalid_tag_token(token, parse_context)
|
120
|
+
if token.end_with?('%}')
|
121
|
+
yield token, token
|
122
|
+
else
|
123
|
+
BlockBody.raise_missing_tag_terminator(token, parse_context)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
private def parse_for_document(tokenizer, parse_context, &block)
|
113
128
|
while (token = tokenizer.shift)
|
114
129
|
next if token.empty?
|
115
130
|
case
|
116
131
|
when token.start_with?(TAGSTART)
|
117
132
|
whitespace_handler(token, parse_context)
|
118
133
|
unless token =~ FullToken
|
119
|
-
|
134
|
+
return handle_invalid_tag_token(token, parse_context, &block)
|
120
135
|
end
|
121
136
|
tag_name = Regexp.last_match(2)
|
122
137
|
markup = Regexp.last_match(4)
|
@@ -150,7 +165,7 @@ module Liquid
|
|
150
165
|
end
|
151
166
|
parse_context.trim_whitespace = false
|
152
167
|
@nodelist << token
|
153
|
-
@blank &&=
|
168
|
+
@blank &&= token.match?(WhitespaceOrNothing)
|
154
169
|
end
|
155
170
|
parse_context.line_number = tokenizer.line_number
|
156
171
|
end
|
data/lib/liquid/condition.rb
CHANGED
@@ -24,6 +24,9 @@ module Liquid
|
|
24
24
|
else
|
25
25
|
false
|
26
26
|
end
|
27
|
+
rescue Encoding::CompatibilityError
|
28
|
+
# "✅".b.include?("✅") raises Encoding::CompatibilityError despite being materially equal
|
29
|
+
left.b.include?(right.b)
|
27
30
|
end,
|
28
31
|
}
|
29
32
|
|
@@ -69,9 +72,9 @@ module Liquid
|
|
69
72
|
|
70
73
|
case condition.child_relation
|
71
74
|
when :or
|
72
|
-
break if result
|
75
|
+
break if Liquid::Utils.to_liquid_value(result)
|
73
76
|
when :and
|
74
|
-
break unless result
|
77
|
+
break unless Liquid::Utils.to_liquid_value(result)
|
75
78
|
else
|
76
79
|
break
|
77
80
|
end
|
@@ -159,8 +162,10 @@ module Liquid
|
|
159
162
|
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
160
163
|
def children
|
161
164
|
[
|
162
|
-
@node.left,
|
163
|
-
@node.
|
165
|
+
@node.left,
|
166
|
+
@node.right,
|
167
|
+
@node.child_condition,
|
168
|
+
@node.attachment
|
164
169
|
].compact
|
165
170
|
end
|
166
171
|
end
|
data/lib/liquid/context.rb
CHANGED
@@ -26,9 +26,9 @@ module Liquid
|
|
26
26
|
@environments = [environments]
|
27
27
|
@environments.flatten!
|
28
28
|
|
29
|
-
@static_environments = [static_environments].
|
29
|
+
@static_environments = [static_environments].flatten(1).freeze
|
30
30
|
@scopes = [(outer_scope || {})]
|
31
|
-
@registers = registers
|
31
|
+
@registers = registers.is_a?(Registers) ? registers : Registers.new(registers)
|
32
32
|
@errors = []
|
33
33
|
@partial = false
|
34
34
|
@strict_variables = false
|
@@ -39,6 +39,10 @@ module Liquid
|
|
39
39
|
@global_filter = nil
|
40
40
|
@disabled_tags = {}
|
41
41
|
|
42
|
+
@registers.static[:cached_partials] ||= {}
|
43
|
+
@registers.static[:file_system] ||= Liquid::Template.file_system
|
44
|
+
@registers.static[:template_factory] ||= Liquid::TemplateFactory.new
|
45
|
+
|
42
46
|
self.exception_renderer = Template.default_exception_renderer
|
43
47
|
if rethrow_errors
|
44
48
|
self.exception_renderer = Liquid::RAISE_EXCEPTION_LAMBDA
|
@@ -140,7 +144,7 @@ module Liquid
|
|
140
144
|
self.class.build(
|
141
145
|
resource_limits: resource_limits,
|
142
146
|
static_environments: static_environments,
|
143
|
-
registers:
|
147
|
+
registers: Registers.new(registers),
|
144
148
|
).tap do |subcontext|
|
145
149
|
subcontext.base_scope_depth = base_scope_depth + 1
|
146
150
|
subcontext.exception_renderer = exception_renderer
|
@@ -193,10 +197,14 @@ module Liquid
|
|
193
197
|
try_variable_find_in_environments(key, raise_on_not_found: raise_on_not_found)
|
194
198
|
end
|
195
199
|
|
196
|
-
variable
|
200
|
+
# update variable's context before invoking #to_liquid
|
197
201
|
variable.context = self if variable.respond_to?(:context=)
|
198
202
|
|
199
|
-
variable
|
203
|
+
liquid_variable = variable.to_liquid
|
204
|
+
|
205
|
+
liquid_variable.context = self if variable != liquid_variable && liquid_variable.respond_to?(:context=)
|
206
|
+
|
207
|
+
liquid_variable
|
200
208
|
end
|
201
209
|
|
202
210
|
def lookup_and_evaluate(obj, key, raise_on_not_found: true)
|
data/lib/liquid/drop.rb
CHANGED
data/lib/liquid/errors.rb
CHANGED
@@ -40,19 +40,20 @@ module Liquid
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
ArgumentError
|
44
|
-
ContextError
|
45
|
-
FileSystemError
|
46
|
-
StandardError
|
47
|
-
SyntaxError
|
48
|
-
StackLevelError
|
49
|
-
MemoryError
|
50
|
-
ZeroDivisionError
|
51
|
-
FloatDomainError
|
52
|
-
UndefinedVariable
|
53
|
-
UndefinedDropMethod
|
54
|
-
UndefinedFilter
|
55
|
-
MethodOverrideError
|
56
|
-
DisabledError
|
57
|
-
InternalError
|
43
|
+
ArgumentError = Class.new(Error)
|
44
|
+
ContextError = Class.new(Error)
|
45
|
+
FileSystemError = Class.new(Error)
|
46
|
+
StandardError = Class.new(Error)
|
47
|
+
SyntaxError = Class.new(Error)
|
48
|
+
StackLevelError = Class.new(Error)
|
49
|
+
MemoryError = Class.new(Error)
|
50
|
+
ZeroDivisionError = Class.new(Error)
|
51
|
+
FloatDomainError = Class.new(Error)
|
52
|
+
UndefinedVariable = Class.new(Error)
|
53
|
+
UndefinedDropMethod = Class.new(Error)
|
54
|
+
UndefinedFilter = Class.new(Error)
|
55
|
+
MethodOverrideError = Class.new(Error)
|
56
|
+
DisabledError = Class.new(Error)
|
57
|
+
InternalError = Class.new(Error)
|
58
|
+
TemplateEncodingError = Class.new(Error)
|
58
59
|
end
|
data/lib/liquid/expression.rb
CHANGED
data/lib/liquid/forloop_drop.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Liquid
|
4
|
+
# @liquid_public_docs
|
5
|
+
# @liquid_type object
|
6
|
+
# @liquid_name forloop
|
7
|
+
# @liquid_summary
|
8
|
+
# Information about a parent [`for` loop](/docs/api/liquid/tags/for).
|
4
9
|
class ForloopDrop < Drop
|
5
10
|
def initialize(name, length, parentloop)
|
6
11
|
@name = name
|
@@ -9,33 +14,68 @@ module Liquid
|
|
9
14
|
@index = 0
|
10
15
|
end
|
11
16
|
|
12
|
-
|
17
|
+
# @liquid_public_docs
|
18
|
+
# @liquid_name length
|
19
|
+
# @liquid_summary
|
20
|
+
# The total number of iterations in the loop.
|
21
|
+
# @liquid_return [number]
|
22
|
+
attr_reader :length
|
13
23
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
24
|
+
# @liquid_public_docs
|
25
|
+
# @liquid_name parentloop
|
26
|
+
# @liquid_summary
|
27
|
+
# The parent `forloop` object.
|
28
|
+
# @liquid_description
|
29
|
+
# If the current `for` loop isn't nested inside another `for` loop, then `nil` is returned.
|
30
|
+
# @liquid_return [forloop]
|
31
|
+
attr_reader :parentloop
|
32
|
+
|
33
|
+
attr_reader :name
|
18
34
|
|
35
|
+
# @liquid_public_docs
|
36
|
+
# @liquid_summary
|
37
|
+
# The 1-based index of the current iteration.
|
38
|
+
# @liquid_return [number]
|
19
39
|
def index
|
20
40
|
@index + 1
|
21
41
|
end
|
22
42
|
|
43
|
+
# @liquid_public_docs
|
44
|
+
# @liquid_summary
|
45
|
+
# The 0-based index of the current iteration.
|
46
|
+
# @liquid_return [number]
|
23
47
|
def index0
|
24
48
|
@index
|
25
49
|
end
|
26
50
|
|
51
|
+
# @liquid_public_docs
|
52
|
+
# @liquid_summary
|
53
|
+
# The 1-based index of the current iteration, in reverse order.
|
54
|
+
# @liquid_return [number]
|
27
55
|
def rindex
|
28
56
|
@length - @index
|
29
57
|
end
|
30
58
|
|
59
|
+
# @liquid_public_docs
|
60
|
+
# @liquid_summary
|
61
|
+
# The 0-based index of the current iteration, in reverse order.
|
62
|
+
# @liquid_return [number]
|
31
63
|
def rindex0
|
32
64
|
@length - @index - 1
|
33
65
|
end
|
34
66
|
|
67
|
+
# @liquid_public_docs
|
68
|
+
# @liquid_summary
|
69
|
+
# Returns `true` if the current iteration is the first. Returns `false` if not.
|
70
|
+
# @liquid_return [boolean]
|
35
71
|
def first
|
36
72
|
@index == 0
|
37
73
|
end
|
38
74
|
|
75
|
+
# @liquid_public_docs
|
76
|
+
# @liquid_summary
|
77
|
+
# Returns `true` if the current iteration is the last. Returns `false` if not.
|
78
|
+
# @liquid_return [boolean]
|
39
79
|
def last
|
40
80
|
@index == @length - 1
|
41
81
|
end
|
data/lib/liquid/lexer.rb
CHANGED
@@ -18,6 +18,7 @@ module Liquid
|
|
18
18
|
IDENTIFIER = /[a-zA-Z_][\w-]*\??/
|
19
19
|
SINGLE_STRING_LITERAL = /'[^\']*'/
|
20
20
|
DOUBLE_STRING_LITERAL = /"[^\"]*"/
|
21
|
+
STRING_LITERAL = Regexp.union(SINGLE_STRING_LITERAL, DOUBLE_STRING_LITERAL)
|
21
22
|
NUMBER_LITERAL = /-?\d+(\.\d+)?/
|
22
23
|
DOTDOT = /\.\./
|
23
24
|
COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains(?=\s)/
|
@@ -35,9 +36,7 @@ module Liquid
|
|
35
36
|
break if @ss.eos?
|
36
37
|
tok = if (t = @ss.scan(COMPARISON_OPERATOR))
|
37
38
|
[:comparison, t]
|
38
|
-
elsif (t = @ss.scan(
|
39
|
-
[:string, t]
|
40
|
-
elsif (t = @ss.scan(DOUBLE_STRING_LITERAL))
|
39
|
+
elsif (t = @ss.scan(STRING_LITERAL))
|
41
40
|
[:string, t]
|
42
41
|
elsif (t = @ss.scan(NUMBER_LITERAL))
|
43
42
|
[:number, t]
|
data/lib/liquid/locales/en.yml
CHANGED
@@ -13,15 +13,17 @@
|
|
13
13
|
for_invalid_attribute: "Invalid attribute in for loop. Valid attributes are limit and offset"
|
14
14
|
if: "Syntax Error in tag 'if' - Valid syntax: if [expression]"
|
15
15
|
include: "Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]"
|
16
|
-
|
16
|
+
inline_comment_invalid: "Syntax error in tag '#' - Each line of comments must be prefixed by the '#' character"
|
17
17
|
invalid_delimiter: "'%{tag}' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}"
|
18
|
+
invalid_template_encoding: "Invalid template encoding"
|
19
|
+
render: "Syntax error in tag 'render' - Template name must be a quoted string"
|
20
|
+
table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
|
21
|
+
tag_never_closed: "'%{block_name}' tag was never closed"
|
22
|
+
tag_termination: "Tag '%{token}' was not properly terminated with regexp: %{tag_end}"
|
18
23
|
unexpected_else: "%{block_name} tag does not expect 'else' tag"
|
19
24
|
unexpected_outer_tag: "Unexpected outer '%{tag}' tag"
|
20
|
-
|
25
|
+
unknown_tag: "Unknown tag '%{tag}'"
|
21
26
|
variable_termination: "Variable '%{token}' was not properly terminated with regexp: %{tag_end}"
|
22
|
-
tag_never_closed: "'%{block_name}' tag was never closed"
|
23
|
-
table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
|
24
|
-
render: "Syntax error in tag 'render' - Template name must be a quoted string"
|
25
27
|
argument:
|
26
28
|
include: "Argument error in tag 'include' - Illegal template name"
|
27
29
|
disabled:
|
data/lib/liquid/partial_cache.rb
CHANGED
@@ -3,20 +3,29 @@
|
|
3
3
|
module Liquid
|
4
4
|
class PartialCache
|
5
5
|
def self.load(template_name, context:, parse_context:)
|
6
|
-
cached_partials =
|
7
|
-
|
6
|
+
cached_partials = context.registers[:cached_partials]
|
7
|
+
cache_key = "#{template_name}:#{parse_context.error_mode}"
|
8
|
+
cached = cached_partials[cache_key]
|
8
9
|
return cached if cached
|
9
10
|
|
10
|
-
file_system =
|
11
|
+
file_system = context.registers[:file_system]
|
11
12
|
source = file_system.read_template_file(template_name)
|
12
13
|
|
13
14
|
parse_context.partial = true
|
14
15
|
|
15
|
-
template_factory =
|
16
|
+
template_factory = context.registers[:template_factory]
|
16
17
|
template = template_factory.for(template_name)
|
17
18
|
|
18
|
-
|
19
|
-
|
19
|
+
begin
|
20
|
+
partial = template.parse(source, parse_context)
|
21
|
+
rescue Liquid::Error => e
|
22
|
+
e.template_name = template&.name || template_name
|
23
|
+
raise e
|
24
|
+
end
|
25
|
+
|
26
|
+
partial.name ||= template_name
|
27
|
+
|
28
|
+
cached_partials[cache_key] = partial
|
20
29
|
ensure
|
21
30
|
parse_context.partial = false
|
22
31
|
end
|
data/lib/liquid/range_lookup.rb
CHANGED
@@ -8,7 +8,17 @@ module Liquid
|
|
8
8
|
if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
|
9
9
|
new(start_obj, end_obj)
|
10
10
|
else
|
11
|
-
|
11
|
+
begin
|
12
|
+
start_obj.to_i..end_obj.to_i
|
13
|
+
rescue NoMethodError
|
14
|
+
invalid_expr = start_markup unless start_obj.respond_to?(:to_i)
|
15
|
+
invalid_expr ||= end_markup unless end_obj.respond_to?(:to_i)
|
16
|
+
if invalid_expr
|
17
|
+
raise Liquid::SyntaxError, "Invalid expression type '#{invalid_expr}' in range expression"
|
18
|
+
end
|
19
|
+
|
20
|
+
raise
|
21
|
+
end
|
12
22
|
end
|
13
23
|
end
|
14
24
|
|
@@ -1,35 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Liquid
|
4
|
-
class
|
4
|
+
class Registers
|
5
5
|
attr_reader :static
|
6
6
|
|
7
7
|
def initialize(registers = {})
|
8
|
-
@static
|
9
|
-
@
|
8
|
+
@static = registers.is_a?(Registers) ? registers.static : registers
|
9
|
+
@changes = {}
|
10
10
|
end
|
11
11
|
|
12
12
|
def []=(key, value)
|
13
|
-
@
|
13
|
+
@changes[key] = value
|
14
14
|
end
|
15
15
|
|
16
16
|
def [](key)
|
17
|
-
if @
|
18
|
-
@
|
17
|
+
if @changes.key?(key)
|
18
|
+
@changes[key]
|
19
19
|
else
|
20
20
|
@static[key]
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
def delete(key)
|
25
|
-
@
|
25
|
+
@changes.delete(key)
|
26
26
|
end
|
27
27
|
|
28
28
|
UNDEFINED = Object.new
|
29
29
|
|
30
30
|
def fetch(key, default = UNDEFINED, &block)
|
31
|
-
if @
|
32
|
-
@
|
31
|
+
if @changes.key?(key)
|
32
|
+
@changes.fetch(key)
|
33
33
|
elsif default != UNDEFINED
|
34
34
|
if block_given?
|
35
35
|
@static.fetch(key, &block)
|
@@ -42,7 +42,10 @@ module Liquid
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def key?(key)
|
45
|
-
@
|
45
|
+
@changes.key?(key) || @static.key?(key)
|
46
46
|
end
|
47
47
|
end
|
48
|
+
|
49
|
+
# Alias for backwards compatibility
|
50
|
+
StaticRegisters = Registers
|
48
51
|
end
|