mirror of
https://github.com/kemko/liquid.git
synced 2026-01-02 16:25:42 +03:00
Compare commits
52 Commits
pz-strict-
...
inline-com
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fc45ca3af | ||
|
|
ab455480fa | ||
|
|
5e8e5e89b1 | ||
|
|
6ddfaec3f9 | ||
|
|
fff6c565c1 | ||
|
|
31f7be8a6d | ||
|
|
dcb5a67089 | ||
|
|
efe44a7e6a | ||
|
|
8625e66453 | ||
|
|
3cae09b968 | ||
|
|
3c499d0241 | ||
|
|
e71e53ffb5 | ||
|
|
260c863e23 | ||
|
|
42b6c07cd0 | ||
|
|
c91a6827f2 | ||
|
|
5dbc3d5701 | ||
|
|
22683cbd2a | ||
|
|
abfab3bef2 | ||
|
|
51e8d6234a | ||
|
|
7ca2846d9c | ||
|
|
7ba0fc7952 | ||
|
|
e2c86d137f | ||
|
|
776a63b61d | ||
|
|
84f9d6957c | ||
|
|
7d32728e16 | ||
|
|
40a9b72b3c | ||
|
|
4ff26cd707 | ||
|
|
462919a28f | ||
|
|
f3e2be9f85 | ||
|
|
4d40f83457 | ||
|
|
00be1e4dd4 | ||
|
|
f7d67b946e | ||
|
|
ae9aee896b | ||
|
|
6dec172743 | ||
|
|
da581d988a | ||
|
|
7960826552 | ||
|
|
84059691b8 | ||
|
|
896288eff1 | ||
|
|
b3f132efd1 | ||
|
|
60214b957c | ||
|
|
7361220af6 | ||
|
|
cb2ad71a31 | ||
|
|
900e3a6491 | ||
|
|
f18084203d | ||
|
|
3358a892f2 | ||
|
|
bbfcaa2cc0 | ||
|
|
ba657871bc | ||
|
|
29d5d9674a | ||
|
|
0a645e72c1 | ||
|
|
1850511334 | ||
|
|
300adfd7ae | ||
|
|
f357662f37 |
5
.github/workflows/liquid.yml
vendored
5
.github/workflows/liquid.yml
vendored
@@ -6,9 +6,8 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
entry:
|
entry:
|
||||||
- { ruby: 2.5, allowed-failure: false }
|
- { ruby: 2.5, allowed-failure: false } # minimum supported
|
||||||
- { ruby: 2.6, allowed-failure: false }
|
- { ruby: 3.0, allowed-failure: false } # latest
|
||||||
- { ruby: 2.7, allowed-failure: false }
|
|
||||||
- { ruby: ruby-head, allowed-failure: true }
|
- { ruby: ruby-head, allowed-failure: true }
|
||||||
name: test (${{ matrix.entry.ruby }})
|
name: test (${{ matrix.entry.ruby }})
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
# Recommended rubocop version: ~> 0.78.0
|
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'db/schema.rb'
|
- 'db/schema.rb'
|
||||||
@@ -78,13 +76,6 @@ Style/BlockDelimiters:
|
|||||||
- proc
|
- proc
|
||||||
- it
|
- it
|
||||||
|
|
||||||
Style/BracesAroundHashParameters:
|
|
||||||
EnforcedStyle: no_braces
|
|
||||||
SupportedStyles:
|
|
||||||
- braces
|
|
||||||
- no_braces
|
|
||||||
- context_dependent
|
|
||||||
|
|
||||||
Layout/CaseIndentation:
|
Layout/CaseIndentation:
|
||||||
EnforcedStyle: end
|
EnforcedStyle: end
|
||||||
SupportedStyles:
|
SupportedStyles:
|
||||||
@@ -204,6 +195,7 @@ Style/FrozenStringLiteralComment:
|
|||||||
SupportedStyles:
|
SupportedStyles:
|
||||||
- always
|
- always
|
||||||
- never
|
- never
|
||||||
|
SafeAutoCorrect: true
|
||||||
|
|
||||||
Style/GlobalVars:
|
Style/GlobalVars:
|
||||||
AllowedVariables: []
|
AllowedVariables: []
|
||||||
@@ -273,7 +265,7 @@ Style/MethodCallWithArgsParentheses:
|
|||||||
- raise
|
- raise
|
||||||
- puts
|
- puts
|
||||||
Exclude:
|
Exclude:
|
||||||
- Gemfile
|
- '**/Gemfile'
|
||||||
|
|
||||||
Style/MethodDefParentheses:
|
Style/MethodDefParentheses:
|
||||||
EnforcedStyle: require_parentheses
|
EnforcedStyle: require_parentheses
|
||||||
@@ -586,6 +578,7 @@ Layout/BlockEndNewline:
|
|||||||
|
|
||||||
Style/CaseEquality:
|
Style/CaseEquality:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
AllowOnConstant: true
|
||||||
|
|
||||||
Style/CharacterLiteral:
|
Style/CharacterLiteral:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
@@ -668,6 +661,9 @@ Style/IfWithSemicolon:
|
|||||||
Style/IdenticalConditionalBranches:
|
Style/IdenticalConditionalBranches:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
|
Layout/IndentationStyle:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
Style/InfiniteLoop:
|
Style/InfiniteLoop:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
@@ -680,7 +676,7 @@ Style/LineEndConcatenation:
|
|||||||
Style/MethodCallWithoutArgsParentheses:
|
Style/MethodCallWithoutArgsParentheses:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Style/MethodMissingSuper:
|
Lint/MissingSuper:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Style/MissingRespondToMissing:
|
Style/MissingRespondToMissing:
|
||||||
@@ -812,9 +808,6 @@ Layout/SpaceInsideRangeLiteral:
|
|||||||
Style/SymbolLiteral:
|
Style/SymbolLiteral:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Layout/Tab:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Layout/TrailingWhitespace:
|
Layout/TrailingWhitespace:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
@@ -843,7 +836,7 @@ Style/ZeroLengthPredicate:
|
|||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Layout/HeredocIndentation:
|
Layout/HeredocIndentation:
|
||||||
EnforcedStyle: squiggly
|
Enabled: true
|
||||||
|
|
||||||
Lint/AmbiguousOperator:
|
Lint/AmbiguousOperator:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
@@ -881,9 +874,6 @@ Lint/EmptyEnsure:
|
|||||||
Lint/EmptyInterpolation:
|
Lint/EmptyInterpolation:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Lint/EndInMethod:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Lint/EnsureReturn:
|
Lint/EnsureReturn:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
@@ -976,7 +966,7 @@ Lint/UselessAccessModifier:
|
|||||||
Lint/UselessAssignment:
|
Lint/UselessAssignment:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Lint/UselessComparison:
|
Lint/BinaryOperatorWithIdenticalOperands:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Lint/UselessElseWithoutRescue:
|
Lint/UselessElseWithoutRescue:
|
||||||
@@ -1027,3 +1017,6 @@ Style/ModuleFunction:
|
|||||||
|
|
||||||
Lint/OrderedMagicComments:
|
Lint/OrderedMagicComments:
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
|
Lint/DeprecatedOpenSSLConstant:
|
||||||
|
Enabled: true
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ Performance:
|
|||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
TargetRubyVersion: 2.4
|
TargetRubyVersion: 2.4
|
||||||
|
NewCops: disable
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'vendor/bundle/**/*'
|
- 'vendor/bundle/**/*'
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# This configuration was generated by
|
# This configuration was generated by
|
||||||
# `rubocop --auto-gen-config`
|
# `rubocop --auto-gen-config`
|
||||||
# on 2019-09-11 06:34:25 +1000 using RuboCop version 0.74.0.
|
# on 2020-12-11 18:53:41 UTC using RuboCop version 1.6.1.
|
||||||
# The point is for the user to remove these configuration records
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the offenses are removed from the code base.
|
# one by one as the offenses are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# Note that changes in the inspected code, or installation of new
|
||||||
@@ -14,14 +14,26 @@ Lint/InheritException:
|
|||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/liquid/interrupts.rb'
|
- 'lib/liquid/interrupts.rb'
|
||||||
|
|
||||||
# Offense count: 98
|
# Offense count: 113
|
||||||
# Cop supports --auto-correct.
|
# Cop supports --auto-correct.
|
||||||
# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
||||||
# URISchemes: http, https
|
# URISchemes: http, https
|
||||||
Layout/LineLength:
|
Layout/LineLength:
|
||||||
Max: 294
|
Max: 260
|
||||||
|
|
||||||
# Offense count: 44
|
# Offense count: 8
|
||||||
|
Lint/MissingSuper:
|
||||||
|
Exclude:
|
||||||
|
- 'lib/liquid/forloop_drop.rb'
|
||||||
|
- 'lib/liquid/tablerowloop_drop.rb'
|
||||||
|
- 'test/integration/assign_test.rb'
|
||||||
|
- 'test/integration/context_test.rb'
|
||||||
|
- 'test/integration/filter_test.rb'
|
||||||
|
- 'test/integration/standard_filter_test.rb'
|
||||||
|
- 'test/integration/tags/for_tag_test.rb'
|
||||||
|
- 'test/integration/tags/table_row_test.rb'
|
||||||
|
|
||||||
|
# Offense count: 43
|
||||||
Naming/ConstantName:
|
Naming/ConstantName:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/liquid.rb'
|
- 'lib/liquid.rb'
|
||||||
@@ -32,7 +44,6 @@ Naming/ConstantName:
|
|||||||
- 'lib/liquid/tags/cycle.rb'
|
- 'lib/liquid/tags/cycle.rb'
|
||||||
- 'lib/liquid/tags/for.rb'
|
- 'lib/liquid/tags/for.rb'
|
||||||
- 'lib/liquid/tags/if.rb'
|
- 'lib/liquid/tags/if.rb'
|
||||||
- 'lib/liquid/tags/include.rb'
|
|
||||||
- 'lib/liquid/tags/raw.rb'
|
- 'lib/liquid/tags/raw.rb'
|
||||||
- 'lib/liquid/tags/table_row.rb'
|
- 'lib/liquid/tags/table_row.rb'
|
||||||
- 'lib/liquid/variable.rb'
|
- 'lib/liquid/variable.rb'
|
||||||
@@ -40,9 +51,7 @@ Naming/ConstantName:
|
|||||||
- 'performance/shopify/paginate.rb'
|
- 'performance/shopify/paginate.rb'
|
||||||
- 'test/integration/tags/include_tag_test.rb'
|
- 'test/integration/tags/include_tag_test.rb'
|
||||||
|
|
||||||
# Offense count: 5
|
# Offense count: 2
|
||||||
Style/ClassVars:
|
Style/ClassVars:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/liquid/condition.rb'
|
- 'lib/liquid/condition.rb'
|
||||||
- 'lib/liquid/strainer.rb'
|
|
||||||
- 'lib/liquid/template.rb'
|
|
||||||
4
Gemfile
4
Gemfile
@@ -18,10 +18,10 @@ group :benchmark, :test do
|
|||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'rubocop', '~> 0.78.0', require: false
|
gem 'rubocop', '~> 1.4', 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: 'inline-comment'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
38
History.md
38
History.md
@@ -1,10 +1,42 @@
|
|||||||
# Liquid Change Log
|
# Liquid Change Log
|
||||||
|
|
||||||
### Unreleased
|
## Unreleased
|
||||||
|
|
||||||
* Split Strainer class as a factory and a template (#1208) [Thierry Joyal]
|
### Features
|
||||||
|
* Allow `#` to be used as an inline comment tag (#1401) [Dylan Thacker-Smith]
|
||||||
|
|
||||||
|
## 5.0.0 / 2021-01-06
|
||||||
|
|
||||||
|
### Features
|
||||||
|
* Add new `{% render %}` tag (#1122) [Samuel Doiron]
|
||||||
|
* Add support for `as` in `{% render %}` and `{% include %}` (#1181) [Mike Angell]
|
||||||
|
* Add `{% liquid %}` and `{% echo %}` tags (#1086) [Justin Li]
|
||||||
|
* Add [usage tracking](README.md#usage-tracking) [Mike Angell]
|
||||||
|
* Add `Tag.disable_tags` for disabling tags that prepend `Tag::Disableable` at render time (#1162, #1274, #1275) [Mike Angell]
|
||||||
|
* Support using a profiler for multiple renders (#1365, #1366) [Dylan Thacker-Smith]
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
* Fix catastrophic backtracking in `RANGES_REGEX` regular expression (#1357) [Dylan Thacker-Smith]
|
||||||
|
* Make sure the for tag's limit and offset are integers (#1094) [David Cornu]
|
||||||
|
* Invokable methods for enumerable reject include (#1151) [Thierry Joyal]
|
||||||
|
* Allow `default` filter to handle `false` as value (#1144) [Mike Angell]
|
||||||
|
* Fix render length resource limit so it doesn't multiply nested output (#1285) [Dylan Thacker-Smith]
|
||||||
|
* Fix duplication of text in raw tags (#1304) [Peter Zhu]
|
||||||
|
* Fix strict parsing of find variable with a name expression (#1317) [Dylan Thacker-Smith]
|
||||||
|
* Use monotonic time to measure durations in Liquid::Profiler (#1362) [Dylan Thacker-Smith]
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
* Require Ruby >= 2.5 (#1131, #1310) [Mike Angell, Dylan Thacker-Smith]
|
||||||
|
* Remove support for taint checking (#1268) [Dylan Thacker-Smith]
|
||||||
|
* Split Strainer class into StrainerFactory and StrainerTemplate (#1208) [Thierry Joyal]
|
||||||
* Remove handling of a nil context in the Strainer class (#1218) [Thierry Joyal]
|
* Remove handling of a nil context in the Strainer class (#1218) [Thierry Joyal]
|
||||||
* StaticRegisters#fetch to raise on missing key (#1250) [Thierry Joyal]
|
* Handle `BlockBody#blank?` at parse time (#1287) [Dylan Thacker-Smith]
|
||||||
|
* Pass the tag markup and tokenizer to `Document#unknown_tag` (#1290) [Dylan Thacker-Smith]
|
||||||
|
* And several internal changes
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
* Reduce allocations (#1073, #1091, #1115, #1099, #1117, #1141, #1322, #1341) [Richard Monette, Florian Weingarten, Ashwin Maroli]
|
||||||
|
* Improve resources limits performance (#1093, #1323) [Florian Weingarten, Dylan Thacker-Smith]
|
||||||
|
|
||||||
## 4.0.3 / 2019-03-12
|
## 4.0.3 / 2019-03-12
|
||||||
|
|
||||||
|
|||||||
@@ -77,9 +77,6 @@ module Liquid
|
|||||||
body.parse(tokens, parse_context) do |end_tag_name, end_tag_params|
|
body.parse(tokens, parse_context) do |end_tag_name, end_tag_params|
|
||||||
@blank &&= body.blank?
|
@blank &&= body.blank?
|
||||||
|
|
||||||
# Instrument for bug 1346
|
|
||||||
Usage.increment("end_tag_params") if end_tag_params && !end_tag_params.empty?
|
|
||||||
|
|
||||||
return false if end_tag_name == block_delimiter
|
return false if end_tag_name == block_delimiter
|
||||||
raise_tag_never_closed(block_name) unless end_tag_name
|
raise_tag_never_closed(block_name) unless end_tag_name
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ require 'English'
|
|||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
class BlockBody
|
class BlockBody
|
||||||
LiquidTagToken = /\A\s*(\w+)\s*(.*?)\z/o
|
LiquidTagToken = /\A\s*(\w+|#)\s*(.*?)\z/o
|
||||||
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(\w+)(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
FullToken = /\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 = "{%"
|
||||||
@@ -99,7 +99,9 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
private def parse_liquid_tag(markup, parse_context)
|
private def parse_liquid_tag(markup, parse_context)
|
||||||
liquid_tag_tokenizer = Tokenizer.new(markup, line_number: parse_context.line_number, for_liquid_tag: true)
|
liquid_tag_tokenizer = parse_context.new_tokenizer(
|
||||||
|
markup, start_line_number: parse_context.line_number, for_liquid_tag: true
|
||||||
|
)
|
||||||
parse_for_liquid_tag(liquid_tag_tokenizer, parse_context) do |end_tag_name, _end_tag_markup|
|
parse_for_liquid_tag(liquid_tag_tokenizer, parse_context) do |end_tag_name, _end_tag_markup|
|
||||||
if end_tag_name
|
if end_tag_name
|
||||||
BlockBody.unknown_tag_in_liquid_tag(end_tag_name, parse_context)
|
BlockBody.unknown_tag_in_liquid_tag(end_tag_name, parse_context)
|
||||||
|
|||||||
@@ -49,14 +49,6 @@ module Liquid
|
|||||||
@@method_literals[markup] || parse_context.parse_expression(markup)
|
@@method_literals[markup] || parse_context.parse_expression(markup)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.strict_parse_expression(parse_context, p)
|
|
||||||
if p.look(:id) && !p.look(:dot, 1) && !p.look(:open_square, 1)
|
|
||||||
parse_expression(parse_context, p.consume)
|
|
||||||
else
|
|
||||||
p.expression
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :attachment, :child_condition
|
attr_reader :attachment, :child_condition
|
||||||
attr_accessor :left, :operator, :right
|
attr_accessor :left, :operator, :right
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ module Liquid
|
|||||||
attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
|
attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
|
||||||
|
|
||||||
# rubocop:disable Metrics/ParameterLists
|
# rubocop:disable Metrics/ParameterLists
|
||||||
def self.build(environments: {}, outer_scope: {}, registers: {}, rethrow_errors: false, resource_limits: nil, static_environments: {})
|
def self.build(environments: {}, outer_scope: {}, registers: {}, rethrow_errors: false, resource_limits: nil, static_environments: {}, &block)
|
||||||
new(environments, outer_scope, registers, rethrow_errors, resource_limits, static_environments)
|
new(environments, outer_scope, registers, rethrow_errors, resource_limits, static_environments, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_environments = {})
|
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_environments = {})
|
||||||
@@ -44,6 +44,8 @@ module Liquid
|
|||||||
self.exception_renderer = Liquid::RAISE_EXCEPTION_LAMBDA
|
self.exception_renderer = Liquid::RAISE_EXCEPTION_LAMBDA
|
||||||
end
|
end
|
||||||
|
|
||||||
|
yield self if block_given?
|
||||||
|
|
||||||
# Do this last, since it could result in this object being passed to a Proc in the environment
|
# Do this last, since it could result in this object being passed to a Proc in the environment
|
||||||
squash_instance_assigns_with_environments
|
squash_instance_assigns_with_environments
|
||||||
end
|
end
|
||||||
@@ -135,7 +137,7 @@ module Liquid
|
|||||||
def new_isolated_subcontext
|
def new_isolated_subcontext
|
||||||
check_overflow
|
check_overflow
|
||||||
|
|
||||||
Context.build(
|
self.class.build(
|
||||||
resource_limits: resource_limits,
|
resource_limits: resource_limits,
|
||||||
static_environments: static_environments,
|
static_environments: static_environments,
|
||||||
registers: StaticRegisters.new(registers)
|
registers: StaticRegisters.new(registers)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
@body.render(context)
|
render_to_output_buffer(context, +'')
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ module Liquid
|
|||||||
DOUBLE_QUOTED_STRING = /\A\s*"(.*)"\s*\z/m
|
DOUBLE_QUOTED_STRING = /\A\s*"(.*)"\s*\z/m
|
||||||
INTEGERS_REGEX = /\A\s*(-?\d+)\s*\z/
|
INTEGERS_REGEX = /\A\s*(-?\d+)\s*\z/
|
||||||
FLOATS_REGEX = /\A\s*(-?\d[\d\.]+)\s*\z/
|
FLOATS_REGEX = /\A\s*(-?\d[\d\.]+)\s*\z/
|
||||||
RANGES_REGEX = /\A\s*\(\s*(\S+)\s*\.\.\s*(\S+)\s*\)\s*\z/
|
|
||||||
|
# Use an atomic group (?>...) to avoid pathological backtracing from
|
||||||
|
# malicious input as described in https://github.com/Shopify/liquid/issues/1357
|
||||||
|
RANGES_REGEX = /\A\s*\(\s*(?>(\S+)\s*\.\.)\s*(\S+)\s*\)\s*\z/
|
||||||
|
|
||||||
def self.parse(markup)
|
def self.parse(markup)
|
||||||
case markup
|
case markup
|
||||||
@@ -33,7 +36,7 @@ module Liquid
|
|||||||
if LITERALS.key?(markup)
|
if LITERALS.key?(markup)
|
||||||
LITERALS[markup]
|
LITERALS[markup]
|
||||||
else
|
else
|
||||||
VariableLookup.lax_parse(markup)
|
VariableLookup.parse(markup)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
tag_never_closed: "'%{block_name}' tag was never closed"
|
tag_never_closed: "'%{block_name}' tag was never closed"
|
||||||
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"
|
||||||
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"
|
||||||
|
inline_comment_invalid: "Syntax error in tag '#' - Each line of comments must be prefixed by the '#' character"
|
||||||
argument:
|
argument:
|
||||||
include: "Argument error in tag 'include' - Illegal template name"
|
include: "Argument error in tag 'include' - Illegal template name"
|
||||||
disabled:
|
disabled:
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ module Liquid
|
|||||||
Liquid::BlockBody.new
|
Liquid::BlockBody.new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def new_tokenizer(markup, start_line_number: nil, for_liquid_tag: false)
|
||||||
|
Tokenizer.new(markup, line_number: start_line_number, for_liquid_tag: for_liquid_tag)
|
||||||
|
end
|
||||||
|
|
||||||
def parse_expression(markup)
|
def parse_expression(markup)
|
||||||
Expression.parse(markup)
|
Expression.parse(markup)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -50,52 +50,53 @@ module Liquid
|
|||||||
token = @tokens[@p]
|
token = @tokens[@p]
|
||||||
case token[0]
|
case token[0]
|
||||||
when :id
|
when :id
|
||||||
if Expression::LITERALS.key?(token[1]) && !look(:dot, 1) && !look(:open_square, 1)
|
str = consume
|
||||||
Expression::LITERALS[consume]
|
str << variable_lookups
|
||||||
else
|
|
||||||
VariableLookup.strict_parse(self)
|
|
||||||
end
|
|
||||||
when :open_square
|
when :open_square
|
||||||
VariableLookup.strict_parse(self)
|
str = consume
|
||||||
when :string
|
str << expression
|
||||||
consume[1..-2]
|
str << consume(:close_square)
|
||||||
when :number
|
str << variable_lookups
|
||||||
num_str = consume
|
when :string, :number
|
||||||
num_str.include?('.') ? num_str.to_f : num_str.to_i
|
consume
|
||||||
when :open_round
|
when :open_round
|
||||||
consume
|
consume
|
||||||
first = expression
|
first = expression
|
||||||
consume(:dotdot)
|
consume(:dotdot)
|
||||||
last = expression
|
last = expression
|
||||||
consume(:close_round)
|
consume(:close_round)
|
||||||
RangeLookup.build(first, last)
|
"(#{first}..#{last})"
|
||||||
else
|
else
|
||||||
raise SyntaxError, "#{token} is not a valid expression"
|
raise SyntaxError, "#{token} is not a valid expression"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def arguments
|
def argument
|
||||||
filter_args = []
|
str = +""
|
||||||
keyword_args = nil
|
# might be a keyword argument (identifier: expression)
|
||||||
|
if look(:id) && look(:colon, 1)
|
||||||
loop do
|
str << consume << consume << ' '
|
||||||
# keyword argument (identifier: expression)
|
|
||||||
if look(:colon, 1)
|
|
||||||
keyword_args ||= {}
|
|
||||||
k = consume(:id)
|
|
||||||
consume
|
|
||||||
v = expression
|
|
||||||
keyword_args[k] = v
|
|
||||||
else
|
|
||||||
filter_args << expression
|
|
||||||
end
|
|
||||||
|
|
||||||
break unless consume?(:comma)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
result = [filter_args]
|
str << expression
|
||||||
result << keyword_args if keyword_args
|
str
|
||||||
result
|
end
|
||||||
|
|
||||||
|
def variable_lookups
|
||||||
|
str = +""
|
||||||
|
loop do
|
||||||
|
if look(:open_square)
|
||||||
|
str << consume
|
||||||
|
str << expression
|
||||||
|
str << consume(:close_square)
|
||||||
|
elsif look(:dot)
|
||||||
|
str << consume
|
||||||
|
str << consume(:id)
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
str
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ module Liquid
|
|||||||
# node.code
|
# node.code
|
||||||
#
|
#
|
||||||
# # Which template and line number of this node.
|
# # Which template and line number of this node.
|
||||||
# # If top level, this will be "<root>".
|
# # The top-level template name is `nil` by default, but can be set in the Liquid::Context before rendering.
|
||||||
# node.partial
|
# node.partial
|
||||||
# node.line_number
|
# node.line_number
|
||||||
#
|
#
|
||||||
@@ -46,126 +46,94 @@ module Liquid
|
|||||||
include Enumerable
|
include Enumerable
|
||||||
|
|
||||||
class Timing
|
class Timing
|
||||||
attr_reader :code, :partial, :line_number, :children, :total_time, :self_time
|
attr_reader :code, :template_name, :line_number, :children
|
||||||
|
attr_accessor :total_time
|
||||||
|
alias_method :render_time, :total_time
|
||||||
|
alias_method :partial, :template_name
|
||||||
|
|
||||||
def initialize(node, partial)
|
def initialize(code: nil, template_name: nil, line_number: nil)
|
||||||
@code = node.respond_to?(:raw) ? node.raw : node
|
@code = code
|
||||||
@partial = partial
|
@template_name = template_name
|
||||||
@line_number = node.respond_to?(:line_number) ? node.line_number : nil
|
@line_number = line_number
|
||||||
@children = []
|
@children = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.start(node, partial)
|
def self_time
|
||||||
new(node, partial).tap(&:start)
|
@self_time ||= begin
|
||||||
end
|
total_children_time = 0.0
|
||||||
|
|
||||||
def start
|
|
||||||
@start_time = Time.now
|
|
||||||
end
|
|
||||||
|
|
||||||
def finish
|
|
||||||
@end_time = Time.now
|
|
||||||
@total_time = @end_time - @start_time
|
|
||||||
|
|
||||||
if @children.empty?
|
|
||||||
@self_time = @total_time
|
|
||||||
else
|
|
||||||
total_children_time = 0
|
|
||||||
@children.each do |child|
|
@children.each do |child|
|
||||||
total_children_time += child.total_time
|
total_children_time += child.total_time
|
||||||
end
|
end
|
||||||
@self_time = @total_time - total_children_time
|
@total_time - total_children_time
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def render_time
|
attr_reader :total_time
|
||||||
@end_time - @start_time
|
alias_method :total_render_time, :total_time
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@root_children = []
|
||||||
|
@current_children = nil
|
||||||
|
@total_time = 0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
def profile(template_name, &block)
|
||||||
|
# nested renders are done from a tag that already has a timing node
|
||||||
|
return yield if @current_children
|
||||||
|
|
||||||
|
root_children = @root_children
|
||||||
|
render_idx = root_children.length
|
||||||
|
begin
|
||||||
|
@current_children = root_children
|
||||||
|
profile_node(template_name, &block)
|
||||||
|
ensure
|
||||||
|
@current_children = nil
|
||||||
|
if (timing = root_children[render_idx])
|
||||||
|
@total_time += timing.total_time
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.profile_node_render(node)
|
def children
|
||||||
if Profiler.current_profile && node.respond_to?(:render)
|
children = @root_children
|
||||||
Profiler.current_profile.start_node(node)
|
if children.length == 1
|
||||||
output = yield
|
children.first.children
|
||||||
Profiler.current_profile.end_node(node)
|
|
||||||
output
|
|
||||||
else
|
else
|
||||||
yield
|
children
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.profile_children(template_name)
|
|
||||||
if Profiler.current_profile
|
|
||||||
Profiler.current_profile.push_partial(template_name)
|
|
||||||
output = yield
|
|
||||||
Profiler.current_profile.pop_partial
|
|
||||||
output
|
|
||||||
else
|
|
||||||
yield
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.current_profile
|
|
||||||
Thread.current[:liquid_profiler]
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(partial_name = "<root>")
|
|
||||||
@partial_stack = [partial_name]
|
|
||||||
|
|
||||||
@root_timing = Timing.new("", current_partial)
|
|
||||||
@timing_stack = [@root_timing]
|
|
||||||
|
|
||||||
@render_start_at = Time.now
|
|
||||||
@render_end_at = @render_start_at
|
|
||||||
end
|
|
||||||
|
|
||||||
def start
|
|
||||||
Thread.current[:liquid_profiler] = self
|
|
||||||
@render_start_at = Time.now
|
|
||||||
end
|
|
||||||
|
|
||||||
def stop
|
|
||||||
Thread.current[:liquid_profiler] = nil
|
|
||||||
@render_end_at = Time.now
|
|
||||||
end
|
|
||||||
|
|
||||||
def total_render_time
|
|
||||||
@render_end_at - @render_start_at
|
|
||||||
end
|
|
||||||
|
|
||||||
def each(&block)
|
def each(&block)
|
||||||
@root_timing.children.each(&block)
|
children.each(&block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def [](idx)
|
def [](idx)
|
||||||
@root_timing.children[idx]
|
children[idx]
|
||||||
end
|
end
|
||||||
|
|
||||||
def length
|
def length
|
||||||
@root_timing.children.length
|
children.length
|
||||||
end
|
end
|
||||||
|
|
||||||
def start_node(node)
|
def profile_node(template_name, code: nil, line_number: nil)
|
||||||
@timing_stack.push(Timing.start(node, current_partial))
|
timing = Timing.new(code: code, template_name: template_name, line_number: line_number)
|
||||||
|
parent_children = @current_children
|
||||||
|
start_time = monotonic_time
|
||||||
|
begin
|
||||||
|
@current_children = timing.children
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
@current_children = parent_children
|
||||||
|
timing.total_time = monotonic_time - start_time
|
||||||
|
parent_children << timing
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def end_node(_node)
|
private
|
||||||
timing = @timing_stack.pop
|
|
||||||
timing.finish
|
|
||||||
|
|
||||||
@timing_stack.last.children << timing
|
def monotonic_time
|
||||||
end
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
||||||
|
|
||||||
def current_partial
|
|
||||||
@partial_stack.last
|
|
||||||
end
|
|
||||||
|
|
||||||
def push_partial(partial_name)
|
|
||||||
@partial_stack.push(partial_name)
|
|
||||||
end
|
|
||||||
|
|
||||||
def pop_partial
|
|
||||||
@partial_stack.pop
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,19 +3,33 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
module BlockBodyProfilingHook
|
module BlockBodyProfilingHook
|
||||||
def render_node(context, output, node)
|
def render_node(context, output, node)
|
||||||
Profiler.profile_node_render(node) do
|
if (profiler = context.profiler)
|
||||||
|
profiler.profile_node(context.template_name, code: node.raw, line_number: node.line_number) do
|
||||||
|
super
|
||||||
|
end
|
||||||
|
else
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
BlockBody.prepend(BlockBodyProfilingHook)
|
BlockBody.prepend(BlockBodyProfilingHook)
|
||||||
|
|
||||||
module IncludeProfilingHook
|
module DocumentProfilingHook
|
||||||
def render_to_output_buffer(context, output)
|
def render_to_output_buffer(context, output)
|
||||||
Profiler.profile_children(context.evaluate(@template_name_expr).to_s) do
|
return super unless context.profiler
|
||||||
super
|
context.profiler.profile(context.template_name) { super }
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Include.prepend(IncludeProfilingHook)
|
Document.prepend(DocumentProfilingHook)
|
||||||
|
|
||||||
|
module ContextProfilingHook
|
||||||
|
attr_accessor :profiler
|
||||||
|
|
||||||
|
def new_isolated_subcontext
|
||||||
|
new_context = super
|
||||||
|
new_context.profiler = profiler
|
||||||
|
new_context
|
||||||
|
end
|
||||||
|
end
|
||||||
|
Context.prepend(ContextProfilingHook)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,43 +5,35 @@ module Liquid
|
|||||||
def self.parse(start_markup, end_markup)
|
def self.parse(start_markup, end_markup)
|
||||||
start_obj = Expression.parse(start_markup)
|
start_obj = Expression.parse(start_markup)
|
||||||
end_obj = Expression.parse(end_markup)
|
end_obj = Expression.parse(end_markup)
|
||||||
build(start_obj, end_obj)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.build(start_obj, end_obj)
|
|
||||||
if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
|
if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
|
||||||
new(start_obj, end_obj)
|
new(start_obj, end_obj)
|
||||||
else
|
else
|
||||||
to_integer(start_obj)..to_integer(end_obj)
|
start_obj.to_i..end_obj.to_i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.to_integer(input)
|
def initialize(start_obj, end_obj)
|
||||||
|
@start_obj = start_obj
|
||||||
|
@end_obj = end_obj
|
||||||
|
end
|
||||||
|
|
||||||
|
def evaluate(context)
|
||||||
|
start_int = to_integer(context.evaluate(@start_obj))
|
||||||
|
end_int = to_integer(context.evaluate(@end_obj))
|
||||||
|
start_int..end_int
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def to_integer(input)
|
||||||
case input
|
case input
|
||||||
when Integer
|
when Integer
|
||||||
input
|
input
|
||||||
when NilClass, String, Float
|
when NilClass, String
|
||||||
input.to_i
|
input.to_i
|
||||||
else
|
else
|
||||||
Utils.to_integer(input)
|
Utils.to_integer(input)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :start_expr, :end_expr
|
|
||||||
|
|
||||||
def initialize(start_expr, end_expr)
|
|
||||||
@start_expr = start_expr
|
|
||||||
@end_expr = end_expr
|
|
||||||
end
|
|
||||||
|
|
||||||
def evaluate(context)
|
|
||||||
start_int = self.class.to_integer(context.evaluate(@start_expr))
|
|
||||||
end_int = self.class.to_integer(context.evaluate(@end_expr))
|
|
||||||
start_int..end_int
|
|
||||||
end
|
|
||||||
|
|
||||||
def ==(other)
|
|
||||||
self.class == other.class && start_expr == other.start_expr && end_expr == other.end_expr
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -89,13 +89,15 @@ module Liquid
|
|||||||
|
|
||||||
def truncatewords(input, words = 15, truncate_string = "...")
|
def truncatewords(input, words = 15, truncate_string = "...")
|
||||||
return if input.nil?
|
return if input.nil?
|
||||||
wordlist = input.to_s.split
|
input = input.to_s
|
||||||
words = Utils.to_integer(words)
|
words = Utils.to_integer(words)
|
||||||
|
words = 1 if words <= 0
|
||||||
|
|
||||||
l = words - 1
|
wordlist = input.split(" ", words + 1)
|
||||||
l = 0 if l < 0
|
return input if wordlist.length <= words
|
||||||
|
|
||||||
wordlist.length > l ? wordlist[0..l].join(" ").concat(truncate_string.to_s) : input
|
wordlist.pop
|
||||||
|
wordlist.join(" ").concat(truncate_string.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Split input string into an array of substrings separated by given pattern.
|
# Split input string into an array of substrings separated by given pattern.
|
||||||
@@ -295,7 +297,7 @@ module Liquid
|
|||||||
|
|
||||||
# Add <br /> tags in front of all newlines in input string
|
# Add <br /> tags in front of all newlines in input string
|
||||||
def newline_to_br(input)
|
def newline_to_br(input)
|
||||||
input.to_s.gsub(/\n/, "<br />\n")
|
input.to_s.gsub(/\r?\n/, "<br />\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Reformat a date using Ruby's core Time#strftime( string ) -> string
|
# Reformat a date using Ruby's core Time#strftime( string ) -> string
|
||||||
|
|||||||
@@ -19,15 +19,16 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def parse(tokens)
|
def parse(tokens)
|
||||||
body = new_body
|
body = case_body = new_body
|
||||||
body = @blocks.last.attachment while parse_body(body, tokens)
|
body = @blocks.last.attachment while parse_body(body, tokens)
|
||||||
@blocks.each do |condition|
|
@blocks.reverse_each do |condition|
|
||||||
body = condition.attachment
|
body = condition.attachment
|
||||||
unless body.frozen?
|
unless body.frozen?
|
||||||
body.remove_blank_strings if blank?
|
body.remove_blank_strings if blank?
|
||||||
body.freeze
|
body.freeze
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
case_body.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def nodelist
|
def nodelist
|
||||||
|
|||||||
@@ -63,11 +63,11 @@ module Liquid
|
|||||||
parse_body(@else_block, tokens)
|
parse_body(@else_block, tokens)
|
||||||
end
|
end
|
||||||
if blank?
|
if blank?
|
||||||
@for_block.remove_blank_strings
|
|
||||||
@else_block&.remove_blank_strings
|
@else_block&.remove_blank_strings
|
||||||
|
@for_block.remove_blank_strings
|
||||||
end
|
end
|
||||||
@for_block.freeze
|
|
||||||
@else_block&.freeze
|
@else_block&.freeze
|
||||||
|
@for_block.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def nodelist
|
def nodelist
|
||||||
@@ -113,9 +113,10 @@ module Liquid
|
|||||||
@variable_name = p.consume(:id)
|
@variable_name = p.consume(:id)
|
||||||
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in") unless p.id?('in')
|
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_in") unless p.id?('in')
|
||||||
|
|
||||||
@collection_name = p.expression
|
collection_name = p.expression
|
||||||
|
@collection_name = parse_expression(collection_name)
|
||||||
|
|
||||||
@name = "#{@variable_name}-#{@collection_name}"
|
@name = "#{@variable_name}-#{collection_name}"
|
||||||
@reversed = p.id?('reversed')
|
@reversed = p.id?('reversed')
|
||||||
|
|
||||||
while p.look(:id) && p.look(:colon, 1)
|
while p.look(:id) && p.look(:colon, 1)
|
||||||
@@ -123,18 +124,7 @@ module Liquid
|
|||||||
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_attribute")
|
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_attribute")
|
||||||
end
|
end
|
||||||
p.consume
|
p.consume
|
||||||
case attribute
|
set_attribute(attribute, p.expression)
|
||||||
when 'offset'
|
|
||||||
@from =
|
|
||||||
if p.id?('continue')
|
|
||||||
Usage.increment('for_offset_continue')
|
|
||||||
:continue
|
|
||||||
else
|
|
||||||
p.expression
|
|
||||||
end
|
|
||||||
when 'limit'
|
|
||||||
@limit = p.expression
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
p.consume(:end_of_string)
|
p.consume(:end_of_string)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ module Liquid
|
|||||||
def parse(tokens)
|
def parse(tokens)
|
||||||
while parse_body(@blocks.last.attachment, tokens)
|
while parse_body(@blocks.last.attachment, tokens)
|
||||||
end
|
end
|
||||||
@blocks.each do |block|
|
@blocks.reverse_each do |block|
|
||||||
block.attachment.remove_blank_strings if blank?
|
block.attachment.remove_blank_strings if blank?
|
||||||
block.attachment.freeze
|
block.attachment.freeze
|
||||||
end
|
end
|
||||||
@@ -75,10 +75,6 @@ module Liquid
|
|||||||
Condition.parse_expression(parse_context, markup)
|
Condition.parse_expression(parse_context, markup)
|
||||||
end
|
end
|
||||||
|
|
||||||
def strict_parse_expression(p)
|
|
||||||
Condition.strict_parse_expression(parse_context, p)
|
|
||||||
end
|
|
||||||
|
|
||||||
def lax_parse(markup)
|
def lax_parse(markup)
|
||||||
expressions = markup.scan(ExpressionsAndOperators)
|
expressions = markup.scan(ExpressionsAndOperators)
|
||||||
raise SyntaxError, options[:locale].t("errors.syntax.if") unless expressions.pop =~ Syntax
|
raise SyntaxError, options[:locale].t("errors.syntax.if") unless expressions.pop =~ Syntax
|
||||||
@@ -118,9 +114,9 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def parse_comparison(p)
|
def parse_comparison(p)
|
||||||
a = strict_parse_expression(p)
|
a = parse_expression(p.expression)
|
||||||
if (op = p.consume?(:comparison))
|
if (op = p.consume?(:comparison))
|
||||||
b = strict_parse_expression(p)
|
b = parse_expression(p.expression)
|
||||||
Condition.new(a, op, b)
|
Condition.new(a, op, b)
|
||||||
else
|
else
|
||||||
Condition.new(a)
|
Condition.new(a)
|
||||||
|
|||||||
25
lib/liquid/tags/inline_comment.rb
Normal file
25
lib/liquid/tags/inline_comment.rb
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Liquid
|
||||||
|
class InlineComment < Tag
|
||||||
|
def initialize(tag_name, markup, options)
|
||||||
|
super
|
||||||
|
# Semantically, a comment should only ignore everything after it on the line.
|
||||||
|
# Currently, this implementation doesn't support mixing a comment with another tag
|
||||||
|
# but we need to reserve future support for this.
|
||||||
|
if markup.match?(/\n\s*[^#]/)
|
||||||
|
raise SyntaxError, options[:locale].t("errors.syntax.inline_comment_invalid")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_to_output_buffer(_context, output)
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
def blank?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Template.register_tag('#', InlineComment)
|
||||||
|
end
|
||||||
@@ -106,12 +106,9 @@ module Liquid
|
|||||||
# Parse source code.
|
# Parse source code.
|
||||||
# Returns self for easy chaining
|
# Returns self for easy chaining
|
||||||
def parse(source, options = {})
|
def parse(source, options = {})
|
||||||
@options = options
|
parse_context = configure_options(options)
|
||||||
@profiling = options[:profile]
|
tokenizer = parse_context.new_tokenizer(source, start_line_number: @line_numbers && 1)
|
||||||
@line_numbers = options[:line_numbers] || @profiling
|
@root = Document.parse(tokenizer, parse_context)
|
||||||
parse_context = options.is_a?(ParseContext) ? options : ParseContext.new(options)
|
|
||||||
@root = Document.parse(tokenize(source), parse_context)
|
|
||||||
@warnings = parse_context.warnings
|
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -189,11 +186,13 @@ module Liquid
|
|||||||
# Retrying a render resets resource usage
|
# Retrying a render resets resource usage
|
||||||
context.resource_limits.reset
|
context.resource_limits.reset
|
||||||
|
|
||||||
|
if @profiling && context.profiler.nil?
|
||||||
|
@profiler = context.profiler = Liquid::Profiler.new
|
||||||
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
# render the nodelist.
|
# render the nodelist.
|
||||||
with_profiling(context) do
|
@root.render_to_output_buffer(context, output || +'')
|
||||||
@root.render_to_output_buffer(context, output || +'')
|
|
||||||
end
|
|
||||||
rescue Liquid::MemoryError => e
|
rescue Liquid::MemoryError => e
|
||||||
context.handle_error(e)
|
context.handle_error(e)
|
||||||
ensure
|
ensure
|
||||||
@@ -212,25 +211,17 @@ module Liquid
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def tokenize(source)
|
def configure_options(options)
|
||||||
Tokenizer.new(source, @line_numbers)
|
if (profiling = options[:profile])
|
||||||
end
|
|
||||||
|
|
||||||
def with_profiling(context)
|
|
||||||
if @profiling && !context.partial
|
|
||||||
raise "Profiler not loaded, require 'liquid/profiler' first" unless defined?(Liquid::Profiler)
|
raise "Profiler not loaded, require 'liquid/profiler' first" unless defined?(Liquid::Profiler)
|
||||||
|
|
||||||
@profiler = Profiler.new(context.template_name)
|
|
||||||
@profiler.start
|
|
||||||
|
|
||||||
begin
|
|
||||||
yield
|
|
||||||
ensure
|
|
||||||
@profiler.stop
|
|
||||||
end
|
|
||||||
else
|
|
||||||
yield
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@options = options
|
||||||
|
@profiling = profiling
|
||||||
|
@line_numbers = options[:line_numbers] || @profiling
|
||||||
|
parse_context = options.is_a?(ParseContext) ? options : ParseContext.new(options)
|
||||||
|
@warnings = parse_context.warnings
|
||||||
|
parse_context
|
||||||
end
|
end
|
||||||
|
|
||||||
def apply_options_to_context(context, options)
|
def apply_options_to_context(context, options)
|
||||||
|
|||||||
@@ -65,15 +65,23 @@ module Liquid
|
|||||||
|
|
||||||
return if p.look(:end_of_string)
|
return if p.look(:end_of_string)
|
||||||
|
|
||||||
@name = p.expression
|
@name = Expression.parse(p.expression)
|
||||||
while p.consume?(:pipe)
|
while p.consume?(:pipe)
|
||||||
filtername = p.consume(:id)
|
filtername = p.consume(:id)
|
||||||
filterargs = p.consume?(:colon) ? p.arguments : [[]]
|
filterargs = p.consume?(:colon) ? parse_filterargs(p) : []
|
||||||
@filters << [filtername, *filterargs]
|
@filters << parse_filter_expressions(filtername, filterargs)
|
||||||
end
|
end
|
||||||
p.consume(:end_of_string)
|
p.consume(:end_of_string)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def parse_filterargs(p)
|
||||||
|
# first argument
|
||||||
|
filterargs = [p.argument]
|
||||||
|
# followed by comma separated others
|
||||||
|
filterargs << p.argument while p.consume?(:comma)
|
||||||
|
filterargs
|
||||||
|
end
|
||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
obj = context.evaluate(@name)
|
obj = context.evaluate(@name)
|
||||||
|
|
||||||
|
|||||||
@@ -7,66 +7,30 @@ module Liquid
|
|||||||
|
|
||||||
attr_reader :name, :lookups
|
attr_reader :name, :lookups
|
||||||
|
|
||||||
class << self
|
def self.parse(markup)
|
||||||
def lax_parse(markup)
|
new(markup)
|
||||||
lookups = markup.scan(VariableParser)
|
|
||||||
|
|
||||||
name = lookups.shift
|
|
||||||
if name =~ SQUARE_BRACKETED
|
|
||||||
name = Expression.parse(Regexp.last_match(1))
|
|
||||||
end
|
|
||||||
|
|
||||||
command_flags = 0
|
|
||||||
|
|
||||||
lookups.each_index do |i|
|
|
||||||
lookup = lookups[i]
|
|
||||||
if lookup =~ SQUARE_BRACKETED
|
|
||||||
lookups[i] = Expression.parse(Regexp.last_match(1))
|
|
||||||
elsif COMMAND_METHODS.include?(lookup)
|
|
||||||
command_flags |= 1 << i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
new(name, lookups, command_flags)
|
|
||||||
end
|
|
||||||
|
|
||||||
def strict_parse(p)
|
|
||||||
if p.look(:id)
|
|
||||||
name = p.consume
|
|
||||||
else
|
|
||||||
p.consume(:open_square)
|
|
||||||
name = p.expression
|
|
||||||
p.consume(:close_square)
|
|
||||||
end
|
|
||||||
|
|
||||||
lookups = []
|
|
||||||
command_flags = 0
|
|
||||||
|
|
||||||
loop do
|
|
||||||
if p.consume?(:open_square)
|
|
||||||
lookups << p.expression
|
|
||||||
p.consume(:close_square)
|
|
||||||
elsif p.consume?(:dot)
|
|
||||||
lookup = p.consume(:id)
|
|
||||||
lookups << lookup
|
|
||||||
if COMMAND_METHODS.include?(lookup)
|
|
||||||
command_flags |= 1 << (lookups.length - 1)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
new(name, lookups, command_flags)
|
|
||||||
end
|
|
||||||
|
|
||||||
private :new
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(name, lookups, command_flags)
|
def initialize(markup)
|
||||||
|
lookups = markup.scan(VariableParser)
|
||||||
|
|
||||||
|
name = lookups.shift
|
||||||
|
if name =~ SQUARE_BRACKETED
|
||||||
|
name = Expression.parse(Regexp.last_match(1))
|
||||||
|
end
|
||||||
@name = name
|
@name = name
|
||||||
@lookups = lookups
|
|
||||||
@command_flags = command_flags
|
@lookups = lookups
|
||||||
|
@command_flags = 0
|
||||||
|
|
||||||
|
@lookups.each_index do |i|
|
||||||
|
lookup = lookups[i]
|
||||||
|
if lookup =~ SQUARE_BRACKETED
|
||||||
|
lookups[i] = Expression.parse(Regexp.last_match(1))
|
||||||
|
elsif COMMAND_METHODS.include?(lookup)
|
||||||
|
@command_flags |= 1 << i
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def evaluate(context)
|
def evaluate(context)
|
||||||
@@ -111,19 +75,6 @@ module Liquid
|
|||||||
self.class == other.class && state == other.state
|
self.class == other.class && state == other.state
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
|
||||||
str = name.dup
|
|
||||||
lookups.each do |lookup|
|
|
||||||
str +=
|
|
||||||
if lookup.instance_of?(String)
|
|
||||||
"['#{lookup}']"
|
|
||||||
else
|
|
||||||
"[#{lookup}]"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
str
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def state
|
def state
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
VERSION = "4.0.3"
|
VERSION = "5.0.0"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ Gem::Specification.new do |s|
|
|||||||
s.required_ruby_version = ">= 2.5.0"
|
s.required_ruby_version = ">= 2.5.0"
|
||||||
s.required_rubygems_version = ">= 1.3.7"
|
s.required_rubygems_version = ">= 1.3.7"
|
||||||
|
|
||||||
|
s.metadata['allowed_push_host'] = 'https://rubygems.org'
|
||||||
|
|
||||||
s.test_files = Dir.glob("{test}/**/*")
|
s.test_files = Dir.glob("{test}/**/*")
|
||||||
s.files = Dir.glob("{lib}/**/*") + %w(LICENSE README.md)
|
s.files = Dir.glob("{lib}/**/*") + %w(LICENSE README.md)
|
||||||
|
|
||||||
|
|||||||
@@ -73,10 +73,14 @@ class ThemeRunner
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def render_layout(template, layout, assigns)
|
||||||
|
assigns['content_for_layout'] = template.render!(assigns)
|
||||||
|
layout&.render!(assigns)
|
||||||
|
end
|
||||||
|
|
||||||
def compile_and_render(template, layout, assigns, page_template, template_file)
|
def compile_and_render(template, layout, assigns, page_template, template_file)
|
||||||
compiled_test = compile_test(template, layout, assigns, page_template, template_file)
|
compiled_test = compile_test(template, layout, assigns, page_template, template_file)
|
||||||
assigns['content_for_layout'] = compiled_test[:tmpl].render!(assigns)
|
render_layout(compiled_test[:tmpl], compiled_test[:layout], compiled_test[:assigns])
|
||||||
compiled_test[:layout].render!(assigns) if layout
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def compile_all_tests
|
def compile_all_tests
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class AssignTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
with_error_mode(:lax) do
|
with_error_mode(:lax) do
|
||||||
assert Template.parse("{% assign foo = ('X' | downcase) %}")
|
assert(Template.parse("{% assign foo = ('X' | downcase) %}"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class BlockTest < Minitest::Test
|
|||||||
|
|
||||||
def test_with_custom_tag
|
def test_with_custom_tag
|
||||||
with_custom_tag('testtag', Block) do
|
with_custom_tag('testtag', Block) do
|
||||||
assert Liquid::Template.parse("{% testtag %} {% endtesttag %}")
|
assert(Liquid::Template.parse("{% testtag %} {% endtesttag %}"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -28,13 +28,13 @@ class BlockTest < Minitest::Test
|
|||||||
with_custom_tag('blabla', klass1) do
|
with_custom_tag('blabla', klass1) do
|
||||||
template = Liquid::Template.parse("{% blabla %} bla {% endblabla %}")
|
template = Liquid::Template.parse("{% blabla %} bla {% endblabla %}")
|
||||||
|
|
||||||
assert_equal 'hello', template.render
|
assert_equal('hello', template.render)
|
||||||
|
|
||||||
buf = +''
|
buf = +''
|
||||||
output = template.render({}, output: buf)
|
output = template.render({}, output: buf)
|
||||||
assert_equal 'hello', output
|
assert_equal('hello', output)
|
||||||
assert_equal 'hello', buf
|
assert_equal('hello', buf)
|
||||||
assert_equal buf.object_id, output.object_id
|
assert_equal(buf.object_id, output.object_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
klass2 = Class.new(klass1) do
|
klass2 = Class.new(klass1) do
|
||||||
@@ -46,33 +46,13 @@ class BlockTest < Minitest::Test
|
|||||||
with_custom_tag('blabla', klass2) do
|
with_custom_tag('blabla', klass2) do
|
||||||
template = Liquid::Template.parse("{% blabla %} foo {% endblabla %}")
|
template = Liquid::Template.parse("{% blabla %} foo {% endblabla %}")
|
||||||
|
|
||||||
assert_equal 'foohellobar', template.render
|
assert_equal('foohellobar', template.render)
|
||||||
|
|
||||||
buf = +''
|
buf = +''
|
||||||
output = template.render({}, output: buf)
|
output = template.render({}, output: buf)
|
||||||
assert_equal 'foohellobar', output
|
assert_equal('foohellobar', output)
|
||||||
assert_equal 'foohellobar', buf
|
assert_equal('foohellobar', buf)
|
||||||
assert_equal buf.object_id, output.object_id
|
assert_equal(buf.object_id, output.object_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_instrument_for_bug_1346
|
|
||||||
calls = []
|
|
||||||
Liquid::Usage.stub(:increment, ->(name) { calls << name }) do
|
|
||||||
Liquid::Template.parse("{% for i in (1..2) %}{{ i }}{% endfor {% foo %}")
|
|
||||||
end
|
|
||||||
assert_equal(["end_tag_params"], calls)
|
|
||||||
|
|
||||||
calls = []
|
|
||||||
Liquid::Usage.stub(:increment, ->(name) { calls << name }) do
|
|
||||||
Liquid::Template.parse("{% for i in (1..2) %}{{ i }}{% endfor test %}")
|
|
||||||
end
|
|
||||||
assert_equal(["end_tag_params"], calls)
|
|
||||||
|
|
||||||
calls = []
|
|
||||||
Liquid::Usage.stub(:increment, ->(name) { calls << name }) do
|
|
||||||
Liquid::Template.parse("{% for i in (1..2) %}{{ i }}{% endfor %}")
|
|
||||||
end
|
|
||||||
assert_equal([], calls)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -461,6 +461,7 @@ class ContextTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_interrupt_avoids_object_allocations
|
def test_interrupt_avoids_object_allocations
|
||||||
|
@context.interrupt? # ruby 3.0.0 allocates on the first call
|
||||||
assert_no_object_allocations do
|
assert_no_object_allocations do
|
||||||
@context.interrupt?
|
@context.interrupt?
|
||||||
end
|
end
|
||||||
@@ -566,9 +567,9 @@ class ContextTest < Minitest::Test
|
|||||||
def test_disables_tag_specified
|
def test_disables_tag_specified
|
||||||
context = Context.new
|
context = Context.new
|
||||||
context.with_disabled_tags(%w(foo bar)) do
|
context.with_disabled_tags(%w(foo bar)) do
|
||||||
assert_equal true, context.tag_disabled?("foo")
|
assert_equal(true, context.tag_disabled?("foo"))
|
||||||
assert_equal true, context.tag_disabled?("bar")
|
assert_equal(true, context.tag_disabled?("bar"))
|
||||||
assert_equal false, context.tag_disabled?("unknown")
|
assert_equal(false, context.tag_disabled?("unknown"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -576,19 +577,19 @@ class ContextTest < Minitest::Test
|
|||||||
context = Context.new
|
context = Context.new
|
||||||
context.with_disabled_tags(["foo"]) do
|
context.with_disabled_tags(["foo"]) do
|
||||||
context.with_disabled_tags(["foo"]) do
|
context.with_disabled_tags(["foo"]) do
|
||||||
assert_equal true, context.tag_disabled?("foo")
|
assert_equal(true, context.tag_disabled?("foo"))
|
||||||
assert_equal false, context.tag_disabled?("bar")
|
assert_equal(false, context.tag_disabled?("bar"))
|
||||||
end
|
end
|
||||||
context.with_disabled_tags(["bar"]) do
|
context.with_disabled_tags(["bar"]) do
|
||||||
assert_equal true, context.tag_disabled?("foo")
|
assert_equal(true, context.tag_disabled?("foo"))
|
||||||
assert_equal true, context.tag_disabled?("bar")
|
assert_equal(true, context.tag_disabled?("bar"))
|
||||||
context.with_disabled_tags(["foo"]) do
|
context.with_disabled_tags(["foo"]) do
|
||||||
assert_equal true, context.tag_disabled?("foo")
|
assert_equal(true, context.tag_disabled?("foo"))
|
||||||
assert_equal true, context.tag_disabled?("bar")
|
assert_equal(true, context.tag_disabled?("bar"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
assert_equal true, context.tag_disabled?("foo")
|
assert_equal(true, context.tag_disabled?("foo"))
|
||||||
assert_equal false, context.tag_disabled?("bar")
|
assert_equal(false, context.tag_disabled?("bar"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -606,16 +607,16 @@ class ContextTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
with_global_filter(global) do
|
with_global_filter(global) do
|
||||||
assert_equal 'Global test', Template.parse("{{'test' | notice }}").render!
|
assert_equal('Global test', Template.parse("{{'test' | notice }}").render!)
|
||||||
assert_equal 'Local test', Template.parse("{{'test' | notice }}").render!({}, filters: [local])
|
assert_equal('Local test', Template.parse("{{'test' | notice }}").render!({}, filters: [local]))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_has_key_will_not_add_an_error_for_missing_keys
|
def test_has_key_will_not_add_an_error_for_missing_keys
|
||||||
with_error_mode :strict do
|
with_error_mode(:strict) do
|
||||||
context = Context.new
|
context = Context.new
|
||||||
context.key?('unknown')
|
context.key?('unknown')
|
||||||
assert_empty context.errors
|
assert_empty(context.errors)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ class DropsTest < Minitest::Test
|
|||||||
def test_object_methods_not_allowed
|
def test_object_methods_not_allowed
|
||||||
[:dup, :clone, :singleton_class, :eval, :class_eval, :inspect].each do |method|
|
[:dup, :clone, :singleton_class, :eval, :class_eval, :inspect].each do |method|
|
||||||
output = Liquid::Template.parse(" {{ product.#{method} }} ").render!('product' => ProductDrop.new)
|
output = Liquid::Template.parse(" {{ product.#{method} }} ").render!('product' => ProductDrop.new)
|
||||||
assert_equal ' ', output
|
assert_equal(' ', output)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -210,28 +210,28 @@ class DropsTest < Minitest::Test
|
|||||||
|
|
||||||
def test_enumerable_drop_will_invoke_liquid_method_missing_for_clashing_method_names
|
def test_enumerable_drop_will_invoke_liquid_method_missing_for_clashing_method_names
|
||||||
["select", "each", "map", "cycle"].each do |method|
|
["select", "each", "map", "cycle"].each do |method|
|
||||||
assert_equal method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
|
assert_equal(method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new))
|
||||||
assert_equal method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new)
|
assert_equal(method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new))
|
||||||
assert_equal method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
|
assert_equal(method.to_s, Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new))
|
||||||
assert_equal method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
|
assert_equal(method.to_s, Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_some_enumerable_methods_still_get_invoked
|
def test_some_enumerable_methods_still_get_invoked
|
||||||
[:count, :max].each do |method|
|
[:count, :max].each do |method|
|
||||||
assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
|
assert_equal("3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new))
|
||||||
assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
|
assert_equal("3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new))
|
||||||
assert_equal "3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
|
assert_equal("3", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new))
|
||||||
assert_equal "3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new)
|
assert_equal("3", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new))
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_equal("yes", Liquid::Template.parse("{% if collection contains 3 %}yes{% endif %}").render!('collection' => RealEnumerableDrop.new))
|
assert_equal("yes", Liquid::Template.parse("{% if collection contains 3 %}yes{% endif %}").render!('collection' => RealEnumerableDrop.new))
|
||||||
|
|
||||||
[:min, :first].each do |method|
|
[:min, :first].each do |method|
|
||||||
assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new)
|
assert_equal("1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => RealEnumerableDrop.new))
|
||||||
assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new)
|
assert_equal("1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => RealEnumerableDrop.new))
|
||||||
assert_equal "1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new)
|
assert_equal("1", Liquid::Template.parse("{{collection.#{method}}}").render!('collection' => EnumerableDrop.new))
|
||||||
assert_equal "1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new)
|
assert_equal("1", Liquid::Template.parse("{{collection[\"#{method}\"]}}").render!('collection' => EnumerableDrop.new))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ class ErrorHandlingTest < Minitest::Test
|
|||||||
|
|
||||||
# Liquid should not catch Exceptions that are not subclasses of StandardError, like Interrupt and NoMemoryError
|
# Liquid should not catch Exceptions that are not subclasses of StandardError, like Interrupt and NoMemoryError
|
||||||
def test_exceptions_propagate
|
def test_exceptions_propagate
|
||||||
assert_raises Exception do
|
assert_raises(Exception) do
|
||||||
template = Liquid::Template.parse('{{ errors.exception }}')
|
template = Liquid::Template.parse('{{ errors.exception }}')
|
||||||
template.render('errors' => ErrorDrop.new)
|
template.render('errors' => ErrorDrop.new)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -29,24 +29,17 @@ class ExpressionTest < Minitest::Test
|
|||||||
def test_range
|
def test_range
|
||||||
assert_equal(1..2, parse_and_eval("(1..2)"))
|
assert_equal(1..2, parse_and_eval("(1..2)"))
|
||||||
assert_equal(3..4, parse_and_eval(" ( 3 .. 4 ) "))
|
assert_equal(3..4, parse_and_eval(" ( 3 .. 4 ) "))
|
||||||
assert_equal(0..0, parse_and_eval("('a'..'b')"))
|
|
||||||
|
|
||||||
with_error_mode(:strict) do
|
|
||||||
assert_raises(Liquid::ArgumentError) { parse_and_eval("(1..(1..5))") }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def parse_and_eval(markup, **assigns)
|
def parse_and_eval(markup, **assigns)
|
||||||
expression =
|
if Liquid::Template.error_mode == :strict
|
||||||
if Liquid::Template.error_mode == :strict
|
p = Liquid::Parser.new(markup)
|
||||||
p = Liquid::Parser.new(markup)
|
markup = p.expression
|
||||||
p.expression
|
p.consume(:end_of_string)
|
||||||
else
|
end
|
||||||
Liquid::Expression.parse(markup)
|
expression = Liquid::Expression.parse(markup)
|
||||||
end
|
|
||||||
|
|
||||||
context = Liquid::Context.new(assigns)
|
context = Liquid::Context.new(assigns)
|
||||||
context.evaluate(expression)
|
context.evaluate(expression)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -169,9 +169,9 @@ class FiltersInTemplate < Minitest::Test
|
|||||||
|
|
||||||
def test_local_global
|
def test_local_global
|
||||||
with_global_filter(MoneyFilter) do
|
with_global_filter(MoneyFilter) do
|
||||||
assert_equal " 1000$ ", Template.parse("{{1000 | money}}").render!(nil, nil)
|
assert_equal(" 1000$ ", Template.parse("{{1000 | money}}").render!(nil, nil))
|
||||||
assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, filters: CanadianMoneyFilter)
|
assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, filters: CanadianMoneyFilter))
|
||||||
assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, filters: [CanadianMoneyFilter])
|
assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render!(nil, filters: [CanadianMoneyFilter]))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class HashOrderingTest < Minitest::Test
|
|||||||
|
|
||||||
def test_global_register_order
|
def test_global_register_order
|
||||||
with_global_filter(MoneyFilter, CanadianMoneyFilter) do
|
with_global_filter(MoneyFilter, CanadianMoneyFilter) do
|
||||||
assert_equal " 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, nil)
|
assert_equal(" 1000$ CAD ", Template.parse("{{1000 | money}}").render(nil, nil))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class ParsingQuirksTest < Minitest::Test
|
|||||||
assert(Template.parse("{{test}}"))
|
assert(Template.parse("{{test}}"))
|
||||||
|
|
||||||
with_error_mode(:lax) do
|
with_error_mode(:lax) do
|
||||||
assert Template.parse("{{|test}}")
|
assert(Template.parse("{{|test}}"))
|
||||||
end
|
end
|
||||||
|
|
||||||
with_error_mode(:strict) do
|
with_error_mode(:strict) do
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
class RenderProfilingTest < Minitest::Test
|
class ProfilerTest < Minitest::Test
|
||||||
include Liquid
|
include Liquid
|
||||||
|
|
||||||
class ProfilingFileSystem
|
class ProfilingFileSystem
|
||||||
@@ -62,6 +62,17 @@ class RenderProfilingTest < Minitest::Test
|
|||||||
assert_equal(2, included_children[1].line_number)
|
assert_equal(2, included_children[1].line_number)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_profiling_render_tag
|
||||||
|
t = Template.parse("{% render 'a_template' %}", profile: true)
|
||||||
|
t.render!
|
||||||
|
|
||||||
|
render_children = t.profiler[0].children
|
||||||
|
render_children.each do |timing|
|
||||||
|
assert_equal('a_template', timing.partial)
|
||||||
|
end
|
||||||
|
assert_equal([1, 2], render_children.map(&:line_number))
|
||||||
|
end
|
||||||
|
|
||||||
def test_profiling_times_the_rendering_of_tokens
|
def test_profiling_times_the_rendering_of_tokens
|
||||||
t = Template.parse("{% include 'a_template' %}", profile: true)
|
t = Template.parse("{% include 'a_template' %}", profile: true)
|
||||||
t.render!
|
t.render!
|
||||||
@@ -77,6 +88,37 @@ class RenderProfilingTest < Minitest::Test
|
|||||||
assert(t.profiler.total_render_time >= 0, "Total render time was not calculated")
|
assert(t.profiler.total_render_time >= 0, "Total render time was not calculated")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class SleepTag < Liquid::Tag
|
||||||
|
def initialize(tag_name, markup, parse_context)
|
||||||
|
super
|
||||||
|
@duration = Float(markup)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_to_output_buffer(_context, _output)
|
||||||
|
sleep(@duration)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_profiling_multiple_renders
|
||||||
|
with_custom_tag('sleep', SleepTag) do
|
||||||
|
context = Liquid::Context.new
|
||||||
|
t = Liquid::Template.parse("{% sleep 0.001 %}", profile: true)
|
||||||
|
context.template_name = 'index'
|
||||||
|
t.render!(context)
|
||||||
|
context.template_name = 'layout'
|
||||||
|
first_render_time = context.profiler.total_time
|
||||||
|
t.render!(context)
|
||||||
|
|
||||||
|
profiler = context.profiler
|
||||||
|
children = profiler.children
|
||||||
|
assert_operator(first_render_time, :>=, 0.001)
|
||||||
|
assert_operator(profiler.total_time, :>=, 0.001 + first_render_time)
|
||||||
|
assert_equal(["index", "layout"], children.map(&:template_name))
|
||||||
|
assert_equal([nil, nil], children.map(&:code))
|
||||||
|
assert_equal(profiler.total_time, children.map(&:total_time).reduce(&:+))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_profiling_uses_include_to_mark_children
|
def test_profiling_uses_include_to_mark_children
|
||||||
t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}", profile: true)
|
t = Template.parse("{{ 'a string' | upcase }}\n{% include 'a_template' %}", profile: true)
|
||||||
t.render!
|
t.render!
|
||||||
@@ -91,7 +133,7 @@ class RenderProfilingTest < Minitest::Test
|
|||||||
|
|
||||||
include_node = t.profiler[1]
|
include_node = t.profiler[1]
|
||||||
include_node.children.each do |child|
|
include_node.children.each do |child|
|
||||||
assert_equal "a_template", child.partial
|
assert_equal("a_template", child.partial)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -101,12 +143,12 @@ class RenderProfilingTest < Minitest::Test
|
|||||||
|
|
||||||
a_template = t.profiler[1]
|
a_template = t.profiler[1]
|
||||||
a_template.children.each do |child|
|
a_template.children.each do |child|
|
||||||
assert_equal "a_template", child.partial
|
assert_equal("a_template", child.partial)
|
||||||
end
|
end
|
||||||
|
|
||||||
b_template = t.profiler[2]
|
b_template = t.profiler[2]
|
||||||
b_template.children.each do |child|
|
b_template.children.each do |child|
|
||||||
assert_equal "b_template", child.partial
|
assert_equal("b_template", child.partial)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -116,12 +158,12 @@ class RenderProfilingTest < Minitest::Test
|
|||||||
|
|
||||||
a_template1 = t.profiler[1]
|
a_template1 = t.profiler[1]
|
||||||
a_template1.children.each do |child|
|
a_template1.children.each do |child|
|
||||||
assert_equal "a_template", child.partial
|
assert_equal("a_template", child.partial)
|
||||||
end
|
end
|
||||||
|
|
||||||
a_template2 = t.profiler[2]
|
a_template2 = t.profiler[2]
|
||||||
a_template2.children.each do |child|
|
a_template2.children.each do |child|
|
||||||
assert_equal "a_template", child.partial
|
assert_equal("a_template", child.partial)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal('1', @filters.url_decode(1))
|
assert_equal('1', @filters.url_decode(1))
|
||||||
assert_equal('2001-02-03', @filters.url_decode(Date.new(2001, 2, 3)))
|
assert_equal('2001-02-03', @filters.url_decode(Date.new(2001, 2, 3)))
|
||||||
assert_nil(@filters.url_decode(nil))
|
assert_nil(@filters.url_decode(nil))
|
||||||
exception = assert_raises Liquid::ArgumentError do
|
exception = assert_raises(Liquid::ArgumentError) do
|
||||||
@filters.url_decode('%ff')
|
@filters.url_decode('%ff')
|
||||||
end
|
end
|
||||||
assert_equal('Liquid error: invalid byte sequence in UTF-8', exception.message)
|
assert_equal('Liquid error: invalid byte sequence in UTF-8', exception.message)
|
||||||
@@ -175,6 +175,9 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
)
|
)
|
||||||
assert_equal("测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5))
|
assert_equal("测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5))
|
||||||
assert_equal('one two1', @filters.truncatewords("one two three", 2, 1))
|
assert_equal('one two1', @filters.truncatewords("one two three", 2, 1))
|
||||||
|
assert_equal('one two three...', @filters.truncatewords("one two\tthree\nfour", 3))
|
||||||
|
assert_equal('one two...', @filters.truncatewords("one two three four", 2))
|
||||||
|
assert_equal('one...', @filters.truncatewords("one two three four", 0))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_strip_html
|
def test_strip_html
|
||||||
@@ -286,7 +289,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
[3],
|
[3],
|
||||||
]
|
]
|
||||||
|
|
||||||
assert_raises Liquid::ArgumentError do
|
assert_raises(Liquid::ArgumentError) do
|
||||||
@filters.sort(foo, "bar")
|
@filters.sort(foo, "bar")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -302,7 +305,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
[3],
|
[3],
|
||||||
]
|
]
|
||||||
|
|
||||||
assert_raises Liquid::ArgumentError do
|
assert_raises(Liquid::ArgumentError) do
|
||||||
@filters.sort_natural(foo, "bar")
|
@filters.sort_natural(foo, "bar")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -337,7 +340,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
[3],
|
[3],
|
||||||
]
|
]
|
||||||
|
|
||||||
assert_raises Liquid::ArgumentError do
|
assert_raises(Liquid::ArgumentError) do
|
||||||
@filters.uniq(foo, "bar")
|
@filters.uniq(foo, "bar")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -353,7 +356,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
[3],
|
[3],
|
||||||
]
|
]
|
||||||
|
|
||||||
assert_raises Liquid::ArgumentError do
|
assert_raises(Liquid::ArgumentError) do
|
||||||
@filters.compact(foo, "bar")
|
@filters.compact(foo, "bar")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -430,7 +433,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
[3],
|
[3],
|
||||||
]
|
]
|
||||||
|
|
||||||
assert_raises Liquid::ArgumentError do
|
assert_raises(Liquid::ArgumentError) do
|
||||||
@filters.map(foo, "bar")
|
@filters.map(foo, "bar")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -441,7 +444,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
[2],
|
[2],
|
||||||
[3],
|
[3],
|
||||||
]
|
]
|
||||||
assert_raises Liquid::ArgumentError do
|
assert_raises(Liquid::ArgumentError) do
|
||||||
@filters.map(foo, nil)
|
@filters.map(foo, nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -485,8 +488,8 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal('', @filters.date('', "%B"))
|
assert_equal('', @filters.date('', "%B"))
|
||||||
|
|
||||||
with_timezone("UTC") do
|
with_timezone("UTC") do
|
||||||
assert_equal "07/05/2006", @filters.date(1152098955, "%m/%d/%Y")
|
assert_equal("07/05/2006", @filters.date(1152098955, "%m/%d/%Y"))
|
||||||
assert_equal "07/05/2006", @filters.date("1152098955", "%m/%d/%Y")
|
assert_equal("07/05/2006", @filters.date("1152098955", "%m/%d/%Y"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -539,6 +542,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
|
|
||||||
def test_newlines_to_br
|
def test_newlines_to_br
|
||||||
assert_template_result("a<br />\nb<br />\nc", "{{ source | newline_to_br }}", 'source' => "a\nb\nc")
|
assert_template_result("a<br />\nb<br />\nc", "{{ source | newline_to_br }}", 'source' => "a\nb\nc")
|
||||||
|
assert_template_result("a<br />\nb<br />\nc", "{{ source | newline_to_br }}", 'source' => "a\r\nb\nc")
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_plus
|
def test_plus
|
||||||
@@ -587,7 +591,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
|
|
||||||
assert_template_result("0.5", "{{ 2.0 | divided_by:4 }}")
|
assert_template_result("0.5", "{{ 2.0 | divided_by:4 }}")
|
||||||
assert_raises(Liquid::ZeroDivisionError) do
|
assert_raises(Liquid::ZeroDivisionError) do
|
||||||
assert_template_result "4", "{{ 1 | modulo: 0 }}"
|
assert_template_result("4", "{{ 1 | modulo: 0 }}")
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_template_result("5", "{{ price | divided_by:2 }}", 'price' => NumberLikeThing.new(10))
|
assert_template_result("5", "{{ price | divided_by:2 }}", 'price' => NumberLikeThing.new(10))
|
||||||
@@ -596,7 +600,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
def test_modulo
|
def test_modulo
|
||||||
assert_template_result("1", "{{ 3 | modulo:2 }}")
|
assert_template_result("1", "{{ 3 | modulo:2 }}")
|
||||||
assert_raises(Liquid::ZeroDivisionError) do
|
assert_raises(Liquid::ZeroDivisionError) do
|
||||||
assert_template_result "4", "{{ 1 | modulo: 0 }}"
|
assert_template_result("4", "{{ 1 | modulo: 0 }}")
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_template_result("1", "{{ price | modulo:2 }}", 'price' => NumberLikeThing.new(3))
|
assert_template_result("1", "{{ price | modulo:2 }}", 'price' => NumberLikeThing.new(3))
|
||||||
@@ -607,7 +611,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_template_result("4", "{{ '4.3' | round }}")
|
assert_template_result("4", "{{ '4.3' | round }}")
|
||||||
assert_template_result("4.56", "{{ input | round: 2 }}", 'input' => 4.5612)
|
assert_template_result("4.56", "{{ input | round: 2 }}", 'input' => 4.5612)
|
||||||
assert_raises(Liquid::FloatDomainError) do
|
assert_raises(Liquid::FloatDomainError) do
|
||||||
assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | round }}"
|
assert_template_result("4", "{{ 1.0 | divided_by: 0.0 | round }}")
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_template_result("5", "{{ price | round }}", 'price' => NumberLikeThing.new(4.6))
|
assert_template_result("5", "{{ price | round }}", 'price' => NumberLikeThing.new(4.6))
|
||||||
@@ -618,7 +622,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_template_result("5", "{{ input | ceil }}", 'input' => 4.6)
|
assert_template_result("5", "{{ input | ceil }}", 'input' => 4.6)
|
||||||
assert_template_result("5", "{{ '4.3' | ceil }}")
|
assert_template_result("5", "{{ '4.3' | ceil }}")
|
||||||
assert_raises(Liquid::FloatDomainError) do
|
assert_raises(Liquid::FloatDomainError) do
|
||||||
assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | ceil }}"
|
assert_template_result("4", "{{ 1.0 | divided_by: 0.0 | ceil }}")
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_template_result("5", "{{ price | ceil }}", 'price' => NumberLikeThing.new(4.6))
|
assert_template_result("5", "{{ price | ceil }}", 'price' => NumberLikeThing.new(4.6))
|
||||||
@@ -628,7 +632,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_template_result("4", "{{ input | floor }}", 'input' => 4.6)
|
assert_template_result("4", "{{ input | floor }}", 'input' => 4.6)
|
||||||
assert_template_result("4", "{{ '4.3' | floor }}")
|
assert_template_result("4", "{{ '4.3' | floor }}")
|
||||||
assert_raises(Liquid::FloatDomainError) do
|
assert_raises(Liquid::FloatDomainError) do
|
||||||
assert_template_result "4", "{{ 1.0 | divided_by: 0.0 | floor }}"
|
assert_template_result("4", "{{ 1.0 | divided_by: 0.0 | floor }}")
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_template_result("5", "{{ price | floor }}", 'price' => NumberLikeThing.new(5.4))
|
assert_template_result("5", "{{ price | floor }}", 'price' => NumberLikeThing.new(5.4))
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ class TagTest < Minitest::Test
|
|||||||
with_custom_tag('blabla', klass1) do
|
with_custom_tag('blabla', klass1) do
|
||||||
template = Liquid::Template.parse("{% blabla %}")
|
template = Liquid::Template.parse("{% blabla %}")
|
||||||
|
|
||||||
assert_equal 'hello', template.render
|
assert_equal('hello', template.render)
|
||||||
|
|
||||||
buf = +''
|
buf = +''
|
||||||
output = template.render({}, output: buf)
|
output = template.render({}, output: buf)
|
||||||
assert_equal 'hello', output
|
assert_equal('hello', output)
|
||||||
assert_equal 'hello', buf
|
assert_equal('hello', buf)
|
||||||
assert_equal buf.object_id, output.object_id
|
assert_equal(buf.object_id, output.object_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
klass2 = Class.new(klass1) do
|
klass2 = Class.new(klass1) do
|
||||||
@@ -33,13 +33,13 @@ class TagTest < Minitest::Test
|
|||||||
with_custom_tag('blabla', klass2) do
|
with_custom_tag('blabla', klass2) do
|
||||||
template = Liquid::Template.parse("{% blabla %}")
|
template = Liquid::Template.parse("{% blabla %}")
|
||||||
|
|
||||||
assert_equal 'foohellobar', template.render
|
assert_equal('foohellobar', template.render)
|
||||||
|
|
||||||
buf = +''
|
buf = +''
|
||||||
output = template.render({}, output: buf)
|
output = template.render({}, output: buf)
|
||||||
assert_equal 'foohellobar', output
|
assert_equal('foohellobar', output)
|
||||||
assert_equal 'foohellobar', buf
|
assert_equal('foohellobar', buf)
|
||||||
assert_equal buf.object_id, output.object_id
|
assert_equal(buf.object_id, output.object_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ class IfElseTagTest < Minitest::Test
|
|||||||
tests.each do |vals, expected|
|
tests.each do |vals, expected|
|
||||||
a, b, c = vals
|
a, b, c = vals
|
||||||
assigns = { 'a' => a, 'b' => b, 'c' => c }
|
assigns = { 'a' => a, 'b' => b, 'c' => c }
|
||||||
assert_template_result expected.to_s, tpl, assigns, assigns.to_s
|
assert_template_result(expected.to_s, tpl, assigns, assigns.to_s)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -226,13 +226,13 @@ class IncludeTagTest < Minitest::Test
|
|||||||
Template.parse("{% include template %}", error_mode: :strict).render!("template" => '{{ "X" || downcase }}')
|
Template.parse("{% include template %}", error_mode: :strict).render!("template" => '{{ "X" || downcase }}')
|
||||||
end
|
end
|
||||||
with_error_mode(:lax) do
|
with_error_mode(:lax) do
|
||||||
assert_equal 'x', Template.parse("{% include template %}", error_mode: :strict, include_options_blacklist: true).render!("template" => '{{ "X" || downcase }}')
|
assert_equal('x', Template.parse("{% include template %}", error_mode: :strict, include_options_blacklist: true).render!("template" => '{{ "X" || downcase }}'))
|
||||||
end
|
end
|
||||||
assert_raises(Liquid::SyntaxError) do
|
assert_raises(Liquid::SyntaxError) do
|
||||||
Template.parse("{% include template %}", error_mode: :strict, include_options_blacklist: [:locale]).render!("template" => '{{ "X" || downcase }}')
|
Template.parse("{% include template %}", error_mode: :strict, include_options_blacklist: [:locale]).render!("template" => '{{ "X" || downcase }}')
|
||||||
end
|
end
|
||||||
with_error_mode(:lax) do
|
with_error_mode(:lax) do
|
||||||
assert_equal 'x', Template.parse("{% include template %}", error_mode: :strict, include_options_blacklist: [:error_mode]).render!("template" => '{{ "X" || downcase }}')
|
assert_equal('x', Template.parse("{% include template %}", error_mode: :strict, include_options_blacklist: [:error_mode]).render!("template" => '{{ "X" || downcase }}'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
59
test/integration/tags/inline_comment_test.rb
Normal file
59
test/integration/tags/inline_comment_test.rb
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class InlineCommentTest < Minitest::Test
|
||||||
|
include Liquid
|
||||||
|
|
||||||
|
def test_tag_in_different_styles
|
||||||
|
assert_template_result('', '{% # This text gets ignored %}')
|
||||||
|
assert_template_result('', '{%# This text gets ignored #%}')
|
||||||
|
assert_template_result('', '{%# This text gets ignored %}')
|
||||||
|
assert_template_result('', '{%#- This text gets ignored -#%}')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_test_syntax_error
|
||||||
|
assert_template_result('fail', '{% #This doesnt work %}')
|
||||||
|
|
||||||
|
assert false
|
||||||
|
rescue
|
||||||
|
# ok good
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_tag_ws_stripping
|
||||||
|
assert_template_result('', ' {%#- This text gets ignored -#%} ')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_comment_inline_tag
|
||||||
|
assert_template_result('ok', '{% echo "ok" # output something from a tag %}')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_comment_line_before_tag
|
||||||
|
assert_template_result('ok', '{% # this sort of comment also
|
||||||
|
echo "ok" %}')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_comment_inline_variable
|
||||||
|
assert_template_result('ok', '{{ "ok" # output something from a variable }}')
|
||||||
|
assert_template_result('ok', '{{ "OK" | downcase # output something from a variable }}')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_inside_liquid_tag
|
||||||
|
source = <<~LIQUID
|
||||||
|
{%- liquid
|
||||||
|
echo "before("
|
||||||
|
# This text gets ignored
|
||||||
|
echo ")after"
|
||||||
|
-%}
|
||||||
|
LIQUID
|
||||||
|
assert_template_result('before()after', source)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_multiline
|
||||||
|
assert_template_result('', '{% # this sort of comment also
|
||||||
|
# will just work, because it parses
|
||||||
|
# as a single call to the "#" tag %}')
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -58,7 +58,7 @@ class RenderTagTest < Minitest::Test
|
|||||||
def test_recursively_rendered_template_does_not_produce_endless_loop
|
def test_recursively_rendered_template_does_not_produce_endless_loop
|
||||||
Liquid::Template.file_system = StubFileSystem.new('loop' => '{% render "loop" %}')
|
Liquid::Template.file_system = StubFileSystem.new('loop' => '{% render "loop" %}')
|
||||||
|
|
||||||
assert_raises Liquid::StackLevelError do
|
assert_raises(Liquid::StackLevelError) do
|
||||||
Template.parse('{% render "loop" %}').render!
|
Template.parse('{% render "loop" %}').render!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -67,7 +67,7 @@ class RenderTagTest < Minitest::Test
|
|||||||
Liquid::Template.file_system = StubFileSystem.new(
|
Liquid::Template.file_system = StubFileSystem.new(
|
||||||
'loop_render' => '{% render "loop_render" %}',
|
'loop_render' => '{% render "loop_render" %}',
|
||||||
)
|
)
|
||||||
assert_raises Liquid::StackLevelError do
|
assert_raises(Liquid::StackLevelError) do
|
||||||
Template.parse('{% render "loop_render" %}').render!
|
Template.parse('{% render "loop_render" %}').render!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -75,7 +75,7 @@ class RenderTagTest < Minitest::Test
|
|||||||
def test_dynamically_choosen_templates_are_not_allowed
|
def test_dynamically_choosen_templates_are_not_allowed
|
||||||
Liquid::Template.file_system = StubFileSystem.new('snippet' => 'should not be rendered')
|
Liquid::Template.file_system = StubFileSystem.new('snippet' => 'should not be rendered')
|
||||||
|
|
||||||
assert_raises Liquid::SyntaxError do
|
assert_raises(Liquid::SyntaxError) do
|
||||||
Liquid::Template.parse("{% assign name = 'snippet' %}{% render name %}")
|
Liquid::Template.parse("{% assign name = 'snippet' %}{% render name %}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ class TemplateTest < Minitest::Test
|
|||||||
context = Context.new('drop' => ErroneousDrop.new)
|
context = Context.new('drop' => ErroneousDrop.new)
|
||||||
t = Template.new.parse('{{ drop.bad_method }}')
|
t = Template.new.parse('{{ drop.bad_method }}')
|
||||||
|
|
||||||
e = assert_raises RuntimeError do
|
e = assert_raises(RuntimeError) do
|
||||||
t.render!(context)
|
t.render!(context)
|
||||||
end
|
end
|
||||||
assert_equal('ruby error in drop', e.message)
|
assert_equal('ruby error in drop', e.message)
|
||||||
@@ -267,7 +267,7 @@ class TemplateTest < Minitest::Test
|
|||||||
def test_undefined_variables_raise
|
def test_undefined_variables_raise
|
||||||
t = Template.parse("{{x}} {{y}} {{z.a}} {{z.b}} {{z.c.d}}")
|
t = Template.parse("{{x}} {{y}} {{z.a}} {{z.b}} {{z.c.d}}")
|
||||||
|
|
||||||
assert_raises UndefinedVariable do
|
assert_raises(UndefinedVariable) do
|
||||||
t.render!({ 'x' => 33, 'z' => { 'a' => 32, 'c' => { 'e' => 31 } } }, strict_variables: true)
|
t.render!({ 'x' => 33, 'z' => { 'a' => 32, 'c' => { 'e' => 31 } } }, strict_variables: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -286,7 +286,7 @@ class TemplateTest < Minitest::Test
|
|||||||
d = DropWithUndefinedMethod.new
|
d = DropWithUndefinedMethod.new
|
||||||
t = Template.new.parse('{{ foo }} {{ woot }}')
|
t = Template.new.parse('{{ foo }} {{ woot }}')
|
||||||
|
|
||||||
assert_raises UndefinedDropMethod do
|
assert_raises(UndefinedDropMethod) do
|
||||||
t.render!(d, strict_variables: true)
|
t.render!(d, strict_variables: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -309,7 +309,7 @@ class TemplateTest < Minitest::Test
|
|||||||
def test_undefined_filters_raise
|
def test_undefined_filters_raise
|
||||||
t = Template.parse("{{x | somefilter1 | upcase | somefilter2}}")
|
t = Template.parse("{{x | somefilter1 | upcase | somefilter2}}")
|
||||||
|
|
||||||
assert_raises UndefinedFilter do
|
assert_raises(UndefinedFilter) do
|
||||||
t.render!({ 'x' => 'foo' }, strict_filters: true)
|
t.render!({ 'x' => 'foo' }, strict_filters: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
def test_contains_works_on_arrays
|
def test_contains_works_on_arrays
|
||||||
@context = Liquid::Context.new
|
@context = Liquid::Context.new
|
||||||
@context['array'] = [1, 2, 3, 4, 5]
|
@context['array'] = [1, 2, 3, 4, 5]
|
||||||
array_expr = parse_variable_lookup("array")
|
array_expr = VariableLookup.new("array")
|
||||||
|
|
||||||
assert_evaluates_false(array_expr, 'contains', 0)
|
assert_evaluates_false(array_expr, 'contains', 0)
|
||||||
assert_evaluates_true(array_expr, 'contains', 1)
|
assert_evaluates_true(array_expr, 'contains', 1)
|
||||||
@@ -91,8 +91,8 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
|
|
||||||
def test_contains_returns_false_for_nil_operands
|
def test_contains_returns_false_for_nil_operands
|
||||||
@context = Liquid::Context.new
|
@context = Liquid::Context.new
|
||||||
assert_evaluates_false(parse_variable_lookup('not_assigned'), 'contains', '0')
|
assert_evaluates_false(VariableLookup.new('not_assigned'), 'contains', '0')
|
||||||
assert_evaluates_false(0, 'contains', parse_variable_lookup('not_assigned'))
|
assert_evaluates_false(0, 'contains', VariableLookup.new('not_assigned'))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_contains_return_false_on_wrong_data_type
|
def test_contains_return_false_on_wrong_data_type
|
||||||
@@ -145,20 +145,11 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
@context = Liquid::Context.new
|
@context = Liquid::Context.new
|
||||||
@context['one'] = @context['another'] = "gnomeslab-and-or-liquid"
|
@context['one'] = @context['another'] = "gnomeslab-and-or-liquid"
|
||||||
|
|
||||||
assert_evaluates_true(parse_variable_lookup("one"), '==', parse_variable_lookup("another"))
|
assert_evaluates_true(VariableLookup.new("one"), '==', VariableLookup.new("another"))
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def parse_variable_lookup(markup)
|
|
||||||
if Liquid::Template.error_mode == :strict
|
|
||||||
p = Liquid::Parser.new(markup)
|
|
||||||
VariableLookup.strict_parse(p)
|
|
||||||
else
|
|
||||||
VariableLookup.lax_parse(markup)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def assert_evaluates_true(left, op, right)
|
def assert_evaluates_true(left, op, right)
|
||||||
assert(Condition.new(left, op, right).evaluate(@context),
|
assert(Condition.new(left, op, right).evaluate(@context),
|
||||||
"Evaluated false: #{left} #{op} #{right}")
|
"Evaluated false: #{left} #{op} #{right}")
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class I18nUnitTest < Minitest::Test
|
|||||||
# end
|
# end
|
||||||
|
|
||||||
def test_raises_unknown_translation
|
def test_raises_unknown_translation
|
||||||
assert_raises I18n::TranslationError do
|
assert_raises(I18n::TranslationError) do
|
||||||
@i18n.translate("doesnt_exist")
|
@i18n.translate("doesnt_exist")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -47,41 +47,32 @@ class ParserUnitTest < Minitest::Test
|
|||||||
|
|
||||||
def test_expressions
|
def test_expressions
|
||||||
p = Parser.new("hi.there hi?[5].there? hi.there.bob")
|
p = Parser.new("hi.there hi?[5].there? hi.there.bob")
|
||||||
assert_equal(VariableLookup.send(:new, 'hi', ['there'], 0), p.expression)
|
assert_equal('hi.there', p.expression)
|
||||||
assert_equal(VariableLookup.send(:new, 'hi?', [5, 'there?'], 0), p.expression)
|
assert_equal('hi?[5].there?', p.expression)
|
||||||
assert_equal(VariableLookup.send(:new, 'hi', ['there', 'bob'], 0), p.expression)
|
assert_equal('hi.there.bob', p.expression)
|
||||||
|
|
||||||
p = Parser.new("nil true false")
|
|
||||||
assert_nil(p.expression)
|
|
||||||
assert_equal(true, p.expression)
|
|
||||||
assert_equal(false, p.expression)
|
|
||||||
|
|
||||||
p = Parser.new("567 6.0 'lol' \"wut\"")
|
p = Parser.new("567 6.0 'lol' \"wut\"")
|
||||||
assert_equal(567, p.expression)
|
assert_equal('567', p.expression)
|
||||||
assert_equal(6.0, p.expression)
|
assert_equal('6.0', p.expression)
|
||||||
assert_equal('lol', p.expression)
|
assert_equal("'lol'", p.expression)
|
||||||
assert_equal('wut', p.expression)
|
assert_equal('"wut"', p.expression)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_ranges
|
def test_ranges
|
||||||
p = Parser.new("(5..7) (1.5..9.6) (young..old) (hi[5].wat..old)")
|
p = Parser.new("(5..7) (1.5..9.6) (young..old) (hi[5].wat..old)")
|
||||||
assert_equal(5..7, p.expression)
|
assert_equal('(5..7)', p.expression)
|
||||||
assert_equal(1..9, p.expression)
|
assert_equal('(1.5..9.6)', p.expression)
|
||||||
assert_equal(
|
assert_equal('(young..old)', p.expression)
|
||||||
RangeLookup.new(VariableLookup.send(:new, 'young', [], 0), VariableLookup.send(:new, 'old', [], 0)),
|
assert_equal('(hi[5].wat..old)', p.expression)
|
||||||
p.expression
|
|
||||||
)
|
|
||||||
assert_equal(
|
|
||||||
RangeLookup.new(VariableLookup.send(:new, 'hi', [5, "wat"], 0), VariableLookup.send(:new, 'old', [], 0)),
|
|
||||||
p.expression
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_arguments
|
def test_arguments
|
||||||
p = Parser.new("filter: hi.there[5], keyarg: 7")
|
p = Parser.new("filter: hi.there[5], keyarg: 7")
|
||||||
assert_equal('filter', p.consume(:id))
|
assert_equal('filter', p.consume(:id))
|
||||||
assert_equal(':', p.consume(:colon))
|
assert_equal(':', p.consume(:colon))
|
||||||
assert_equal([[VariableLookup.send(:new, "hi", ["there", 5], 0)], { "keyarg" => 7 }], p.arguments)
|
assert_equal('hi.there[5]', p.argument)
|
||||||
|
assert_equal(',', p.consume(:comma))
|
||||||
|
assert_equal('keyarg: 7', p.argument)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_invalid_expression
|
def test_invalid_expression
|
||||||
|
|||||||
@@ -32,21 +32,26 @@ class TokenizerTest < Minitest::Test
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def new_tokenizer(source, parse_context: Liquid::ParseContext.new, start_line_number: nil)
|
||||||
|
parse_context.new_tokenizer(source, start_line_number: start_line_number)
|
||||||
|
end
|
||||||
|
|
||||||
def tokenize(source)
|
def tokenize(source)
|
||||||
tokenizer = Liquid::Tokenizer.new(source)
|
tokenizer = new_tokenizer(source)
|
||||||
tokens = []
|
tokens = []
|
||||||
while (t = tokenizer.shift)
|
# shift is private in Liquid::C::Tokenizer, since it is only for unit testing
|
||||||
|
while (t = tokenizer.send(:shift))
|
||||||
tokens << t
|
tokens << t
|
||||||
end
|
end
|
||||||
tokens
|
tokens
|
||||||
end
|
end
|
||||||
|
|
||||||
def tokenize_line_numbers(source)
|
def tokenize_line_numbers(source)
|
||||||
tokenizer = Liquid::Tokenizer.new(source, true)
|
tokenizer = new_tokenizer(source, start_line_number: 1)
|
||||||
line_numbers = []
|
line_numbers = []
|
||||||
loop do
|
loop do
|
||||||
line_number = tokenizer.line_number
|
line_number = tokenizer.line_number
|
||||||
if tokenizer.shift
|
if tokenizer.send(:shift)
|
||||||
line_numbers << line_number
|
line_numbers << line_number
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class VariableLookupUnitTest < Minitest::Test
|
|
||||||
include Liquid
|
|
||||||
|
|
||||||
def test_variable_lookup_parsing
|
|
||||||
lookup = parse_variable_lookup('a.b.c')
|
|
||||||
assert_equal('a', lookup.name)
|
|
||||||
assert_equal(['b', 'c'], lookup.lookups)
|
|
||||||
|
|
||||||
lookup = parse_variable_lookup('a[b]')
|
|
||||||
assert_equal('a', lookup.name)
|
|
||||||
assert_equal([parse_variable_lookup('b')], lookup.lookups)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_to_s
|
|
||||||
lookup = parse_variable_lookup('a.b.c')
|
|
||||||
assert_equal("a['b']['c']", lookup.to_s)
|
|
||||||
|
|
||||||
lookup = parse_variable_lookup('a[b.c].d')
|
|
||||||
assert_equal("a[b['c']]['d']", lookup.to_s)
|
|
||||||
|
|
||||||
lookup = parse_variable_lookup('a["foo.bar"].d')
|
|
||||||
assert_equal("a['foo.bar']['d']", lookup.to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def parse_variable_lookup(markup)
|
|
||||||
if Liquid::Template.error_mode == :strict
|
|
||||||
p = Liquid::Parser.new(markup)
|
|
||||||
VariableLookup.strict_parse(p)
|
|
||||||
else
|
|
||||||
VariableLookup.lax_parse(markup)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -7,20 +7,20 @@ class VariableUnitTest < Minitest::Test
|
|||||||
|
|
||||||
def test_variable
|
def test_variable
|
||||||
var = create_variable('hello')
|
var = create_variable('hello')
|
||||||
assert_equal(parse_variable_lookup('hello'), var.name)
|
assert_equal(VariableLookup.new('hello'), var.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_filters
|
def test_filters
|
||||||
var = create_variable('hello | textileze')
|
var = create_variable('hello | textileze')
|
||||||
assert_equal(parse_variable_lookup('hello'), var.name)
|
assert_equal(VariableLookup.new('hello'), var.name)
|
||||||
assert_equal([['textileze', []]], var.filters)
|
assert_equal([['textileze', []]], var.filters)
|
||||||
|
|
||||||
var = create_variable('hello | textileze | paragraph')
|
var = create_variable('hello | textileze | paragraph')
|
||||||
assert_equal(parse_variable_lookup('hello'), var.name)
|
assert_equal(VariableLookup.new('hello'), var.name)
|
||||||
assert_equal([['textileze', []], ['paragraph', []]], var.filters)
|
assert_equal([['textileze', []], ['paragraph', []]], var.filters)
|
||||||
|
|
||||||
var = create_variable(%( hello | strftime: '%Y'))
|
var = create_variable(%( hello | strftime: '%Y'))
|
||||||
assert_equal(parse_variable_lookup('hello'), var.name)
|
assert_equal(VariableLookup.new('hello'), var.name)
|
||||||
assert_equal([['strftime', ['%Y']]], var.filters)
|
assert_equal([['strftime', ['%Y']]], var.filters)
|
||||||
|
|
||||||
var = create_variable(%( 'typo' | link_to: 'Typo', true ))
|
var = create_variable(%( 'typo' | link_to: 'Typo', true ))
|
||||||
@@ -44,11 +44,11 @@ class VariableUnitTest < Minitest::Test
|
|||||||
assert_equal([['repeat', [3, 3, 3]]], var.filters)
|
assert_equal([['repeat', [3, 3, 3]]], var.filters)
|
||||||
|
|
||||||
var = create_variable(%( hello | strftime: '%Y, okay?'))
|
var = create_variable(%( hello | strftime: '%Y, okay?'))
|
||||||
assert_equal(parse_variable_lookup('hello'), var.name)
|
assert_equal(VariableLookup.new('hello'), var.name)
|
||||||
assert_equal([['strftime', ['%Y, okay?']]], var.filters)
|
assert_equal([['strftime', ['%Y, okay?']]], var.filters)
|
||||||
|
|
||||||
var = create_variable(%( hello | things: "%Y, okay?", 'the other one'))
|
var = create_variable(%( hello | things: "%Y, okay?", 'the other one'))
|
||||||
assert_equal(parse_variable_lookup('hello'), var.name)
|
assert_equal(VariableLookup.new('hello'), var.name)
|
||||||
assert_equal([['things', ['%Y, okay?', 'the other one']]], var.filters)
|
assert_equal([['things', ['%Y, okay?', 'the other one']]], var.filters)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -60,24 +60,22 @@ class VariableUnitTest < Minitest::Test
|
|||||||
|
|
||||||
def test_filters_without_whitespace
|
def test_filters_without_whitespace
|
||||||
var = create_variable('hello | textileze | paragraph')
|
var = create_variable('hello | textileze | paragraph')
|
||||||
assert_equal(parse_variable_lookup('hello'), var.name)
|
assert_equal(VariableLookup.new('hello'), var.name)
|
||||||
assert_equal([['textileze', []], ['paragraph', []]], var.filters)
|
assert_equal([['textileze', []], ['paragraph', []]], var.filters)
|
||||||
|
|
||||||
var = create_variable('hello|textileze|paragraph')
|
var = create_variable('hello|textileze|paragraph')
|
||||||
assert_equal(parse_variable_lookup('hello'), var.name)
|
assert_equal(VariableLookup.new('hello'), var.name)
|
||||||
assert_equal([['textileze', []], ['paragraph', []]], var.filters)
|
assert_equal([['textileze', []], ['paragraph', []]], var.filters)
|
||||||
|
|
||||||
var = create_variable("hello|replace:'foo','bar'|textileze")
|
var = create_variable("hello|replace:'foo','bar'|textileze")
|
||||||
assert_equal(parse_variable_lookup('hello'), var.name)
|
assert_equal(VariableLookup.new('hello'), var.name)
|
||||||
assert_equal([['replace', ['foo', 'bar']], ['textileze', []]], var.filters)
|
assert_equal([['replace', ['foo', 'bar']], ['textileze', []]], var.filters)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_symbol
|
def test_symbol
|
||||||
with_error_mode(:lax) do
|
var = create_variable("http://disney.com/logo.gif | image: 'med' ", error_mode: :lax)
|
||||||
var = create_variable("http://disney.com/logo.gif | image: 'med' ", error_mode: :lax)
|
assert_equal(VariableLookup.new('http://disney.com/logo.gif'), var.name)
|
||||||
assert_equal(parse_variable_lookup('http://disney.com/logo.gif'), var.name)
|
assert_equal([['image', ['med']]], var.filters)
|
||||||
assert_equal([['image', ['med']]], var.filters)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_string_to_filter
|
def test_string_to_filter
|
||||||
@@ -107,10 +105,10 @@ class VariableUnitTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_dashes
|
def test_dashes
|
||||||
assert_equal(parse_variable_lookup('foo-bar'), create_variable('foo-bar').name)
|
assert_equal(VariableLookup.new('foo-bar'), create_variable('foo-bar').name)
|
||||||
assert_equal(parse_variable_lookup('foo-bar-2'), create_variable('foo-bar-2').name)
|
assert_equal(VariableLookup.new('foo-bar-2'), create_variable('foo-bar-2').name)
|
||||||
|
|
||||||
with_error_mode :strict do
|
with_error_mode(:strict) do
|
||||||
assert_raises(Liquid::SyntaxError) { create_variable('foo - bar') }
|
assert_raises(Liquid::SyntaxError) { create_variable('foo - bar') }
|
||||||
assert_raises(Liquid::SyntaxError) { create_variable('-foo') }
|
assert_raises(Liquid::SyntaxError) { create_variable('-foo') }
|
||||||
assert_raises(Liquid::SyntaxError) { create_variable('2foo') }
|
assert_raises(Liquid::SyntaxError) { create_variable('2foo') }
|
||||||
@@ -124,18 +122,18 @@ class VariableUnitTest < Minitest::Test
|
|||||||
|
|
||||||
def test_string_dot
|
def test_string_dot
|
||||||
var = create_variable(%( test.test ))
|
var = create_variable(%( test.test ))
|
||||||
assert_equal(parse_variable_lookup('test.test'), var.name)
|
assert_equal(VariableLookup.new('test.test'), var.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_filter_with_keyword_arguments
|
def test_filter_with_keyword_arguments
|
||||||
var = create_variable(%( hello | things: greeting: "world", farewell: 'goodbye'))
|
var = create_variable(%( hello | things: greeting: "world", farewell: 'goodbye'))
|
||||||
assert_equal(parse_variable_lookup('hello'), var.name)
|
assert_equal(VariableLookup.new('hello'), var.name)
|
||||||
assert_equal([['things', [], { 'greeting' => 'world', 'farewell' => 'goodbye' }]], var.filters)
|
assert_equal([['things', [], { 'greeting' => 'world', 'farewell' => 'goodbye' }]], var.filters)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_lax_filter_argument_parsing
|
def test_lax_filter_argument_parsing
|
||||||
var = create_variable(%( number_of_comments | pluralize: 'comment': 'comments' ), error_mode: :lax)
|
var = create_variable(%( number_of_comments | pluralize: 'comment': 'comments' ), error_mode: :lax)
|
||||||
assert_equal(parse_variable_lookup('number_of_comments'), var.name)
|
assert_equal(VariableLookup.new('number_of_comments'), var.name)
|
||||||
assert_equal([['pluralize', ['comment', 'comments']]], var.filters)
|
assert_equal([['pluralize', ['comment', 'comments']]], var.filters)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -152,17 +150,14 @@ class VariableUnitTest < Minitest::Test
|
|||||||
assert_equal(" name_of_variable | upcase ", var.raw)
|
assert_equal(" name_of_variable | upcase ", var.raw)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
def test_variable_lookup_interface
|
||||||
|
lookup = VariableLookup.new('a.b.c')
|
||||||
def parse_variable_lookup(markup)
|
assert_equal('a', lookup.name)
|
||||||
if Liquid::Template.error_mode == :strict
|
assert_equal(['b', 'c'], lookup.lookups)
|
||||||
p = Liquid::Parser.new(markup)
|
|
||||||
VariableLookup.strict_parse(p)
|
|
||||||
else
|
|
||||||
VariableLookup.lax_parse(markup)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
def create_variable(markup, options = {})
|
def create_variable(markup, options = {})
|
||||||
Variable.new(markup, ParseContext.new(options))
|
Variable.new(markup, ParseContext.new(options))
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user