mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-13 07:50:43 -07:00
Compare commits
36
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24dceef552 | ||
|
|
428c66ffac | ||
|
|
0fe4a5d144 | ||
|
|
e650dc4195 | ||
|
|
9ab688eada | ||
|
|
abef59d129 | ||
|
|
940c3a4207 | ||
|
|
84a0289ebc | ||
|
|
4599e5459f | ||
|
|
59c445f0e1 | ||
|
|
bd9c3802c8 | ||
|
|
2b40850e4a | ||
|
|
22ded5f304 | ||
|
|
ddc32b7bd8 | ||
|
|
74e505f6fa | ||
|
|
6a888d4564 | ||
|
|
dd257b3d66 | ||
|
|
1aaf6ed019 | ||
|
|
daf93a83c2 | ||
|
|
e889a9da0b | ||
|
|
0f11c97623 | ||
|
|
e804f36681 | ||
|
|
619ed3fcd7 | ||
|
|
cdb5cb06b2 | ||
|
|
bc153159e6 | ||
|
|
128b4e35be | ||
|
|
c743936a78 | ||
|
|
bf711a0521 | ||
|
|
e8731f27d9 | ||
|
|
6a44c1ec77 | ||
|
|
1beb87b446 | ||
|
|
b839deb3a8 | ||
|
|
0b826120c0 | ||
|
|
936f803a4e | ||
|
|
5cd8a83fa6 | ||
|
|
c2c6cb2b15 |
@@ -1,5 +1,10 @@
|
||||
name: Liquid
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
BUNDLE_JOBS: 4
|
||||
BUNDLE_RETRY: 3
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -7,22 +12,18 @@ jobs:
|
||||
matrix:
|
||||
entry:
|
||||
- { ruby: 2.7, allowed-failure: false } # minimum supported
|
||||
- { ruby: 3.1, allowed-failure: false } # latest
|
||||
- { ruby: 3.2, allowed-failure: false } # latest
|
||||
- { ruby: ruby-head, allowed-failure: true }
|
||||
name: test (${{ matrix.entry.ruby }})
|
||||
name: Test Ruby ${{ matrix.entry.ruby }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.entry.ruby }}
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: vendor/bundle
|
||||
key: ${{ runner.os }}-gems-${{ hashFiles('Gemfile') }}
|
||||
restore-keys: ${{ runner.os }}-gems-
|
||||
- run: bundle install --jobs=3 --retry=3 --path=vendor/bundle
|
||||
bundler-cache: true
|
||||
- run: bundle exec rake
|
||||
continue-on-error: ${{ matrix.entry.allowed-failure }}
|
||||
|
||||
memory_profile:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -30,10 +31,5 @@ jobs:
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 2.7
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: vendor/bundle
|
||||
key: ${{ runner.os }}-gems-${{ hashFiles('Gemfile') }}
|
||||
restore-keys: ${{ runner.os }}-gems-
|
||||
- run: bundle install --jobs=3 --retry=3 --path=vendor/bundle
|
||||
bundler-cache: true
|
||||
- run: bundle exec rake memory_profile:run
|
||||
|
||||
+8
-3
@@ -12,6 +12,7 @@ Performance:
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.7
|
||||
NewCops: disable
|
||||
SuggestExtensions: false
|
||||
Exclude:
|
||||
- 'vendor/bundle/**/*'
|
||||
|
||||
@@ -19,6 +20,10 @@ Naming/MethodName:
|
||||
Exclude:
|
||||
- 'example/server/liquid_servlet.rb'
|
||||
|
||||
# Backport https://github.com/Shopify/ruby-style-guide/pull/258
|
||||
Layout/BeginEndAlignment:
|
||||
Enabled: true
|
||||
Style/ClassMethodsDefinitions:
|
||||
Enabled: false
|
||||
|
||||
# liquid filter calls were being mistaken to be calls on arrays
|
||||
Style/ConcatArrayLiterals:
|
||||
Exclude:
|
||||
- 'test/integration/standard_filter_test.rb'
|
||||
|
||||
@@ -18,7 +18,8 @@ group :benchmark, :test do
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'rubocop-shopify', '~> 2.7.0', require: false
|
||||
gem 'rubocop', '~> 1.44.0'
|
||||
gem 'rubocop-shopify', '~> 2.12.0', require: false
|
||||
gem 'rubocop-performance', require: false
|
||||
|
||||
platform :mri, :truffleruby do
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
* [Contributing guidelines](CONTRIBUTING.md)
|
||||
* [Version history](History.md)
|
||||
* [Liquid documentation from Shopify](https://shopify.dev/api/liquid)
|
||||
* [Liquid documentation from Shopify](https://shopify.dev/docs/docs/api/liquid)
|
||||
* [Liquid Wiki at GitHub](https://github.com/Shopify/liquid/wiki)
|
||||
* [Website](http://liquidmarkup.org/)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ require 'rake/testtask'
|
||||
$LOAD_PATH.unshift(File.expand_path("../lib", __FILE__))
|
||||
require "liquid/version"
|
||||
|
||||
task(default: [:test, "test:migrator_integration", :rubocop])
|
||||
task(default: [:test, :rubocop])
|
||||
|
||||
desc('run test suite with default parser')
|
||||
Rake::TestTask.new(:base_test) do |t|
|
||||
@@ -33,23 +33,6 @@ task :rubocop do
|
||||
end
|
||||
end
|
||||
|
||||
namespace :test do
|
||||
task :migrator_integration do
|
||||
ENV['LIQUID_MIGRATOR'] = '1'
|
||||
original_parse_mode = ENV['LIQUID_PARSER_MODE']
|
||||
begin
|
||||
Rake::Task['integration_test'].reenable
|
||||
["lax", "strict"].each do |parse_mode|
|
||||
ENV['LIQUID_PARSER_MODE'] = parse_mode
|
||||
Rake::Task['integration_test'].invoke
|
||||
Rake::Task['integration_test'].reenable
|
||||
end
|
||||
ensure
|
||||
ENV['LIQUID_PARSER_MODE'] = original_parse_mode
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc('runs test suite with both strict and lax parsers')
|
||||
task :test do
|
||||
ENV['LIQUID_PARSER_MODE'] = 'lax'
|
||||
|
||||
@@ -30,14 +30,18 @@ class Servlet < LiquidServlet
|
||||
private
|
||||
|
||||
def products_list
|
||||
[{ 'name' => 'Arbor Draft', 'price' => 39900, 'description' => 'the *arbor draft* is a excellent product' },
|
||||
{ 'name' => 'Arbor Element', 'price' => 40000, 'description' => 'the *arbor element* rocks for freestyling' },
|
||||
{ 'name' => 'Arbor Diamond', 'price' => 59900, 'description' => 'the *arbor diamond* is a made up product because im obsessed with arbor and have no creativity' }]
|
||||
[
|
||||
{ 'name' => 'Arbor Draft', 'price' => 39900, 'description' => 'the *arbor draft* is a excellent product' },
|
||||
{ 'name' => 'Arbor Element', 'price' => 40000, 'description' => 'the *arbor element* rocks for freestyling' },
|
||||
{ 'name' => 'Arbor Diamond', 'price' => 59900, 'description' => 'the *arbor diamond* is a made up product because im obsessed with arbor and have no creativity' }
|
||||
]
|
||||
end
|
||||
|
||||
def more_products_list
|
||||
[{ 'name' => 'Arbor Catalyst', 'price' => 39900, 'description' => 'the *arbor catalyst* is an advanced drop-through for freestyle and flatground performance and versatility' },
|
||||
{ 'name' => 'Arbor Fish', 'price' => 40000, 'description' => 'the *arbor fish* is a compact pin that features an extended wheelbase and time-honored teardrop shape' }]
|
||||
[
|
||||
{ 'name' => 'Arbor Catalyst', 'price' => 39900, 'description' => 'the *arbor catalyst* is an advanced drop-through for freestyle and flatground performance and versatility' },
|
||||
{ 'name' => 'Arbor Fish', 'price' => 40000, 'description' => 'the *arbor fish* is a compact pin that features an extended wheelbase and time-honored teardrop shape' }
|
||||
]
|
||||
end
|
||||
|
||||
def description
|
||||
|
||||
+2
-2
@@ -41,7 +41,7 @@ module Liquid
|
||||
AnyStartingTag = /#{TagStart}|#{VariableStart}/o
|
||||
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om
|
||||
TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
|
||||
VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/o
|
||||
VariableParser = /\[(?>[^\[\]]+|\g<0>)*\]|#{VariableSegment}+\??/o
|
||||
|
||||
RAISE_EXCEPTION_LAMBDA = ->(_e) { raise }
|
||||
|
||||
@@ -68,7 +68,6 @@ require 'liquid/parser_switching'
|
||||
require 'liquid/tag'
|
||||
require 'liquid/tag/disabler'
|
||||
require 'liquid/tag/disableable'
|
||||
require 'liquid/parse_context'
|
||||
require 'liquid/block'
|
||||
require 'liquid/block_body'
|
||||
require 'liquid/document'
|
||||
@@ -82,6 +81,7 @@ require 'liquid/standardfilters'
|
||||
require 'liquid/condition'
|
||||
require 'liquid/utils'
|
||||
require 'liquid/tokenizer'
|
||||
require 'liquid/parse_context'
|
||||
require 'liquid/partial_cache'
|
||||
require 'liquid/usage'
|
||||
require 'liquid/registers'
|
||||
|
||||
+8
-19
@@ -36,13 +36,17 @@ module Liquid
|
||||
# @api private
|
||||
def self.raise_unknown_tag(tag, block_name, block_delimiter, parse_context)
|
||||
if tag == 'else'
|
||||
raise SyntaxError, parse_context.locale.t("errors.syntax.unexpected_else",
|
||||
block_name: block_name)
|
||||
raise SyntaxError, parse_context.locale.t(
|
||||
"errors.syntax.unexpected_else",
|
||||
block_name: block_name,
|
||||
)
|
||||
elsif tag.start_with?('end')
|
||||
raise SyntaxError, parse_context.locale.t("errors.syntax.invalid_delimiter",
|
||||
raise SyntaxError, parse_context.locale.t(
|
||||
"errors.syntax.invalid_delimiter",
|
||||
tag: tag,
|
||||
block_name: block_name,
|
||||
block_delimiter: block_delimiter)
|
||||
block_delimiter: block_delimiter,
|
||||
)
|
||||
else
|
||||
raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag)
|
||||
end
|
||||
@@ -60,21 +64,6 @@ module Liquid
|
||||
@block_delimiter ||= "end#{block_name}"
|
||||
end
|
||||
|
||||
def self.migrate_body(start_tag_name, tokenizer, parse_context)
|
||||
new_body, unknown_tag = BlockBody.migrate(tokenizer, parse_context)
|
||||
|
||||
raise SyntaxError unless unknown_tag
|
||||
|
||||
block_delimiter = "end#{start_tag_name}"
|
||||
if unknown_tag.tag_name == block_delimiter
|
||||
new_body << unknown_tag.replaced_markup("") # markup was ignored on end tags
|
||||
return [new_body, nil]
|
||||
end
|
||||
|
||||
# handle the delimiter tag in the caller
|
||||
[new_body, unknown_tag]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# @api public
|
||||
|
||||
@@ -30,16 +30,6 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
def self.migrate(tokenizer, parse_context, &block)
|
||||
parse_context.line_number = tokenizer.line_number
|
||||
|
||||
if tokenizer.for_liquid_tag
|
||||
migrate_for_liquid_tag(tokenizer, parse_context, &block)
|
||||
else
|
||||
migrate_for_document(tokenizer, parse_context, &block)
|
||||
end
|
||||
end
|
||||
|
||||
def freeze
|
||||
@nodelist.freeze
|
||||
super
|
||||
@@ -70,57 +60,6 @@ module Liquid
|
||||
yield nil, nil
|
||||
end
|
||||
|
||||
class UnknownTagMigrator
|
||||
attr_reader :tag_name, :markup
|
||||
|
||||
def initialize(match:, markup_capture_number:, tag_name:, markup:)
|
||||
@match = match
|
||||
@tag_name = tag_name
|
||||
@markup = markup
|
||||
@markup_capture_number = markup_capture_number
|
||||
end
|
||||
|
||||
def original_tag_string
|
||||
@match[0]
|
||||
end
|
||||
|
||||
def replaced_markup(new_markup)
|
||||
Utils.match_capture_replace(@match, @markup_capture_number, new_markup)
|
||||
end
|
||||
end
|
||||
|
||||
private_class_method def self.migrate_for_liquid_tag(tokenizer, parse_context)
|
||||
result = +""
|
||||
while (token = tokenizer.shift)
|
||||
token += "\n" if tokenizer.more?
|
||||
if token.empty? || token.match?(WhitespaceOrNothing)
|
||||
result << token
|
||||
else
|
||||
# modified version of LiquidTagToken with following changes:
|
||||
# * TagName is optional, to continue supporting its absence in the comment tag
|
||||
# * trailing spaces is allowed to support the newline appended above and so the tag
|
||||
# migrate method doesn't have to handle trailing whitespace
|
||||
match = token.match(/\A\s*(#{TagName})?\s*(.*?)\s*\z/o)
|
||||
tag_name = match[1]
|
||||
markup = match[2]
|
||||
unless (tag = Template.tags[tag_name])
|
||||
# delegate handling of unknown tags to the caller, where a block tag may treat
|
||||
# it as an end tag or body delimiter.
|
||||
unknown_tag = UnknownTagMigrator.new(
|
||||
match: match, markup_capture_number: 2, tag_name: tag_name, markup: markup
|
||||
)
|
||||
return [result, unknown_tag]
|
||||
end
|
||||
new_markup, new_tag_body = tag.migrate(tag_name, markup, tokenizer, parse_context)
|
||||
result << Utils.match_capture_replace(match, 2, new_markup)
|
||||
result << new_tag_body.to_s
|
||||
end
|
||||
parse_context.line_number = tokenizer.line_number
|
||||
end
|
||||
|
||||
[result, nil]
|
||||
end
|
||||
|
||||
# @api private
|
||||
def self.unknown_tag_in_liquid_tag(tag, parse_context)
|
||||
Block.raise_unknown_tag(tag, 'liquid', '%}', parse_context)
|
||||
@@ -170,15 +109,6 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
private_class_method def self.migrate_liquid_tag(markup, parse_context)
|
||||
liquid_tag_tokenizer = parse_context.new_tokenizer(
|
||||
markup, start_line_number: parse_context.line_number, for_liquid_tag: true
|
||||
)
|
||||
result, unknown_tag = migrate_for_liquid_tag(liquid_tag_tokenizer, parse_context)
|
||||
raise SyntaxError if unknown_tag
|
||||
result
|
||||
end
|
||||
|
||||
private def handle_invalid_tag_token(token, parse_context)
|
||||
if token.end_with?('%}')
|
||||
yield token, token
|
||||
@@ -236,58 +166,6 @@ module Liquid
|
||||
yield nil, nil
|
||||
end
|
||||
|
||||
private_class_method def self.migrate_for_document(tokenizer, parse_context, &block)
|
||||
result = +""
|
||||
while (token = tokenizer.shift)
|
||||
next if token.empty?
|
||||
|
||||
case
|
||||
when token.start_with?(TAGSTART)
|
||||
raise SyntaxError unless token.end_with?('%}')
|
||||
# modified FullToken regex with optional tag name, to allow its absence in a comment tag
|
||||
match = token.match(/\A#{TagStart}#{WhitespaceControl}?(\s*)(#{TagName})?(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om)
|
||||
tag_name = match[2]
|
||||
markup = match[4]
|
||||
|
||||
if parse_context.line_number
|
||||
# newlines inside the tag should increase the line number,
|
||||
# particularly important for multiline {% liquid %} tags
|
||||
parse_context.line_number += Regexp.last_match(1).count("\n") + Regexp.last_match(3).count("\n")
|
||||
end
|
||||
|
||||
if tag_name == 'liquid'
|
||||
new_markup = Utils.migrate_stripped(markup) do |stripped_markup|
|
||||
migrate_liquid_tag(stripped_markup, parse_context)
|
||||
end
|
||||
result << Utils.match_capture_replace(match, 4, new_markup)
|
||||
next
|
||||
end
|
||||
|
||||
unless (tag = Template.tags[tag_name])
|
||||
# delegate handling of unknown tags to the caller, where a block tag may treat
|
||||
# it as an end tag or body delimiter.
|
||||
unknown_tag = UnknownTagMigrator.new(
|
||||
match: match, markup_capture_number: 4, tag_name: tag_name, markup: markup
|
||||
)
|
||||
return [result, unknown_tag]
|
||||
end
|
||||
new_tag_body = nil
|
||||
new_markup = Utils.migrate_stripped(markup) do |stripped_markup|
|
||||
new_stripped_markup, new_tag_body = tag.migrate(tag_name, stripped_markup, tokenizer, parse_context)
|
||||
new_stripped_markup
|
||||
end
|
||||
result << Utils.match_capture_replace(match, 4, new_markup) << new_tag_body.to_s
|
||||
when token.start_with?(VARSTART)
|
||||
result << migrate_variable(token, parse_context)
|
||||
else
|
||||
result << token
|
||||
end
|
||||
parse_context.line_number = tokenizer.line_number
|
||||
end
|
||||
|
||||
[result, nil]
|
||||
end
|
||||
|
||||
def whitespace_handler(token, parse_context)
|
||||
if token[2] == WhitespaceControl
|
||||
previous_token = @nodelist.last
|
||||
@@ -368,17 +246,6 @@ module Liquid
|
||||
BlockBody.raise_missing_variable_terminator(token, parse_context)
|
||||
end
|
||||
|
||||
private_class_method def self.migrate_variable(token, parse_context)
|
||||
match = token.match(ContentOfVariable)
|
||||
if match
|
||||
new_markup = Utils.migrate_stripped(match[1]) do |markup|
|
||||
Variable.migrate(markup, parse_context)
|
||||
end
|
||||
return Utils.match_capture_replace(match, 1, new_markup)
|
||||
end
|
||||
BlockBody.raise_missing_variable_terminator(token, parse_context)
|
||||
end
|
||||
|
||||
# @deprecated Use {.raise_missing_tag_terminator} instead
|
||||
def raise_missing_tag_terminator(token, parse_context)
|
||||
BlockBody.raise_missing_tag_terminator(token, parse_context)
|
||||
|
||||
@@ -159,8 +159,10 @@ module Liquid
|
||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
||||
def children
|
||||
[
|
||||
@node.left, @node.right,
|
||||
@node.child_condition, @node.attachment
|
||||
@node.left,
|
||||
@node.right,
|
||||
@node.child_condition,
|
||||
@node.attachment
|
||||
].compact
|
||||
end
|
||||
end
|
||||
|
||||
@@ -144,7 +144,7 @@ module Liquid
|
||||
self.class.build(
|
||||
resource_limits: resource_limits,
|
||||
static_environments: static_environments,
|
||||
registers: Registers.new(registers)
|
||||
registers: Registers.new(registers),
|
||||
).tap do |subcontext|
|
||||
subcontext.base_scope_depth = base_scope_depth + 1
|
||||
subcontext.exception_renderer = exception_renderer
|
||||
|
||||
@@ -28,16 +28,6 @@ module Liquid
|
||||
raise
|
||||
end
|
||||
|
||||
def self.migrate(tokenizer, parse_context)
|
||||
new_body, unknown_tag = BlockBody.migrate(tokenizer, parse_context)
|
||||
raise SyntaxError if unknown_tag
|
||||
|
||||
new_body
|
||||
rescue SyntaxError => e
|
||||
e.line_number ||= parse_context.line_number
|
||||
raise
|
||||
end
|
||||
|
||||
def unknown_tag(tag, _markup, _tokenizer)
|
||||
case tag
|
||||
when 'else', 'end'
|
||||
|
||||
@@ -25,6 +25,10 @@ module Liquid
|
||||
class Drop
|
||||
attr_writer :context
|
||||
|
||||
def initialize
|
||||
@context = nil
|
||||
end
|
||||
|
||||
# Catch all for the method
|
||||
def liquid_method_missing(method)
|
||||
return nil unless @context&.strict_variables
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
module Liquid
|
||||
class Expression
|
||||
LITERALS = {
|
||||
nil => nil, 'nil' => nil, 'null' => nil, '' => nil,
|
||||
nil => nil,
|
||||
'nil' => nil,
|
||||
'null' => nil,
|
||||
'' => nil,
|
||||
'true' => true,
|
||||
'false' => false,
|
||||
'blank' => '',
|
||||
@@ -41,36 +44,5 @@ module Liquid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.lax_migrate(markup)
|
||||
Utils.migrate_stripped(markup) do |markup|
|
||||
raise ArgumentError, "unexpected empty expression" if markup.empty?
|
||||
|
||||
if (markup.start_with?('"') && markup.end_with?('"')) ||
|
||||
(markup.start_with?("'") && markup.end_with?("'"))
|
||||
markup
|
||||
else
|
||||
case markup
|
||||
when INTEGERS_REGEX
|
||||
markup
|
||||
when RANGES_REGEX
|
||||
match = Regexp.last_match
|
||||
new_start, new_end = RangeLookup.lax_migrate(match[1], match[2])
|
||||
Utils.match_captures_replace(match, 1 => new_start, 2 => new_end)
|
||||
when FLOATS_REGEX
|
||||
# lax parser allowed multiple periods, but the second period and following characters were ignored
|
||||
new_markup = markup.slice(/\A(-?\d+\.\d*)/)
|
||||
new_markup << "0" if new_markup.end_with?(".")
|
||||
new_markup
|
||||
else
|
||||
if LITERALS.key?(markup)
|
||||
markup
|
||||
else
|
||||
VariableLookup.lax_migrate(markup)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@ module Liquid
|
||||
# @liquid_type object
|
||||
# @liquid_name forloop
|
||||
# @liquid_summary
|
||||
# Information about a parent [`for` loop](/api/liquid/tags/for).
|
||||
# Information about a parent [`for` loop](/docs/api/liquid/tags/for).
|
||||
class ForloopDrop < Drop
|
||||
def initialize(name, length, parentloop)
|
||||
@name = name
|
||||
|
||||
@@ -5,9 +5,6 @@ module Liquid
|
||||
attr_accessor :locale, :line_number, :trim_whitespace, :depth
|
||||
attr_reader :partial, :warnings, :error_mode
|
||||
|
||||
# @api private
|
||||
attr_writer :error_mode
|
||||
|
||||
def initialize(options = {})
|
||||
@template_options = options ? options.dup : {}
|
||||
|
||||
|
||||
@@ -2,30 +2,6 @@
|
||||
|
||||
module Liquid
|
||||
module ParserSwitching
|
||||
module ClassMethods
|
||||
def migrate_with_selected_parser(tag_name, markup, tokenizer, parse_context)
|
||||
case parse_context.error_mode
|
||||
when :strict then strict_migrate(tag_name, markup, tokenizer, parse_context)
|
||||
when :lax then lax_migrate(tag_name, markup, tokenizer, parse_context)
|
||||
when :warn
|
||||
begin
|
||||
parse_context.error_mode = :strict
|
||||
begin
|
||||
# Use exception side effect to conditionally branch to lax migration
|
||||
parse(tag_name, markup, tokenizer, parse_context)
|
||||
ensure
|
||||
parse_context.error_mode = :warn
|
||||
end
|
||||
|
||||
strict_migrate(tag_name, markup, tokenizer, parse_context)
|
||||
rescue SyntaxError => e
|
||||
parse_context.warnings << e
|
||||
lax_migrate(tag_name, markup, tokenizer, parse_context)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def strict_parse_with_error_mode_fallback(markup)
|
||||
strict_parse_with_error_context(markup)
|
||||
rescue SyntaxError => e
|
||||
|
||||
@@ -16,6 +16,8 @@ module Liquid
|
||||
template = template_factory.for(template_name)
|
||||
|
||||
partial = template.parse(source, parse_context)
|
||||
partial.name ||= template_name
|
||||
|
||||
cached_partials[template_name] = partial
|
||||
ensure
|
||||
parse_context.partial = false
|
||||
|
||||
@@ -22,37 +22,6 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
def self.lax_migrate(start_markup, end_markup)
|
||||
new_start = Expression.lax_migrate(start_markup)
|
||||
new_end = Expression.lax_migrate(end_markup)
|
||||
|
||||
# cast literals
|
||||
start_obj = Expression.parse(new_start)
|
||||
end_obj = Expression.parse(new_end)
|
||||
if start_obj.respond_to?(:evaluate) || end_obj.respond_to?(:evaluate)
|
||||
new_start = lax_migrate_range_expression(new_start, start_obj)
|
||||
new_end = lax_migrate_range_expression(new_end, end_obj)
|
||||
else
|
||||
new_start = start_obj.to_i.to_s unless start_obj.is_a?(Integer)
|
||||
new_end = end_obj.to_i.to_s unless end_obj.is_a?(Integer)
|
||||
end
|
||||
|
||||
[new_start, new_end]
|
||||
end
|
||||
|
||||
def self.lax_migrate_range_expression(markup, expression)
|
||||
return markup if expression.respond_to?(:evaluate)
|
||||
|
||||
case expression
|
||||
when Integer
|
||||
markup
|
||||
when NilClass, String
|
||||
expression.to_i.to_s
|
||||
else
|
||||
Utils.to_integer(input).to_s
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :start_obj, :end_obj
|
||||
|
||||
def initialize(start_obj, end_obj)
|
||||
|
||||
@@ -25,7 +25,7 @@ module Liquid
|
||||
STRIP_HTML_BLOCKS = Regexp.union(
|
||||
%r{<script.*?</script>}m,
|
||||
/<!--.*?-->/m,
|
||||
%r{<style.*?</style>}m
|
||||
%r{<style.*?</style>}m,
|
||||
)
|
||||
STRIP_HTML_TAGS = /<.*?>/m
|
||||
|
||||
@@ -615,7 +615,7 @@ module Liquid
|
||||
# @liquid_description
|
||||
# > Note:
|
||||
# > The `concat` filter won't filter out duplicates. If you want to remove duplicates, then you need to use the
|
||||
# > [`uniq` filter](/api/liquid/filters/uniq).
|
||||
# > [`uniq` filter](/docs/api/liquid/filters/uniq).
|
||||
# @liquid_syntax array | concat: array
|
||||
# @liquid_return [array[untyped]]
|
||||
def concat(input, array)
|
||||
@@ -757,7 +757,7 @@ module Liquid
|
||||
# @liquid_type filter
|
||||
# @liquid_category math
|
||||
# @liquid_summary
|
||||
# Divides a number by a given number.
|
||||
# Divides a number by a given number. The `divided_by` filter produces a result of the same type as the divisor. This means if you divide by an integer, the result will be an integer, and if you divide by a float, the result will be a float.
|
||||
# @liquid_syntax number | divided_by: number
|
||||
# @liquid_return [number]
|
||||
def divided_by(input, operand)
|
||||
@@ -857,9 +857,9 @@ module Liquid
|
||||
# @liquid_summary
|
||||
# Sets a default value for any variable whose value is one of the following:
|
||||
#
|
||||
# - [`empty`](/api/liquid/basics#empty)
|
||||
# - [`false`](/api/liquid/basics#truthy-and-falsy)
|
||||
# - [`nil`](/api/liquid/basics#nil)
|
||||
# - [`empty`](/docs/api/liquid/basics#empty)
|
||||
# - [`false`](/docs/api/liquid/basics#truthy-and-falsy)
|
||||
# - [`nil`](/docs/api/liquid/basics#nil)
|
||||
# @liquid_syntax variable | default: variable
|
||||
# @liquid_return [untyped]
|
||||
# @liquid_optional_param allow_false [boolean] Whether to use false values instead of the default.
|
||||
|
||||
@@ -5,7 +5,7 @@ module Liquid
|
||||
# @liquid_type object
|
||||
# @liquid_name tablerowloop
|
||||
# @liquid_summary
|
||||
# Information about a parent [`tablerow` loop](/api/liquid/tags/tablerow).
|
||||
# Information about a parent [`tablerow` loop](/docs/api/liquid/tags/tablerow).
|
||||
class TablerowloopDrop < Drop
|
||||
def initialize(length, cols)
|
||||
@length = length
|
||||
|
||||
@@ -7,8 +7,6 @@ module Liquid
|
||||
include ParserSwitching
|
||||
|
||||
class << self
|
||||
include ParserSwitching::ClassMethods
|
||||
|
||||
def parse(tag_name, markup, tokenizer, parse_context)
|
||||
tag = new(tag_name, markup, parse_context)
|
||||
tag.parse(tokenizer)
|
||||
|
||||
@@ -8,7 +8,7 @@ module Liquid
|
||||
# @liquid_summary
|
||||
# Creates a new variable.
|
||||
# @liquid_description
|
||||
# You can create variables of any [basic type](/api/liquid/basics#types), [object](/api/liquid/objects), or object property.
|
||||
# You can create variables of any [basic type](/docs/api/liquid/basics#types), [object](/docs/api/liquid/objects), or object property.
|
||||
# @liquid_syntax
|
||||
# {% assign variable_name = value %}
|
||||
# @liquid_syntax_keyword variable_name The name of the variable being created.
|
||||
@@ -21,18 +21,6 @@ module Liquid
|
||||
raise Liquid::SyntaxError, parse_context.locale.t('errors.syntax.assign')
|
||||
end
|
||||
|
||||
def self.migrate(tag_name, markup, tokenizer, parse_context)
|
||||
match = markup.match(Syntax)
|
||||
new_variable_markup = Variable.migrate(match[2], parse_context)
|
||||
new_markup = Utils.match_captures_replace(match, 2 => new_variable_markup)
|
||||
|
||||
# replace scanned over characters with a space to ensure there is a space
|
||||
# to separate the tag name and the variable name
|
||||
new_markup.prepend(" ") if match.begin(0) > 0
|
||||
|
||||
new_markup
|
||||
end
|
||||
|
||||
attr_reader :to, :from
|
||||
|
||||
def initialize(tag_name, markup, parse_context)
|
||||
|
||||
@@ -15,16 +15,12 @@ module Liquid
|
||||
# @liquid_category iteration
|
||||
# @liquid_name break
|
||||
# @liquid_summary
|
||||
# Stops a [`for` loop](/api/liquid/tags/for) from iterating.
|
||||
# Stops a [`for` loop](/docs/api/liquid/tags/for) from iterating.
|
||||
# @liquid_syntax
|
||||
# {% break %}
|
||||
class Break < Tag
|
||||
INTERRUPT = BreakInterrupt.new.freeze
|
||||
|
||||
def self.migrate(_tag_name, _markup, _tokenizer, _parse_context)
|
||||
"" # markup was ignored
|
||||
end
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
context.push_interrupt(INTERRUPT)
|
||||
output
|
||||
|
||||
@@ -18,21 +18,6 @@ module Liquid
|
||||
class Capture < Block
|
||||
Syntax = /(#{VariableSignature}+)/o
|
||||
|
||||
def self.migrate(tag_name, markup, tokenizer, parse_context)
|
||||
match = markup.match(Syntax)
|
||||
|
||||
new_markup = match[1]
|
||||
|
||||
# replace scanned over characters with a space to ensure there is a space
|
||||
# to separate the tag name and the variable name
|
||||
new_markup.prepend(" ") if match.begin(0) > 0
|
||||
|
||||
new_body, unknown_tag = migrate_body(tag_name, tokenizer, parse_context)
|
||||
raise SyntaxError if unknown_tag
|
||||
|
||||
[new_markup, new_body]
|
||||
end
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
if markup =~ Syntax
|
||||
|
||||
+1
-65
@@ -28,20 +28,6 @@ module Liquid
|
||||
|
||||
attr_reader :blocks, :left
|
||||
|
||||
def self.migrate(tag_name, markup, tokenizer, parse_context)
|
||||
match = markup.match(/\s*#{Syntax}\s*/o) || raise(SyntaxError)
|
||||
|
||||
new_expression = Expression.lax_migrate(match[1])
|
||||
new_markup = Utils.match_captures_replace(match, { 1 => new_expression })
|
||||
|
||||
# replace scanned over characters with a space to ensure there is a space
|
||||
# to separate the tag name and the variable name
|
||||
new_markup.prepend(" ") if match.begin(0) > 0
|
||||
|
||||
new_body = migrate_body(tag_name, tokenizer, parse_context)
|
||||
[new_markup, new_body]
|
||||
end
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
@blocks = []
|
||||
@@ -53,37 +39,6 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
def self.migrate_body(start_tag_name, tokenizer, parse_context)
|
||||
result = +""
|
||||
|
||||
# body before first `when` delimiter tag is ignored
|
||||
unused_body, delimiter_tag = super(start_tag_name, tokenizer, parse_context)
|
||||
unless delimiter_tag
|
||||
raise NotImplementedError, "TODO: migrate `case` tag with no `when` or `else` tags"
|
||||
end
|
||||
|
||||
result << Utils.migrate_stripped(unused_body) { "" } # just keep whitespace (e.g. newline and indent)
|
||||
|
||||
while delimiter_tag
|
||||
break unless delimiter_tag
|
||||
|
||||
case delimiter_tag.tag_name
|
||||
when "when"
|
||||
new_markup = migrate_when_markup(delimiter_tag.markup)
|
||||
result << delimiter_tag.replaced_markup(new_markup)
|
||||
when "else"
|
||||
result << delimiter_tag.original_tag_string
|
||||
else
|
||||
raise SyntaxError
|
||||
end
|
||||
|
||||
new_body, delimiter_tag = super(start_tag_name, tokenizer, parse_context)
|
||||
result << new_body
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def parse(tokens)
|
||||
body = case_body = new_body
|
||||
body = @blocks.last.attachment while parse_body(body, tokens)
|
||||
@@ -122,7 +77,7 @@ module Liquid
|
||||
end
|
||||
|
||||
result = Liquid::Utils.to_liquid_value(
|
||||
block.evaluate(context)
|
||||
block.evaluate(context),
|
||||
)
|
||||
|
||||
if result
|
||||
@@ -136,25 +91,6 @@ module Liquid
|
||||
|
||||
private
|
||||
|
||||
private_class_method def self.migrate_when_markup(unstripped_markup)
|
||||
Utils.migrate_stripped(unstripped_markup) do |markup|
|
||||
match = markup.match(WhenSyntax) || raise(SyntaxError)
|
||||
|
||||
replacements = { 1 => Expression.lax_migrate(match[1]) }
|
||||
if (right = match[2])
|
||||
replacements[2] = migrate_when_markup(right)
|
||||
end
|
||||
|
||||
new_markup = Utils.match_captures_replace(match, replacements)
|
||||
|
||||
# replace scanned over characters with a space to ensure there is a space
|
||||
# to separate the tag name and the variable name
|
||||
new_markup.prepend(" ") if match.begin(0) > 0
|
||||
|
||||
new_markup
|
||||
end
|
||||
end
|
||||
|
||||
def record_when_condition(markup)
|
||||
body = new_body
|
||||
|
||||
|
||||
@@ -15,24 +15,6 @@ module Liquid
|
||||
# {% endcomment %}
|
||||
# @liquid_syntax_keyword content The content of the comment.
|
||||
class Comment < Block
|
||||
def self.migrate(tag_name, _markup, tokenizer, parse_context)
|
||||
new_markup = "" # markup was ignored
|
||||
new_body = migrate_body(tag_name, tokenizer, parse_context)
|
||||
[new_markup, new_body]
|
||||
end
|
||||
|
||||
def self.migrate_body(start_tag_name, tokenizer, parse_context)
|
||||
result = +""
|
||||
loop do
|
||||
new_body, delimiter_tag = super(start_tag_name, tokenizer, parse_context)
|
||||
result << new_body
|
||||
break unless delimiter_tag
|
||||
|
||||
result << delimiter_tag.original_tag_string # unknown tags allowed
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def render_to_output_buffer(_context, output)
|
||||
output
|
||||
end
|
||||
|
||||
@@ -6,16 +6,12 @@ module Liquid
|
||||
# @liquid_category iteration
|
||||
# @liquid_name continue
|
||||
# @liquid_summary
|
||||
# Causes a [`for` loop](/api/liquid/tags/for) to skip to the next iteration.
|
||||
# Causes a [`for` loop](/docs/api/liquid/tags/for) to skip to the next iteration.
|
||||
# @liquid_syntax
|
||||
# {% continue %}
|
||||
class Continue < Tag
|
||||
INTERRUPT = ContinueInterrupt.new.freeze
|
||||
|
||||
def self.migrate(_tag_name, _markup, _tokenizer, _parse_context)
|
||||
"" # markup was ignored
|
||||
end
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
context.push_interrupt(INTERRUPT)
|
||||
output
|
||||
|
||||
@@ -6,7 +6,7 @@ module Liquid
|
||||
# @liquid_category iteration
|
||||
# @liquid_name cycle
|
||||
# @liquid_summary
|
||||
# Loops through a group of strings and outputs them one at a time for each iteration of a [`for` loop](/api/liquid/tags/for).
|
||||
# Loops through a group of strings and outputs them one at a time for each iteration of a [`for` loop](/docs/api/liquid/tags/for).
|
||||
# @liquid_description
|
||||
# The `cycle` tag must be used inside a `for` loop.
|
||||
#
|
||||
@@ -18,43 +18,8 @@ module Liquid
|
||||
SimpleSyntax = /\A#{QuotedFragment}+/o
|
||||
NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
|
||||
|
||||
def self.migrate(tag_name, markup, tokenizer, parse_context)
|
||||
new_markup = case markup
|
||||
when NamedSyntax
|
||||
match = Regexp.last_match
|
||||
|
||||
new_name_syntax = Expression.lax_migrate(match[1])
|
||||
new_variables_markup = migrate_variables_from_string(match[2])
|
||||
|
||||
Utils.match_captures_replace(match, 1 => new_name_syntax, 2 => new_variables_markup)
|
||||
when SimpleSyntax
|
||||
match = Regexp.last_match
|
||||
migrate_variables_from_string(markup)
|
||||
else
|
||||
raise SyntaxError
|
||||
end
|
||||
|
||||
# replace scanned over characters with a space to ensure there is a space
|
||||
# to separate the tag name and the variable name
|
||||
new_markup.prepend(" ") if match.begin(0) > 0
|
||||
|
||||
new_markup
|
||||
end
|
||||
|
||||
def self.migrate_variables_from_string(markup)
|
||||
markup.split(',').collect do |var|
|
||||
match = var.match(/\s*(#{QuotedFragment})\s*/o)
|
||||
if match
|
||||
Utils.match_captures_replace(match, 1 => Expression.lax_migrate(match[1]))
|
||||
end
|
||||
end.compact.join(",")
|
||||
end
|
||||
|
||||
attr_reader :variables
|
||||
|
||||
# @api private
|
||||
attr_reader :name
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
case markup
|
||||
|
||||
@@ -12,8 +12,8 @@ module Liquid
|
||||
# or [section](/themes/architecture/sections) file that they're created in. However, the variable is shared across
|
||||
# [snippets](/themes/architecture#snippets) included in the file.
|
||||
#
|
||||
# Similarly, variables that are created with `decrement` are independent from those created with [`assign`](/api/liquid/tags/assign)
|
||||
# and [`capture`](/api/liquid/tags/capture). However, `decrement` and [`increment`](/api/liquid/tags/increment) share
|
||||
# Similarly, variables that are created with `decrement` are independent from those created with [`assign`](/docs/api/liquid/tags/assign)
|
||||
# and [`capture`](/docs/api/liquid/tags/capture). However, `decrement` and [`increment`](/docs/api/liquid/tags/increment) share
|
||||
# variables.
|
||||
# @liquid_syntax
|
||||
# {% decrement variable_name %}
|
||||
@@ -21,10 +21,6 @@ module Liquid
|
||||
class Decrement < Tag
|
||||
attr_reader :variable_name
|
||||
|
||||
def self.migrate(_tag_name, markup, _tokenizer, _parse_context)
|
||||
markup # no characters ignored, it just uses anything for the variable name
|
||||
end
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
@variable_name = markup.strip
|
||||
|
||||
@@ -9,10 +9,10 @@ module Liquid
|
||||
# Outputs an expression.
|
||||
# @liquid_description
|
||||
# Using the `echo` tag is the same as wrapping an expression in curly brackets (`{{` and `}}`). However, unlike the curly
|
||||
# bracket method, you can use the `echo` tag inside [`liquid` tags](/api/liquid/tags/liquid).
|
||||
# bracket method, you can use the `echo` tag inside [`liquid` tags](/docs/api/liquid/tags/liquid).
|
||||
#
|
||||
# > Tip:
|
||||
# > You can use [filters](/api/liquid/filters) on expressions inside `echo` tags.
|
||||
# > You can use [filters](/docs/api/liquid/filters) on expressions inside `echo` tags.
|
||||
# @liquid_syntax
|
||||
# {% liquid
|
||||
# echo expression
|
||||
@@ -21,10 +21,6 @@ module Liquid
|
||||
class Echo < Tag
|
||||
attr_reader :variable
|
||||
|
||||
def self.migrate(tag_name, markup, tokenizer, parse_context)
|
||||
Variable.migrate(markup, parse_context)
|
||||
end
|
||||
|
||||
def initialize(tag_name, markup, parse_context)
|
||||
super
|
||||
@variable = Variable.new(markup, parse_context)
|
||||
|
||||
+3
-41
@@ -9,10 +9,10 @@ module Liquid
|
||||
# Renders an expression for every item in an array.
|
||||
# @liquid_description
|
||||
# You can do a maximum of 50 iterations with a `for` loop. If you need to iterate over more than 50 items, then use the
|
||||
# [`paginate` tag](/api/liquid/tags/paginate) to split the items over multiple pages.
|
||||
# [`paginate` tag](/docs/api/liquid/tags/paginate) to split the items over multiple pages.
|
||||
#
|
||||
# > Tip:
|
||||
# > Every `for` loop has an associated [`forloop` object](/api/liquid/objects/forloop) with information about the loop.
|
||||
# > Every `for` loop has an associated [`forloop` object](/docs/api/liquid/objects/forloop) with information about the loop.
|
||||
# @liquid_syntax
|
||||
# {% for variable in array %}
|
||||
# expression
|
||||
@@ -25,16 +25,10 @@ module Liquid
|
||||
# @liquid_optional_param range [untyped] A custom numeric range to iterate over.
|
||||
# @liquid_optional_param reversed [untyped] Iterate in reverse order.
|
||||
class For < Block
|
||||
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)(\s*reversed)?/o
|
||||
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
|
||||
|
||||
attr_reader :collection_name, :variable_name, :limit, :from
|
||||
|
||||
def self.migrate(tag_name, markup, tokenizer, parse_context)
|
||||
new_markup = migrate_with_selected_parser(tag_name, markup, tokenizer, parse_context)
|
||||
new_body = migrate_body(tag_name, tokenizer, parse_context)
|
||||
[new_markup, new_body]
|
||||
end
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
@from = @limit = nil
|
||||
@@ -59,27 +53,6 @@ module Liquid
|
||||
@else_block ? [@for_block, @else_block] : [@for_block]
|
||||
end
|
||||
|
||||
def self.migrate_body(start_tag_name, tokenizer, parse_context)
|
||||
result = +""
|
||||
|
||||
new_body, delimiter_tag = super(start_tag_name, tokenizer, parse_context)
|
||||
result << new_body
|
||||
|
||||
else_tag = delimiter_tag
|
||||
else_body = nil
|
||||
while delimiter_tag
|
||||
raise SyntaxError unless delimiter_tag.tag_name == 'else'
|
||||
else_tag = delimiter_tag
|
||||
else_body, delimiter_tag = super(start_tag_name, tokenizer, parse_context)
|
||||
end
|
||||
if else_tag
|
||||
result << else_tag.replaced_markup("") # markup was ignored in else tags
|
||||
result << else_body
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def unknown_tag(tag, markup, tokens)
|
||||
return super unless tag == 'else'
|
||||
@else_block = new_body
|
||||
@@ -99,13 +72,6 @@ module Liquid
|
||||
|
||||
protected
|
||||
|
||||
private_class_method def self.lax_migrate(tag_name, markup, tokenizer, parse_context)
|
||||
match = markup.match(Syntax) || raise(SyntaxError)
|
||||
new_collection_name = Expression.lax_migrate(match[2])
|
||||
new_markup = Utils.match_captures_replace(match, { 2 => new_collection_name }.compact)
|
||||
new_markup << Utils.migrate_tag_attributes(markup)
|
||||
end
|
||||
|
||||
def lax_parse(markup)
|
||||
if markup =~ Syntax
|
||||
@variable_name = Regexp.last_match(1)
|
||||
@@ -121,10 +87,6 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
private_class_method def self.strict_migrate(tag_name, markup, tokenizer, parse_context)
|
||||
markup
|
||||
end
|
||||
|
||||
def strict_parse(markup)
|
||||
p = Parser.new(markup)
|
||||
@variable_name = p.consume(:id)
|
||||
|
||||
+1
-72
@@ -20,39 +20,6 @@ module Liquid
|
||||
|
||||
attr_reader :blocks
|
||||
|
||||
def self.migrate(tag_name, markup, tokenizer, parse_context)
|
||||
new_markup = migrate_with_selected_parser(tag_name, markup, tokenizer, parse_context)
|
||||
new_body = migrate_body(tag_name, tokenizer, parse_context)
|
||||
[new_markup, new_body]
|
||||
end
|
||||
|
||||
def self.migrate_body(start_tag_name, tokenizer, parse_context)
|
||||
result = +""
|
||||
|
||||
loop do
|
||||
new_body, delimiter_tag = super(start_tag_name, tokenizer, parse_context)
|
||||
result << new_body
|
||||
|
||||
break unless delimiter_tag
|
||||
|
||||
case delimiter_tag.tag_name
|
||||
when "else"
|
||||
result << delimiter_tag.replaced_markup("") # markup was ignored on else tags
|
||||
when "elsif"
|
||||
new_markup = migrate_with_selected_parser(delimiter_tag.tag_name, delimiter_tag.markup, tokenizer, parse_context)
|
||||
result << delimiter_tag.replaced_markup(new_markup)
|
||||
else
|
||||
raise SyntaxError
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
private_class_method def self.strict_migrate(tag_name, markup, tokenizer, parse_context)
|
||||
markup
|
||||
end
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
@blocks = []
|
||||
@@ -86,7 +53,7 @@ module Liquid
|
||||
def render_to_output_buffer(context, output)
|
||||
@blocks.each do |block|
|
||||
result = Liquid::Utils.to_liquid_value(
|
||||
block.evaluate(context)
|
||||
block.evaluate(context),
|
||||
)
|
||||
|
||||
if result
|
||||
@@ -114,10 +81,6 @@ module Liquid
|
||||
Condition.parse_expression(parse_context, markup)
|
||||
end
|
||||
|
||||
private_class_method def self.lax_migrate_expression(markup)
|
||||
Expression.lax_migrate(markup)
|
||||
end
|
||||
|
||||
def lax_parse(markup)
|
||||
expressions = markup.scan(ExpressionsAndOperators)
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.if") unless expressions.pop =~ Syntax
|
||||
@@ -138,40 +101,6 @@ module Liquid
|
||||
condition
|
||||
end
|
||||
|
||||
private_class_method def self.lax_migrate(tag_name, markup, tokenizer, parse_context)
|
||||
expressions = markup.scan(ExpressionsAndOperators)
|
||||
|
||||
new_markup = lax_migrate_condition(expressions.pop)
|
||||
until expressions.empty?
|
||||
operator = expressions.pop
|
||||
new_left_markup = lax_migrate_condition(expressions.pop)
|
||||
new_markup = new_left_markup << operator << new_markup
|
||||
end
|
||||
|
||||
new_markup
|
||||
end
|
||||
|
||||
private_class_method def self.lax_migrate_condition(markup)
|
||||
Utils.migrate_stripped(markup) do |markup|
|
||||
match = markup.match(Syntax)
|
||||
left = lax_migrate_expression(match[1])
|
||||
op = match[2]
|
||||
right_capture = match[3]
|
||||
if op
|
||||
right = lax_migrate_expression(right_capture) if right_capture
|
||||
elsif right_capture
|
||||
right = "" # remove right capture, since it is ignored with no operator
|
||||
end
|
||||
new_markup = Utils.match_captures_replace(match, { 1 => left, 2 => op, 3 => right }.compact)
|
||||
new_markup.prepend(' ') if match.begin(0) > 0
|
||||
new_markup << ' ' if match.end(0) < markup.length
|
||||
if op && !right # missing right operand missing
|
||||
new_markup << " nil" # replace with nil, which it was semantically treated as
|
||||
end
|
||||
new_markup
|
||||
end
|
||||
end
|
||||
|
||||
def strict_parse(markup)
|
||||
p = Parser.new(markup)
|
||||
condition = parse_binary_comparisons(p)
|
||||
|
||||
@@ -2,15 +2,6 @@
|
||||
|
||||
module Liquid
|
||||
class Ifchanged < Block
|
||||
def self.migrate(tag_name, _markup, tokenizer, parse_context)
|
||||
new_markup = "" # markup was ignored
|
||||
|
||||
new_body, unknown_tag = migrate_body(tag_name, tokenizer, parse_context)
|
||||
raise SyntaxError if unknown_tag
|
||||
|
||||
[new_markup, new_body]
|
||||
end
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
block_output = +''
|
||||
super(context, block_output)
|
||||
|
||||
@@ -8,7 +8,7 @@ module Liquid
|
||||
# @liquid_summary
|
||||
# Renders a [snippet](/themes/architecture#snippets).
|
||||
# @liquid_description
|
||||
# Inside the snippet, you can access and alter variables that are [created](/api/liquid/tags/variable-tags) outside of the
|
||||
# Inside the snippet, you can access and alter variables that are [created](/docs/api/liquid/tags/variable-tags) outside of the
|
||||
# snippet.
|
||||
# @liquid_syntax
|
||||
# {% include 'filename' %}
|
||||
@@ -16,7 +16,7 @@ module Liquid
|
||||
# @liquid_deprecated
|
||||
# Deprecated because the way that variables are handled reduces performance and makes code harder to both read and maintain.
|
||||
#
|
||||
# The `include` tag has been replaced by [`render`](/api/liquid/tags/render).
|
||||
# The `include` tag has been replaced by [`render`](/docs/api/liquid/tags/render).
|
||||
class Include < Tag
|
||||
prepend Tag::Disableable
|
||||
|
||||
@@ -25,22 +25,6 @@ module Liquid
|
||||
|
||||
attr_reader :template_name_expr, :variable_name_expr, :attributes
|
||||
|
||||
def self.migrate(_tag_name, markup, _tokenizer, parse_context)
|
||||
match = markup.match(SYNTAX) || raise(SyntaxError)
|
||||
|
||||
template_name = Expression.lax_migrate(match[1])
|
||||
variable_name = Expression.lax_migrate(match[3]) if match[3]
|
||||
|
||||
new_markup = Utils.match_captures_replace(match, { 1 => template_name, 3 => variable_name }.compact)
|
||||
new_markup << Utils.migrate_tag_attributes(markup)
|
||||
|
||||
# replace scanned over characters with a space to ensure there is a space
|
||||
# to separate the tag name and the variable name
|
||||
new_markup.prepend(" ") if match.begin(0) > 0
|
||||
|
||||
new_markup
|
||||
end
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
|
||||
@@ -73,7 +57,7 @@ module Liquid
|
||||
partial = PartialCache.load(
|
||||
template_name,
|
||||
context: context,
|
||||
parse_context: parse_context
|
||||
parse_context: parse_context,
|
||||
)
|
||||
|
||||
context_variable_name = @alias_name || template_name.split('/').last
|
||||
@@ -86,9 +70,11 @@ module Liquid
|
||||
|
||||
old_template_name = context.template_name
|
||||
old_partial = context.partial
|
||||
|
||||
begin
|
||||
context.template_name = template_name
|
||||
context.partial = true
|
||||
context.template_name = partial.name
|
||||
context.partial = true
|
||||
|
||||
context.stack do
|
||||
@attributes.each do |key, value|
|
||||
context[key] = context.evaluate(value)
|
||||
|
||||
@@ -12,8 +12,8 @@ module Liquid
|
||||
# or [section](/themes/architecture/sections) file that they're created in. However, the variable is shared across
|
||||
# [snippets](/themes/architecture#snippets) included in the file.
|
||||
#
|
||||
# Similarly, variables that are created with `increment` are independent from those created with [`assign`](/api/liquid/tags/assign)
|
||||
# and [`capture`](/api/liquid/tags/capture). However, `increment` and [`decrement`](/api/liquid/tags/decrement) share
|
||||
# Similarly, variables that are created with `increment` are independent from those created with [`assign`](/docs/api/liquid/tags/assign)
|
||||
# and [`capture`](/docs/api/liquid/tags/capture). However, `increment` and [`decrement`](/docs/api/liquid/tags/decrement) share
|
||||
# variables.
|
||||
# @liquid_syntax
|
||||
# {% increment variable_name %}
|
||||
@@ -21,10 +21,6 @@ module Liquid
|
||||
class Increment < Tag
|
||||
attr_reader :variable_name
|
||||
|
||||
def self.migrate(_tag_name, markup, _tokenizer, _parse_context)
|
||||
markup # no characters ignored, it just uses anything for the variable name
|
||||
end
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
@variable_name = markup.strip
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
|
||||
module Liquid
|
||||
class InlineComment < Tag
|
||||
def self.migrate(_tag_name, markup, _tokenizer, _parse_context)
|
||||
markup
|
||||
end
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
|
||||
|
||||
+1
-26
@@ -14,32 +14,7 @@ module Liquid
|
||||
# @liquid_syntax_keyword expression The expression to be output without being rendered.
|
||||
class Raw < Block
|
||||
Syntax = /\A\s*\z/
|
||||
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om
|
||||
|
||||
def self.migrate(tag_name, markup, tokenizer, parse_context)
|
||||
raise SyntaxError unless Syntax.match?(markup)
|
||||
|
||||
new_body = migrate_body(tag_name, tokenizer, parse_context)
|
||||
[markup, new_body]
|
||||
end
|
||||
|
||||
def self.migrate_body(start_tag_name, tokenizer, parse_context)
|
||||
block_delimiter = "end#{start_tag_name}"
|
||||
|
||||
body = +''
|
||||
while (token = tokenizer.shift)
|
||||
match = token.match(FullTokenPossiblyInvalid)
|
||||
if match && block_delimiter == match[2]
|
||||
body << Utils.match_captures_replace(match, { 3 => "" })
|
||||
return body
|
||||
end
|
||||
body << token unless token.empty?
|
||||
end
|
||||
|
||||
raise SyntaxError
|
||||
end
|
||||
|
||||
attr_reader :body
|
||||
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}#{WhitespaceControl}?\s*(\w+)\s*(.*)?#{WhitespaceControl}?#{TagEnd}\z/om
|
||||
|
||||
def initialize(tag_name, markup, parse_context)
|
||||
super
|
||||
|
||||
@@ -8,19 +8,19 @@ module Liquid
|
||||
# @liquid_summary
|
||||
# Renders a [snippet](/themes/architecture#snippets) or [app block](/themes/architecture/sections/section-schema#render-app-blocks).
|
||||
# @liquid_description
|
||||
# Inside snippets and app blocks, you can't directly access variables that are [created](/api/liquid/tags/variable-tags) outside
|
||||
# of the snippet or app block. However, you can [specify variables as parameters](/api/liquid/tags/render#render-passing-variables-to-a-snippet)
|
||||
# Inside snippets and app blocks, you can't directly access variables that are [created](/docs/api/liquid/tags/variable-tags) outside
|
||||
# of the snippet or app block. However, you can [specify variables as parameters](/docs/api/liquid/tags/render#render-passing-variables-to-a-snippet)
|
||||
# to pass outside variables to snippets.
|
||||
#
|
||||
# While you can't directly access created variables, you can access global objects, as well as any objects that are
|
||||
# directly accessible outside the snippet or app block. For example, a snippet or app block inside the [product template](/themes/architecture/templates/product)
|
||||
# can access the [`product` object](/api/liquid/objects/product), and a snippet or app block inside a [section](/themes/architecture/sections)
|
||||
# can access the [`section` object](/api/liquid/objects/section).
|
||||
# can access the [`product` object](/docs/api/liquid/objects/product), and a snippet or app block inside a [section](/themes/architecture/sections)
|
||||
# can access the [`section` object](/docs/api/liquid/objects/section).
|
||||
#
|
||||
# Outside a snippet or app block, you can't access variables created inside the snippet or app block.
|
||||
#
|
||||
# > Note:
|
||||
# > When you render a snippet using the `render` tag, you can't use the [`include` tag](/api/liquid/tags/include)
|
||||
# > When you render a snippet using the `render` tag, you can't use the [`include` tag](/docs/api/liquid/tags/include)
|
||||
# > inside the snippet.
|
||||
# @liquid_syntax
|
||||
# {% render 'filename' %}
|
||||
@@ -29,22 +29,6 @@ module Liquid
|
||||
FOR = 'for'
|
||||
SYNTAX = /(#{QuotedString}+)(\s+(with|#{FOR})\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
|
||||
|
||||
def self.migrate(_tag_name, markup, _tokenizer, parse_context)
|
||||
match = markup.match(SYNTAX)
|
||||
|
||||
template_name = Expression.lax_migrate(match[1])
|
||||
variable_name = Expression.lax_migrate(match[4]) if match[4]
|
||||
|
||||
new_markup = Utils.match_captures_replace(match, { 1 => template_name, 4 => variable_name }.compact)
|
||||
new_markup << Utils.migrate_tag_attributes(markup)
|
||||
|
||||
# replace scanned over characters with a space to ensure there is a space
|
||||
# to separate the tag name and the variable name
|
||||
new_markup.prepend(" ") if match.begin(0) > 0
|
||||
|
||||
new_markup
|
||||
end
|
||||
|
||||
disable_tags "include"
|
||||
|
||||
attr_reader :template_name_expr, :variable_name_expr, :attributes, :alias_name
|
||||
@@ -85,14 +69,14 @@ module Liquid
|
||||
partial = PartialCache.load(
|
||||
template_name,
|
||||
context: context,
|
||||
parse_context: parse_context
|
||||
parse_context: parse_context,
|
||||
)
|
||||
|
||||
context_variable_name = @alias_name || template_name.split('/').last
|
||||
|
||||
render_partial_func = ->(var, forloop) {
|
||||
inner_context = context.new_isolated_subcontext
|
||||
inner_context.template_name = template_name
|
||||
inner_context.template_name = partial.name
|
||||
inner_context.partial = true
|
||||
inner_context['forloop'] = forloop if forloop
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ module Liquid
|
||||
# The `tablerow` tag must be wrapped in HTML `<table>` and `</table>` tags.
|
||||
#
|
||||
# > Tip:
|
||||
# > Every `tablerow` loop has an associated [`tablerowloop` object](/api/liquid/objects/tablerowloop) with information about the loop.
|
||||
# > Every `tablerow` loop has an associated [`tablerowloop` object](/docs/api/liquid/objects/tablerowloop) with information about the loop.
|
||||
# @liquid_syntax
|
||||
# {% tablerow variable in array %}
|
||||
# expression
|
||||
@@ -26,20 +26,6 @@ module Liquid
|
||||
class TableRow < Block
|
||||
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
|
||||
|
||||
def self.migrate(tag_name, markup, tokenizer, parse_context)
|
||||
match = markup.match(Syntax) || raise(SyntaxError)
|
||||
|
||||
new_collection_name = Expression.lax_migrate(match[2])
|
||||
|
||||
new_markup = Utils.match_captures_replace(match, { 2 => new_collection_name }.compact)
|
||||
new_markup << Utils.migrate_tag_attributes(markup)
|
||||
|
||||
new_body, unknown_tag = migrate_body(tag_name, tokenizer, parse_context)
|
||||
raise SyntaxError if unknown_tag
|
||||
|
||||
[new_markup, new_body]
|
||||
end
|
||||
|
||||
attr_reader :variable_name, :collection_name, :attributes
|
||||
|
||||
def initialize(tag_name, markup, options)
|
||||
@@ -59,13 +45,13 @@ module Liquid
|
||||
def render_to_output_buffer(context, output)
|
||||
(collection = context.evaluate(@collection_name)) || (return '')
|
||||
|
||||
from = @attributes.key?('offset') ? context.evaluate(@attributes['offset']).to_i : 0
|
||||
to = @attributes.key?('limit') ? from + context.evaluate(@attributes['limit']).to_i : nil
|
||||
from = @attributes.key?('offset') ? to_integer(context.evaluate(@attributes['offset'])) : 0
|
||||
to = @attributes.key?('limit') ? from + to_integer(context.evaluate(@attributes['limit'])) : nil
|
||||
|
||||
collection = Utils.slice_collection(collection, from, to)
|
||||
length = collection.length
|
||||
|
||||
cols = @attributes.key?('cols') ? context.evaluate(@attributes['cols']).to_i : length
|
||||
cols = @attributes.key?('cols') ? to_integer(context.evaluate(@attributes['cols'])) : length
|
||||
|
||||
output << "<tr class=\"row1\">\n"
|
||||
context.stack do
|
||||
@@ -96,6 +82,14 @@ module Liquid
|
||||
super + @node.attributes.values + [@node.collection_name]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def to_integer(value)
|
||||
value.to_i
|
||||
rescue NoMethodError
|
||||
raise Liquid::ArgumentError, "invalid integer"
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('tablerow', TableRow)
|
||||
|
||||
@@ -11,7 +11,7 @@ module Liquid
|
||||
# Renders an expression unless a specific condition is `true`.
|
||||
# @liquid_description
|
||||
# > Tip:
|
||||
# > Similar to the [`if` tag](/api/liquid/tags/if), you can use `elsif` to add more conditions to an `unless` tag.
|
||||
# > Similar to the [`if` tag](/docs/api/liquid/tags/if), you can use `elsif` to add more conditions to an `unless` tag.
|
||||
# @liquid_syntax
|
||||
# {% unless condition %}
|
||||
# expression
|
||||
@@ -23,7 +23,7 @@ module Liquid
|
||||
# First condition is interpreted backwards ( if not )
|
||||
first_block = @blocks.first
|
||||
result = Liquid::Utils.to_liquid_value(
|
||||
first_block.evaluate(context)
|
||||
first_block.evaluate(context),
|
||||
)
|
||||
|
||||
unless result
|
||||
@@ -33,7 +33,7 @@ module Liquid
|
||||
# After the first condition unless works just like if
|
||||
@blocks[1..-1].each do |block|
|
||||
result = Liquid::Utils.to_liquid_value(
|
||||
block.evaluate(context)
|
||||
block.evaluate(context),
|
||||
)
|
||||
|
||||
if result
|
||||
|
||||
+1
-12
@@ -15,7 +15,7 @@ module Liquid
|
||||
# template.render('user_name' => 'bob')
|
||||
#
|
||||
class Template
|
||||
attr_accessor :root
|
||||
attr_accessor :root, :name
|
||||
attr_reader :resource_limits, :warnings
|
||||
|
||||
class TagRegistry
|
||||
@@ -96,11 +96,6 @@ module Liquid
|
||||
def parse(source, options = {})
|
||||
new.parse(source, options)
|
||||
end
|
||||
|
||||
def migrate(source, parse_options = {})
|
||||
parse(source, parse_options) # raise if source has syntax errors
|
||||
new.migrate(source, parse_options)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize
|
||||
@@ -117,12 +112,6 @@ module Liquid
|
||||
self
|
||||
end
|
||||
|
||||
def migrate(source, parse_options = {})
|
||||
parse_context = configure_options(parse_options.merge(disable_liquid_c_nodes: true))
|
||||
tokenizer = parse_context.new_tokenizer(source, start_line_number: @line_numbers && 1)
|
||||
Document.migrate(tokenizer, parse_context)
|
||||
end
|
||||
|
||||
def registers
|
||||
@registers ||= {}
|
||||
end
|
||||
|
||||
@@ -25,10 +25,6 @@ module Liquid
|
||||
token
|
||||
end
|
||||
|
||||
def more?
|
||||
@offset < @tokens.length
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tokenize
|
||||
|
||||
@@ -89,48 +89,5 @@ module Liquid
|
||||
# Otherwise return the object itself
|
||||
obj
|
||||
end
|
||||
|
||||
def self.migrate_stripped(markup)
|
||||
match = markup.match(/\A\s*(.*?)\s*\z/m)
|
||||
new_markup = yield match[1]
|
||||
Utils.match_captures_replace(match, 1 => new_markup)
|
||||
end
|
||||
|
||||
def self.migrate_tag_attributes(markup)
|
||||
attributes = []
|
||||
markup.scan(/\s*,?\s*#{TagAttributes}/) do
|
||||
tag_match = Regexp.last_match
|
||||
new_value_markup = Expression.lax_migrate(tag_match[2])
|
||||
attribute_markup = Utils.match_captures_replace(tag_match, { 2 => new_value_markup })
|
||||
unless attribute_markup.match?(/\A[,\s]/)
|
||||
attribute_markup.prepend(", ")
|
||||
end
|
||||
attributes << attribute_markup
|
||||
end
|
||||
return "" if attributes.empty?
|
||||
attributes.join
|
||||
end
|
||||
|
||||
# @api private
|
||||
def self.match_capture_replace(match, capture_number, replacement_string)
|
||||
match_captures_replace(match, { capture_number => replacement_string })
|
||||
end
|
||||
|
||||
def self.match_captures_replace(match, replacements = {})
|
||||
new_string = match[0].dup
|
||||
capture_numbers = replacements.keys
|
||||
unless capture_numbers.all?(Integer)
|
||||
raise TypeError, "Currently, only numbered captures are supported"
|
||||
end
|
||||
# replace from later captures first, to avoid affecting the position for following replacements
|
||||
match_begin = match.begin(0)
|
||||
capture_numbers.sort.reverse_each do |capture_number|
|
||||
replacement_string = replacements.fetch(capture_number)
|
||||
capture_start = match.begin(capture_number)
|
||||
capture_length = match.end(capture_number) - capture_start
|
||||
new_string[capture_start - match_begin, capture_length] = replacement_string
|
||||
end
|
||||
new_string
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -41,79 +41,6 @@ module Liquid
|
||||
"in \"{{#{markup}}}\""
|
||||
end
|
||||
|
||||
STRICT_PARSE_CONTEXT = ParseContext.new(error_mode: :strict)
|
||||
private_constant :STRICT_PARSE_CONTEXT
|
||||
|
||||
def self.migrate(markup, parse_context)
|
||||
new(markup, STRICT_PARSE_CONTEXT)
|
||||
# TODO: migrate non-integer range expression literals
|
||||
markup
|
||||
rescue Liquid::SyntaxError
|
||||
raise if parse_context.error_mode == :strict
|
||||
lax_migrate(markup, parse_context)
|
||||
end
|
||||
|
||||
def self.lax_migrate(markup, parse_context)
|
||||
# unanchored match that may skip over characters preceding the name expression
|
||||
markup_match = markup.match(MarkupWithQuotedFragment)
|
||||
unless markup_match
|
||||
# Treated as a blank variable (e.g. `{{ -}}`), which outputs nothing
|
||||
# but may still have an effect on whitespace trimming
|
||||
return ""
|
||||
end
|
||||
|
||||
name_markup = markup_match[1]
|
||||
filters_markup = markup_match[2]
|
||||
|
||||
new_name_markup = Expression.lax_migrate(name_markup)
|
||||
|
||||
new_filter_markup = ""
|
||||
# unanchored match that may skip over characters preceding the pipe for the first filter
|
||||
if (filters_match = filters_markup.match(/\s*#{FilterMarkupRegex}/o))
|
||||
filters = filters_match[1].scan(FilterParser) # may skip over unterminated quote characters
|
||||
filters.map! do |f|
|
||||
filter_match = f.match(/\A(\s*)\W*(\w+)(\s*)/)
|
||||
next unless filter_match
|
||||
|
||||
# omit non-word characters preceding the filter name that the lax parser skips over
|
||||
transformed_filter = +"#{filter_match[1]}#{filter_match[2]}#{filter_match[3]}"
|
||||
|
||||
filter_args = []
|
||||
f.scan(/#{FilterArgsRegex}\s*/o) do # may skip over characters before the argument separator
|
||||
filter_arg_match = Regexp.last_match
|
||||
new_filter_arg = lax_migrate_filter_argument(filter_arg_match[1])
|
||||
filter_arg_string = Utils.match_captures_replace(filter_arg_match, 1 => new_filter_arg)
|
||||
filter_arg_string = filter_arg_string[1...] # remove separator character
|
||||
filter_args << filter_arg_string
|
||||
end
|
||||
|
||||
unless filter_args.empty?
|
||||
transformed_filter << ":" << filter_args.join(",")
|
||||
end
|
||||
|
||||
transformed_filter
|
||||
end
|
||||
filters.compact!
|
||||
new_filters_markup = filters.join('|')
|
||||
|
||||
# include pipe separator along with whitespace surrounding it
|
||||
new_filter_markup = Utils.match_captures_replace(filters_match, 1 => new_filters_markup)
|
||||
end
|
||||
|
||||
new_markup = Utils.match_captures_replace(markup_match, 1 => new_name_markup, 2 => new_filter_markup)
|
||||
new_markup.prepend(" ") if markup_match.begin(0) > 0
|
||||
new_markup
|
||||
end
|
||||
|
||||
def self.lax_migrate_filter_argument(unparsed_arg)
|
||||
if (match = unparsed_arg.match(JustTagAttributes))
|
||||
new_value_markup = Expression.lax_migrate(match[2])
|
||||
Utils.match_captures_replace(match, 2 => new_value_markup)
|
||||
else
|
||||
Expression.lax_migrate(unparsed_arg)
|
||||
end
|
||||
end
|
||||
|
||||
def lax_parse(markup)
|
||||
@filters = []
|
||||
return unless markup =~ MarkupWithQuotedFragment
|
||||
|
||||
@@ -10,43 +10,6 @@ module Liquid
|
||||
new(markup)
|
||||
end
|
||||
|
||||
LITERALS = Expression::LITERALS.keys.freeze
|
||||
private_constant :LITERALS
|
||||
|
||||
def self.lax_migrate(markup)
|
||||
new_markup = nil
|
||||
last_match = nil
|
||||
first_match = nil
|
||||
markup.scan(VariableParser) do |lookup|
|
||||
last_match = Regexp.last_match
|
||||
first_match ||= last_match
|
||||
new_markup ||= +""
|
||||
if lookup&.start_with?('[') && lookup&.end_with?(']')
|
||||
new_markup << "[" << Expression.lax_migrate(lookup[1..-2]) << "]"
|
||||
elsif !lookup.match?(/\A#{Liquid::Lexer::IDENTIFIER}\z/)
|
||||
# quote non-strictly valid identifiers
|
||||
new_markup << "['" << lookup << "']"
|
||||
else
|
||||
new_markup << "." unless new_markup.empty?
|
||||
new_markup << lookup
|
||||
end
|
||||
end
|
||||
|
||||
case new_markup
|
||||
when nil
|
||||
# `markup.scan(VariableParser)` may skip over all characters
|
||||
new_markup ||= " nil "
|
||||
when Expression::INTEGERS_REGEX, Expression::RANGES_REGEX, Expression::FLOATS_REGEX, *LITERALS
|
||||
# Quote variable lookups that match literals after characters are skipped by regex scanning
|
||||
new_markup = "['#{new_markup}']"
|
||||
else
|
||||
new_markup.prepend(" ") if first_match.begin(0) > 0
|
||||
new_markup << ' ' if last_match.end(0) < markup.length
|
||||
end
|
||||
|
||||
new_markup
|
||||
end
|
||||
|
||||
def initialize(markup)
|
||||
lookups = markup.scan(VariableParser)
|
||||
|
||||
|
||||
@@ -49,8 +49,3 @@ module Database
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if __FILE__ == $PROGRAM_NAME
|
||||
p(Database.tables['collections']['frontpage'].keys)
|
||||
# p Database.tables['blog']['articles']
|
||||
end
|
||||
|
||||
@@ -45,8 +45,8 @@ class Paginate < Liquid::Block
|
||||
|
||||
pagination['items'] = collection_size
|
||||
pagination['pages'] = page_count - 1
|
||||
pagination['previous'] = link('« Previous', current_page - 1) unless 1 >= current_page
|
||||
pagination['next'] = link('Next »', current_page + 1) unless page_count <= current_page + 1
|
||||
pagination['previous'] = link('« Previous', current_page - 1) if 1 < current_page
|
||||
pagination['next'] = link('Next »', current_page + 1) if page_count > current_page + 1
|
||||
pagination['parts'] = []
|
||||
|
||||
hellip_break = false
|
||||
|
||||
@@ -14,19 +14,25 @@ class AssignTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_assigned_variable
|
||||
assert_template_result('.foo.',
|
||||
assert_template_result(
|
||||
'.foo.',
|
||||
'{% assign foo = values %}.{{ foo[0] }}.',
|
||||
{ 'values' => %w(foo bar baz) })
|
||||
{ 'values' => %w(foo bar baz) },
|
||||
)
|
||||
|
||||
assert_template_result('.bar.',
|
||||
assert_template_result(
|
||||
'.bar.',
|
||||
'{% assign foo = values %}.{{ foo[1] }}.',
|
||||
{ 'values' => %w(foo bar baz) })
|
||||
{ 'values' => %w(foo bar baz) },
|
||||
)
|
||||
end
|
||||
|
||||
def test_assign_with_filter
|
||||
assert_template_result('.bar.',
|
||||
assert_template_result(
|
||||
'.bar.',
|
||||
'{% assign foo = values | split: "," %}.{{ foo[1] }}.',
|
||||
{ 'values' => "foo,bar,baz" })
|
||||
{ 'values' => "foo,bar,baz" },
|
||||
)
|
||||
end
|
||||
|
||||
def test_assign_syntax_error
|
||||
@@ -34,8 +40,11 @@ class AssignTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_assign_uses_error_mode
|
||||
assert_match_syntax_error("Expected dotdot but found pipe in ",
|
||||
"{% assign foo = ('X' | downcase) %}", error_mode: :strict)
|
||||
assert_match_syntax_error(
|
||||
"Expected dotdot but found pipe in ",
|
||||
"{% assign foo = ('X' | downcase) %}",
|
||||
error_mode: :strict,
|
||||
)
|
||||
assert_template_result("", "{% assign foo = ('X' | downcase) %}", error_mode: :lax)
|
||||
end
|
||||
|
||||
|
||||
@@ -57,9 +57,11 @@ class BlankTest < Minitest::Test
|
||||
|
||||
def test_nested_blocks_are_blank_but_only_if_all_children_are
|
||||
assert_template_result("", wrap(wrap(" ")))
|
||||
assert_template_result("\n but this is not " * (N + 1),
|
||||
assert_template_result(
|
||||
"\n but this is not " * (N + 1),
|
||||
wrap('{% if true %} {% comment %} this is blank {% endcomment %} {% endif %}
|
||||
{% if true %} but this is not {% endif %}'))
|
||||
{% if true %} but this is not {% endif %}'),
|
||||
)
|
||||
end
|
||||
|
||||
def test_assigns_are_blank
|
||||
@@ -89,12 +91,21 @@ class BlankTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_include_is_blank
|
||||
assert_template_result("foobar" * (N + 1), wrap("{% include 'foobar' %}"),
|
||||
partials: { 'foobar' => 'foobar' })
|
||||
assert_template_result(" foobar " * (N + 1), wrap("{% include ' foobar ' %}"),
|
||||
partials: { ' foobar ' => ' foobar ' })
|
||||
assert_template_result(" " * (N + 1), wrap(" {% include ' ' %} "),
|
||||
partials: { ' ' => ' ' })
|
||||
assert_template_result(
|
||||
"foobar" * (N + 1),
|
||||
wrap("{% include 'foobar' %}"),
|
||||
partials: { 'foobar' => 'foobar' },
|
||||
)
|
||||
assert_template_result(
|
||||
" foobar " * (N + 1),
|
||||
wrap("{% include ' foobar ' %}"),
|
||||
partials: { ' foobar ' => ' foobar ' },
|
||||
)
|
||||
assert_template_result(
|
||||
" " * (N + 1),
|
||||
wrap(" {% include ' ' %} "),
|
||||
partials: { ' ' => ' ' },
|
||||
)
|
||||
end
|
||||
|
||||
def test_case_is_blank
|
||||
|
||||
@@ -121,14 +121,23 @@ class ContextTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_length_query
|
||||
assert_template_result("true", "{% if numbers.size == 4 %}true{% endif %}",
|
||||
{ "numbers" => [1, 2, 3, 4] })
|
||||
assert_template_result(
|
||||
"true",
|
||||
"{% if numbers.size == 4 %}true{% endif %}",
|
||||
{ "numbers" => [1, 2, 3, 4] },
|
||||
)
|
||||
|
||||
assert_template_result("true", "{% if numbers.size == 4 %}true{% endif %}",
|
||||
{ "numbers" => { 1 => 1, 2 => 2, 3 => 3, 4 => 4 } })
|
||||
assert_template_result(
|
||||
"true",
|
||||
"{% if numbers.size == 4 %}true{% endif %}",
|
||||
{ "numbers" => { 1 => 1, 2 => 2, 3 => 3, 4 => 4 } },
|
||||
)
|
||||
|
||||
assert_template_result("true", "{% if numbers.size == 1000 %}true{% endif %}",
|
||||
{ "numbers" => { 1 => 1, 2 => 2, 3 => 3, 4 => 4, 'size' => 1000 } })
|
||||
assert_template_result(
|
||||
"true",
|
||||
"{% if numbers.size == 1000 %}true{% endif %}",
|
||||
{ "numbers" => { 1 => 1, 2 => 2, 3 => 3, 4 => 4, 'size' => 1000 } },
|
||||
)
|
||||
end
|
||||
|
||||
def test_hyphenated_variable
|
||||
@@ -228,12 +237,14 @@ class ContextTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_hash_to_array_transition
|
||||
assigns = { 'colors' => {
|
||||
'Blue' => ['003366', '336699', '6699CC', '99CCFF'],
|
||||
'Green' => ['003300', '336633', '669966', '99CC99'],
|
||||
'Yellow' => ['CC9900', 'FFCC00', 'FFFF99', 'FFFFCC'],
|
||||
'Red' => ['660000', '993333', 'CC6666', 'FF9999'],
|
||||
} }
|
||||
assigns = {
|
||||
'colors' => {
|
||||
'Blue' => ['003366', '336699', '6699CC', '99CCFF'],
|
||||
'Green' => ['003300', '336633', '669966', '99CC99'],
|
||||
'Yellow' => ['CC9900', 'FFCC00', 'FFFF99', 'FFFFCC'],
|
||||
'Red' => ['660000', '993333', 'CC6666', 'FF9999'],
|
||||
},
|
||||
}
|
||||
|
||||
assert_template_result("003366", "{{ colors.Blue[0] }}", assigns)
|
||||
assert_template_result("FF9999", "{{ colors.Red[3] }}", assigns)
|
||||
@@ -262,7 +273,7 @@ class ContextTest < Minitest::Test
|
||||
assigns = { 'product' => { 'variants' => [{ 'title' => 'draft151cm' }, { 'title' => 'element151cm' }] } }
|
||||
assert_template_result("draft151cm", '{{ product["variants"][0]["title"] }}', assigns)
|
||||
assert_template_result("element151cm", '{{ product["variants"][1]["title"] }}', assigns)
|
||||
assert_template_result("draft151cm", '{{ product["variants"][0]["title"] }}', assigns)
|
||||
assert_template_result("draft151cm", '{{ product["variants"].first["title"] }}', assigns)
|
||||
assert_template_result("element151cm", '{{ product["variants"].last["title"] }}', assigns)
|
||||
end
|
||||
|
||||
@@ -410,10 +421,12 @@ class ContextTest < Minitest::Test
|
||||
def test_nested_lambda_is_called_once
|
||||
@global = 0
|
||||
|
||||
@context['callcount'] = { "lambda" => proc {
|
||||
@global += 1
|
||||
@global.to_s
|
||||
} }
|
||||
@context['callcount'] = {
|
||||
"lambda" => proc {
|
||||
@global += 1
|
||||
@global.to_s
|
||||
},
|
||||
}
|
||||
|
||||
assert_equal('1', @context['callcount.lambda'])
|
||||
assert_equal('1', @context['callcount.lambda'])
|
||||
@@ -423,10 +436,11 @@ class ContextTest < Minitest::Test
|
||||
def test_lambda_in_array_is_called_once
|
||||
@global = 0
|
||||
|
||||
@context['callcount'] = [1, 2, proc {
|
||||
@global += 1
|
||||
@global.to_s
|
||||
}, 4, 5]
|
||||
p = proc {
|
||||
@global += 1
|
||||
@global.to_s
|
||||
}
|
||||
@context['callcount'] = [1, 2, p, 4, 5]
|
||||
|
||||
assert_equal('1', @context['callcount[2]'])
|
||||
assert_equal('1', @context['callcount[2]'])
|
||||
@@ -473,7 +487,7 @@ class ContextTest < Minitest::Test
|
||||
def test_static_environments_are_read_with_lower_priority_than_environments
|
||||
context = Context.build(
|
||||
static_environments: { 'shadowed' => 'static', 'unshadowed' => 'static' },
|
||||
environments: { 'shadowed' => 'dynamic' }
|
||||
environments: { 'shadowed' => 'dynamic' },
|
||||
)
|
||||
|
||||
assert_equal('dynamic', context['shadowed'])
|
||||
|
||||
@@ -105,7 +105,8 @@ class ErrorHandlingTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_parsing_warn_with_line_numbers_adds_numbers_to_lexer_errors
|
||||
template = Liquid::Template.parse('
|
||||
template = Liquid::Template.parse(
|
||||
'
|
||||
foobar
|
||||
|
||||
{% if 1 =! 2 %}ok{% endif %}
|
||||
@@ -113,15 +114,19 @@ class ErrorHandlingTest < Minitest::Test
|
||||
bla
|
||||
',
|
||||
error_mode: :warn,
|
||||
line_numbers: true)
|
||||
line_numbers: true,
|
||||
)
|
||||
|
||||
assert_equal(['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
|
||||
template.warnings.map(&:message))
|
||||
assert_equal(
|
||||
['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
|
||||
template.warnings.map(&:message),
|
||||
)
|
||||
end
|
||||
|
||||
def test_parsing_strict_with_line_numbers_adds_numbers_to_lexer_errors
|
||||
err = assert_raises(SyntaxError) do
|
||||
Liquid::Template.parse('
|
||||
Liquid::Template.parse(
|
||||
'
|
||||
foobar
|
||||
|
||||
{% if 1 =! 2 %}ok{% endif %}
|
||||
@@ -129,7 +134,8 @@ class ErrorHandlingTest < Minitest::Test
|
||||
bla
|
||||
',
|
||||
error_mode: :strict,
|
||||
line_numbers: true)
|
||||
line_numbers: true,
|
||||
)
|
||||
end
|
||||
|
||||
assert_equal('Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message)
|
||||
|
||||
@@ -32,11 +32,11 @@ class ExpressionTest < Minitest::Test
|
||||
|
||||
assert_match_syntax_error(
|
||||
"Liquid syntax error (line 1): Invalid expression type 'false' in range expression",
|
||||
"{{ (false..true) }}"
|
||||
"{{ (false..true) }}",
|
||||
)
|
||||
assert_match_syntax_error(
|
||||
"Liquid syntax error (line 1): Invalid expression type '(1..2)' in range expression",
|
||||
"{{ ((1..2)..3) }}"
|
||||
"{{ ((1..2)..3) }}",
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class FilterKwargTest < Minitest::Test
|
||||
with_global_filter(KwargFilter) do
|
||||
assert_equal(
|
||||
"data-src='src' data-widths='100, 200'",
|
||||
Template.parse("{{ 'img' | html_tag: data-src: 'src', data-widths: '100, 200' }}").render(nil, nil)
|
||||
Template.parse("{{ 'img' | html_tag: data-src: 'src', data-widths: '100, 200' }}").render(nil, nil),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -68,22 +68,34 @@ class FiltersTest < Minitest::Test
|
||||
|
||||
def test_sort
|
||||
assert_template_result("1 2 3 4", "{{numbers | sort | join}}", { "numbers" => [2, 1, 4, 3] })
|
||||
assert_template_result("alphabetic as expected", "{{words | sort | join}}",
|
||||
{ "words" => ['expected', 'as', 'alphabetic'] })
|
||||
assert_template_result(
|
||||
"alphabetic as expected",
|
||||
"{{words | sort | join}}",
|
||||
{ "words" => ['expected', 'as', 'alphabetic'] },
|
||||
)
|
||||
assert_template_result("3", "{{value | sort}}", { "value" => 3 })
|
||||
assert_template_result('are flower', "{{arrays | sort | join}}", { 'arrays' => ['flower', 'are'] })
|
||||
assert_template_result("Expected case sensitive", "{{case_sensitive | sort | join}}",
|
||||
{ "case_sensitive" => ["sensitive", "Expected", "case"] })
|
||||
assert_template_result(
|
||||
"Expected case sensitive",
|
||||
"{{case_sensitive | sort | join}}",
|
||||
{ "case_sensitive" => ["sensitive", "Expected", "case"] },
|
||||
)
|
||||
end
|
||||
|
||||
def test_sort_natural
|
||||
# Test strings
|
||||
assert_template_result("Assert case Insensitive", "{{words | sort_natural | join}}",
|
||||
{ "words" => ["case", "Assert", "Insensitive"] })
|
||||
assert_template_result(
|
||||
"Assert case Insensitive",
|
||||
"{{words | sort_natural | join}}",
|
||||
{ "words" => ["case", "Assert", "Insensitive"] },
|
||||
)
|
||||
|
||||
# Test hashes
|
||||
assert_template_result("A b C", "{{hashes | sort_natural: 'a' | map: 'a' | join}}",
|
||||
{ "hashes" => [{ "a" => "A" }, { "a" => "b" }, { "a" => "C" }] })
|
||||
assert_template_result(
|
||||
"A b C",
|
||||
"{{hashes | sort_natural: 'a' | map: 'a' | join}}",
|
||||
{ "hashes" => [{ "a" => "A" }, { "a" => "b" }, { "a" => "C" }] },
|
||||
)
|
||||
|
||||
# Test objects
|
||||
@context['objects'] = [TestObject.new('A'), TestObject.new('b'), TestObject.new('C')]
|
||||
@@ -92,12 +104,18 @@ class FiltersTest < Minitest::Test
|
||||
|
||||
def test_compact
|
||||
# Test strings
|
||||
assert_template_result("a b c", "{{words | compact | join}}",
|
||||
{ "words" => ['a', nil, 'b', nil, 'c'] })
|
||||
assert_template_result(
|
||||
"a b c",
|
||||
"{{words | compact | join}}",
|
||||
{ "words" => ['a', nil, 'b', nil, 'c'] },
|
||||
)
|
||||
|
||||
# Test hashes
|
||||
assert_template_result("A C", "{{hashes | compact: 'a' | map: 'a' | join}}",
|
||||
{ "hashes" => [{ "a" => "A" }, { "a" => nil }, { "a" => "C" }] })
|
||||
assert_template_result(
|
||||
"A C",
|
||||
"{{hashes | compact: 'a' | map: 'a' | join}}",
|
||||
{ "hashes" => [{ "a" => "A" }, { "a" => nil }, { "a" => "C" }] },
|
||||
)
|
||||
|
||||
# Test objects
|
||||
@context['objects'] = [TestObject.new('A'), TestObject.new(nil), TestObject.new('C')]
|
||||
@@ -109,8 +127,11 @@ class FiltersTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_strip_html_ignore_comments_with_html
|
||||
assert_template_result("bla blub", "{{ var | strip_html }}",
|
||||
{ "var" => "<!-- split and some <ul> tag --><b>bla blub</a>" })
|
||||
assert_template_result(
|
||||
"bla blub",
|
||||
"{{ var | strip_html }}",
|
||||
{ "var" => "<!-- split and some <ul> tag --><b>bla blub</a>" },
|
||||
)
|
||||
end
|
||||
|
||||
def test_capitalize
|
||||
|
||||
@@ -188,7 +188,7 @@ class StandardFiltersTest < Minitest::Test
|
||||
def test_base64_url_safe_encode
|
||||
assert_equal(
|
||||
'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMTIzNDU2Nzg5MCAhQCMkJV4mKigpLT1fKy8_Ljo7W117fVx8',
|
||||
@filters.base64_url_safe_encode('abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 !@#$%^&*()-=_+/?.:;[]{}\|')
|
||||
@filters.base64_url_safe_encode('abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 !@#$%^&*()-=_+/?.:;[]{}\|'),
|
||||
)
|
||||
assert_equal('', @filters.base64_url_safe_encode(nil))
|
||||
end
|
||||
@@ -196,7 +196,7 @@ class StandardFiltersTest < Minitest::Test
|
||||
def test_base64_url_safe_decode
|
||||
assert_equal(
|
||||
'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 !@#$%^&*()-=_+/?.:;[]{}\|',
|
||||
@filters.base64_url_safe_decode('YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMTIzNDU2Nzg5MCAhQCMkJV4mKigpLT1fKy8_Ljo7W117fVx8')
|
||||
@filters.base64_url_safe_decode('YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMTIzNDU2Nzg5MCAhQCMkJV4mKigpLT1fKy8_Ljo7W117fVx8'),
|
||||
)
|
||||
exception = assert_raises(Liquid::ArgumentError) do
|
||||
@filters.base64_url_safe_decode("invalidbase64")
|
||||
@@ -230,7 +230,7 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal('one two three', @filters.truncatewords('one two three'))
|
||||
assert_equal(
|
||||
'Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13”...',
|
||||
@filters.truncatewords('Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13” x 16” x 10.5” high) with cover.', 15)
|
||||
@filters.truncatewords('Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13” x 16” x 10.5” high) with cover.', 15),
|
||||
)
|
||||
assert_equal("测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5))
|
||||
assert_equal('one two1', @filters.truncatewords("one two three", 2, 1))
|
||||
@@ -433,8 +433,11 @@ class StandardFiltersTest < Minitest::Test
|
||||
|
||||
def test_map
|
||||
assert_equal([1, 2, 3, 4], @filters.map([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], 'a'))
|
||||
assert_template_result('abc', "{{ ary | map:'foo' | map:'bar' }}",
|
||||
{ 'ary' => [{ 'foo' => { 'bar' => 'a' } }, { 'foo' => { 'bar' => 'b' } }, { 'foo' => { 'bar' => 'c' } }] })
|
||||
assert_template_result(
|
||||
'abc',
|
||||
"{{ ary | map:'foo' | map:'bar' }}",
|
||||
{ 'ary' => [{ 'foo' => { 'bar' => 'a' } }, { 'foo' => { 'bar' => 'b' } }, { 'foo' => { 'bar' => 'c' } }] },
|
||||
)
|
||||
end
|
||||
|
||||
def test_map_doesnt_call_arbitrary_stuff
|
||||
@@ -458,8 +461,11 @@ class StandardFiltersTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_map_on_hashes
|
||||
assert_template_result("4217", '{{ thing | map: "foo" | map: "bar" }}',
|
||||
{ "thing" => { "foo" => [{ "bar" => 42 }, { "bar" => 17 }] } })
|
||||
assert_template_result(
|
||||
"4217",
|
||||
'{{ thing | map: "foo" | map: "bar" }}',
|
||||
{ "thing" => { "foo" => [{ "bar" => 42 }, { "bar" => 17 }] } },
|
||||
)
|
||||
end
|
||||
|
||||
def test_legacy_map_on_hashes_with_dynamic_key
|
||||
|
||||
@@ -73,9 +73,11 @@ HERE
|
||||
|
||||
def test_for_helpers
|
||||
assigns = { 'array' => [1, 2, 3] }
|
||||
assert_template_result(' 1/3 2/3 3/3 ',
|
||||
assert_template_result(
|
||||
' 1/3 2/3 3/3 ',
|
||||
'{%for item in array%} {{forloop.index}}/{{forloop.length}} {%endfor%}',
|
||||
assigns)
|
||||
assigns,
|
||||
)
|
||||
assert_template_result(' 1 2 3 ', '{%for item in array%} {{forloop.index}} {%endfor%}', assigns)
|
||||
assert_template_result(' 0 1 2 ', '{%for item in array%} {{forloop.index0}} {%endfor%}', assigns)
|
||||
assert_template_result(' 2 1 0 ', '{%for item in array%} {{forloop.rindex0}} {%endfor%}', assigns)
|
||||
@@ -86,9 +88,11 @@ HERE
|
||||
|
||||
def test_for_and_if
|
||||
assigns = { 'array' => [1, 2, 3] }
|
||||
assert_template_result('+--',
|
||||
assert_template_result(
|
||||
'+--',
|
||||
'{%for item in array%}{% if forloop.first %}+{% else %}-{% endif %}{%endfor%}',
|
||||
assigns)
|
||||
assigns,
|
||||
)
|
||||
end
|
||||
|
||||
def test_for_else
|
||||
@@ -103,6 +107,7 @@ HERE
|
||||
assert_template_result('1234', '{%for i in array limit:4 %}{{ i }}{%endfor%}', assigns)
|
||||
assert_template_result('3456', '{%for i in array limit:4 offset:2 %}{{ i }}{%endfor%}', assigns)
|
||||
assert_template_result('3456', '{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}', assigns)
|
||||
assert_template_result('3456', '{%for i in array, limit: 4, offset: 2 %}{{ i }}{%endfor%}', assigns)
|
||||
end
|
||||
|
||||
def test_limiting_with_invalid_limit
|
||||
@@ -324,15 +329,20 @@ HERE
|
||||
# ruby 1.9.3 no longer supports .each on String though we mimic
|
||||
# the functionality for backwards compatibility
|
||||
|
||||
assert_template_result('test string',
|
||||
assert_template_result(
|
||||
'test string',
|
||||
'{%for val in string%}{{val}}{%endfor%}',
|
||||
{ 'string' => "test string" })
|
||||
{ 'string' => "test string" },
|
||||
)
|
||||
|
||||
assert_template_result('test string',
|
||||
assert_template_result(
|
||||
'test string',
|
||||
'{%for val in string limit:1%}{{val}}{%endfor%}',
|
||||
{ 'string' => "test string" })
|
||||
{ 'string' => "test string" },
|
||||
)
|
||||
|
||||
assert_template_result('val-string-1-1-0-1-0-true-true-test string',
|
||||
assert_template_result(
|
||||
'val-string-1-1-0-1-0-true-true-test string',
|
||||
'{%for val in string%}' \
|
||||
'{{forloop.name}}-' \
|
||||
'{{forloop.index}}-' \
|
||||
@@ -343,23 +353,28 @@ HERE
|
||||
'{{forloop.first}}-' \
|
||||
'{{forloop.last}}-' \
|
||||
'{{val}}{%endfor%}',
|
||||
{ 'string' => "test string" })
|
||||
{ 'string' => "test string" },
|
||||
)
|
||||
end
|
||||
|
||||
def test_for_parentloop_references_parent_loop
|
||||
assert_template_result('1.1 1.2 1.3 2.1 2.2 2.3 ',
|
||||
assert_template_result(
|
||||
'1.1 1.2 1.3 2.1 2.2 2.3 ',
|
||||
'{% for inner in outer %}{% for k in inner %}' \
|
||||
'{{ forloop.parentloop.index }}.{{ forloop.index }} ' \
|
||||
'{% endfor %}{% endfor %}',
|
||||
{ 'outer' => [[1, 1, 1], [1, 1, 1]] })
|
||||
{ 'outer' => [[1, 1, 1], [1, 1, 1]] },
|
||||
)
|
||||
end
|
||||
|
||||
def test_for_parentloop_nil_when_not_present
|
||||
assert_template_result('.1 .2 ',
|
||||
assert_template_result(
|
||||
'.1 .2 ',
|
||||
'{% for inner in outer %}' \
|
||||
'{{ forloop.parentloop.index }}.{{ forloop.index }} ' \
|
||||
'{% endfor %}',
|
||||
{ 'outer' => [[1, 1, 1], [1, 1, 1]] })
|
||||
{ 'outer' => [[1, 1, 1], [1, 1, 1]] },
|
||||
)
|
||||
end
|
||||
|
||||
def test_inner_for_over_empty_input
|
||||
|
||||
@@ -7,8 +7,10 @@ class IfElseTagTest < Minitest::Test
|
||||
|
||||
def test_if
|
||||
assert_template_result(' ', ' {% if false %} this text should not go into the output {% endif %} ')
|
||||
assert_template_result(' this text should go into the output ',
|
||||
' {% if true %} this text should go into the output {% endif %} ')
|
||||
assert_template_result(
|
||||
' this text should go into the output ',
|
||||
' {% if true %} this text should go into the output {% endif %} ',
|
||||
)
|
||||
assert_template_result(' you rock ?', '{% if false %} you suck {% endif %} {% if true %} you rock {% endif %}?')
|
||||
end
|
||||
|
||||
@@ -51,18 +53,22 @@ class IfElseTagTest < Minitest::Test
|
||||
|
||||
def test_comparison_of_expressions_starting_with_and_or_or
|
||||
assigns = { 'order' => { 'items_count' => 0 }, 'android' => { 'name' => 'Roy' } }
|
||||
assert_template_result("YES",
|
||||
assert_template_result(
|
||||
"YES",
|
||||
"{% if android.name == 'Roy' %}YES{% endif %}",
|
||||
assigns)
|
||||
assert_template_result("YES",
|
||||
assigns,
|
||||
)
|
||||
assert_template_result(
|
||||
"YES",
|
||||
"{% if order.items_count == 0 %}YES{% endif %}",
|
||||
assigns)
|
||||
assigns,
|
||||
)
|
||||
end
|
||||
|
||||
def test_if_and
|
||||
assert_template_result(' YES ', '{% if true and true %} YES {% endif %}')
|
||||
assert_template_result('', '{% if false and true %} YES {% endif %}')
|
||||
assert_template_result('', '{% if false and true %} YES {% endif %}')
|
||||
assert_template_result('', '{% if true and false %} YES {% endif %}')
|
||||
end
|
||||
|
||||
def test_hash_miss_generates_false
|
||||
@@ -155,8 +161,10 @@ class IfElseTagTest < Minitest::Test
|
||||
original_op = Condition.operators['contains']
|
||||
Condition.operators['contains'] = :[]
|
||||
|
||||
assert_template_result('yes',
|
||||
%({% if 'gnomeslab-and-or-liquid' contains 'gnomeslab-and-or-liquid' %}yes{% endif %}))
|
||||
assert_template_result(
|
||||
'yes',
|
||||
%({% if 'gnomeslab-and-or-liquid' contains 'gnomeslab-and-or-liquid' %}yes{% endif %}),
|
||||
)
|
||||
ensure
|
||||
Condition.operators['contains'] = original_op
|
||||
end
|
||||
|
||||
@@ -58,65 +58,88 @@ class IncludeTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_include_tag_looks_for_file_system_in_registers_first
|
||||
assert_equal('from OtherFileSystem',
|
||||
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: OtherFileSystem.new }))
|
||||
assert_equal(
|
||||
'from OtherFileSystem',
|
||||
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: OtherFileSystem.new }),
|
||||
)
|
||||
end
|
||||
|
||||
def test_include_tag_with
|
||||
assert_template_result("Product: Draft 151cm ",
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm ",
|
||||
"{% include 'product' with products[0] %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: { "product" => "Product: {{ product.title }} " })
|
||||
partials: { "product" => "Product: {{ product.title }} " },
|
||||
)
|
||||
end
|
||||
|
||||
def test_include_tag_with_alias
|
||||
assert_template_result("Product: Draft 151cm ",
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm ",
|
||||
"{% include 'product_alias' with products[0] as product %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: { "product_alias" => "Product: {{ product.title }} " })
|
||||
partials: { "product_alias" => "Product: {{ product.title }} " },
|
||||
)
|
||||
end
|
||||
|
||||
def test_include_tag_for_alias
|
||||
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm Product: Element 155cm ",
|
||||
"{% include 'product_alias' for products as product %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: { "product_alias" => "Product: {{ product.title }} " })
|
||||
partials: { "product_alias" => "Product: {{ product.title }} " },
|
||||
)
|
||||
end
|
||||
|
||||
def test_include_tag_with_default_name
|
||||
assert_template_result("Product: Draft 151cm ",
|
||||
"{% include 'product' %}", { "product" => { 'title' => 'Draft 151cm' } },
|
||||
partials: { "product" => "Product: {{ product.title }} " })
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm ",
|
||||
"{% include 'product' %}",
|
||||
{ "product" => { 'title' => 'Draft 151cm' } },
|
||||
partials: { "product" => "Product: {{ product.title }} " },
|
||||
)
|
||||
end
|
||||
|
||||
def test_include_tag_for
|
||||
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm Product: Element 155cm ",
|
||||
"{% include 'product' for products %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: { "product" => "Product: {{ product.title }} " })
|
||||
partials: { "product" => "Product: {{ product.title }} " },
|
||||
)
|
||||
end
|
||||
|
||||
def test_include_tag_with_local_variables
|
||||
assert_template_result("Locale: test123 ", "{% include 'locale_variables' echo1: 'test123' %}",
|
||||
partials: { "locale_variables" => "Locale: {{echo1}} {{echo2}}" })
|
||||
assert_template_result(
|
||||
"Locale: test123 ",
|
||||
"{% include 'locale_variables' echo1: 'test123' %}",
|
||||
partials: { "locale_variables" => "Locale: {{echo1}} {{echo2}}" },
|
||||
)
|
||||
end
|
||||
|
||||
def test_include_tag_with_multiple_local_variables
|
||||
assert_template_result("Locale: test123 test321",
|
||||
assert_template_result(
|
||||
"Locale: test123 test321",
|
||||
"{% include 'locale_variables' echo1: 'test123', echo2: 'test321' %}",
|
||||
partials: { "locale_variables" => "Locale: {{echo1}} {{echo2}}" })
|
||||
partials: { "locale_variables" => "Locale: {{echo1}} {{echo2}}" },
|
||||
)
|
||||
end
|
||||
|
||||
def test_include_tag_with_multiple_local_variables_from_context
|
||||
assert_template_result("Locale: test123 test321",
|
||||
assert_template_result(
|
||||
"Locale: test123 test321",
|
||||
"{% include 'locale_variables' echo1: echo1, echo2: more_echos.echo2 %}",
|
||||
{ 'echo1' => 'test123', 'more_echos' => { "echo2" => 'test321' } },
|
||||
partials: { "locale_variables" => "Locale: {{echo1}} {{echo2}}" })
|
||||
partials: { "locale_variables" => "Locale: {{echo1}} {{echo2}}" },
|
||||
)
|
||||
end
|
||||
|
||||
def test_included_templates_assigns_variables
|
||||
assert_template_result("bar", "{% include 'assignments' %}{{ foo }}",
|
||||
partials: { 'assignments' => "{% assign foo = 'bar' %}" })
|
||||
assert_template_result(
|
||||
"bar",
|
||||
"{% include 'assignments' %}{{ foo }}",
|
||||
partials: { 'assignments' => "{% assign foo = 'bar' %}" },
|
||||
)
|
||||
end
|
||||
|
||||
def test_nested_include_tag
|
||||
@@ -137,13 +160,19 @@ class IncludeTagTest < Minitest::Test
|
||||
"details" => "details",
|
||||
}
|
||||
|
||||
assert_template_result("Product: Draft 151cm details ",
|
||||
"{% include 'nested_product_template' with product %}", { "product" => { "title" => 'Draft 151cm' } },
|
||||
partials: partials)
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm details ",
|
||||
"{% include 'nested_product_template' with product %}",
|
||||
{ "product" => { "title" => 'Draft 151cm' } },
|
||||
partials: partials,
|
||||
)
|
||||
|
||||
assert_template_result("Product: Draft 151cm details Product: Element 155cm details ",
|
||||
"{% include 'nested_product_template' for products %}", { "products" => [{ "title" => 'Draft 151cm' }, { "title" => 'Element 155cm' }] },
|
||||
partials: partials)
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm details Product: Element 155cm details ",
|
||||
"{% include 'nested_product_template' for products %}",
|
||||
{ "products" => [{ "title" => 'Draft 151cm' }, { "title" => 'Element 155cm' }] },
|
||||
partials: partials,
|
||||
)
|
||||
end
|
||||
|
||||
def test_recursively_included_template_does_not_produce_endless_loop
|
||||
@@ -161,46 +190,68 @@ class IncludeTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_dynamically_choosen_template
|
||||
assert_template_result("Test123", "{% include template %}", { "template" => 'Test123' },
|
||||
partials: { "Test123" => "Test123" })
|
||||
assert_template_result(
|
||||
"Test123",
|
||||
"{% include template %}",
|
||||
{ "template" => 'Test123' },
|
||||
partials: { "Test123" => "Test123" },
|
||||
)
|
||||
|
||||
assert_template_result("Test321", "{% include template %}", { "template" => 'Test321' },
|
||||
partials: { "Test321" => "Test321" })
|
||||
assert_template_result(
|
||||
"Test321",
|
||||
"{% include template %}",
|
||||
{ "template" => 'Test321' },
|
||||
partials: { "Test321" => "Test321" },
|
||||
)
|
||||
|
||||
assert_template_result("Product: Draft 151cm ", "{% include template for product %}",
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm ",
|
||||
"{% include template for product %}",
|
||||
{ "template" => 'product', 'product' => { 'title' => 'Draft 151cm' } },
|
||||
partials: { "product" => "Product: {{ product.title }} " })
|
||||
partials: { "product" => "Product: {{ product.title }} " },
|
||||
)
|
||||
end
|
||||
|
||||
def test_include_tag_caches_second_read_of_same_partial
|
||||
file_system = CountingFileSystem.new
|
||||
assert_equal('from CountingFileSystemfrom CountingFileSystem',
|
||||
Template.parse("{% include 'pick_a_source' %}{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system }))
|
||||
assert_equal(
|
||||
'from CountingFileSystemfrom CountingFileSystem',
|
||||
Template.parse("{% include 'pick_a_source' %}{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system }),
|
||||
)
|
||||
assert_equal(1, file_system.count)
|
||||
end
|
||||
|
||||
def test_include_tag_doesnt_cache_partials_across_renders
|
||||
file_system = CountingFileSystem.new
|
||||
assert_equal('from CountingFileSystem',
|
||||
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system }))
|
||||
assert_equal(
|
||||
'from CountingFileSystem',
|
||||
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system }),
|
||||
)
|
||||
assert_equal(1, file_system.count)
|
||||
|
||||
assert_equal('from CountingFileSystem',
|
||||
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system }))
|
||||
assert_equal(
|
||||
'from CountingFileSystem',
|
||||
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system }),
|
||||
)
|
||||
assert_equal(2, file_system.count)
|
||||
end
|
||||
|
||||
def test_include_tag_within_if_statement
|
||||
assert_template_result("foo_if_true", "{% if true %}{% include 'foo_if_true' %}{% endif %}",
|
||||
partials: { "foo_if_true" => "foo_if_true" })
|
||||
assert_template_result(
|
||||
"foo_if_true",
|
||||
"{% if true %}{% include 'foo_if_true' %}{% endif %}",
|
||||
partials: { "foo_if_true" => "foo_if_true" },
|
||||
)
|
||||
end
|
||||
|
||||
def test_custom_include_tag
|
||||
original_tag = Liquid::Template.tags['include']
|
||||
Liquid::Template.tags['include'] = CustomInclude
|
||||
begin
|
||||
assert_equal("custom_foo",
|
||||
Template.parse("{% include 'custom_foo' %}").render!)
|
||||
assert_equal(
|
||||
"custom_foo",
|
||||
Template.parse("{% include 'custom_foo' %}").render!,
|
||||
)
|
||||
ensure
|
||||
Liquid::Template.tags['include'] = original_tag
|
||||
end
|
||||
@@ -210,8 +261,10 @@ class IncludeTagTest < Minitest::Test
|
||||
original_tag = Liquid::Template.tags['include']
|
||||
Liquid::Template.tags['include'] = CustomInclude
|
||||
begin
|
||||
assert_equal("custom_foo_if_true",
|
||||
Template.parse("{% if true %}{% include 'custom_foo_if_true' %}{% endif %}").render!)
|
||||
assert_equal(
|
||||
"custom_foo_if_true",
|
||||
Template.parse("{% if true %}{% include 'custom_foo_if_true' %}{% endif %}").render!,
|
||||
)
|
||||
ensure
|
||||
Liquid::Template.tags['include'] = original_tag
|
||||
end
|
||||
@@ -242,31 +295,49 @@ class IncludeTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_render_raise_argument_error_when_template_is_undefined
|
||||
assert_template_result("Liquid error (line 1): Argument error in tag 'include' - Illegal template name",
|
||||
"{% include undefined_variable %}", render_errors: true)
|
||||
assert_template_result(
|
||||
"Liquid error (line 1): Argument error in tag 'include' - Illegal template name",
|
||||
"{% include undefined_variable %}",
|
||||
render_errors: true,
|
||||
)
|
||||
|
||||
assert_template_result("Liquid error (line 1): Argument error in tag 'include' - Illegal template name",
|
||||
"{% include nil %}", render_errors: true)
|
||||
assert_template_result(
|
||||
"Liquid error (line 1): Argument error in tag 'include' - Illegal template name",
|
||||
"{% include nil %}",
|
||||
render_errors: true,
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_raise_argument_error_when_template_is_not_a_string
|
||||
assert_template_result("Liquid error (line 1): Argument error in tag 'include' - Illegal template name",
|
||||
"{% include 123 %}", render_errors: true)
|
||||
assert_template_result(
|
||||
"Liquid error (line 1): Argument error in tag 'include' - Illegal template name",
|
||||
"{% include 123 %}",
|
||||
render_errors: true,
|
||||
)
|
||||
end
|
||||
|
||||
def test_including_via_variable_value
|
||||
assert_template_result("from TestFileSystem", "{% assign page = 'pick_a_source' %}{% include page %}",
|
||||
partials: { "pick_a_source" => "from TestFileSystem" })
|
||||
assert_template_result(
|
||||
"from TestFileSystem",
|
||||
"{% assign page = 'pick_a_source' %}{% include page %}",
|
||||
partials: { "pick_a_source" => "from TestFileSystem" },
|
||||
)
|
||||
|
||||
partials = { "product" => "Product: {{ product.title }} " }
|
||||
|
||||
assert_template_result("Product: Draft 151cm ", "{% assign page = 'product' %}{% include page %}",
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm ",
|
||||
"{% assign page = 'product' %}{% include page %}",
|
||||
{ "product" => { 'title' => 'Draft 151cm' } },
|
||||
partials: partials)
|
||||
partials: partials,
|
||||
)
|
||||
|
||||
assert_template_result("Product: Draft 151cm ", "{% assign page = 'product' %}{% include page for foo %}",
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm ",
|
||||
"{% assign page = 'product' %}{% include page for foo %}",
|
||||
{ "foo" => { 'title' => 'Draft 151cm' } },
|
||||
partials: partials)
|
||||
partials: partials,
|
||||
)
|
||||
end
|
||||
|
||||
def test_including_with_strict_variables
|
||||
@@ -279,7 +350,31 @@ class IncludeTagTest < Minitest::Test
|
||||
|
||||
def test_break_through_include
|
||||
assert_template_result("1", "{% for i in (1..3) %}{{ i }}{% break %}{{ i }}{% endfor %}")
|
||||
assert_template_result("1", "{% for i in (1..3) %}{{ i }}{% include 'break' %}{{ i }}{% endfor %}",
|
||||
partials: { 'break' => "{% break %}" })
|
||||
assert_template_result(
|
||||
"1",
|
||||
"{% for i in (1..3) %}{{ i }}{% include 'break' %}{{ i }}{% endfor %}",
|
||||
partials: { 'break' => "{% break %}" },
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_tag_renders_error_with_template_name
|
||||
assert_template_result(
|
||||
'Liquid error (foo line 1): standard error',
|
||||
"{% include 'foo' with errors %}",
|
||||
{ 'errors' => ErrorDrop.new },
|
||||
partials: { 'foo' => '{{ foo.standard_error }}' },
|
||||
render_errors: true,
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_tag_renders_error_with_template_name_from_template_factory
|
||||
assert_template_result(
|
||||
'Liquid error (some/path/foo line 1): standard error',
|
||||
"{% include 'foo' with errors %}",
|
||||
{ 'errors' => ErrorDrop.new },
|
||||
partials: { 'foo' => '{{ foo.standard_error }}' },
|
||||
template_factory: StubTemplateFactory.new,
|
||||
render_errors: true,
|
||||
)
|
||||
end
|
||||
end # IncludeTagTest
|
||||
|
||||
@@ -8,19 +8,23 @@ class IncrementTagTest < Minitest::Test
|
||||
def test_inc
|
||||
assert_template_result('0 1', '{%increment port %} {{ port }}')
|
||||
assert_template_result(' 0 1 2', '{{port}} {%increment port %} {%increment port%} {{port}}')
|
||||
assert_template_result('0 0 1 2 1',
|
||||
assert_template_result(
|
||||
'0 0 1 2 1',
|
||||
'{%increment port %} {%increment starboard%} ' \
|
||||
'{%increment port %} {%increment port%} ' \
|
||||
'{%increment starboard %}')
|
||||
'{%increment starboard %}',
|
||||
)
|
||||
end
|
||||
|
||||
def test_dec
|
||||
assert_template_result('-1 -1', '{%decrement port %} {{ port }}', { 'port' => 10 })
|
||||
assert_template_result(' -1 -2 -2', '{{port}} {%decrement port %} {%decrement port%} {{port}}')
|
||||
assert_template_result('0 1 2 0 3 1 1 3',
|
||||
assert_template_result(
|
||||
'0 1 2 0 3 1 1 3',
|
||||
'{%increment starboard %} {%increment starboard%} {%increment starboard%} ' \
|
||||
'{%increment port %} {%increment starboard%} ' \
|
||||
'{%increment port %} {%decrement port%} ' \
|
||||
'{%decrement starboard %}')
|
||||
'{%decrement starboard %}',
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,12 +6,15 @@ class RawTagTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def test_tag_in_raw
|
||||
assert_template_result('{% comment %} test {% endcomment %}',
|
||||
'{% raw %}{% comment %} test {% endcomment %}{% endraw %}')
|
||||
assert_template_result(
|
||||
'{% comment %} test {% endcomment %}',
|
||||
'{% raw %}{% comment %} test {% endcomment %}{% endraw %}',
|
||||
)
|
||||
end
|
||||
|
||||
def test_output_in_raw
|
||||
assert_template_result('{{ test }}', '{% raw %}{{ test }}{% endraw %}')
|
||||
# assert_template_result('{{ test }}', '{% raw %}{{ test }}{% endraw %}')
|
||||
assert_template_result('>{{ test }}<', '> {%- raw -%}{{ test }}{%- endraw -%} <')
|
||||
end
|
||||
|
||||
def test_open_tag_in_raw
|
||||
|
||||
@@ -6,52 +6,79 @@ class RenderTagTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def test_render_with_no_arguments
|
||||
assert_template_result('rendered content', '{% render "source" %}',
|
||||
partials: { 'source' => 'rendered content' })
|
||||
assert_template_result(
|
||||
'rendered content',
|
||||
'{% render "source" %}',
|
||||
partials: { 'source' => 'rendered content' },
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_tag_looks_for_file_system_in_registers_first
|
||||
assert_template_result('from register file system', '{% render "pick_a_source" %}',
|
||||
partials: { 'pick_a_source' => 'from register file system' })
|
||||
assert_template_result(
|
||||
'from register file system',
|
||||
'{% render "pick_a_source" %}',
|
||||
partials: { 'pick_a_source' => 'from register file system' },
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_passes_named_arguments_into_inner_scope
|
||||
assert_template_result('My Product', '{% render "product", inner_product: outer_product %}',
|
||||
assert_template_result(
|
||||
'My Product',
|
||||
'{% render "product", inner_product: outer_product %}',
|
||||
{ 'outer_product' => { 'title' => 'My Product' } },
|
||||
partials: { 'product' => '{{ inner_product.title }}' })
|
||||
partials: { 'product' => '{{ inner_product.title }}' },
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_accepts_literals_as_arguments
|
||||
assert_template_result('123', '{% render "snippet", price: 123 %}',
|
||||
partials: { 'snippet' => '{{ price }}' })
|
||||
assert_template_result(
|
||||
'123',
|
||||
'{% render "snippet", price: 123 %}',
|
||||
partials: { 'snippet' => '{{ price }}' },
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_accepts_multiple_named_arguments
|
||||
assert_template_result('1 2', '{% render "snippet", one: 1, two: 2 %}',
|
||||
partials: { 'snippet' => '{{ one }} {{ two }}' })
|
||||
assert_template_result(
|
||||
'1 2',
|
||||
'{% render "snippet", one: 1, two: 2 %}',
|
||||
partials: { 'snippet' => '{{ one }} {{ two }}' },
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_does_not_inherit_parent_scope_variables
|
||||
assert_template_result('', '{% assign outer_variable = "should not be visible" %}{% render "snippet" %}',
|
||||
partials: { 'snippet' => '{{ outer_variable }}' })
|
||||
assert_template_result(
|
||||
'',
|
||||
'{% assign outer_variable = "should not be visible" %}{% render "snippet" %}',
|
||||
partials: { 'snippet' => '{{ outer_variable }}' },
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_does_not_inherit_variable_with_same_name_as_snippet
|
||||
assert_template_result('', "{% assign snippet = 'should not be visible' %}{% render 'snippet' %}",
|
||||
partials: { 'snippet' => '{{ snippet }}' })
|
||||
assert_template_result(
|
||||
'',
|
||||
"{% assign snippet = 'should not be visible' %}{% render 'snippet' %}",
|
||||
partials: { 'snippet' => '{{ snippet }}' },
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_does_not_mutate_parent_scope
|
||||
assert_template_result('', "{% render 'snippet' %}{{ inner }}",
|
||||
partials: { 'snippet' => '{% assign inner = 1 %}' })
|
||||
assert_template_result(
|
||||
'',
|
||||
"{% render 'snippet' %}{{ inner }}",
|
||||
partials: { 'snippet' => '{% assign inner = 1 %}' },
|
||||
)
|
||||
end
|
||||
|
||||
def test_nested_render_tag
|
||||
assert_template_result('one two', "{% render 'one' %}",
|
||||
assert_template_result(
|
||||
'one two',
|
||||
"{% render 'one' %}",
|
||||
partials: {
|
||||
'one' => "one {% render 'two' %}",
|
||||
'two' => 'two',
|
||||
})
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
def test_recursively_rendered_template_does_not_produce_endless_loop
|
||||
@@ -77,27 +104,36 @@ class RenderTagTest < Minitest::Test
|
||||
|
||||
def test_include_tag_caches_second_read_of_same_partial
|
||||
file_system = StubFileSystem.new('snippet' => 'echo')
|
||||
assert_equal('echoecho',
|
||||
assert_equal(
|
||||
'echoecho',
|
||||
Template.parse('{% render "snippet" %}{% render "snippet" %}')
|
||||
.render!({}, registers: { file_system: file_system }))
|
||||
.render!({}, registers: { file_system: file_system }),
|
||||
)
|
||||
assert_equal(1, file_system.file_read_count)
|
||||
end
|
||||
|
||||
def test_render_tag_doesnt_cache_partials_across_renders
|
||||
file_system = StubFileSystem.new('snippet' => 'my message')
|
||||
|
||||
assert_equal('my message',
|
||||
Template.parse('{% include "snippet" %}').render!({}, registers: { file_system: file_system }))
|
||||
assert_equal(
|
||||
'my message',
|
||||
Template.parse('{% include "snippet" %}').render!({}, registers: { file_system: file_system }),
|
||||
)
|
||||
assert_equal(1, file_system.file_read_count)
|
||||
|
||||
assert_equal('my message',
|
||||
Template.parse('{% include "snippet" %}').render!({}, registers: { file_system: file_system }))
|
||||
assert_equal(
|
||||
'my message',
|
||||
Template.parse('{% include "snippet" %}').render!({}, registers: { file_system: file_system }),
|
||||
)
|
||||
assert_equal(2, file_system.file_read_count)
|
||||
end
|
||||
|
||||
def test_render_tag_within_if_statement
|
||||
assert_template_result('my message', '{% if true %}{% render "snippet" %}{% endif %}',
|
||||
partials: { 'snippet' => 'my message' })
|
||||
assert_template_result(
|
||||
'my message',
|
||||
'{% if true %}{% render "snippet" %}{% endif %}',
|
||||
partials: { 'snippet' => 'my message' },
|
||||
)
|
||||
end
|
||||
|
||||
def test_break_through_render
|
||||
@@ -107,13 +143,19 @@ class RenderTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_increment_is_isolated_between_renders
|
||||
assert_template_result('010', '{% increment %}{% increment %}{% render "incr" %}',
|
||||
partials: { 'incr' => '{% increment %}' })
|
||||
assert_template_result(
|
||||
'010',
|
||||
'{% increment %}{% increment %}{% render "incr" %}',
|
||||
partials: { 'incr' => '{% increment %}' },
|
||||
)
|
||||
end
|
||||
|
||||
def test_decrement_is_isolated_between_renders
|
||||
assert_template_result('-1-2-1', '{% decrement %}{% decrement %}{% render "decr" %}',
|
||||
partials: { 'decr' => '{% decrement %}' })
|
||||
assert_template_result(
|
||||
'-1-2-1',
|
||||
'{% decrement %}{% decrement %}{% render "decr" %}',
|
||||
partials: { 'decr' => '{% decrement %}' },
|
||||
)
|
||||
end
|
||||
|
||||
def test_includes_will_not_render_inside_render_tag
|
||||
@@ -124,7 +166,7 @@ class RenderTagTest < Minitest::Test
|
||||
partials: {
|
||||
'foo' => 'bar',
|
||||
'test_include' => '{% include "foo" %}',
|
||||
}
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
@@ -138,72 +180,109 @@ class RenderTagTest < Minitest::Test
|
||||
'nested_render_with_sibling_include' => '{% render "test_include" %}{% include "foo" %}',
|
||||
'test_include' => '{% include "foo" %}',
|
||||
},
|
||||
render_errors: true
|
||||
render_errors: true,
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_tag_with
|
||||
assert_template_result("Product: Draft 151cm ",
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm ",
|
||||
"{% render 'product' with products[0] %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: {
|
||||
'product' => "Product: {{ product.title }} ",
|
||||
'product_alias' => "Product: {{ product.title }} ",
|
||||
})
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_tag_with_alias
|
||||
assert_template_result("Product: Draft 151cm ",
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm ",
|
||||
"{% render 'product_alias' with products[0] as product %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: {
|
||||
'product' => "Product: {{ product.title }} ",
|
||||
'product_alias' => "Product: {{ product.title }} ",
|
||||
})
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_tag_for_alias
|
||||
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm Product: Element 155cm ",
|
||||
"{% render 'product_alias' for products as product %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: {
|
||||
'product' => "Product: {{ product.title }} ",
|
||||
'product_alias' => "Product: {{ product.title }} ",
|
||||
})
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_tag_for
|
||||
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm Product: Element 155cm ",
|
||||
"{% render 'product' for products %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: {
|
||||
'product' => "Product: {{ product.title }} ",
|
||||
'product_alias' => "Product: {{ product.title }} ",
|
||||
})
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_tag_forloop
|
||||
assert_template_result("Product: Draft 151cm first index:1 Product: Element 155cm last index:2 ",
|
||||
assert_template_result(
|
||||
"Product: Draft 151cm first index:1 Product: Element 155cm last index:2 ",
|
||||
"{% render 'product' for products %}",
|
||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
||||
partials: {
|
||||
'product' => "Product: {{ product.title }} {% if forloop.first %}first{% endif %} {% if forloop.last %}last{% endif %} index:{{ forloop.index }} ",
|
||||
})
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_tag_for_drop
|
||||
assert_template_result("123",
|
||||
"{% render 'loop' for loop as value %}", { "loop" => TestEnumerable.new },
|
||||
assert_template_result(
|
||||
"123",
|
||||
"{% render 'loop' for loop as value %}",
|
||||
{ "loop" => TestEnumerable.new },
|
||||
partials: {
|
||||
'loop' => "{{ value.foo }}",
|
||||
})
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_tag_with_drop
|
||||
assert_template_result("TestEnumerable",
|
||||
"{% render 'loop' with loop as value %}", { "loop" => TestEnumerable.new },
|
||||
assert_template_result(
|
||||
"TestEnumerable",
|
||||
"{% render 'loop' with loop as value %}",
|
||||
{ "loop" => TestEnumerable.new },
|
||||
partials: {
|
||||
'loop' => "{{ value }}",
|
||||
})
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_tag_renders_error_with_template_name
|
||||
assert_template_result(
|
||||
'Liquid error (foo line 1): standard error',
|
||||
"{% render 'foo' with errors %}",
|
||||
{ 'errors' => ErrorDrop.new },
|
||||
partials: { 'foo' => '{{ foo.standard_error }}' },
|
||||
render_errors: true,
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_tag_renders_error_with_template_name_from_template_factory
|
||||
assert_template_result(
|
||||
'Liquid error (some/path/foo line 1): standard error',
|
||||
"{% render 'foo' with errors %}",
|
||||
{ 'errors' => ErrorDrop.new },
|
||||
partials: { 'foo' => '{{ foo.standard_error }}' },
|
||||
template_factory: StubTemplateFactory.new,
|
||||
render_errors: true,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,8 +6,10 @@ class StandardTagTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def test_no_transform
|
||||
assert_template_result('this text should come out of the template without change...',
|
||||
'this text should come out of the template without change...')
|
||||
assert_template_result(
|
||||
'this text should come out of the template without change...',
|
||||
'this text should come out of the template without change...',
|
||||
)
|
||||
|
||||
assert_template_result('blah', 'blah')
|
||||
assert_template_result('<blah>', '<blah>')
|
||||
@@ -20,8 +22,10 @@ class StandardTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_has_a_block_which_does_nothing
|
||||
assert_template_result(%(the comment block should be removed .. right?),
|
||||
%(the comment block should be removed {%comment%} be gone.. {%endcomment%} .. right?))
|
||||
assert_template_result(
|
||||
%(the comment block should be removed .. right?),
|
||||
%(the comment block should be removed {%comment%} be gone.. {%endcomment%} .. right?),
|
||||
)
|
||||
|
||||
assert_template_result('', '{%comment%}{%endcomment%}')
|
||||
assert_template_result('', '{%comment%}{% endcomment %}')
|
||||
@@ -64,61 +68,81 @@ class StandardTagTest < Minitest::Test
|
||||
|
||||
def test_capture
|
||||
assigns = { 'var' => 'content' }
|
||||
assert_template_result('content foo content foo ',
|
||||
assert_template_result(
|
||||
'content foo content foo ',
|
||||
'{{ var2 }}{% capture var2 %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}',
|
||||
assigns)
|
||||
assigns,
|
||||
)
|
||||
end
|
||||
|
||||
def test_capture_detects_bad_syntax
|
||||
assert_raises(SyntaxError) do
|
||||
assert_template_result('content foo content foo ',
|
||||
assert_template_result(
|
||||
'content foo content foo ',
|
||||
'{{ var2 }}{% capture %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}',
|
||||
{ 'var' => 'content' })
|
||||
{ 'var' => 'content' },
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def test_case
|
||||
assigns = { 'condition' => 2 }
|
||||
assert_template_result(' its 2 ',
|
||||
assert_template_result(
|
||||
' its 2 ',
|
||||
'{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}',
|
||||
assigns)
|
||||
assigns,
|
||||
)
|
||||
|
||||
assigns = { 'condition' => 1 }
|
||||
assert_template_result(' its 1 ',
|
||||
assert_template_result(
|
||||
' its 1 ',
|
||||
'{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}',
|
||||
assigns)
|
||||
assigns,
|
||||
)
|
||||
|
||||
assigns = { 'condition' => 3 }
|
||||
assert_template_result('',
|
||||
assert_template_result(
|
||||
'',
|
||||
'{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}',
|
||||
assigns)
|
||||
assigns,
|
||||
)
|
||||
|
||||
assigns = { 'condition' => "string here" }
|
||||
assert_template_result(' hit ',
|
||||
assert_template_result(
|
||||
' hit ',
|
||||
'{% case condition %}{% when "string here" %} hit {% endcase %}',
|
||||
assigns)
|
||||
assigns,
|
||||
)
|
||||
|
||||
assigns = { 'condition' => "bad string here" }
|
||||
assert_template_result('',
|
||||
assert_template_result(
|
||||
'',
|
||||
'{% case condition %}{% when "string here" %} hit {% endcase %}',\
|
||||
assigns)
|
||||
assigns,
|
||||
)
|
||||
end
|
||||
|
||||
def test_case_with_else
|
||||
assigns = { 'condition' => 5 }
|
||||
assert_template_result(' hit ',
|
||||
assert_template_result(
|
||||
' hit ',
|
||||
'{% case condition %}{% when 5 %} hit {% else %} else {% endcase %}',
|
||||
assigns)
|
||||
assigns,
|
||||
)
|
||||
|
||||
assigns = { 'condition' => 6 }
|
||||
assert_template_result(' else ',
|
||||
assert_template_result(
|
||||
' else ',
|
||||
'{% case condition %}{% when 5 %} hit {% else %} else {% endcase %}',
|
||||
assigns)
|
||||
assigns,
|
||||
)
|
||||
|
||||
assigns = { 'condition' => 6 }
|
||||
assert_template_result(' else ',
|
||||
assert_template_result(
|
||||
' else ',
|
||||
'{% case condition %} {% when 5 %} hit {% else %} else {% endcase %}',
|
||||
assigns)
|
||||
assigns,
|
||||
)
|
||||
end
|
||||
|
||||
def test_case_on_size
|
||||
@@ -131,47 +155,67 @@ class StandardTagTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_case_on_size_with_else
|
||||
assert_template_result('else',
|
||||
assert_template_result(
|
||||
'else',
|
||||
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
||||
{ 'a' => [] })
|
||||
{ 'a' => [] },
|
||||
)
|
||||
|
||||
assert_template_result('1',
|
||||
assert_template_result(
|
||||
'1',
|
||||
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
||||
{ 'a' => [1] })
|
||||
{ 'a' => [1] },
|
||||
)
|
||||
|
||||
assert_template_result('2',
|
||||
assert_template_result(
|
||||
'2',
|
||||
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
||||
{ 'a' => [1, 1] })
|
||||
{ 'a' => [1, 1] },
|
||||
)
|
||||
|
||||
assert_template_result('else',
|
||||
assert_template_result(
|
||||
'else',
|
||||
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
||||
{ 'a' => [1, 1, 1] })
|
||||
{ 'a' => [1, 1, 1] },
|
||||
)
|
||||
|
||||
assert_template_result('else',
|
||||
assert_template_result(
|
||||
'else',
|
||||
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
||||
{ 'a' => [1, 1, 1, 1] })
|
||||
{ 'a' => [1, 1, 1, 1] },
|
||||
)
|
||||
|
||||
assert_template_result('else',
|
||||
assert_template_result(
|
||||
'else',
|
||||
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
||||
{ 'a' => [1, 1, 1, 1, 1] })
|
||||
{ 'a' => [1, 1, 1, 1, 1] },
|
||||
)
|
||||
end
|
||||
|
||||
def test_case_on_length_with_else
|
||||
assert_template_result('else',
|
||||
assert_template_result(
|
||||
'else',
|
||||
'{% case a.empty? %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
|
||||
{})
|
||||
{},
|
||||
)
|
||||
|
||||
assert_template_result('false',
|
||||
assert_template_result(
|
||||
'false',
|
||||
'{% case false %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
|
||||
{})
|
||||
{},
|
||||
)
|
||||
|
||||
assert_template_result('true',
|
||||
assert_template_result(
|
||||
'true',
|
||||
'{% case true %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
|
||||
{})
|
||||
{},
|
||||
)
|
||||
|
||||
assert_template_result('else',
|
||||
assert_template_result(
|
||||
'else',
|
||||
'{% case NULL %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
|
||||
{})
|
||||
{},
|
||||
)
|
||||
end
|
||||
|
||||
def test_assign_from_case
|
||||
@@ -253,24 +297,33 @@ class StandardTagTest < Minitest::Test
|
||||
|
||||
assert_template_result('one two one', '{%cycle "one", "two"%} {%cycle "one", "two"%} {%cycle "one", "two"%}')
|
||||
|
||||
assert_template_result('text-align: left text-align: right',
|
||||
'{%cycle "text-align: left", "text-align: right" %} {%cycle "text-align: left", "text-align: right"%}')
|
||||
assert_template_result(
|
||||
'text-align: left text-align: right',
|
||||
'{%cycle "text-align: left", "text-align: right" %} {%cycle "text-align: left", "text-align: right"%}',
|
||||
)
|
||||
end
|
||||
|
||||
def test_multiple_cycles
|
||||
assert_template_result('1 2 1 1 2 3 1',
|
||||
'{%cycle 1,2%} {%cycle 1,2%} {%cycle 1,2%} {%cycle 1,2,3%} {%cycle 1,2,3%} {%cycle 1,2,3%} {%cycle 1,2,3%}')
|
||||
assert_template_result(
|
||||
'1 2 1 1 2 3 1',
|
||||
'{%cycle 1,2%} {%cycle 1,2%} {%cycle 1,2%} {%cycle 1,2,3%} {%cycle 1,2,3%} {%cycle 1,2,3%} {%cycle 1,2,3%}',
|
||||
)
|
||||
end
|
||||
|
||||
def test_multiple_named_cycles
|
||||
assert_template_result('one one two two one one',
|
||||
'{%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %}')
|
||||
assert_template_result(
|
||||
'one one two two one one',
|
||||
'{%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %}',
|
||||
)
|
||||
end
|
||||
|
||||
def test_multiple_named_cycles_with_names_from_context
|
||||
assigns = { "var1" => 1, "var2" => 2 }
|
||||
assert_template_result('one one two two one one',
|
||||
'{%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %}', assigns)
|
||||
assert_template_result(
|
||||
'one one two two one one',
|
||||
'{%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %}',
|
||||
assigns,
|
||||
)
|
||||
end
|
||||
|
||||
def test_size_of_array
|
||||
|
||||
@@ -18,52 +18,70 @@ class TableRowTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_table_row
|
||||
assert_template_result("<tr class=\"row1\">\n<td class=\"col1\"> 1 </td><td class=\"col2\"> 2 </td><td class=\"col3\"> 3 </td></tr>\n<tr class=\"row2\"><td class=\"col1\"> 4 </td><td class=\"col2\"> 5 </td><td class=\"col3\"> 6 </td></tr>\n",
|
||||
assert_template_result(
|
||||
"<tr class=\"row1\">\n<td class=\"col1\"> 1 </td><td class=\"col2\"> 2 </td><td class=\"col3\"> 3 </td></tr>\n<tr class=\"row2\"><td class=\"col1\"> 4 </td><td class=\"col2\"> 5 </td><td class=\"col3\"> 6 </td></tr>\n",
|
||||
'{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}',
|
||||
{ 'numbers' => [1, 2, 3, 4, 5, 6] })
|
||||
{ 'numbers' => [1, 2, 3, 4, 5, 6] },
|
||||
)
|
||||
|
||||
assert_template_result("<tr class=\"row1\">\n</tr>\n",
|
||||
assert_template_result(
|
||||
"<tr class=\"row1\">\n</tr>\n",
|
||||
'{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}',
|
||||
{ 'numbers' => [] })
|
||||
{ 'numbers' => [] },
|
||||
)
|
||||
end
|
||||
|
||||
def test_table_row_with_different_cols
|
||||
assert_template_result("<tr class=\"row1\">\n<td class=\"col1\"> 1 </td><td class=\"col2\"> 2 </td><td class=\"col3\"> 3 </td><td class=\"col4\"> 4 </td><td class=\"col5\"> 5 </td></tr>\n<tr class=\"row2\"><td class=\"col1\"> 6 </td></tr>\n",
|
||||
assert_template_result(
|
||||
"<tr class=\"row1\">\n<td class=\"col1\"> 1 </td><td class=\"col2\"> 2 </td><td class=\"col3\"> 3 </td><td class=\"col4\"> 4 </td><td class=\"col5\"> 5 </td></tr>\n<tr class=\"row2\"><td class=\"col1\"> 6 </td></tr>\n",
|
||||
'{% tablerow n in numbers cols:5%} {{n}} {% endtablerow %}',
|
||||
{ 'numbers' => [1, 2, 3, 4, 5, 6] })
|
||||
{ 'numbers' => [1, 2, 3, 4, 5, 6] },
|
||||
)
|
||||
end
|
||||
|
||||
def test_table_col_counter
|
||||
assert_template_result("<tr class=\"row1\">\n<td class=\"col1\">1</td><td class=\"col2\">2</td></tr>\n<tr class=\"row2\"><td class=\"col1\">1</td><td class=\"col2\">2</td></tr>\n<tr class=\"row3\"><td class=\"col1\">1</td><td class=\"col2\">2</td></tr>\n",
|
||||
assert_template_result(
|
||||
"<tr class=\"row1\">\n<td class=\"col1\">1</td><td class=\"col2\">2</td></tr>\n<tr class=\"row2\"><td class=\"col1\">1</td><td class=\"col2\">2</td></tr>\n<tr class=\"row3\"><td class=\"col1\">1</td><td class=\"col2\">2</td></tr>\n",
|
||||
'{% tablerow n in numbers cols:2%}{{tablerowloop.col}}{% endtablerow %}',
|
||||
{ 'numbers' => [1, 2, 3, 4, 5, 6] })
|
||||
{ 'numbers' => [1, 2, 3, 4, 5, 6] },
|
||||
)
|
||||
end
|
||||
|
||||
def test_quoted_fragment
|
||||
assert_template_result("<tr class=\"row1\">\n<td class=\"col1\"> 1 </td><td class=\"col2\"> 2 </td><td class=\"col3\"> 3 </td></tr>\n<tr class=\"row2\"><td class=\"col1\"> 4 </td><td class=\"col2\"> 5 </td><td class=\"col3\"> 6 </td></tr>\n",
|
||||
assert_template_result(
|
||||
"<tr class=\"row1\">\n<td class=\"col1\"> 1 </td><td class=\"col2\"> 2 </td><td class=\"col3\"> 3 </td></tr>\n<tr class=\"row2\"><td class=\"col1\"> 4 </td><td class=\"col2\"> 5 </td><td class=\"col3\"> 6 </td></tr>\n",
|
||||
"{% tablerow n in collections.frontpage cols:3%} {{n}} {% endtablerow %}",
|
||||
{ 'collections' => { 'frontpage' => [1, 2, 3, 4, 5, 6] } })
|
||||
assert_template_result("<tr class=\"row1\">\n<td class=\"col1\"> 1 </td><td class=\"col2\"> 2 </td><td class=\"col3\"> 3 </td></tr>\n<tr class=\"row2\"><td class=\"col1\"> 4 </td><td class=\"col2\"> 5 </td><td class=\"col3\"> 6 </td></tr>\n",
|
||||
{ 'collections' => { 'frontpage' => [1, 2, 3, 4, 5, 6] } },
|
||||
)
|
||||
assert_template_result(
|
||||
"<tr class=\"row1\">\n<td class=\"col1\"> 1 </td><td class=\"col2\"> 2 </td><td class=\"col3\"> 3 </td></tr>\n<tr class=\"row2\"><td class=\"col1\"> 4 </td><td class=\"col2\"> 5 </td><td class=\"col3\"> 6 </td></tr>\n",
|
||||
"{% tablerow n in collections['frontpage'] cols:3%} {{n}} {% endtablerow %}",
|
||||
{ 'collections' => { 'frontpage' => [1, 2, 3, 4, 5, 6] } })
|
||||
{ 'collections' => { 'frontpage' => [1, 2, 3, 4, 5, 6] } },
|
||||
)
|
||||
end
|
||||
|
||||
def test_enumerable_drop
|
||||
assert_template_result("<tr class=\"row1\">\n<td class=\"col1\"> 1 </td><td class=\"col2\"> 2 </td><td class=\"col3\"> 3 </td></tr>\n<tr class=\"row2\"><td class=\"col1\"> 4 </td><td class=\"col2\"> 5 </td><td class=\"col3\"> 6 </td></tr>\n",
|
||||
assert_template_result(
|
||||
"<tr class=\"row1\">\n<td class=\"col1\"> 1 </td><td class=\"col2\"> 2 </td><td class=\"col3\"> 3 </td></tr>\n<tr class=\"row2\"><td class=\"col1\"> 4 </td><td class=\"col2\"> 5 </td><td class=\"col3\"> 6 </td></tr>\n",
|
||||
'{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}',
|
||||
{ 'numbers' => ArrayDrop.new([1, 2, 3, 4, 5, 6]) })
|
||||
{ 'numbers' => ArrayDrop.new([1, 2, 3, 4, 5, 6]) },
|
||||
)
|
||||
end
|
||||
|
||||
def test_offset_and_limit
|
||||
assert_template_result("<tr class=\"row1\">\n<td class=\"col1\"> 1 </td><td class=\"col2\"> 2 </td><td class=\"col3\"> 3 </td></tr>\n<tr class=\"row2\"><td class=\"col1\"> 4 </td><td class=\"col2\"> 5 </td><td class=\"col3\"> 6 </td></tr>\n",
|
||||
assert_template_result(
|
||||
"<tr class=\"row1\">\n<td class=\"col1\"> 1 </td><td class=\"col2\"> 2 </td><td class=\"col3\"> 3 </td></tr>\n<tr class=\"row2\"><td class=\"col1\"> 4 </td><td class=\"col2\"> 5 </td><td class=\"col3\"> 6 </td></tr>\n",
|
||||
'{% tablerow n in numbers cols:3 offset:1 limit:6%} {{n}} {% endtablerow %}',
|
||||
{ 'numbers' => [0, 1, 2, 3, 4, 5, 6, 7] })
|
||||
{ 'numbers' => [0, 1, 2, 3, 4, 5, 6, 7] },
|
||||
)
|
||||
end
|
||||
|
||||
def test_blank_string_not_iterable
|
||||
assert_template_result("<tr class=\"row1\">\n</tr>\n",
|
||||
assert_template_result(
|
||||
"<tr class=\"row1\">\n</tr>\n",
|
||||
"{% tablerow char in characters cols:3 %}I WILL NOT BE OUTPUT{% endtablerow %}",
|
||||
{ 'characters' => '' })
|
||||
{ 'characters' => '' },
|
||||
)
|
||||
end
|
||||
|
||||
def test_cols_nil_constant_same_as_evaluated_nil_expression
|
||||
@@ -72,12 +90,50 @@ class TableRowTest < Minitest::Test
|
||||
"<td class=\"col2\">false</td>" \
|
||||
"</tr>\n"
|
||||
|
||||
assert_template_result(expect,
|
||||
"{% tablerow i in (1..2) cols:nil %}{{ tablerowloop.col_last }}{% endtablerow %}")
|
||||
assert_template_result(
|
||||
expect,
|
||||
"{% tablerow i in (1..2) cols:nil %}{{ tablerowloop.col_last }}{% endtablerow %}",
|
||||
)
|
||||
|
||||
assert_template_result(expect,
|
||||
assert_template_result(
|
||||
expect,
|
||||
"{% tablerow i in (1..2) cols:var %}{{ tablerowloop.col_last }}{% endtablerow %}",
|
||||
{ "var" => nil })
|
||||
{ "var" => nil },
|
||||
)
|
||||
end
|
||||
|
||||
def test_nil_limit_is_treated_as_zero
|
||||
expect = "<tr class=\"row1\">\n" \
|
||||
"</tr>\n"
|
||||
|
||||
assert_template_result(
|
||||
expect,
|
||||
"{% tablerow i in (1..2) limit:nil %}{{ i }}{% endtablerow %}",
|
||||
)
|
||||
|
||||
assert_template_result(
|
||||
expect,
|
||||
"{% tablerow i in (1..2) limit:var %}{{ i }}{% endtablerow %}",
|
||||
{ "var" => nil },
|
||||
)
|
||||
end
|
||||
|
||||
def test_nil_offset_is_treated_as_zero
|
||||
expect = "<tr class=\"row1\">\n" \
|
||||
"<td class=\"col1\">1:false</td>" \
|
||||
"<td class=\"col2\">2:true</td>" \
|
||||
"</tr>\n"
|
||||
|
||||
assert_template_result(
|
||||
expect,
|
||||
"{% tablerow i in (1..2) offset:nil %}{{ i }}:{{ tablerowloop.col_last }}{% endtablerow %}",
|
||||
)
|
||||
|
||||
assert_template_result(
|
||||
expect,
|
||||
"{% tablerow i in (1..2) offset:var %}{{ i }}:{{ tablerowloop.col_last }}{% endtablerow %}",
|
||||
{ "var" => nil },
|
||||
)
|
||||
end
|
||||
|
||||
def test_tablerow_loop_drop_attributes
|
||||
@@ -131,4 +187,24 @@ class TableRowTest < Minitest::Test
|
||||
|
||||
assert_template_result(expected_output, template)
|
||||
end
|
||||
|
||||
def test_table_row_renders_correct_error_message_for_invalid_parameters
|
||||
assert_template_result(
|
||||
"Liquid error (line 1): invalid integer",
|
||||
'{% tablerow n in (1...10) limit:true %} {{n}} {% endtablerow %}',
|
||||
render_errors: true,
|
||||
)
|
||||
|
||||
assert_template_result(
|
||||
"Liquid error (line 1): invalid integer",
|
||||
'{% tablerow n in (1...10) offset:true %} {{n}} {% endtablerow %}',
|
||||
render_errors: true,
|
||||
)
|
||||
|
||||
assert_template_result(
|
||||
"Liquid error (line 1): invalid integer",
|
||||
'{% tablerow n in (1...10) cols:true %} {{n}} {% endtablerow %}',
|
||||
render_errors: true,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,8 +7,10 @@ class UnlessElseTagTest < Minitest::Test
|
||||
|
||||
def test_unless
|
||||
assert_template_result(' ', ' {% unless true %} this text should not go into the output {% endunless %} ')
|
||||
assert_template_result(' this text should go into the output ',
|
||||
' {% unless false %} this text should go into the output {% endunless %} ')
|
||||
assert_template_result(
|
||||
' this text should go into the output ',
|
||||
' {% unless false %} this text should go into the output {% endunless %} ',
|
||||
)
|
||||
assert_template_result(' you rock ?', '{% unless true %} you suck {% endunless %} {% unless false %} you rock {% endunless %}?')
|
||||
end
|
||||
|
||||
|
||||
@@ -93,10 +93,12 @@ class TemplateTest < Minitest::Test
|
||||
|
||||
def test_lambda_is_called_once_from_custom_assigns_over_multiple_parses_and_renders
|
||||
t = Template.new
|
||||
assigns = { 'number' => -> {
|
||||
@global ||= 0
|
||||
@global += 1
|
||||
} }
|
||||
assigns = {
|
||||
'number' => -> {
|
||||
@global ||= 0
|
||||
@global += 1
|
||||
},
|
||||
}
|
||||
assert_equal('1', t.parse("{{number}}").render!(assigns))
|
||||
assert_equal('1', t.parse("{{number}}").render!(assigns))
|
||||
assert_equal('1', t.render!(assigns))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
require 'timeout'
|
||||
|
||||
class VariableTest < Minitest::Test
|
||||
include Liquid
|
||||
@@ -24,17 +25,22 @@ class VariableTest < Minitest::Test
|
||||
|
||||
def test_if_tag_calls_to_liquid_value
|
||||
assert_template_result('one', '{% if foo == 1 %}one{% endif %}', { 'foo' => IntegerDrop.new('1') })
|
||||
assert_template_result('one', '{% if foo == eqv %}one{% endif %}', { 'foo' => IntegerDrop.new(1), 'eqv' => IntegerDrop.new(1) })
|
||||
assert_template_result('one', '{% if 0 < foo %}one{% endif %}', { 'foo' => IntegerDrop.new('1') })
|
||||
assert_template_result('one', '{% if foo > 0 %}one{% endif %}', { 'foo' => IntegerDrop.new('1') })
|
||||
assert_template_result('one', '{% if b > a %}one{% endif %}', { 'b' => IntegerDrop.new(1), 'a' => IntegerDrop.new(0) })
|
||||
assert_template_result('true', '{% if foo == true %}true{% endif %}', { 'foo' => BooleanDrop.new(true) })
|
||||
assert_template_result('true', '{% if foo %}true{% endif %}', { 'foo' => BooleanDrop.new(true) })
|
||||
|
||||
assert_template_result('', '{% if foo %}true{% endif %}', { 'foo' => BooleanDrop.new(false) })
|
||||
assert_template_result('', '{% if foo == true %}True{% endif %}', { 'foo' => BooleanDrop.new(false) })
|
||||
|
||||
assert_template_result('one', '{% if a contains x %}one{% endif %}', { 'a' => [1], 'x' => IntegerDrop.new(1) })
|
||||
end
|
||||
|
||||
def test_unless_tag_calls_to_liquid_value
|
||||
assert_template_result('', '{% unless foo %}true{% endunless %}', { 'foo' => BooleanDrop.new(true) })
|
||||
assert_template_result('true', '{% unless foo %}true{% endunless %}', { 'foo' => BooleanDrop.new(false) })
|
||||
end
|
||||
|
||||
def test_case_tag_calls_to_liquid_value
|
||||
@@ -130,4 +136,72 @@ class VariableTest < Minitest::Test
|
||||
def test_raw_value_variable
|
||||
assert_template_result('bar', '{{ [key] }}', { 'key' => 'foo', 'foo' => 'bar' })
|
||||
end
|
||||
|
||||
def test_dynamic_find_var_with_drop
|
||||
assert_template_result(
|
||||
'bar',
|
||||
'{{ [list[settings.zero]] }}',
|
||||
{
|
||||
'list' => ['foo'],
|
||||
'settings' => SettingsDrop.new("zero" => 0),
|
||||
'foo' => 'bar',
|
||||
},
|
||||
)
|
||||
|
||||
assert_template_result(
|
||||
'foo',
|
||||
'{{ [list[settings.zero]["foo"]] }}',
|
||||
{
|
||||
'list' => [{ 'foo' => 'bar' }],
|
||||
'settings' => SettingsDrop.new("zero" => 0),
|
||||
'bar' => 'foo',
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
def test_double_nested_variable_lookup
|
||||
assert_template_result(
|
||||
'bar',
|
||||
'{{ list[list[settings.zero]]["foo"] }}',
|
||||
{
|
||||
'list' => [1, { 'foo' => 'bar' }],
|
||||
'settings' => SettingsDrop.new("zero" => 0),
|
||||
'bar' => 'foo',
|
||||
},
|
||||
)
|
||||
end
|
||||
|
||||
def test_variable_lookup_should_not_hang_with_invalid_syntax
|
||||
Timeout.timeout(1) do
|
||||
assert_template_result(
|
||||
'bar',
|
||||
"{{['foo'}}",
|
||||
{
|
||||
'foo' => 'bar',
|
||||
},
|
||||
error_mode: :lax,
|
||||
)
|
||||
end
|
||||
|
||||
very_long_key = "1234567890" * 100
|
||||
|
||||
template_list = [
|
||||
"{{['#{very_long_key}']}}", # valid
|
||||
"{{['#{very_long_key}'}}", # missing closing bracket
|
||||
"{{[['#{very_long_key}']}}", # extra open bracket
|
||||
]
|
||||
|
||||
template_list.each do |template|
|
||||
Timeout.timeout(1) do
|
||||
assert_template_result(
|
||||
'bar',
|
||||
template,
|
||||
{
|
||||
very_long_key => 'bar',
|
||||
},
|
||||
error_mode: :lax,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+18
-37
@@ -39,37 +39,17 @@ module Minitest
|
||||
|
||||
def assert_template_result(
|
||||
expected, template, assigns = {},
|
||||
message: nil, partials: nil, error_mode: nil, render_errors: false
|
||||
message: nil, partials: nil, error_mode: nil, render_errors: false,
|
||||
template_factory: nil
|
||||
)
|
||||
template = Liquid::Template.parse(template, line_numbers: true, error_mode: error_mode&.to_sym)
|
||||
file_system = StubFileSystem.new(partials || {})
|
||||
registers = Liquid::Registers.new(file_system: file_system)
|
||||
registers = Liquid::Registers.new(file_system: file_system, template_factory: template_factory)
|
||||
context = Liquid::Context.build(static_environments: assigns, rethrow_errors: !render_errors, registers: registers)
|
||||
output = template.render(context)
|
||||
assert_equal(expected, output, message)
|
||||
end
|
||||
|
||||
if ENV['LIQUID_MIGRATOR']
|
||||
puts "-- Liquid Migrator Enabled"
|
||||
|
||||
alias_method(:assert_template_result_without_migrator, :assert_template_result)
|
||||
|
||||
def assert_template_result(expected, source, assigns = {}, error_mode: nil, partials: nil, **kwargs)
|
||||
migrated_source = Liquid::Template.migrate(source, line_numbers: true, error_mode: error_mode&.to_sym)
|
||||
assert_no_migration(migrated_source)
|
||||
if partials
|
||||
migrated_partials = {}
|
||||
partials.each do |name, partial|
|
||||
new_partial = Liquid::Template.migrate(partial, line_numbers: true, error_mode: error_mode&.to_sym)
|
||||
assert_no_migration(new_partial)
|
||||
migrated_partials[name] = new_partial
|
||||
end
|
||||
end
|
||||
assert_template_result_without_migrator(expected, migrated_source, assigns,
|
||||
error_mode: 'strict', partials: migrated_partials, **kwargs)
|
||||
end
|
||||
end
|
||||
|
||||
def assert_match_syntax_error(match, template, error_mode: nil)
|
||||
exception = assert_raises(Liquid::SyntaxError) do
|
||||
Template.parse(template, line_numbers: true, error_mode: error_mode&.to_sym).render
|
||||
@@ -81,10 +61,6 @@ module Minitest
|
||||
assert_match_syntax_error("", template, error_mode: error_mode)
|
||||
end
|
||||
|
||||
def assert_no_migration(source)
|
||||
assert_equal(source, Liquid::Template.migrate(source))
|
||||
end
|
||||
|
||||
def assert_usage_increment(name, times: 1)
|
||||
old_method = Liquid::Usage.method(:increment)
|
||||
calls = 0
|
||||
@@ -150,16 +126,23 @@ class ThingWithToLiquid
|
||||
end
|
||||
end
|
||||
|
||||
class SettingsDrop < Liquid::Drop
|
||||
def initialize(settings)
|
||||
super()
|
||||
@settings = settings
|
||||
end
|
||||
|
||||
def liquid_method_missing(key)
|
||||
@settings[key]
|
||||
end
|
||||
end
|
||||
|
||||
class IntegerDrop < Liquid::Drop
|
||||
def initialize(value)
|
||||
super()
|
||||
@value = value.to_i
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
@value == other
|
||||
end
|
||||
|
||||
def to_s
|
||||
@value.to_s
|
||||
end
|
||||
@@ -175,10 +158,6 @@ class BooleanDrop < Liquid::Drop
|
||||
@value = value
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
@value == other
|
||||
end
|
||||
|
||||
def to_liquid_value
|
||||
@value
|
||||
end
|
||||
@@ -231,8 +210,10 @@ class StubTemplateFactory
|
||||
@count = 0
|
||||
end
|
||||
|
||||
def for(_template_name)
|
||||
def for(template_name)
|
||||
@count += 1
|
||||
Liquid::Template.new
|
||||
template = Liquid::Template.new
|
||||
template.name = "some/path/" + template_name
|
||||
template
|
||||
end
|
||||
end
|
||||
|
||||
@@ -35,8 +35,10 @@ class BlockUnitTest < Minitest::Test
|
||||
def test_variable_many_embedded_fragments
|
||||
template = Liquid::Template.parse(" {{funk}} {{so}} {{brother}} ")
|
||||
assert_equal(7, template.root.nodelist.size)
|
||||
assert_equal([String, Variable, String, Variable, String, Variable, String],
|
||||
block_types(template.root.nodelist))
|
||||
assert_equal(
|
||||
[String, Variable, String, Variable, String, Variable, String],
|
||||
block_types(template.root.nodelist),
|
||||
)
|
||||
end
|
||||
|
||||
def test_with_block
|
||||
|
||||
@@ -164,13 +164,17 @@ class ConditionUnitTest < Minitest::Test
|
||||
private
|
||||
|
||||
def assert_evaluates_true(left, op, right)
|
||||
assert(Condition.new(left, op, right).evaluate(@context),
|
||||
"Evaluated false: #{left} #{op} #{right}")
|
||||
assert(
|
||||
Condition.new(left, op, right).evaluate(@context),
|
||||
"Evaluated false: #{left} #{op} #{right}",
|
||||
)
|
||||
end
|
||||
|
||||
def assert_evaluates_false(left, op, right)
|
||||
assert(!Condition.new(left, op, right).evaluate(@context),
|
||||
"Evaluated true: #{left} #{op} #{right}")
|
||||
assert(
|
||||
!Condition.new(left, op, right).evaluate(@context),
|
||||
"Evaluated true: #{left} #{op} #{right}",
|
||||
)
|
||||
end
|
||||
|
||||
def assert_evaluates_argument_error(left, op, right)
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class MigrateUnitTest < Minitest::Test
|
||||
def test_migrate_preserves_valid_markup
|
||||
[
|
||||
"{{a}}",
|
||||
" {{- \ta\n -}} ",
|
||||
"{{a.b['c'].d[5]|default:6,allow_false:true|truncate:7,'..'}}",
|
||||
"{{ a . b [ 'c' ] . d [ 5 ] | default : 6 , allow_false : true | truncate : 4 , '..' }}",
|
||||
"{%assign x=a.b['c'].d[5]|default:6,allow_false:true|truncate:7,'..'%}",
|
||||
"{% assign x =\na . b [ 'c' ] . d [ 5 ] | default : 6 , allow_false : true | truncate : 4 , '..' %}",
|
||||
"{% if a and b > c %}A{% elsif d or f contains g %}B{% else %}C{% endif %}",
|
||||
<<~LIQUID,
|
||||
{% liquid
|
||||
if x > 0
|
||||
assign x = x | plus: 1
|
||||
|
||||
endif
|
||||
%}
|
||||
LIQUID
|
||||
].each do |source|
|
||||
assert_no_migration(source)
|
||||
end
|
||||
end
|
||||
|
||||
def test_migrate_variable
|
||||
with_error_mode(:lax) do
|
||||
assert_migration({
|
||||
%({{ ,|"' }}) => "{{ }}", # no MarkupWithQuotedFragment match, skipping characters
|
||||
%({{ ,|"' 123 }}) => "{{ 123 }}", # MarkupWithQuotedFragment skipped characters
|
||||
"{{,-2}}" => "{{ -2}}", # preserve separators when removing ignored characters
|
||||
"{{ 12 34 }}" => "{{ 12 }}", # no FilterMarkupRegex match, skipping characters
|
||||
"{{ -12 34 | abs }}" => "{{ -12 | abs }}", # FilterMarkupRegex skipped characters
|
||||
%({{ -12 | '" abs }}) => "{{ -12 | abs }}", # FilterParser skipped characters
|
||||
"{{ -1 | abs ' plus: 1 }}" => "{{ -1 | abs | plus: 1 }}", # FilterParser unexpected separator
|
||||
"{{ -1 | ! abs }}" => "{{ -1 | abs }}", # ignored non-word characters preceding filter name
|
||||
"{{ 'a' | append WAT: 'b' }}" => "{{ 'a' | append : 'b' }}", # FilterArgsRegex skipped characters
|
||||
"{{ '!' | replace, '!': '?' }}" => "{{ '!' | replace: '!', '?' }}", # FilterArgsRegex unexpected separators
|
||||
"{{ -a.1b }}" => "{{ ['-a']['1b'] }}", # quote separators when removing ignored characters
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def test_migrate_expression
|
||||
with_error_mode(:lax) do
|
||||
assert_migration({
|
||||
"{{ (1.9...2.8) }}" => "{{ (1..2) }}", # apply constant range coercion
|
||||
"{{ 1.2.3.4 }}" => "{{ 1.2 }}", # multiple periods allowed by FLOATS_REGEX, truncated by to_f
|
||||
"{{ 1. }}" => "{{ 1.0 }}", # FLOATS_REGEX didn't require digits after the period
|
||||
"{{ .empty }}" => "{{ ['empty'] }}", # skipped character prevents exact literal lookup
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def test_migrate_variable_lookup
|
||||
with_error_mode(:lax) do
|
||||
assert_migration({
|
||||
"{{@a[b].c@}}" => "{{ a[b].c }}", # VariableParser skipped characters
|
||||
"{{ a!b$c }}" => "{{ a.b.c }}", # VariableParser unexpected separators
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def test_migrate_assign
|
||||
with_error_mode(:lax) do
|
||||
assert_migration({
|
||||
"{% assign!a = b!%}" => "{% assign a = b %}", # Syntax skipped characters
|
||||
"{% assign a = @b ! %}" => "{% assign a = b %}", # Variable skipped characters
|
||||
"{% assign|x=1 %}" => "{% assign x=1 %}", # ensure tag name separated from markup
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def test_lax_migrate_if
|
||||
with_error_mode(:lax) do
|
||||
assert_migration({
|
||||
"{% if@a@%}Y{% endif %}" => "{% if a %}Y{% endif %}", # Syntax skipped character
|
||||
"{% if &a contains^b and *c %}A{% elsif %d or$e %}B{% endif %}" =>
|
||||
"{% if a contains b and c %}A{% elsif d or e %}B{% endif %}", # test more expressions
|
||||
"{% if b 1 %}Y{% endif %}" => "{% if b %}Y{% endif %}", # missing operator with right operand
|
||||
"{% if c == %}Y{% endif %}" => "{% if c == nil %}Y{% endif %}", # operator with missing right operand
|
||||
"{% if!a!%}T{% endif %}" => "{% if a %}T{% endif %}", # VariableParser skipped characters
|
||||
"{% if!%}T{% endif %}" => "{% if nil %}T{% endif %}", # VariableParser skipping all characters
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
def test_migrate_liquid_tag
|
||||
with_error_mode(:lax) do
|
||||
source = <<~LIQUID
|
||||
{% liquid
|
||||
assign ! a = 1
|
||||
assign a = @b !
|
||||
%}
|
||||
LIQUID
|
||||
expect = <<~LIQUID
|
||||
{% liquid
|
||||
assign a = 1
|
||||
assign a = b
|
||||
%}
|
||||
LIQUID
|
||||
assert_migration({ source => expect })
|
||||
end
|
||||
end
|
||||
|
||||
def test_migrate_for
|
||||
with_error_mode(:lax) do
|
||||
assert_migration({
|
||||
# VariableLookup ignored character
|
||||
"{% for i in !array %}x{% endfor %}" => "{% for i in array %}x{% endfor %}",
|
||||
|
||||
# TagAttributes scanned over separators
|
||||
"{% for i in array|offset: 1|limit: 5 %}x{% endfor %}" => "{% for i in array, offset: 1, limit: 5 %}x{% endfor %}",
|
||||
|
||||
# TagAttributes scans all the markup, which can overlap with what Syntax already matched
|
||||
"{% for i in foo.offset: wat %}x{% endfor %}" => "{% for i in foo.offset , offset: wat %}x{% endfor %}",
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assert_migration(source_to_expected_output_hash)
|
||||
source_to_expected_output_hash.each do |source, expect|
|
||||
message = "source: #{source.inspect}"
|
||||
assert_equal(expect, Liquid::Template.migrate(source), message)
|
||||
assert_no_migration(expect)
|
||||
assert_equal(Liquid::Template.parse(expect, parse_mode: :strict).render!, Liquid::Template.parse(source).render!, message)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -8,245 +8,245 @@ class ParseTreeVisitorTest < Minitest::Test
|
||||
def test_variable
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({{ test }}))
|
||||
visit(%({{ test }})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_varible_with_filter
|
||||
assert_equal(
|
||||
["test", "infilter"],
|
||||
visit(%({{ test | split: infilter }}))
|
||||
visit(%({{ test | split: infilter }})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_dynamic_variable
|
||||
assert_equal(
|
||||
["test", "inlookup"],
|
||||
visit(%({{ test[inlookup] }}))
|
||||
visit(%({{ test[inlookup] }})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_echo
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% echo test %}))
|
||||
visit(%({% echo test %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_if_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% if test %}{% endif %}))
|
||||
visit(%({% if test %}{% endif %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_complex_if_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% if 1 == 1 and 2 == test %}{% endif %}))
|
||||
visit(%({% if 1 == 1 and 2 == test %}{% endif %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_if_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% if 1 == 1 %}{{ test }}{% endif %}))
|
||||
visit(%({% if 1 == 1 %}{{ test }}{% endif %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_unless_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% unless test %}{% endunless %}))
|
||||
visit(%({% unless test %}{% endunless %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_complex_unless_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% unless 1 == 1 and 2 == test %}{% endunless %}))
|
||||
visit(%({% unless 1 == 1 and 2 == test %}{% endunless %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_unless_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% unless 1 == 1 %}{{ test }}{% endunless %}))
|
||||
visit(%({% unless 1 == 1 %}{{ test }}{% endunless %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_elsif_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% if 1 == 1 %}{% elsif test %}{% endif %}))
|
||||
visit(%({% if 1 == 1 %}{% elsif test %}{% endif %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_complex_elsif_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% if 1 == 1 %}{% elsif 1 == 1 and 2 == test %}{% endif %}))
|
||||
visit(%({% if 1 == 1 %}{% elsif 1 == 1 and 2 == test %}{% endif %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_elsif_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% if 1 == 1 %}{% elsif 2 == 2 %}{{ test }}{% endif %}))
|
||||
visit(%({% if 1 == 1 %}{% elsif 2 == 2 %}{{ test }}{% endif %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_else_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% if 1 == 1 %}{% else %}{{ test }}{% endif %}))
|
||||
visit(%({% if 1 == 1 %}{% else %}{{ test }}{% endif %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_case_left
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% case test %}{% endcase %}))
|
||||
visit(%({% case test %}{% endcase %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_case_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% case 1 %}{% when test %}{% endcase %}))
|
||||
visit(%({% case 1 %}{% when test %}{% endcase %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_case_when_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% case 1 %}{% when 2 %}{{ test }}{% endcase %}))
|
||||
visit(%({% case 1 %}{% when 2 %}{{ test }}{% endcase %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_case_else_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% case 1 %}{% else %}{{ test }}{% endcase %}))
|
||||
visit(%({% case 1 %}{% else %}{{ test }}{% endcase %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_for_in
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% for x in test %}{% endfor %}))
|
||||
visit(%({% for x in test %}{% endfor %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_for_limit
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% for x in (1..5) limit: test %}{% endfor %}))
|
||||
visit(%({% for x in (1..5) limit: test %}{% endfor %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_for_offset
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% for x in (1..5) offset: test %}{% endfor %}))
|
||||
visit(%({% for x in (1..5) offset: test %}{% endfor %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_for_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% for x in (1..5) %}{{ test }}{% endfor %}))
|
||||
visit(%({% for x in (1..5) %}{{ test }}{% endfor %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_for_range
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% for x in (1..test) %}{% endfor %}))
|
||||
visit(%({% for x in (1..test) %}{% endfor %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_tablerow_in
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% tablerow x in test %}{% endtablerow %}))
|
||||
visit(%({% tablerow x in test %}{% endtablerow %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_tablerow_limit
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% tablerow x in (1..5) limit: test %}{% endtablerow %}))
|
||||
visit(%({% tablerow x in (1..5) limit: test %}{% endtablerow %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_tablerow_offset
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% tablerow x in (1..5) offset: test %}{% endtablerow %}))
|
||||
visit(%({% tablerow x in (1..5) offset: test %}{% endtablerow %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_tablerow_body
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% tablerow x in (1..5) %}{{ test }}{% endtablerow %}))
|
||||
visit(%({% tablerow x in (1..5) %}{{ test }}{% endtablerow %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_cycle
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% cycle test %}))
|
||||
visit(%({% cycle test %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_assign
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% assign x = test %}))
|
||||
visit(%({% assign x = test %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_capture
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% capture x %}{{ test }}{% endcapture %}))
|
||||
visit(%({% capture x %}{{ test }}{% endcapture %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_include
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% include test %}))
|
||||
visit(%({% include test %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_include_with
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% include "hai" with test %}))
|
||||
visit(%({% include "hai" with test %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_include_for
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% include "hai" for test %}))
|
||||
visit(%({% include "hai" for test %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_with
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% render "hai" with test %}))
|
||||
visit(%({% render "hai" with test %})),
|
||||
)
|
||||
end
|
||||
|
||||
def test_render_for
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% render "hai" for test %}))
|
||||
visit(%({% render "hai" for test %})),
|
||||
)
|
||||
end
|
||||
|
||||
@@ -257,7 +257,7 @@ class ParseTreeVisitorTest < Minitest::Test
|
||||
["test", []],
|
||||
["xs", []],
|
||||
]]],
|
||||
traversal(%({% for x in xs offset: test %}{{ other }}{% endfor %})).visit
|
||||
traversal(%({% for x in xs offset: test %}{{ other }}{% endfor %})).visit,
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@ class PartialCacheUnitTest < Minitest::Test
|
||||
context = Liquid::Context.build(
|
||||
registers: {
|
||||
file_system: StubFileSystem.new('my_partial' => 'my partial body'),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
partial = Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context,
|
||||
parse_context: Liquid::ParseContext.new
|
||||
parse_context: Liquid::ParseContext.new,
|
||||
)
|
||||
|
||||
assert_equal('my partial body', partial.render)
|
||||
@@ -22,14 +22,14 @@ class PartialCacheUnitTest < Minitest::Test
|
||||
def test_reads_from_the_file_system_only_once_per_file
|
||||
file_system = StubFileSystem.new('my_partial' => 'some partial body')
|
||||
context = Liquid::Context.build(
|
||||
registers: { file_system: file_system }
|
||||
registers: { file_system: file_system },
|
||||
)
|
||||
|
||||
2.times do
|
||||
Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context,
|
||||
parse_context: Liquid::ParseContext.new
|
||||
parse_context: Liquid::ParseContext.new,
|
||||
)
|
||||
end
|
||||
|
||||
@@ -39,31 +39,31 @@ class PartialCacheUnitTest < Minitest::Test
|
||||
def test_cache_state_is_stored_per_context
|
||||
parse_context = Liquid::ParseContext.new
|
||||
shared_file_system = StubFileSystem.new(
|
||||
'my_partial' => 'my shared value'
|
||||
'my_partial' => 'my shared value',
|
||||
)
|
||||
context_one = Liquid::Context.build(
|
||||
registers: {
|
||||
file_system: shared_file_system,
|
||||
}
|
||||
},
|
||||
)
|
||||
context_two = Liquid::Context.build(
|
||||
registers: {
|
||||
file_system: shared_file_system,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
2.times do
|
||||
Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context_one,
|
||||
parse_context: parse_context
|
||||
parse_context: parse_context,
|
||||
)
|
||||
end
|
||||
|
||||
Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context_two,
|
||||
parse_context: parse_context
|
||||
parse_context: parse_context,
|
||||
)
|
||||
|
||||
assert_equal(2, shared_file_system.file_read_count)
|
||||
@@ -72,18 +72,18 @@ class PartialCacheUnitTest < Minitest::Test
|
||||
def test_cache_is_not_broken_when_a_different_parse_context_is_used
|
||||
file_system = StubFileSystem.new('my_partial' => 'some partial body')
|
||||
context = Liquid::Context.build(
|
||||
registers: { file_system: file_system }
|
||||
registers: { file_system: file_system },
|
||||
)
|
||||
|
||||
Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context,
|
||||
parse_context: Liquid::ParseContext.new(my_key: 'value one')
|
||||
parse_context: Liquid::ParseContext.new(my_key: 'value one'),
|
||||
)
|
||||
Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context,
|
||||
parse_context: Liquid::ParseContext.new(my_key: 'value two')
|
||||
parse_context: Liquid::ParseContext.new(my_key: 'value two'),
|
||||
)
|
||||
|
||||
# Technically what we care about is that the file was parsed twice,
|
||||
@@ -95,13 +95,13 @@ class PartialCacheUnitTest < Minitest::Test
|
||||
context = Liquid::Context.build(
|
||||
registers: {
|
||||
file_system: StubFileSystem.new('my_partial' => 'my partial body'),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
partial = Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context,
|
||||
parse_context: Liquid::ParseContext.new
|
||||
parse_context: Liquid::ParseContext.new,
|
||||
)
|
||||
|
||||
assert_equal('my partial body', partial.render)
|
||||
@@ -113,13 +113,13 @@ class PartialCacheUnitTest < Minitest::Test
|
||||
registers: {
|
||||
file_system: StubFileSystem.new('my_partial' => 'my partial body'),
|
||||
template_factory: template_factory,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
partial = Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context,
|
||||
parse_context: Liquid::ParseContext.new
|
||||
parse_context: Liquid::ParseContext.new,
|
||||
)
|
||||
|
||||
assert_equal('my partial body', partial.render)
|
||||
@@ -129,12 +129,12 @@ class PartialCacheUnitTest < Minitest::Test
|
||||
def test_cache_state_is_shared_for_subcontexts
|
||||
parse_context = Liquid::ParseContext.new
|
||||
shared_file_system = StubFileSystem.new(
|
||||
'my_partial' => 'my shared value'
|
||||
'my_partial' => 'my shared value',
|
||||
)
|
||||
context = Liquid::Context.build(
|
||||
registers: Liquid::Registers.new(
|
||||
file_system: shared_file_system,
|
||||
)
|
||||
),
|
||||
)
|
||||
subcontext = context.new_isolated_subcontext
|
||||
|
||||
@@ -144,16 +144,34 @@ class PartialCacheUnitTest < Minitest::Test
|
||||
Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context,
|
||||
parse_context: parse_context
|
||||
parse_context: parse_context,
|
||||
)
|
||||
|
||||
Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: subcontext,
|
||||
parse_context: parse_context
|
||||
parse_context: parse_context,
|
||||
)
|
||||
end
|
||||
|
||||
assert_equal(1, shared_file_system.file_read_count)
|
||||
end
|
||||
|
||||
def test_uses_template_name_from_template_factory
|
||||
template_factory = StubTemplateFactory.new
|
||||
context = Liquid::Context.build(
|
||||
registers: {
|
||||
file_system: StubFileSystem.new('my_partial' => 'my partial body'),
|
||||
template_factory: template_factory,
|
||||
},
|
||||
)
|
||||
|
||||
partial = Liquid::PartialCache.load(
|
||||
'my_partial',
|
||||
context: context,
|
||||
parse_context: Liquid::ParseContext.new,
|
||||
)
|
||||
|
||||
assert_equal('some/path/my_partial', partial.name)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
require 'timeout'
|
||||
|
||||
class RegexpUnitTest < Minitest::Test
|
||||
include Liquid
|
||||
@@ -37,10 +38,22 @@ class RegexpUnitTest < Minitest::Test
|
||||
|
||||
def test_variable_parser
|
||||
assert_equal(['var'], 'var'.scan(VariableParser))
|
||||
assert_equal(['[var]'], '[var]'.scan(VariableParser))
|
||||
assert_equal(['var', 'method'], 'var.method'.scan(VariableParser))
|
||||
assert_equal(['var', '[method]'], 'var[method]'.scan(VariableParser))
|
||||
assert_equal(['var', '[method]', '[0]'], 'var[method][0]'.scan(VariableParser))
|
||||
assert_equal(['var', '["method"]', '[0]'], 'var["method"][0]'.scan(VariableParser))
|
||||
assert_equal(['var', '[method]', '[0]', 'method'], 'var[method][0].method'.scan(VariableParser))
|
||||
end
|
||||
|
||||
def test_variable_parser_with_large_input
|
||||
Timeout.timeout(1) { assert_equal(['[var]'], '[var]'.scan(VariableParser)) }
|
||||
|
||||
very_long_string = "foo" * 1000
|
||||
|
||||
# valid dynamic lookup
|
||||
Timeout.timeout(1) { assert_equal(["[#{very_long_string}]"], "[#{very_long_string}]".scan(VariableParser)) }
|
||||
# invalid dynamic lookup with missing closing bracket
|
||||
Timeout.timeout(1) { assert_equal([very_long_string], "[#{very_long_string}".scan(VariableParser)) }
|
||||
end
|
||||
end # RegexpTest
|
||||
|
||||
@@ -50,7 +50,7 @@ class StrainerFactoryUnitTest < Minitest::Test
|
||||
|
||||
assert_match(
|
||||
/\ALiquid error: wrong number of arguments \((1 for 0|given 1, expected 0)\)\z/,
|
||||
exception.message
|
||||
exception.message,
|
||||
)
|
||||
source = AccessScopeFilters.instance_method(:public_filter).source_location
|
||||
assert_equal(source.map(&:to_s), exception.backtrace[0].split(':')[0..1])
|
||||
|
||||
Reference in New Issue
Block a user