mirror of
https://github.com/Shopify/liquid.git
synced 2025-08-16 00:00:29 -04:00
Compare commits
57 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a0411e0927 | ||
|
ed421202e2 | ||
|
d6ca569e8a | ||
|
d36937d17f | ||
|
77bc56a1c2 | ||
|
88d013c8da | ||
|
36c7fc8e07 | ||
|
2b4810006b | ||
|
fc4f19471e | ||
|
8596bb2e38 | ||
|
6bf18775e7 | ||
|
56a0b7c42b | ||
|
dba733084e | ||
|
4a4fe3c72a | ||
|
4f35b0bc66 | ||
|
a5e5fab82a | ||
|
02ecaab9d1 | ||
|
1b2b62964e | ||
|
9b38a15282 | ||
|
7b25b770af | ||
|
cf76c0bbec | ||
|
6a0fe3f7e3 | ||
|
730ad3684a | ||
|
3ac7e470e6 | ||
|
369a6c55e3 | ||
|
f5ed5404b5 | ||
|
a3c837687e | ||
|
96a036372d | ||
|
4924822c88 | ||
|
fc9c338682 | ||
|
3c5ad7db61 | ||
|
c658bf970a | ||
|
c618ac1c9f | ||
|
0f0d5d889f | ||
|
11a1f8e673 | ||
|
24d461a9e3 | ||
|
cbb422e5d3 | ||
|
bf0f79f36c | ||
|
cf2787791e | ||
|
6a5ebb0e85 | ||
|
6dafc19b6d | ||
|
41f65173b0 | ||
|
e180535784 | ||
|
a681e73aec | ||
|
2abf52d546 | ||
|
eada2b65a2 | ||
|
dbf0aa8cd5 | ||
|
407a8e5b0f | ||
|
e3dcc75ab5 | ||
|
ceb7a4237f | ||
|
7b60b7fef5 | ||
|
33e1a8ffbc | ||
|
cc47fa8f03 | ||
|
b1b9b9f691 | ||
|
75e7725f57 | ||
|
de6d15a73e | ||
|
2c5d2be193 |
5
.github/workflows/liquid.yml
vendored
5
.github/workflows/liquid.yml
vendored
@ -12,7 +12,8 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
entry:
|
entry:
|
||||||
- { ruby: 2.7, allowed-failure: false } # minimum supported
|
- { ruby: 2.7, allowed-failure: false } # minimum supported
|
||||||
- { ruby: 3.2, allowed-failure: false } # latest
|
- { ruby: 3.2, allowed-failure: false }
|
||||||
|
- { ruby: 3.3, allowed-failure: false } # latest
|
||||||
- { ruby: ruby-head, allowed-failure: true }
|
- { ruby: ruby-head, allowed-failure: true }
|
||||||
name: Test Ruby ${{ matrix.entry.ruby }}
|
name: Test Ruby ${{ matrix.entry.ruby }}
|
||||||
steps:
|
steps:
|
||||||
@ -21,6 +22,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.entry.ruby }}
|
ruby-version: ${{ matrix.entry.ruby }}
|
||||||
bundler-cache: true
|
bundler-cache: true
|
||||||
|
bundler: latest
|
||||||
- run: bundle exec rake
|
- run: bundle exec rake
|
||||||
continue-on-error: ${{ matrix.entry.allowed-failure }}
|
continue-on-error: ${{ matrix.entry.allowed-failure }}
|
||||||
|
|
||||||
@ -30,6 +32,5 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ruby/setup-ruby@v1
|
- uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 2.7
|
|
||||||
bundler-cache: true
|
bundler-cache: true
|
||||||
- run: bundle exec rake memory_profile:run
|
- run: bundle exec rake memory_profile:run
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,7 +4,5 @@
|
|||||||
pkg
|
pkg
|
||||||
*.rbc
|
*.rbc
|
||||||
.rvmrc
|
.rvmrc
|
||||||
.ruby-version
|
|
||||||
Gemfile.lock
|
|
||||||
.bundle
|
.bundle
|
||||||
.byebug_history
|
.byebug_history
|
||||||
|
@ -10,7 +10,6 @@ Performance:
|
|||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
TargetRubyVersion: 2.7
|
|
||||||
NewCops: disable
|
NewCops: disable
|
||||||
SuggestExtensions: false
|
SuggestExtensions: false
|
||||||
Exclude:
|
Exclude:
|
||||||
|
1
.ruby-version
Normal file
1
.ruby-version
Normal file
@ -0,0 +1 @@
|
|||||||
|
3.3.0
|
6
Gemfile
6
Gemfile
@ -7,6 +7,8 @@ end
|
|||||||
|
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
|
gem "base64"
|
||||||
|
|
||||||
group :benchmark, :test do
|
group :benchmark, :test do
|
||||||
gem 'benchmark-ips'
|
gem 'benchmark-ips'
|
||||||
gem 'memory_profiler'
|
gem 'memory_profiler'
|
||||||
@ -18,11 +20,11 @@ group :benchmark, :test do
|
|||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'rubocop', '~> 1.44.0'
|
gem 'rubocop', '~> 1.61.0'
|
||||||
gem 'rubocop-shopify', '~> 2.12.0', require: false
|
gem 'rubocop-shopify', '~> 2.12.0', require: false
|
||||||
gem 'rubocop-performance', require: false
|
gem 'rubocop-performance', require: false
|
||||||
|
|
||||||
platform :mri, :truffleruby do
|
platform :mri, :truffleruby do
|
||||||
gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'master'
|
gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'main'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
75
Gemfile.lock
Normal file
75
Gemfile.lock
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
GIT
|
||||||
|
remote: https://github.com/Shopify/liquid-c.git
|
||||||
|
revision: 5a786af7284df55e013ea20551c4b688d02e8326
|
||||||
|
ref: main
|
||||||
|
specs:
|
||||||
|
liquid-c (4.2.0)
|
||||||
|
liquid (>= 5.0.1)
|
||||||
|
|
||||||
|
PATH
|
||||||
|
remote: .
|
||||||
|
specs:
|
||||||
|
liquid (5.5.1)
|
||||||
|
|
||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
ast (2.4.2)
|
||||||
|
base64 (0.2.0)
|
||||||
|
benchmark-ips (2.13.0)
|
||||||
|
json (2.7.2)
|
||||||
|
language_server-protocol (3.17.0.3)
|
||||||
|
memory_profiler (1.0.1)
|
||||||
|
minitest (5.22.3)
|
||||||
|
parallel (1.24.0)
|
||||||
|
parser (3.3.0.5)
|
||||||
|
ast (~> 2.4.1)
|
||||||
|
racc
|
||||||
|
racc (1.7.3)
|
||||||
|
rainbow (3.1.1)
|
||||||
|
rake (13.2.1)
|
||||||
|
regexp_parser (2.9.0)
|
||||||
|
rexml (3.2.6)
|
||||||
|
rubocop (1.61.0)
|
||||||
|
json (~> 2.3)
|
||||||
|
language_server-protocol (>= 3.17.0)
|
||||||
|
parallel (~> 1.10)
|
||||||
|
parser (>= 3.3.0.2)
|
||||||
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
|
regexp_parser (>= 1.8, < 3.0)
|
||||||
|
rexml (>= 3.2.5, < 4.0)
|
||||||
|
rubocop-ast (>= 1.30.0, < 2.0)
|
||||||
|
ruby-progressbar (~> 1.7)
|
||||||
|
unicode-display_width (>= 2.4.0, < 3.0)
|
||||||
|
rubocop-ast (1.31.2)
|
||||||
|
parser (>= 3.3.0.4)
|
||||||
|
rubocop-performance (1.19.1)
|
||||||
|
rubocop (>= 1.7.0, < 2.0)
|
||||||
|
rubocop-ast (>= 0.4.0)
|
||||||
|
rubocop-shopify (2.12.0)
|
||||||
|
rubocop (~> 1.44)
|
||||||
|
ruby-progressbar (1.13.0)
|
||||||
|
stackprof (0.2.26)
|
||||||
|
terminal-table (3.0.2)
|
||||||
|
unicode-display_width (>= 1.1.1, < 3)
|
||||||
|
unicode-display_width (2.5.0)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
base64
|
||||||
|
benchmark-ips
|
||||||
|
liquid!
|
||||||
|
liquid-c!
|
||||||
|
memory_profiler
|
||||||
|
minitest
|
||||||
|
rake (~> 13.0)
|
||||||
|
rubocop (~> 1.61.0)
|
||||||
|
rubocop-performance
|
||||||
|
rubocop-shopify (~> 2.12.0)
|
||||||
|
stackprof
|
||||||
|
terminal-table
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.5.7
|
@ -1,5 +1,9 @@
|
|||||||
# Liquid Change Log
|
# Liquid Change Log
|
||||||
|
|
||||||
|
## 5.5.0 2024-03-21
|
||||||
|
|
||||||
|
Please reference the GitHub release for more information.
|
||||||
|
|
||||||
## 5.4.0 2022-07-29
|
## 5.4.0 2022-07-29
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
[](http://travis-ci.org/Shopify/liquid)
|
[](https://github.com/Shopify/liquid/actions/workflows/liquid.yml)
|
||||||
[](http://inch-ci.org/github/Shopify/liquid)
|
[](http://inch-ci.org/github/Shopify/liquid)
|
||||||
|
|
||||||
# Liquid template engine
|
# Liquid template engine
|
||||||
|
@ -6,6 +6,7 @@ module Liquid
|
|||||||
class BlockBody
|
class BlockBody
|
||||||
LiquidTagToken = /\A\s*(#{TagName})\s*(.*?)\z/o
|
LiquidTagToken = /\A\s*(#{TagName})\s*(.*?)\z/o
|
||||||
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(#{TagName})(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(#{TagName})(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
||||||
|
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}#{WhitespaceControl}?\s*(\w+)\s*(.*)?#{WhitespaceControl}?#{TagEnd}\z/om
|
||||||
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
|
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
|
||||||
WhitespaceOrNothing = /\A\s*\z/
|
WhitespaceOrNothing = /\A\s*\z/
|
||||||
TAGSTART = "{%"
|
TAGSTART = "{%"
|
||||||
|
@ -24,6 +24,9 @@ module Liquid
|
|||||||
else
|
else
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
rescue Encoding::CompatibilityError
|
||||||
|
# "✅".b.include?("✅") raises Encoding::CompatibilityError despite being materially equal
|
||||||
|
left.b.include?(right.b)
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ module Liquid
|
|||||||
@environments.flatten!
|
@environments.flatten!
|
||||||
|
|
||||||
@static_environments = [static_environments].flatten(1).freeze
|
@static_environments = [static_environments].flatten(1).freeze
|
||||||
@scopes = [(outer_scope || {})]
|
@scopes = [outer_scope || {}]
|
||||||
@registers = registers.is_a?(Registers) ? registers : Registers.new(registers)
|
@registers = registers.is_a?(Registers) ? registers : Registers.new(registers)
|
||||||
@errors = []
|
@errors = []
|
||||||
@partial = false
|
@partial = false
|
||||||
@ -197,10 +197,14 @@ module Liquid
|
|||||||
try_variable_find_in_environments(key, raise_on_not_found: raise_on_not_found)
|
try_variable_find_in_environments(key, raise_on_not_found: raise_on_not_found)
|
||||||
end
|
end
|
||||||
|
|
||||||
variable = variable.to_liquid
|
# update variable's context before invoking #to_liquid
|
||||||
variable.context = self if variable.respond_to?(:context=)
|
variable.context = self if variable.respond_to?(:context=)
|
||||||
|
|
||||||
variable
|
liquid_variable = variable.to_liquid
|
||||||
|
|
||||||
|
liquid_variable.context = self if variable != liquid_variable && liquid_variable.respond_to?(:context=)
|
||||||
|
|
||||||
|
liquid_variable
|
||||||
end
|
end
|
||||||
|
|
||||||
def lookup_and_evaluate(obj, key, raise_on_not_found: true)
|
def lookup_and_evaluate(obj, key, raise_on_not_found: true)
|
||||||
|
@ -40,19 +40,20 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
ArgumentError = Class.new(Error)
|
ArgumentError = Class.new(Error)
|
||||||
ContextError = Class.new(Error)
|
ContextError = Class.new(Error)
|
||||||
FileSystemError = Class.new(Error)
|
FileSystemError = Class.new(Error)
|
||||||
StandardError = Class.new(Error)
|
StandardError = Class.new(Error)
|
||||||
SyntaxError = Class.new(Error)
|
SyntaxError = Class.new(Error)
|
||||||
StackLevelError = Class.new(Error)
|
StackLevelError = Class.new(Error)
|
||||||
MemoryError = Class.new(Error)
|
MemoryError = Class.new(Error)
|
||||||
ZeroDivisionError = Class.new(Error)
|
ZeroDivisionError = Class.new(Error)
|
||||||
FloatDomainError = Class.new(Error)
|
FloatDomainError = Class.new(Error)
|
||||||
UndefinedVariable = Class.new(Error)
|
UndefinedVariable = Class.new(Error)
|
||||||
UndefinedDropMethod = Class.new(Error)
|
UndefinedDropMethod = Class.new(Error)
|
||||||
UndefinedFilter = Class.new(Error)
|
UndefinedFilter = Class.new(Error)
|
||||||
MethodOverrideError = Class.new(Error)
|
MethodOverrideError = Class.new(Error)
|
||||||
DisabledError = Class.new(Error)
|
DisabledError = Class.new(Error)
|
||||||
InternalError = Class.new(Error)
|
InternalError = Class.new(Error)
|
||||||
|
TemplateEncodingError = Class.new(Error)
|
||||||
end
|
end
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
include: "Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]"
|
include: "Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]"
|
||||||
inline_comment_invalid: "Syntax error in tag '#' - Each line of comments must be prefixed by the '#' character"
|
inline_comment_invalid: "Syntax error in tag '#' - Each line of comments must be prefixed by the '#' character"
|
||||||
invalid_delimiter: "'%{tag}' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}"
|
invalid_delimiter: "'%{tag}' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}"
|
||||||
|
invalid_template_encoding: "Invalid template encoding"
|
||||||
render: "Syntax error in tag 'render' - Template name must be a quoted string"
|
render: "Syntax error in tag 'render' - Template name must be a quoted string"
|
||||||
table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
|
table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
|
||||||
tag_never_closed: "'%{block_name}' tag was never closed"
|
tag_never_closed: "'%{block_name}' tag was never closed"
|
||||||
|
@ -29,6 +29,19 @@ module Liquid
|
|||||||
)
|
)
|
||||||
STRIP_HTML_TAGS = /<.*?>/m
|
STRIP_HTML_TAGS = /<.*?>/m
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def try_coerce_encoding(input, encoding:)
|
||||||
|
original_encoding = input.encoding
|
||||||
|
if input.encoding != encoding
|
||||||
|
input.force_encoding(encoding)
|
||||||
|
unless input.valid_encoding?
|
||||||
|
input.force_encoding(original_encoding)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
input
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# @liquid_public_docs
|
# @liquid_public_docs
|
||||||
# @liquid_type filter
|
# @liquid_type filter
|
||||||
# @liquid_category array
|
# @liquid_category array
|
||||||
@ -150,7 +163,8 @@ module Liquid
|
|||||||
# @liquid_syntax string | base64_decode
|
# @liquid_syntax string | base64_decode
|
||||||
# @liquid_return [string]
|
# @liquid_return [string]
|
||||||
def base64_decode(input)
|
def base64_decode(input)
|
||||||
Base64.strict_decode64(input.to_s)
|
input = input.to_s
|
||||||
|
StandardFilters.try_coerce_encoding(Base64.strict_decode64(input), encoding: input.encoding)
|
||||||
rescue ::ArgumentError
|
rescue ::ArgumentError
|
||||||
raise Liquid::ArgumentError, "invalid base64 provided to base64_decode"
|
raise Liquid::ArgumentError, "invalid base64 provided to base64_decode"
|
||||||
end
|
end
|
||||||
@ -174,7 +188,8 @@ module Liquid
|
|||||||
# @liquid_syntax string | base64_url_safe_decode
|
# @liquid_syntax string | base64_url_safe_decode
|
||||||
# @liquid_return [string]
|
# @liquid_return [string]
|
||||||
def base64_url_safe_decode(input)
|
def base64_url_safe_decode(input)
|
||||||
Base64.urlsafe_decode64(input.to_s)
|
input = input.to_s
|
||||||
|
StandardFilters.try_coerce_encoding(Base64.urlsafe_decode64(input), encoding: input.encoding)
|
||||||
rescue ::ArgumentError
|
rescue ::ArgumentError
|
||||||
raise Liquid::ArgumentError, "invalid base64 provided to base64_url_safe_decode"
|
raise Liquid::ArgumentError, "invalid base64 provided to base64_url_safe_decode"
|
||||||
end
|
end
|
||||||
@ -892,9 +907,11 @@ module Liquid
|
|||||||
raise_property_error(property)
|
raise_property_error(property)
|
||||||
end
|
end
|
||||||
|
|
||||||
InputIterator.new(values_for_sum, context).sum do |item|
|
result = InputIterator.new(values_for_sum, context).sum do |item|
|
||||||
Utils.to_number(item)
|
Utils.to_number(item)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
result.is_a?(BigDecimal) ? result.to_f : result
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@ -927,6 +944,8 @@ module Liquid
|
|||||||
def nil_safe_casecmp(a, b)
|
def nil_safe_casecmp(a, b)
|
||||||
if !a.nil? && !b.nil?
|
if !a.nil? && !b.nil?
|
||||||
a.to_s.casecmp(b.to_s)
|
a.to_s.casecmp(b.to_s)
|
||||||
|
elsif a.nil? && b.nil?
|
||||||
|
0
|
||||||
else
|
else
|
||||||
a.nil? ? 1 : -1
|
a.nil? ? 1 : -1
|
||||||
end
|
end
|
||||||
|
@ -54,7 +54,8 @@ module Liquid
|
|||||||
# of the `render_to_output_buffer` method will become the default and the `render`
|
# of the `render_to_output_buffer` method will become the default and the `render`
|
||||||
# method will be removed.
|
# method will be removed.
|
||||||
def render_to_output_buffer(context, output)
|
def render_to_output_buffer(context, output)
|
||||||
output << render(context)
|
render_result = render(context)
|
||||||
|
output << render_result if render_result
|
||||||
output
|
output
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -25,6 +25,65 @@ module Liquid
|
|||||||
def blank?
|
def blank?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def parse_body(body, tokenizer)
|
||||||
|
if parse_context.depth >= MAX_DEPTH
|
||||||
|
raise StackLevelError, "Nesting too deep"
|
||||||
|
end
|
||||||
|
|
||||||
|
parse_context.depth += 1
|
||||||
|
comment_tag_depth = 1
|
||||||
|
|
||||||
|
begin
|
||||||
|
# Consume tokens without creating child nodes.
|
||||||
|
# The children tag doesn't require to be a valid Liquid except the comment and raw tag.
|
||||||
|
# The child comment and raw tag must be closed.
|
||||||
|
while (token = tokenizer.send(:shift))
|
||||||
|
tag_name = if tokenizer.for_liquid_tag
|
||||||
|
next if token.empty? || token.match?(BlockBody::WhitespaceOrNothing)
|
||||||
|
|
||||||
|
tag_name_match = BlockBody::LiquidTagToken.match(token)
|
||||||
|
|
||||||
|
next if tag_name_match.nil?
|
||||||
|
|
||||||
|
tag_name_match[1]
|
||||||
|
else
|
||||||
|
token =~ BlockBody::FullToken
|
||||||
|
Regexp.last_match(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
case tag_name
|
||||||
|
when "raw"
|
||||||
|
parse_raw_tag_body(tokenizer)
|
||||||
|
when "comment"
|
||||||
|
comment_tag_depth += 1
|
||||||
|
when "endcomment"
|
||||||
|
comment_tag_depth -= 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if comment_tag_depth.zero?
|
||||||
|
parse_context.trim_whitespace = (token[-3] == WhitespaceControl) unless tokenizer.for_liquid_tag
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
raise_tag_never_closed(block_name)
|
||||||
|
ensure
|
||||||
|
parse_context.depth -= 1
|
||||||
|
end
|
||||||
|
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_raw_tag_body(tokenizer)
|
||||||
|
while (token = tokenizer.send(:shift))
|
||||||
|
return if token =~ BlockBody::FullTokenPossiblyInvalid && "endraw" == Regexp.last_match(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
raise_tag_never_closed("raw")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Template.register_tag('comment', Comment)
|
Template.register_tag('comment', Comment)
|
||||||
|
@ -26,14 +26,20 @@ module Liquid
|
|||||||
when NamedSyntax
|
when NamedSyntax
|
||||||
@variables = variables_from_string(Regexp.last_match(2))
|
@variables = variables_from_string(Regexp.last_match(2))
|
||||||
@name = parse_expression(Regexp.last_match(1))
|
@name = parse_expression(Regexp.last_match(1))
|
||||||
|
@is_named = true
|
||||||
when SimpleSyntax
|
when SimpleSyntax
|
||||||
@variables = variables_from_string(markup)
|
@variables = variables_from_string(markup)
|
||||||
@name = @variables.to_s
|
@name = @variables.to_s
|
||||||
|
@is_named = !@name.match?(/\w+:0x\h{8}/)
|
||||||
else
|
else
|
||||||
raise SyntaxError, options[:locale].t("errors.syntax.cycle")
|
raise SyntaxError, options[:locale].t("errors.syntax.cycle")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def named?
|
||||||
|
@is_named
|
||||||
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render_to_output_buffer(context, output)
|
||||||
context.registers[:cycle] ||= {}
|
context.registers[:cycle] ||= {}
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ module Liquid
|
|||||||
def parse_binary_comparisons(p)
|
def parse_binary_comparisons(p)
|
||||||
condition = parse_comparison(p)
|
condition = parse_comparison(p)
|
||||||
first_condition = condition
|
first_condition = condition
|
||||||
while (op = (p.id?('and') || p.id?('or')))
|
while (op = p.id?('and') || p.id?('or'))
|
||||||
child_condition = parse_comparison(p)
|
child_condition = parse_comparison(p)
|
||||||
condition.send(op, child_condition)
|
condition.send(op, child_condition)
|
||||||
condition = child_condition
|
condition = child_condition
|
||||||
|
@ -14,7 +14,6 @@ module Liquid
|
|||||||
# @liquid_syntax_keyword expression The expression to be output without being rendered.
|
# @liquid_syntax_keyword expression The expression to be output without being rendered.
|
||||||
class Raw < Block
|
class Raw < Block
|
||||||
Syntax = /\A\s*\z/
|
Syntax = /\A\s*\z/
|
||||||
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}#{WhitespaceControl}?\s*(\w+)\s*(.*)?#{WhitespaceControl}?#{TagEnd}\z/om
|
|
||||||
|
|
||||||
def initialize(tag_name, markup, parse_context)
|
def initialize(tag_name, markup, parse_context)
|
||||||
super
|
super
|
||||||
@ -25,7 +24,7 @@ module Liquid
|
|||||||
def parse(tokens)
|
def parse(tokens)
|
||||||
@body = +''
|
@body = +''
|
||||||
while (token = tokens.shift)
|
while (token = tokens.shift)
|
||||||
if token =~ FullTokenPossiblyInvalid && block_delimiter == Regexp.last_match(2)
|
if token =~ BlockBody::FullTokenPossiblyInvalid && block_delimiter == Regexp.last_match(2)
|
||||||
parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
|
parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
|
||||||
@body << Regexp.last_match(1) if Regexp.last_match(1) != ""
|
@body << Regexp.last_match(1) if Regexp.last_match(1) != ""
|
||||||
return
|
return
|
||||||
|
@ -107,6 +107,12 @@ module Liquid
|
|||||||
# Returns self for easy chaining
|
# Returns self for easy chaining
|
||||||
def parse(source, options = {})
|
def parse(source, options = {})
|
||||||
parse_context = configure_options(options)
|
parse_context = configure_options(options)
|
||||||
|
source = source.to_s.to_str
|
||||||
|
|
||||||
|
unless source.valid_encoding?
|
||||||
|
raise TemplateEncodingError, parse_context.locale.t("errors.syntax.invalid_template_encoding")
|
||||||
|
end
|
||||||
|
|
||||||
tokenizer = parse_context.new_tokenizer(source, start_line_number: @line_numbers && 1)
|
tokenizer = parse_context.new_tokenizer(source, start_line_number: @line_numbers && 1)
|
||||||
@root = Document.parse(tokenizer, parse_context)
|
@root = Document.parse(tokenizer, parse_context)
|
||||||
self
|
self
|
||||||
|
@ -5,7 +5,7 @@ module Liquid
|
|||||||
attr_reader :line_number, :for_liquid_tag
|
attr_reader :line_number, :for_liquid_tag
|
||||||
|
|
||||||
def initialize(source, line_numbers = false, line_number: nil, for_liquid_tag: false)
|
def initialize(source, line_numbers = false, line_number: nil, for_liquid_tag: false)
|
||||||
@source = source.to_s.to_str
|
@source = source
|
||||||
@line_number = line_number || (line_numbers ? 1 : nil)
|
@line_number = line_number || (line_numbers ? 1 : nil)
|
||||||
@for_liquid_tag = for_liquid_tag
|
@for_liquid_tag = for_liquid_tag
|
||||||
@offset = 0
|
@offset = 0
|
||||||
|
@ -2,5 +2,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
VERSION = "5.4.0"
|
VERSION = "5.5.1"
|
||||||
end
|
end
|
||||||
|
@ -36,6 +36,24 @@ class Category
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class ProductsDrop < Liquid::Drop
|
||||||
|
def initialize(products)
|
||||||
|
@products = products
|
||||||
|
end
|
||||||
|
|
||||||
|
def size
|
||||||
|
@products.size
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_liquid
|
||||||
|
if @context["forloop"]
|
||||||
|
@products.first(@context["forloop"].length)
|
||||||
|
else
|
||||||
|
@products
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class CategoryDrop < Liquid::Drop
|
class CategoryDrop < Liquid::Drop
|
||||||
attr_accessor :category, :context
|
attr_accessor :category, :context
|
||||||
|
|
||||||
@ -635,6 +653,25 @@ class ContextTest < Minitest::Test
|
|||||||
assert_equal(:my_value, c.registers[:my_register])
|
assert_equal(:my_value, c.registers[:my_register])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_variable_to_liquid_returns_contextual_drop
|
||||||
|
context = {
|
||||||
|
"products" => ProductsDrop.new(["A", "B", "C", "D", "E"]),
|
||||||
|
}
|
||||||
|
|
||||||
|
template = Liquid::Template.parse(<<~LIQUID)
|
||||||
|
{%- for i in (1..3) -%}
|
||||||
|
for_loop_products_count: {{ products | size }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
unscoped_products_count: {{ products | size }}
|
||||||
|
LIQUID
|
||||||
|
|
||||||
|
result = template.render(context)
|
||||||
|
|
||||||
|
assert_includes(result, "for_loop_products_count: 3")
|
||||||
|
assert_includes(result, "unscoped_products_count: 5")
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def assert_no_object_allocations
|
def assert_no_object_allocations
|
||||||
|
@ -176,7 +176,17 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_base64_decode
|
def test_base64_decode
|
||||||
assert_equal('one two three', @filters.base64_decode('b25lIHR3byB0aHJlZQ=='))
|
decoded = @filters.base64_decode('b25lIHR3byB0aHJlZQ==')
|
||||||
|
assert_equal('one two three', decoded)
|
||||||
|
assert_equal(Encoding::UTF_8, decoded.encoding)
|
||||||
|
|
||||||
|
decoded = @filters.base64_decode('4pyF')
|
||||||
|
assert_equal('✅', decoded)
|
||||||
|
assert_equal(Encoding::UTF_8, decoded.encoding)
|
||||||
|
|
||||||
|
decoded = @filters.base64_decode("/w==")
|
||||||
|
assert_equal(Encoding::ASCII_8BIT, decoded.encoding)
|
||||||
|
assert_equal((+"\xFF").force_encoding(Encoding::ASCII_8BIT), decoded)
|
||||||
|
|
||||||
exception = assert_raises(Liquid::ArgumentError) do
|
exception = assert_raises(Liquid::ArgumentError) do
|
||||||
@filters.base64_decode("invalidbase64")
|
@filters.base64_decode("invalidbase64")
|
||||||
@ -194,10 +204,21 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_base64_url_safe_decode
|
def test_base64_url_safe_decode
|
||||||
|
decoded = @filters.base64_url_safe_decode('YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMTIzNDU2Nzg5MCAhQCMkJV4mKigpLT1fKy8_Ljo7W117fVx8')
|
||||||
assert_equal(
|
assert_equal(
|
||||||
'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 !@#$%^&*()-=_+/?.:;[]{}\|',
|
'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 !@#$%^&*()-=_+/?.:;[]{}\|',
|
||||||
@filters.base64_url_safe_decode('YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMTIzNDU2Nzg5MCAhQCMkJV4mKigpLT1fKy8_Ljo7W117fVx8'),
|
decoded,
|
||||||
)
|
)
|
||||||
|
assert_equal(Encoding::UTF_8, decoded.encoding)
|
||||||
|
|
||||||
|
decoded = @filters.base64_url_safe_decode('4pyF')
|
||||||
|
assert_equal('✅', decoded)
|
||||||
|
assert_equal(Encoding::UTF_8, decoded.encoding)
|
||||||
|
|
||||||
|
decoded = @filters.base64_url_safe_decode("_w==")
|
||||||
|
assert_equal(Encoding::ASCII_8BIT, decoded.encoding)
|
||||||
|
assert_equal((+"\xFF").force_encoding(Encoding::ASCII_8BIT), decoded)
|
||||||
|
|
||||||
exception = assert_raises(Liquid::ArgumentError) do
|
exception = assert_raises(Liquid::ArgumentError) do
|
||||||
@filters.base64_url_safe_decode("invalidbase64")
|
@filters.base64_url_safe_decode("invalidbase64")
|
||||||
end
|
end
|
||||||
@ -310,8 +331,8 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
{ "price" => "1", "handle" => "gamma" },
|
{ "price" => "1", "handle" => "gamma" },
|
||||||
{ "price" => 2, "handle" => "epsilon" },
|
{ "price" => 2, "handle" => "epsilon" },
|
||||||
{ "price" => "4", "handle" => "alpha" },
|
{ "price" => "4", "handle" => "alpha" },
|
||||||
{ "handle" => "delta" },
|
|
||||||
{ "handle" => "beta" },
|
{ "handle" => "beta" },
|
||||||
|
{ "handle" => "delta" },
|
||||||
]
|
]
|
||||||
assert_equal(expectation, @filters.sort_natural(input, "price"))
|
assert_equal(expectation, @filters.sort_natural(input, "price"))
|
||||||
end
|
end
|
||||||
@ -994,6 +1015,42 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert(t.foo > 0)
|
assert(t.foo > 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_sum_of_floats
|
||||||
|
input = [0.1, 0.2, 0.3]
|
||||||
|
assert_equal(0.6, @filters.sum(input))
|
||||||
|
assert_template_result("0.6", "{{ input | sum }}", { "input" => input })
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_sum_of_negative_floats
|
||||||
|
input = [0.1, 0.2, -0.3]
|
||||||
|
assert_equal(0.0, @filters.sum(input))
|
||||||
|
assert_template_result("0.0", "{{ input | sum }}", { "input" => input })
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_sum_with_float_strings
|
||||||
|
input = [0.1, "0.2", "0.3"]
|
||||||
|
assert_equal(0.6, @filters.sum(input))
|
||||||
|
assert_template_result("0.6", "{{ input | sum }}", { "input" => input })
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_sum_resulting_in_negative_float
|
||||||
|
input = [0.1, -0.2, -0.3]
|
||||||
|
assert_equal(-0.4, @filters.sum(input))
|
||||||
|
assert_template_result("-0.4", "{{ input | sum }}", { "input" => input })
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_sum_with_floats_and_indexable_map_values
|
||||||
|
input = [{ "quantity" => 1 }, { "quantity" => 0.2, "weight" => -0.3 }, { "weight" => 0.4 }]
|
||||||
|
assert_equal(0.0, @filters.sum(input))
|
||||||
|
assert_equal(1.2, @filters.sum(input, "quantity"))
|
||||||
|
assert_equal(0.1, @filters.sum(input, "weight"))
|
||||||
|
assert_equal(0.0, @filters.sum(input, "subtotal"))
|
||||||
|
assert_template_result("0", "{{ input | sum }}", { "input" => input })
|
||||||
|
assert_template_result("1.2", "{{ input | sum: 'quantity' }}", { "input" => input })
|
||||||
|
assert_template_result("0.1", "{{ input | sum: 'weight' }}", { "input" => input })
|
||||||
|
assert_template_result("0", "{{ input | sum: 'subtotal' }}", { "input" => input })
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def with_timezone(tz)
|
def with_timezone(tz)
|
||||||
|
@ -337,4 +337,22 @@ class TemplateTest < Minitest::Test
|
|||||||
assert_equal("x=2", output)
|
assert_equal("x=2", output)
|
||||||
assert_instance_of(String, output)
|
assert_instance_of(String, output)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_raises_error_with_invalid_utf8
|
||||||
|
e = assert_raises(TemplateEncodingError) do
|
||||||
|
Template.parse(<<~LIQUID)
|
||||||
|
{% comment %}
|
||||||
|
\xC0
|
||||||
|
{% endcomment %}
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal('Liquid error: Invalid template encoding', e.message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_allows_non_string_values_as_source
|
||||||
|
assert_equal('', Template.parse(nil).render)
|
||||||
|
assert_equal('1', Template.parse(1).render)
|
||||||
|
assert_equal('true', Template.parse(true).render)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -55,6 +55,11 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
assert_evaluates_false('bob', 'contains', '---')
|
assert_evaluates_false('bob', 'contains', '---')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_contains_binary_encoding_compatibility_with_utf8
|
||||||
|
assert_evaluates_true('🙈'.b, 'contains', '🙈')
|
||||||
|
assert_evaluates_true('🙈', 'contains', '🙈'.b)
|
||||||
|
end
|
||||||
|
|
||||||
def test_invalid_comparation_operator
|
def test_invalid_comparation_operator
|
||||||
assert_evaluates_argument_error(1, '~~', 0)
|
assert_evaluates_argument_error(1, '~~', 0)
|
||||||
end
|
end
|
||||||
@ -166,14 +171,14 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
def assert_evaluates_true(left, op, right)
|
def assert_evaluates_true(left, op, right)
|
||||||
assert(
|
assert(
|
||||||
Condition.new(left, op, right).evaluate(@context),
|
Condition.new(left, op, right).evaluate(@context),
|
||||||
"Evaluated false: #{left} #{op} #{right}",
|
"Evaluated false: #{left.inspect} #{op} #{right.inspect}",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_evaluates_false(left, op, right)
|
def assert_evaluates_false(left, op, right)
|
||||||
assert(
|
assert(
|
||||||
!Condition.new(left, op, right).evaluate(@context),
|
!Condition.new(left, op, right).evaluate(@context),
|
||||||
"Evaluated true: #{left} #{op} #{right}",
|
"Evaluated true: #{left.inspect} #{op} #{right.inspect}",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -20,4 +20,13 @@ class TagUnitTest < Minitest::Test
|
|||||||
tag = Tag.parse("some_tag", "", Tokenizer.new(""), ParseContext.new)
|
tag = Tag.parse("some_tag", "", Tokenizer.new(""), ParseContext.new)
|
||||||
assert_equal('some_tag', tag.tag_name)
|
assert_equal('some_tag', tag.tag_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class CustomTag < Liquid::Tag
|
||||||
|
def render(_context); end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_tag_render_to_output_buffer_nil_value
|
||||||
|
custom_tag = CustomTag.parse("some_tag", "", Tokenizer.new(""), ParseContext.new)
|
||||||
|
assert_equal('some string', custom_tag.render_to_output_buffer(Context.new, "some string"))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
202
test/unit/tags/comment_tag_unit_test.rb
Normal file
202
test/unit/tags/comment_tag_unit_test.rb
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class CommentTagUnitTest < Minitest::Test
|
||||||
|
def test_comment_inside_liquid_tag
|
||||||
|
assert_template_result("", <<~LIQUID.chomp)
|
||||||
|
{% liquid
|
||||||
|
if 1 != 1
|
||||||
|
comment
|
||||||
|
else
|
||||||
|
echo 123
|
||||||
|
endcomment
|
||||||
|
endif
|
||||||
|
%}
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_does_not_parse_nodes_inside_a_comment
|
||||||
|
assert_template_result("", <<~LIQUID.chomp)
|
||||||
|
{% comment %}
|
||||||
|
{% if true %}
|
||||||
|
{% if ... %}
|
||||||
|
{%- for ? -%}
|
||||||
|
{% while true %}
|
||||||
|
{%
|
||||||
|
unless if
|
||||||
|
%}
|
||||||
|
{% endcase %}
|
||||||
|
{% endcomment %}
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_allows_unclosed_tags
|
||||||
|
assert_template_result('', <<~LIQUID.chomp)
|
||||||
|
{% comment %}
|
||||||
|
{% if true %}
|
||||||
|
{% endcomment %}
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_open_tags_in_comment
|
||||||
|
assert_template_result('', <<~LIQUID.chomp)
|
||||||
|
{% comment %}
|
||||||
|
{% assign a = 123 {% comment %}
|
||||||
|
{% endcomment %}
|
||||||
|
LIQUID
|
||||||
|
|
||||||
|
assert_raises(Liquid::SyntaxError) do
|
||||||
|
assert_template_result("", <<~LIQUID.chomp)
|
||||||
|
{% comment %}
|
||||||
|
{% assign foo = "1"
|
||||||
|
{% endcomment %}
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_raises(Liquid::SyntaxError) do
|
||||||
|
assert_template_result("", <<~LIQUID.chomp)
|
||||||
|
{% comment %}
|
||||||
|
{% comment %}
|
||||||
|
{% invalid
|
||||||
|
{% endcomment %}
|
||||||
|
{% endcomment %}
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_raises(Liquid::SyntaxError) do
|
||||||
|
assert_template_result("", <<~LIQUID.chomp)
|
||||||
|
{% comment %}
|
||||||
|
{% {{ {%- endcomment %}
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_child_comment_tags_need_to_be_closed
|
||||||
|
assert_template_result("", <<~LIQUID.chomp)
|
||||||
|
{% comment %}
|
||||||
|
{% comment %}
|
||||||
|
{% comment %}{% endcomment %}
|
||||||
|
{% endcomment %}
|
||||||
|
{% endcomment %}
|
||||||
|
LIQUID
|
||||||
|
|
||||||
|
assert_raises(Liquid::SyntaxError) do
|
||||||
|
assert_template_result("", <<~LIQUID.chomp)
|
||||||
|
{% comment %}
|
||||||
|
{% comment %}
|
||||||
|
{% comment %}
|
||||||
|
{% endcomment %}
|
||||||
|
{% endcomment %}
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_child_raw_tags_need_to_be_closed
|
||||||
|
assert_template_result("", <<~LIQUID.chomp)
|
||||||
|
{% comment %}
|
||||||
|
{% raw %}
|
||||||
|
{% endcomment %}
|
||||||
|
{% endraw %}
|
||||||
|
{% endcomment %}
|
||||||
|
LIQUID
|
||||||
|
|
||||||
|
assert_raises(Liquid::SyntaxError) do
|
||||||
|
Liquid::Template.parse(<<~LIQUID.chomp)
|
||||||
|
{% comment %}
|
||||||
|
{% raw %}
|
||||||
|
{% endcomment %}
|
||||||
|
{% endcomment %}
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_error_line_number_is_correct
|
||||||
|
template = Liquid::Template.parse(<<~LIQUID.chomp, line_numbers: true)
|
||||||
|
{% comment %}
|
||||||
|
{% if true %}
|
||||||
|
{% endcomment %}
|
||||||
|
{{ errors.standard_error }}
|
||||||
|
LIQUID
|
||||||
|
|
||||||
|
output = template.render('errors' => ErrorDrop.new)
|
||||||
|
expected = <<~TEXT.chomp
|
||||||
|
|
||||||
|
Liquid error (line 4): standard error
|
||||||
|
TEXT
|
||||||
|
|
||||||
|
assert_equal(expected, output)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_comment_tag_delimiter_with_extra_strings
|
||||||
|
assert_template_result(
|
||||||
|
'',
|
||||||
|
<<~LIQUID.chomp,
|
||||||
|
{% comment %}
|
||||||
|
{% comment %}
|
||||||
|
{% endcomment
|
||||||
|
{% if true %}
|
||||||
|
{% endif %}
|
||||||
|
{% endcomment %}
|
||||||
|
LIQUID
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_nested_comment_tag_with_extra_strings
|
||||||
|
assert_template_result(
|
||||||
|
'',
|
||||||
|
<<~LIQUID.chomp,
|
||||||
|
{% comment %}
|
||||||
|
{% comment
|
||||||
|
{% assign foo = 1 %}
|
||||||
|
{% endcomment
|
||||||
|
{% assign foo = 1 %}
|
||||||
|
{% endcomment %}
|
||||||
|
LIQUID
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_ignores_delimiter_with_extra_strings
|
||||||
|
assert_template_result(
|
||||||
|
'',
|
||||||
|
<<~LIQUID.chomp,
|
||||||
|
{% if true %}
|
||||||
|
{% comment %}
|
||||||
|
{% commentXXXXX %}wut{% endcommentXXXXX %}
|
||||||
|
{% endcomment %}
|
||||||
|
{% endif %}
|
||||||
|
LIQUID
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_delimiter_can_have_extra_strings
|
||||||
|
assert_template_result('', "{% comment %}123{% endcomment xyz %}")
|
||||||
|
assert_template_result('', "{% comment %}123{% endcomment\txyz %}")
|
||||||
|
assert_template_result('', "{% comment %}123{% endcomment\nxyz %}")
|
||||||
|
assert_template_result('', "{% comment %}123{% endcomment\n xyz endcomment %}")
|
||||||
|
assert_template_result('', "{%comment}{% assign a = 1 %}{%endcomment}{% endif %}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_with_whitespace_control
|
||||||
|
assert_template_result("Hello!", " {%- comment -%}123{%- endcomment -%}Hello!")
|
||||||
|
assert_template_result("Hello!", "{%- comment -%}123{%- endcomment -%} Hello!")
|
||||||
|
assert_template_result("Hello!", " {%- comment -%}123{%- endcomment -%} Hello!")
|
||||||
|
|
||||||
|
assert_template_result("Hello!", <<~LIQUID.chomp)
|
||||||
|
{%- comment %}Whitespace control!{% endcomment -%}
|
||||||
|
Hello!
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_dont_override_liquid_tag_whitespace_control
|
||||||
|
assert_template_result("Hello!World!", <<~LIQUID.chomp)
|
||||||
|
Hello!
|
||||||
|
{%- liquid
|
||||||
|
comment
|
||||||
|
this is inside a liquid tag
|
||||||
|
endcomment
|
||||||
|
-%}
|
||||||
|
World!
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user