mirror of
https://github.com/Shopify/liquid.git
synced 2026-10-02 00:25:12 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f78cc1ce8e |
@@ -1,36 +1,39 @@
|
|||||||
name: Liquid
|
name: Liquid
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
env:
|
|
||||||
BUNDLE_JOBS: 4
|
|
||||||
BUNDLE_RETRY: 3
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
entry:
|
entry:
|
||||||
- { ruby: 3.0, allowed-failure: false } # minimum supported
|
- { ruby: 2.7, allowed-failure: false } # minimum supported
|
||||||
- { ruby: 3.2, allowed-failure: false }
|
- { ruby: 3.1, allowed-failure: false } # latest
|
||||||
- { ruby: 3.3, allowed-failure: false } # latest
|
- { ruby: ruby-head, allowed-failure: true }
|
||||||
- { ruby: ruby-head, allowed-failure: false }
|
name: test (${{ matrix.entry.ruby }})
|
||||||
name: Test Ruby ${{ matrix.entry.ruby }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ruby/setup-ruby@v1
|
- uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.entry.ruby }}
|
ruby-version: ${{ matrix.entry.ruby }}
|
||||||
bundler-cache: true
|
- uses: actions/cache@v1
|
||||||
bundler: latest
|
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
|
||||||
- run: bundle exec rake
|
- run: bundle exec rake
|
||||||
continue-on-error: ${{ matrix.entry.allowed-failure }}
|
continue-on-error: ${{ matrix.entry.allowed-failure }}
|
||||||
|
|
||||||
memory_profile:
|
memory_profile:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: ruby/setup-ruby@v1
|
- uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
bundler-cache: true
|
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
|
||||||
- run: bundle exec rake memory_profile:run
|
- run: bundle exec rake memory_profile:run
|
||||||
|
|||||||
+2
-1
@@ -4,6 +4,7 @@
|
|||||||
pkg
|
pkg
|
||||||
*.rbc
|
*.rbc
|
||||||
.rvmrc
|
.rvmrc
|
||||||
|
.ruby-version
|
||||||
|
Gemfile.lock
|
||||||
.bundle
|
.bundle
|
||||||
.byebug_history
|
.byebug_history
|
||||||
Gemfile.lock
|
|
||||||
|
|||||||
+4
-8
@@ -10,8 +10,8 @@ Performance:
|
|||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
|
TargetRubyVersion: 2.7
|
||||||
NewCops: disable
|
NewCops: disable
|
||||||
SuggestExtensions: false
|
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'vendor/bundle/**/*'
|
- 'vendor/bundle/**/*'
|
||||||
|
|
||||||
@@ -19,10 +19,6 @@ Naming/MethodName:
|
|||||||
Exclude:
|
Exclude:
|
||||||
- 'example/server/liquid_servlet.rb'
|
- 'example/server/liquid_servlet.rb'
|
||||||
|
|
||||||
Style/ClassMethodsDefinitions:
|
# Backport https://github.com/Shopify/ruby-style-guide/pull/258
|
||||||
Enabled: false
|
Layout/BeginEndAlignment:
|
||||||
|
Enabled: true
|
||||||
# liquid filter calls were being mistaken to be calls on arrays
|
|
||||||
Style/ConcatArrayLiterals:
|
|
||||||
Exclude:
|
|
||||||
- 'test/integration/standard_filter_test.rb'
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
3.3.4
|
|
||||||
@@ -7,8 +7,6 @@ end
|
|||||||
|
|
||||||
gemspec
|
gemspec
|
||||||
|
|
||||||
gem "base64"
|
|
||||||
|
|
||||||
group :benchmark, :test do
|
group :benchmark, :test do
|
||||||
gem 'benchmark-ips'
|
gem 'benchmark-ips'
|
||||||
gem 'memory_profiler'
|
gem 'memory_profiler'
|
||||||
@@ -20,11 +18,10 @@ group :benchmark, :test do
|
|||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'rubocop', '~> 1.61.0'
|
gem 'rubocop-shopify', '~> 2.7.0', require: false
|
||||||
gem 'rubocop-shopify', '~> 2.12.0', require: false
|
|
||||||
gem 'rubocop-performance', require: false
|
gem 'rubocop-performance', require: false
|
||||||
|
|
||||||
platform :mri, :truffleruby do
|
platform :mri, :truffleruby do
|
||||||
gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'main'
|
gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'master'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
# Liquid Change Log
|
# Liquid Change Log
|
||||||
|
|
||||||
## 5.5.0 2024-03-21
|
|
||||||
|
|
||||||
Please reference the GitHub release for more information.
|
|
||||||
|
|
||||||
## 5.4.0 2022-07-29
|
## 5.4.0 2022-07-29
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
[](https://github.com/Shopify/liquid/actions/workflows/liquid.yml)
|
[](http://travis-ci.org/Shopify/liquid)
|
||||||
[](http://inch-ci.org/github/Shopify/liquid)
|
[](http://inch-ci.org/github/Shopify/liquid)
|
||||||
|
|
||||||
# Liquid template engine
|
# Liquid template engine
|
||||||
|
|
||||||
* [Contributing guidelines](CONTRIBUTING.md)
|
* [Contributing guidelines](CONTRIBUTING.md)
|
||||||
* [Version history](History.md)
|
* [Version history](History.md)
|
||||||
* [Liquid documentation from Shopify](https://shopify.dev/docs/api/liquid)
|
* [Liquid documentation from Shopify](https://shopify.dev/api/liquid)
|
||||||
* [Liquid Wiki at GitHub](https://github.com/Shopify/liquid/wiki)
|
* [Liquid Wiki at GitHub](https://github.com/Shopify/liquid/wiki)
|
||||||
* [Website](http://liquidmarkup.org/)
|
* [Website](http://liquidmarkup.org/)
|
||||||
|
|
||||||
@@ -52,47 +52,6 @@ For standard use you can just pass it the content of a file and call render with
|
|||||||
@template.render('name' => 'tobi') # => "hi tobi"
|
@template.render('name' => 'tobi') # => "hi tobi"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Concept of Environments
|
|
||||||
|
|
||||||
In Liquid, a "Environment" is a scoped environment that encapsulates custom tags, filters, and other configurations. This allows you to define and isolate different sets of functionality for different contexts, avoiding global overrides that can lead to conflicts and unexpected behavior.
|
|
||||||
|
|
||||||
By using environments, you can:
|
|
||||||
|
|
||||||
1. **Encapsulate Logic**: Keep the logic for different parts of your application separate.
|
|
||||||
2. **Avoid Conflicts**: Prevent custom tags and filters from clashing with each other.
|
|
||||||
3. **Improve Maintainability**: Make it easier to manage and understand the scope of customizations.
|
|
||||||
4. **Enhance Security**: Limit the availability of certain tags and filters to specific contexts.
|
|
||||||
|
|
||||||
We encourage the use of Environments over globally overriding things because it promotes better software design principles such as modularity, encapsulation, and separation of concerns.
|
|
||||||
|
|
||||||
Here's an example of how you can define and use Environments in Liquid:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
user_environment = Liquid::Environment.build do |environment|
|
|
||||||
environment.register_tag("renderobj", RenderObjTag)
|
|
||||||
end
|
|
||||||
|
|
||||||
Liquid::Template.parse(<<~LIQUID, environment: user_environment)
|
|
||||||
{% renderobj src: "path/to/model.obj" %}
|
|
||||||
LIQUID
|
|
||||||
```
|
|
||||||
|
|
||||||
In this example, `RenderObjTag` is a custom tag that is only available within the `user_environment`.
|
|
||||||
|
|
||||||
Similarly, you can define another environment for a different context, such as email templates:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
email_environment = Liquid::Environment.build do |environment|
|
|
||||||
environment.register_tag("unsubscribe_footer", UnsubscribeFooter)
|
|
||||||
end
|
|
||||||
|
|
||||||
Liquid::Template.parse(<<~LIQUID, environment: email_environment)
|
|
||||||
{% unsubscribe_footer %}
|
|
||||||
LIQUID
|
|
||||||
```
|
|
||||||
|
|
||||||
By using Environments, you ensure that custom tags and filters are only available in the contexts where they are needed, making your Liquid templates more robust and easier to manage.
|
|
||||||
|
|
||||||
### Error Modes
|
### Error Modes
|
||||||
|
|
||||||
Setting the error mode of Liquid lets you specify how strictly you want your templates to be interpreted.
|
Setting the error mode of Liquid lets you specify how strictly you want your templates to be interpreted.
|
||||||
@@ -103,10 +62,9 @@ Liquid also comes with a stricter parser that can be used when editing templates
|
|||||||
when templates are invalid. You can enable this new parser like this:
|
when templates are invalid. You can enable this new parser like this:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
Liquid::Environment.default.error_mode = :strict
|
Liquid::Template.error_mode = :strict # Raises a SyntaxError when invalid syntax is used
|
||||||
Liquid::Environment.default.error_mode = :strict # Raises a SyntaxError when invalid syntax is used
|
Liquid::Template.error_mode = :warn # Adds strict errors to template.errors but continues as normal
|
||||||
Liquid::Environment.default.error_mode = :warn # Adds strict errors to template.errors but continues as normal
|
Liquid::Template.error_mode = :lax # The default mode, accepts almost anything.
|
||||||
Liquid::Environment.default.error_mode = :lax # The default mode, accepts almost anything.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to set the error mode only on specific templates you can pass `:error_mode` as an option to `parse`:
|
If you want to set the error mode only on specific templates you can pass `:error_mode` as an option to `parse`:
|
||||||
@@ -153,4 +111,4 @@ template.render!({ 'x' => 1}, { strict_variables: true })
|
|||||||
|
|
||||||
To help track usages of a feature or code path in production, we have released opt-in usage tracking. To enable this, we provide an empty `Liquid:: Usage.increment` method which you can customize to your needs. The feature is well suited to https://github.com/Shopify/statsd-instrument. However, the choice of implementation is up to you.
|
To help track usages of a feature or code path in production, we have released opt-in usage tracking. To enable this, we provide an empty `Liquid:: Usage.increment` method which you can customize to your needs. The feature is well suited to https://github.com/Shopify/statsd-instrument. However, the choice of implementation is up to you.
|
||||||
|
|
||||||
Once you have enabled usage tracking, we recommend reporting any events through Github Issues that your system may be logging. It is highly likely this event has been added to consider deprecating or improving code specific to this event, so please raise any concerns.
|
Once you have enabled usage tracking, we recommend reporting any events through Github Issues that your system may be logging. It is highly likely this event has been added to consider deprecating or improving code specific to this event, so please raise any concerns.
|
||||||
@@ -81,14 +81,6 @@ namespace :benchmark do
|
|||||||
task :strict do
|
task :strict do
|
||||||
ruby "./performance/benchmark.rb strict"
|
ruby "./performance/benchmark.rb strict"
|
||||||
end
|
end
|
||||||
|
|
||||||
desc "Run unit benchmarks"
|
|
||||||
task :unit do
|
|
||||||
Dir["./performance/unit/*_benchmark.rb"].each do |file|
|
|
||||||
puts "🧪 Running #{file}"
|
|
||||||
ruby file
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
namespace :profile do
|
namespace :profile do
|
||||||
|
|||||||
@@ -30,18 +30,14 @@ class Servlet < LiquidServlet
|
|||||||
private
|
private
|
||||||
|
|
||||||
def products_list
|
def products_list
|
||||||
[
|
[{ 'name' => 'Arbor Draft', 'price' => 39900, 'description' => 'the *arbor draft* is a excellent product' },
|
||||||
{ '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 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 Diamond', 'price' => 59900, 'description' => 'the *arbor diamond* is a made up product because im obsessed with arbor and have no creativity' }
|
|
||||||
]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def more_products_list
|
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 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 Fish', 'price' => 40000, 'description' => 'the *arbor fish* is a compact pin that features an extended wheelbase and time-honored teardrop shape' }
|
|
||||||
]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def description
|
def description
|
||||||
|
|||||||
+11
-12
@@ -41,7 +41,7 @@ module Liquid
|
|||||||
AnyStartingTag = /#{TagStart}|#{VariableStart}/o
|
AnyStartingTag = /#{TagStart}|#{VariableStart}/o
|
||||||
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om
|
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om
|
||||||
TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
|
TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
|
||||||
VariableParser = /\[(?>[^\[\]]+|\g<0>)*\]|#{VariableSegment}+\??/o
|
VariableParser = /\[[^\]]+\]|#{VariableSegment}+\??/o
|
||||||
|
|
||||||
RAISE_EXCEPTION_LAMBDA = ->(_e) { raise }
|
RAISE_EXCEPTION_LAMBDA = ->(_e) { raise }
|
||||||
|
|
||||||
@@ -50,18 +50,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
require "liquid/version"
|
require "liquid/version"
|
||||||
require "liquid/deprecations"
|
|
||||||
require "liquid/const"
|
|
||||||
require "liquid/template/tag_registry"
|
|
||||||
require 'liquid/standardfilters'
|
|
||||||
require 'liquid/file_system'
|
|
||||||
require 'liquid/parser_switching'
|
|
||||||
require 'liquid/tag'
|
|
||||||
require 'liquid/block'
|
|
||||||
require 'liquid/parse_tree_visitor'
|
require 'liquid/parse_tree_visitor'
|
||||||
require 'liquid/interrupts'
|
|
||||||
require 'liquid/tags'
|
|
||||||
require "liquid/environment"
|
|
||||||
require 'liquid/lexer'
|
require 'liquid/lexer'
|
||||||
require 'liquid/parser'
|
require 'liquid/parser'
|
||||||
require 'liquid/i18n'
|
require 'liquid/i18n'
|
||||||
@@ -75,14 +64,20 @@ require 'liquid/strainer_template'
|
|||||||
require 'liquid/strainer_factory'
|
require 'liquid/strainer_factory'
|
||||||
require 'liquid/expression'
|
require 'liquid/expression'
|
||||||
require 'liquid/context'
|
require 'liquid/context'
|
||||||
|
require 'liquid/parser_switching'
|
||||||
require 'liquid/tag'
|
require 'liquid/tag'
|
||||||
|
require 'liquid/tag/disabler'
|
||||||
|
require 'liquid/tag/disableable'
|
||||||
|
require 'liquid/block'
|
||||||
require 'liquid/block_body'
|
require 'liquid/block_body'
|
||||||
require 'liquid/document'
|
require 'liquid/document'
|
||||||
require 'liquid/variable'
|
require 'liquid/variable'
|
||||||
require 'liquid/variable_lookup'
|
require 'liquid/variable_lookup'
|
||||||
require 'liquid/range_lookup'
|
require 'liquid/range_lookup'
|
||||||
|
require 'liquid/file_system'
|
||||||
require 'liquid/resource_limits'
|
require 'liquid/resource_limits'
|
||||||
require 'liquid/template'
|
require 'liquid/template'
|
||||||
|
require 'liquid/standardfilters'
|
||||||
require 'liquid/condition'
|
require 'liquid/condition'
|
||||||
require 'liquid/utils'
|
require 'liquid/utils'
|
||||||
require 'liquid/tokenizer'
|
require 'liquid/tokenizer'
|
||||||
@@ -91,3 +86,7 @@ require 'liquid/partial_cache'
|
|||||||
require 'liquid/usage'
|
require 'liquid/usage'
|
||||||
require 'liquid/registers'
|
require 'liquid/registers'
|
||||||
require 'liquid/template_factory'
|
require 'liquid/template_factory'
|
||||||
|
|
||||||
|
# Load all the tags of the standard library
|
||||||
|
#
|
||||||
|
Dir["#{__dir__}/liquid/tags/*.rb"].each { |f| require f }
|
||||||
|
|||||||
+4
-8
@@ -36,17 +36,13 @@ module Liquid
|
|||||||
# @api private
|
# @api private
|
||||||
def self.raise_unknown_tag(tag, block_name, block_delimiter, parse_context)
|
def self.raise_unknown_tag(tag, block_name, block_delimiter, parse_context)
|
||||||
if tag == 'else'
|
if tag == 'else'
|
||||||
raise SyntaxError, parse_context.locale.t(
|
raise SyntaxError, parse_context.locale.t("errors.syntax.unexpected_else",
|
||||||
"errors.syntax.unexpected_else",
|
block_name: block_name)
|
||||||
block_name: block_name,
|
|
||||||
)
|
|
||||||
elsif tag.start_with?('end')
|
elsif tag.start_with?('end')
|
||||||
raise SyntaxError, parse_context.locale.t(
|
raise SyntaxError, parse_context.locale.t("errors.syntax.invalid_delimiter",
|
||||||
"errors.syntax.invalid_delimiter",
|
|
||||||
tag: tag,
|
tag: tag,
|
||||||
block_name: block_name,
|
block_name: block_name,
|
||||||
block_delimiter: block_delimiter,
|
block_delimiter: block_delimiter)
|
||||||
)
|
|
||||||
else
|
else
|
||||||
raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag)
|
raise SyntaxError, parse_context.locale.t("errors.syntax.unknown_tag", tag: tag)
|
||||||
end
|
end
|
||||||
|
|||||||
+10
-28
@@ -6,7 +6,6 @@ module Liquid
|
|||||||
class BlockBody
|
class BlockBody
|
||||||
LiquidTagToken = /\A\s*(#{TagName})\s*(.*?)\z/o
|
LiquidTagToken = /\A\s*(#{TagName})\s*(.*?)\z/o
|
||||||
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(#{TagName})(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(#{TagName})(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
||||||
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}#{WhitespaceControl}?\s*(\w+)\s*(.*)?#{WhitespaceControl}?#{TagEnd}\z/om
|
|
||||||
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
|
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
|
||||||
WhitespaceOrNothing = /\A\s*\z/
|
WhitespaceOrNothing = /\A\s*\z/
|
||||||
TAGSTART = "{%"
|
TAGSTART = "{%"
|
||||||
@@ -46,13 +45,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
tag_name = Regexp.last_match(1)
|
tag_name = Regexp.last_match(1)
|
||||||
markup = Regexp.last_match(2)
|
markup = Regexp.last_match(2)
|
||||||
|
unless (tag = registered_tags[tag_name])
|
||||||
if tag_name == 'liquid'
|
|
||||||
parse_context.line_number -= 1
|
|
||||||
next parse_liquid_tag(markup, parse_context)
|
|
||||||
end
|
|
||||||
|
|
||||||
unless (tag = parse_context.environment.tag_for_name(tag_name))
|
|
||||||
# end parsing if we reach an unknown tag and let the caller decide
|
# end parsing if we reach an unknown tag and let the caller decide
|
||||||
# determine how to proceed
|
# determine how to proceed
|
||||||
return yield tag_name, markup
|
return yield tag_name, markup
|
||||||
@@ -116,22 +109,14 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private def handle_invalid_tag_token(token, parse_context)
|
private def parse_for_document(tokenizer, parse_context)
|
||||||
if token.end_with?('%}')
|
|
||||||
yield token, token
|
|
||||||
else
|
|
||||||
BlockBody.raise_missing_tag_terminator(token, parse_context)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private def parse_for_document(tokenizer, parse_context, &block)
|
|
||||||
while (token = tokenizer.shift)
|
while (token = tokenizer.shift)
|
||||||
next if token.empty?
|
next if token.empty?
|
||||||
case
|
case
|
||||||
when token.start_with?(TAGSTART)
|
when token.start_with?(TAGSTART)
|
||||||
whitespace_handler(token, parse_context)
|
whitespace_handler(token, parse_context)
|
||||||
unless token =~ FullToken
|
unless token =~ FullToken
|
||||||
return handle_invalid_tag_token(token, parse_context, &block)
|
BlockBody.raise_missing_tag_terminator(token, parse_context)
|
||||||
end
|
end
|
||||||
tag_name = Regexp.last_match(2)
|
tag_name = Regexp.last_match(2)
|
||||||
markup = Regexp.last_match(4)
|
markup = Regexp.last_match(4)
|
||||||
@@ -147,7 +132,7 @@ module Liquid
|
|||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
|
||||||
unless (tag = parse_context.environment.tag_for_name(tag_name))
|
unless (tag = registered_tags[tag_name])
|
||||||
# end parsing if we reach an unknown tag and let the caller decide
|
# end parsing if we reach an unknown tag and let the caller decide
|
||||||
# determine how to proceed
|
# determine how to proceed
|
||||||
return yield tag_name, markup
|
return yield tag_name, markup
|
||||||
@@ -246,17 +231,10 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def create_variable(token, parse_context)
|
def create_variable(token, parse_context)
|
||||||
if token.end_with?("}}")
|
if token =~ ContentOfVariable
|
||||||
i = 2
|
markup = Regexp.last_match(1)
|
||||||
i = 3 if token[i] == "-"
|
|
||||||
parse_end = token.length - 3
|
|
||||||
parse_end -= 1 if token[parse_end] == "-"
|
|
||||||
markup_end = parse_end - i + 1
|
|
||||||
markup = markup_end <= 0 ? "" : token.slice(i, markup_end)
|
|
||||||
|
|
||||||
return Variable.new(markup, parse_context)
|
return Variable.new(markup, parse_context)
|
||||||
end
|
end
|
||||||
|
|
||||||
BlockBody.raise_missing_variable_terminator(token, parse_context)
|
BlockBody.raise_missing_variable_terminator(token, parse_context)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -269,5 +247,9 @@ module Liquid
|
|||||||
def raise_missing_variable_terminator(token, parse_context)
|
def raise_missing_variable_terminator(token, parse_context)
|
||||||
BlockBody.raise_missing_variable_terminator(token, parse_context)
|
BlockBody.raise_missing_variable_terminator(token, parse_context)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def registered_tags
|
||||||
|
Template.tags
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -24,9 +24,6 @@ module Liquid
|
|||||||
else
|
else
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
rescue Encoding::CompatibilityError
|
|
||||||
# "✅".b.include?("✅") raises Encoding::CompatibilityError despite being materially equal
|
|
||||||
left.b.include?(right.b)
|
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,9 +69,9 @@ module Liquid
|
|||||||
|
|
||||||
case condition.child_relation
|
case condition.child_relation
|
||||||
when :or
|
when :or
|
||||||
break if Liquid::Utils.to_liquid_value(result)
|
break if result
|
||||||
when :and
|
when :and
|
||||||
break unless Liquid::Utils.to_liquid_value(result)
|
break unless result
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@@ -162,10 +159,8 @@ module Liquid
|
|||||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
||||||
def children
|
def children
|
||||||
[
|
[
|
||||||
@node.left,
|
@node.left, @node.right,
|
||||||
@node.right,
|
@node.child_condition, @node.attachment
|
||||||
@node.child_condition,
|
|
||||||
@node.attachment
|
|
||||||
].compact
|
].compact
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Liquid
|
|
||||||
module Const
|
|
||||||
EMPTY_HASH = {}.freeze
|
|
||||||
EMPTY_ARRAY = [].freeze
|
|
||||||
end
|
|
||||||
end
|
|
||||||
+11
-16
@@ -15,25 +15,24 @@ module Liquid
|
|||||||
# context['bob'] #=> nil class Context
|
# context['bob'] #=> nil class Context
|
||||||
class Context
|
class Context
|
||||||
attr_reader :scopes, :errors, :registers, :environments, :resource_limits, :static_registers, :static_environments
|
attr_reader :scopes, :errors, :registers, :environments, :resource_limits, :static_registers, :static_environments
|
||||||
attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters, :environment
|
attr_accessor :exception_renderer, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
|
||||||
|
|
||||||
# rubocop:disable Metrics/ParameterLists
|
# rubocop:disable Metrics/ParameterLists
|
||||||
def self.build(environment: Environment.default, environments: {}, outer_scope: {}, registers: {}, rethrow_errors: false, resource_limits: nil, static_environments: {}, &block)
|
def self.build(environments: {}, outer_scope: {}, registers: {}, rethrow_errors: false, resource_limits: nil, static_environments: {}, &block)
|
||||||
new(environments, outer_scope, registers, rethrow_errors, resource_limits, static_environments, &block)
|
new(environments, outer_scope, registers, rethrow_errors, resource_limits, static_environments, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_environments = {}, environment = Environment.default)
|
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil, static_environments = {})
|
||||||
@environment = environment
|
|
||||||
@environments = [environments]
|
@environments = [environments]
|
||||||
@environments.flatten!
|
@environments.flatten!
|
||||||
|
|
||||||
@static_environments = [static_environments].flatten(1).freeze
|
@static_environments = [static_environments].flatten(1).freeze
|
||||||
@scopes = [outer_scope || {}]
|
@scopes = [(outer_scope || {})]
|
||||||
@registers = registers.is_a?(Registers) ? registers : Registers.new(registers)
|
@registers = registers.is_a?(Registers) ? registers : Registers.new(registers)
|
||||||
@errors = []
|
@errors = []
|
||||||
@partial = false
|
@partial = false
|
||||||
@strict_variables = false
|
@strict_variables = false
|
||||||
@resource_limits = resource_limits || ResourceLimits.new(environment.default_resource_limits)
|
@resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits)
|
||||||
@base_scope_depth = 0
|
@base_scope_depth = 0
|
||||||
@interrupts = []
|
@interrupts = []
|
||||||
@filters = []
|
@filters = []
|
||||||
@@ -41,10 +40,10 @@ module Liquid
|
|||||||
@disabled_tags = {}
|
@disabled_tags = {}
|
||||||
|
|
||||||
@registers.static[:cached_partials] ||= {}
|
@registers.static[:cached_partials] ||= {}
|
||||||
@registers.static[:file_system] ||= environment.file_system
|
@registers.static[:file_system] ||= Liquid::Template.file_system
|
||||||
@registers.static[:template_factory] ||= Liquid::TemplateFactory.new
|
@registers.static[:template_factory] ||= Liquid::TemplateFactory.new
|
||||||
|
|
||||||
self.exception_renderer = environment.exception_renderer
|
self.exception_renderer = Template.default_exception_renderer
|
||||||
if rethrow_errors
|
if rethrow_errors
|
||||||
self.exception_renderer = Liquid::RAISE_EXCEPTION_LAMBDA
|
self.exception_renderer = Liquid::RAISE_EXCEPTION_LAMBDA
|
||||||
end
|
end
|
||||||
@@ -61,7 +60,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def strainer
|
def strainer
|
||||||
@strainer ||= @environment.create_strainer(self, @filters)
|
@strainer ||= StrainerFactory.create(self, @filters)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Adds filters to this context.
|
# Adds filters to this context.
|
||||||
@@ -145,7 +144,7 @@ module Liquid
|
|||||||
self.class.build(
|
self.class.build(
|
||||||
resource_limits: resource_limits,
|
resource_limits: resource_limits,
|
||||||
static_environments: static_environments,
|
static_environments: static_environments,
|
||||||
registers: Registers.new(registers),
|
registers: Registers.new(registers)
|
||||||
).tap do |subcontext|
|
).tap do |subcontext|
|
||||||
subcontext.base_scope_depth = base_scope_depth + 1
|
subcontext.base_scope_depth = base_scope_depth + 1
|
||||||
subcontext.exception_renderer = exception_renderer
|
subcontext.exception_renderer = exception_renderer
|
||||||
@@ -198,14 +197,10 @@ module Liquid
|
|||||||
try_variable_find_in_environments(key, raise_on_not_found: raise_on_not_found)
|
try_variable_find_in_environments(key, raise_on_not_found: raise_on_not_found)
|
||||||
end
|
end
|
||||||
|
|
||||||
# update variable's context before invoking #to_liquid
|
variable = variable.to_liquid
|
||||||
variable.context = self if variable.respond_to?(:context=)
|
variable.context = self if variable.respond_to?(:context=)
|
||||||
|
|
||||||
liquid_variable = variable.to_liquid
|
variable
|
||||||
|
|
||||||
liquid_variable.context = self if variable != liquid_variable && liquid_variable.respond_to?(:context=)
|
|
||||||
|
|
||||||
liquid_variable
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def lookup_and_evaluate(obj, key, raise_on_not_found: true)
|
def lookup_and_evaluate(obj, key, raise_on_not_found: true)
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require "set"
|
|
||||||
|
|
||||||
module Liquid
|
|
||||||
class Deprecations
|
|
||||||
class << self
|
|
||||||
attr_accessor :warned
|
|
||||||
|
|
||||||
Deprecations.warned = Set.new
|
|
||||||
|
|
||||||
def warn(name, alternative)
|
|
||||||
return if warned.include?(name)
|
|
||||||
|
|
||||||
warned << name
|
|
||||||
|
|
||||||
caller_location = caller_locations(2, 1).first
|
|
||||||
Warning.warn("[DEPRECATION] #{name} is deprecated. Use #{alternative} instead. Called from #{caller_location}\n")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -25,10 +25,6 @@ module Liquid
|
|||||||
class Drop
|
class Drop
|
||||||
attr_writer :context
|
attr_writer :context
|
||||||
|
|
||||||
def initialize
|
|
||||||
@context = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# Catch all for the method
|
# Catch all for the method
|
||||||
def liquid_method_missing(method)
|
def liquid_method_missing(method)
|
||||||
return nil unless @context&.strict_variables
|
return nil unless @context&.strict_variables
|
||||||
|
|||||||
@@ -1,159 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Liquid
|
|
||||||
# The Environment is the container for all configuration options of Liquid, such as
|
|
||||||
# the registered tags, filters, and the default error mode.
|
|
||||||
class Environment
|
|
||||||
# The default error mode for all templates. This can be overridden on a
|
|
||||||
# per-template basis.
|
|
||||||
attr_accessor :error_mode
|
|
||||||
|
|
||||||
# The tags that are available to use in the template.
|
|
||||||
attr_accessor :tags
|
|
||||||
|
|
||||||
# The strainer template which is used to store filters that are available to
|
|
||||||
# use in templates.
|
|
||||||
attr_accessor :strainer_template
|
|
||||||
|
|
||||||
# The exception renderer that is used to render exceptions that are raised
|
|
||||||
# when rendering a template
|
|
||||||
attr_accessor :exception_renderer
|
|
||||||
|
|
||||||
# The default file system that is used to load templates from.
|
|
||||||
attr_accessor :file_system
|
|
||||||
|
|
||||||
# The default resource limits that are used to limit the resources that a
|
|
||||||
# template can consume.
|
|
||||||
attr_accessor :default_resource_limits
|
|
||||||
|
|
||||||
class << self
|
|
||||||
# Creates a new environment instance.
|
|
||||||
#
|
|
||||||
# @param tags [Hash] The tags that are available to use in
|
|
||||||
# the template.
|
|
||||||
# @param file_system The default file system that is used
|
|
||||||
# to load templates from.
|
|
||||||
# @param error_mode [Symbol] The default error mode for all templates
|
|
||||||
# (either :strict, :warn, or :lax).
|
|
||||||
# @param exception_renderer [Proc] The exception renderer that is used to
|
|
||||||
# render exceptions.
|
|
||||||
# @yieldparam environment [Environment] The environment instance that is being built.
|
|
||||||
# @return [Environment] The new environment instance.
|
|
||||||
def build(tags: nil, file_system: nil, error_mode: nil, exception_renderer: nil)
|
|
||||||
ret = new
|
|
||||||
ret.tags = Template::TagRegistry.new(tags) if tags
|
|
||||||
ret.file_system = file_system if file_system
|
|
||||||
ret.error_mode = error_mode if error_mode
|
|
||||||
ret.exception_renderer = exception_renderer if exception_renderer
|
|
||||||
yield ret if block_given?
|
|
||||||
ret.freeze
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the default environment instance.
|
|
||||||
#
|
|
||||||
# @return [Environment] The default environment instance.
|
|
||||||
def default
|
|
||||||
@default ||= new
|
|
||||||
end
|
|
||||||
|
|
||||||
# Sets the default environment instance for the duration of the block
|
|
||||||
#
|
|
||||||
# @param environment [Environment] The environment instance to use as the default for the
|
|
||||||
# duration of the block.
|
|
||||||
# @yield
|
|
||||||
# @return [Object] The return value of the block.
|
|
||||||
def dangerously_override(environment)
|
|
||||||
original_default = @default
|
|
||||||
@default = environment
|
|
||||||
yield
|
|
||||||
ensure
|
|
||||||
@default = original_default
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Initializes a new environment instance.
|
|
||||||
# @api private
|
|
||||||
def initialize
|
|
||||||
@tags = Template::TagRegistry.new(Tags::STANDARD_TAGS)
|
|
||||||
@error_mode = :lax
|
|
||||||
@strainer_template = Class.new(StrainerTemplate).tap do |klass|
|
|
||||||
klass.add_filter(StandardFilters)
|
|
||||||
end
|
|
||||||
@exception_renderer = ->(exception) { exception }
|
|
||||||
@file_system = BlankFileSystem.new
|
|
||||||
@default_resource_limits = Const::EMPTY_HASH
|
|
||||||
@strainer_template_class_cache = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Registers a new tag with the environment.
|
|
||||||
#
|
|
||||||
# @param name [String] The name of the tag.
|
|
||||||
# @param klass [Liquid::Tag] The class that implements the tag.
|
|
||||||
# @return [void]
|
|
||||||
def register_tag(name, klass)
|
|
||||||
@tags[name] = klass
|
|
||||||
end
|
|
||||||
|
|
||||||
# Registers a new filter with the environment.
|
|
||||||
#
|
|
||||||
# @param filter [Module] The module that contains the filter methods.
|
|
||||||
# @return [void]
|
|
||||||
def register_filter(filter)
|
|
||||||
@strainer_template_class_cache.clear
|
|
||||||
@strainer_template.add_filter(filter)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Registers multiple filters with this environment.
|
|
||||||
#
|
|
||||||
# @param filters [Array<Module>] The modules that contain the filter methods.
|
|
||||||
# @return [self]
|
|
||||||
def register_filters(filters)
|
|
||||||
@strainer_template_class_cache.clear
|
|
||||||
filters.each { |f| @strainer_template.add_filter(f) }
|
|
||||||
self
|
|
||||||
end
|
|
||||||
|
|
||||||
# Creates a new strainer instance with the given filters, caching the result
|
|
||||||
# for faster lookup.
|
|
||||||
#
|
|
||||||
# @param context [Liquid::Context] The context that the strainer will be
|
|
||||||
# used in.
|
|
||||||
# @param filters [Array<Module>] The filters that the strainer will have
|
|
||||||
# access to.
|
|
||||||
# @return [Liquid::Strainer] The new strainer instance.
|
|
||||||
def create_strainer(context, filters = Const::EMPTY_ARRAY)
|
|
||||||
return @strainer_template.new(context) if filters.empty?
|
|
||||||
|
|
||||||
strainer_template = @strainer_template_class_cache[filters] ||= begin
|
|
||||||
klass = Class.new(@strainer_template)
|
|
||||||
filters.each { |f| klass.add_filter(f) }
|
|
||||||
klass
|
|
||||||
end
|
|
||||||
|
|
||||||
strainer_template.new(context)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the names of all the filter methods that are available to use in
|
|
||||||
# the strainer template.
|
|
||||||
#
|
|
||||||
# @return [Array<String>] The names of all the filter methods.
|
|
||||||
def filter_method_names
|
|
||||||
@strainer_template.filter_method_names
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the tag class for the given tag name.
|
|
||||||
#
|
|
||||||
# @param name [String] The name of the tag.
|
|
||||||
# @return [Liquid::Tag] The tag class.
|
|
||||||
def tag_for_name(name)
|
|
||||||
@tags[name]
|
|
||||||
end
|
|
||||||
|
|
||||||
def freeze
|
|
||||||
@tags.freeze
|
|
||||||
# TODO: freeze the tags, currently this is not possible because of liquid-c
|
|
||||||
# @strainer_template.freeze
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
+15
-16
@@ -40,20 +40,19 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
ArgumentError = Class.new(Error)
|
ArgumentError = Class.new(Error)
|
||||||
ContextError = Class.new(Error)
|
ContextError = Class.new(Error)
|
||||||
FileSystemError = Class.new(Error)
|
FileSystemError = Class.new(Error)
|
||||||
StandardError = Class.new(Error)
|
StandardError = Class.new(Error)
|
||||||
SyntaxError = Class.new(Error)
|
SyntaxError = Class.new(Error)
|
||||||
StackLevelError = Class.new(Error)
|
StackLevelError = Class.new(Error)
|
||||||
MemoryError = Class.new(Error)
|
MemoryError = Class.new(Error)
|
||||||
ZeroDivisionError = Class.new(Error)
|
ZeroDivisionError = Class.new(Error)
|
||||||
FloatDomainError = Class.new(Error)
|
FloatDomainError = Class.new(Error)
|
||||||
UndefinedVariable = Class.new(Error)
|
UndefinedVariable = Class.new(Error)
|
||||||
UndefinedDropMethod = Class.new(Error)
|
UndefinedDropMethod = Class.new(Error)
|
||||||
UndefinedFilter = Class.new(Error)
|
UndefinedFilter = Class.new(Error)
|
||||||
MethodOverrideError = Class.new(Error)
|
MethodOverrideError = Class.new(Error)
|
||||||
DisabledError = Class.new(Error)
|
DisabledError = Class.new(Error)
|
||||||
InternalError = Class.new(Error)
|
InternalError = Class.new(Error)
|
||||||
TemplateEncodingError = Class.new(Error)
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,10 +3,7 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class Expression
|
class Expression
|
||||||
LITERALS = {
|
LITERALS = {
|
||||||
nil => nil,
|
nil => nil, 'nil' => nil, 'null' => nil, '' => nil,
|
||||||
'nil' => nil,
|
|
||||||
'null' => nil,
|
|
||||||
'' => nil,
|
|
||||||
'true' => true,
|
'true' => true,
|
||||||
'false' => false,
|
'false' => false,
|
||||||
'blank' => '',
|
'blank' => '',
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ module Liquid
|
|||||||
# @liquid_type object
|
# @liquid_type object
|
||||||
# @liquid_name forloop
|
# @liquid_name forloop
|
||||||
# @liquid_summary
|
# @liquid_summary
|
||||||
# Information about a parent [`for` loop](/docs/api/liquid/tags/for).
|
# Information about a parent [`for` loop](/api/liquid/tags#for).
|
||||||
class ForloopDrop < Drop
|
class ForloopDrop < Drop
|
||||||
def initialize(name, length, parentloop)
|
def initialize(name, length, parentloop)
|
||||||
@name = name
|
@name = name
|
||||||
@@ -30,7 +30,10 @@ module Liquid
|
|||||||
# @liquid_return [forloop]
|
# @liquid_return [forloop]
|
||||||
attr_reader :parentloop
|
attr_reader :parentloop
|
||||||
|
|
||||||
attr_reader :name
|
def name
|
||||||
|
Usage.increment('forloop_drop_name')
|
||||||
|
@name
|
||||||
|
end
|
||||||
|
|
||||||
# @liquid_public_docs
|
# @liquid_public_docs
|
||||||
# @liquid_summary
|
# @liquid_summary
|
||||||
|
|||||||
+1
-155
@@ -1,9 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "strscan"
|
require "strscan"
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
class Lexer1
|
class Lexer
|
||||||
SPECIALS = {
|
SPECIALS = {
|
||||||
'|' => :pipe,
|
'|' => :pipe,
|
||||||
'.' => :dot,
|
'.' => :dot,
|
||||||
@@ -59,157 +58,4 @@ module Liquid
|
|||||||
@output << [:end_of_string]
|
@output << [:end_of_string]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Lexer2
|
|
||||||
CLOSE_ROUND = [:close_round, ")"].freeze
|
|
||||||
CLOSE_SQUARE = [:close_square, "]"].freeze
|
|
||||||
COLON = [:colon, ":"].freeze
|
|
||||||
COMMA = [:comma, ","].freeze
|
|
||||||
COMPARISION_NOT_EQUAL = [:comparison, "!="].freeze
|
|
||||||
COMPARISON_CONTAINS = [:comparison, "contains"].freeze
|
|
||||||
COMPARISON_EQUAL = [:comparison, "=="].freeze
|
|
||||||
COMPARISON_GREATER_THAN = [:comparison, ">"].freeze
|
|
||||||
COMPARISON_GREATER_THAN_OR_EQUAL = [:comparison, ">="].freeze
|
|
||||||
COMPARISON_LESS_THAN = [:comparison, "<"].freeze
|
|
||||||
COMPARISON_LESS_THAN_OR_EQUAL = [:comparison, "<="].freeze
|
|
||||||
COMPARISON_NOT_EQUAL_ALT = [:comparison, "<>"].freeze
|
|
||||||
CONTAINS = /contains(?=\s)/
|
|
||||||
DASH = [:dash, "-"].freeze
|
|
||||||
DOT = [:dot, "."].freeze
|
|
||||||
DOTDOT = [:dotdot, ".."].freeze
|
|
||||||
DOT_ORD = ".".ord
|
|
||||||
DOUBLE_STRING_LITERAL = /"[^\"]*"/
|
|
||||||
EOS = [:end_of_string].freeze
|
|
||||||
IDENTIFIER = /[a-zA-Z_][\w-]*\??/
|
|
||||||
NUMBER_LITERAL = /-?\d+(\.\d+)?/
|
|
||||||
OPEN_ROUND = [:open_round, "("].freeze
|
|
||||||
OPEN_SQUARE = [:open_square, "["].freeze
|
|
||||||
PIPE = [:pipe, "|"].freeze
|
|
||||||
QUESTION = [:question, "?"].freeze
|
|
||||||
RUBY_WHITESPACE = [" ", "\t", "\r", "\n", "\f"].freeze
|
|
||||||
SINGLE_STRING_LITERAL = /'[^\']*'/
|
|
||||||
WHITESPACE_OR_NOTHING = /\s*/
|
|
||||||
|
|
||||||
COMPARISON_JUMP_TABLE = [].tap do |table|
|
|
||||||
table["=".ord] = [].tap do |sub_table|
|
|
||||||
sub_table["=".ord] = COMPARISON_EQUAL
|
|
||||||
sub_table.freeze
|
|
||||||
end
|
|
||||||
table["!".ord] = [].tap do |sub_table|
|
|
||||||
sub_table["=".ord] = COMPARISION_NOT_EQUAL
|
|
||||||
sub_table.freeze
|
|
||||||
end
|
|
||||||
table["<".ord] = [].tap do |sub_table|
|
|
||||||
sub_table["=".ord] = COMPARISON_LESS_THAN_OR_EQUAL
|
|
||||||
sub_table[">".ord] = COMPARISON_NOT_EQUAL_ALT
|
|
||||||
RUBY_WHITESPACE.each { |c| sub_table[c.ord] = COMPARISON_LESS_THAN }
|
|
||||||
sub_table.freeze
|
|
||||||
end
|
|
||||||
table[">".ord] = [].tap do |sub_table|
|
|
||||||
sub_table["=".ord] = COMPARISON_GREATER_THAN_OR_EQUAL
|
|
||||||
RUBY_WHITESPACE.each { |c| sub_table[c.ord] = COMPARISON_GREATER_THAN }
|
|
||||||
sub_table.freeze
|
|
||||||
end
|
|
||||||
table.freeze
|
|
||||||
end
|
|
||||||
|
|
||||||
NEXT_MATCHER_JUMP_TABLE = [].tap do |table|
|
|
||||||
"a".upto("z") do |c|
|
|
||||||
table[c.ord] = [:id, IDENTIFIER].freeze
|
|
||||||
table[c.upcase.ord] = [:id, IDENTIFIER].freeze
|
|
||||||
end
|
|
||||||
table["_".ord] = [:id, IDENTIFIER].freeze
|
|
||||||
|
|
||||||
"0".upto("9") do |c|
|
|
||||||
table[c.ord] = [:number, NUMBER_LITERAL].freeze
|
|
||||||
end
|
|
||||||
table["-".ord] = [:number, NUMBER_LITERAL].freeze
|
|
||||||
|
|
||||||
table["'".ord] = [:string, SINGLE_STRING_LITERAL].freeze
|
|
||||||
table["\"".ord] = [:string, DOUBLE_STRING_LITERAL].freeze
|
|
||||||
table.freeze
|
|
||||||
end
|
|
||||||
|
|
||||||
SPECIAL_TABLE = [].tap do |table|
|
|
||||||
table["|".ord] = PIPE
|
|
||||||
table[".".ord] = DOT
|
|
||||||
table[":".ord] = COLON
|
|
||||||
table[",".ord] = COMMA
|
|
||||||
table["[".ord] = OPEN_SQUARE
|
|
||||||
table["]".ord] = CLOSE_SQUARE
|
|
||||||
table["(".ord] = OPEN_ROUND
|
|
||||||
table[")".ord] = CLOSE_ROUND
|
|
||||||
table["?".ord] = QUESTION
|
|
||||||
table["-".ord] = DASH
|
|
||||||
end
|
|
||||||
|
|
||||||
NUMBER_TABLE = [].tap do |table|
|
|
||||||
"0".upto("9") do |c|
|
|
||||||
table[c.ord] = true
|
|
||||||
end
|
|
||||||
table.freeze
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(input)
|
|
||||||
@ss = StringScanner.new(input)
|
|
||||||
end
|
|
||||||
|
|
||||||
# rubocop:disable Metrics/BlockNesting
|
|
||||||
def tokenize
|
|
||||||
@output = []
|
|
||||||
|
|
||||||
until @ss.eos?
|
|
||||||
@ss.skip(WHITESPACE_OR_NOTHING)
|
|
||||||
|
|
||||||
break if @ss.eos?
|
|
||||||
|
|
||||||
peeked = @ss.peek_byte
|
|
||||||
|
|
||||||
if (special = SPECIAL_TABLE[peeked])
|
|
||||||
@ss.scan_byte
|
|
||||||
# Special case for ".."
|
|
||||||
if special == DOT && @ss.peek_byte == DOT_ORD
|
|
||||||
@ss.scan_byte
|
|
||||||
@output << DOTDOT
|
|
||||||
elsif special == DASH
|
|
||||||
# Special case for negative numbers
|
|
||||||
if NUMBER_TABLE[@ss.peek_byte]
|
|
||||||
@ss.pos -= 1
|
|
||||||
@output << [:number, @ss.scan(NUMBER_LITERAL)]
|
|
||||||
else
|
|
||||||
@output << special
|
|
||||||
end
|
|
||||||
else
|
|
||||||
@output << special
|
|
||||||
end
|
|
||||||
elsif (sub_table = COMPARISON_JUMP_TABLE[peeked])
|
|
||||||
@ss.scan_byte
|
|
||||||
if (found = sub_table[@ss.peek_byte])
|
|
||||||
@output << found
|
|
||||||
@ss.scan_byte
|
|
||||||
else
|
|
||||||
raise SyntaxError, "Unexpected character #{peeked.chr}"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
type, pattern = NEXT_MATCHER_JUMP_TABLE[peeked]
|
|
||||||
|
|
||||||
if type && (t = @ss.scan(pattern))
|
|
||||||
# Special case for "contains"
|
|
||||||
@output << if type == :id && t == "contains"
|
|
||||||
COMPARISON_CONTAINS
|
|
||||||
else
|
|
||||||
[type, t]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
raise SyntaxError, "Unexpected character #{peeked.chr}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# rubocop:enable Metrics/BlockNesting
|
|
||||||
|
|
||||||
@output << EOS
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Lexer = StringScanner.instance_methods.include?(:scan_byte) ? Lexer2 : Lexer1
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
include: "Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]"
|
include: "Error in tag 'include' - Valid syntax: include '[template]' (with|for) [object|collection]"
|
||||||
inline_comment_invalid: "Syntax error in tag '#' - Each line of comments must be prefixed by the '#' character"
|
inline_comment_invalid: "Syntax error in tag '#' - Each line of comments must be prefixed by the '#' character"
|
||||||
invalid_delimiter: "'%{tag}' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}"
|
invalid_delimiter: "'%{tag}' is not a valid delimiter for %{block_name} tags. use %{block_delimiter}"
|
||||||
invalid_template_encoding: "Invalid template encoding"
|
|
||||||
render: "Syntax error in tag 'render' - Template name must be a quoted string"
|
render: "Syntax error in tag 'render' - Template name must be a quoted string"
|
||||||
table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
|
table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
|
||||||
tag_never_closed: "'%{block_name}' tag was never closed"
|
tag_never_closed: "'%{block_name}' tag was never closed"
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class ParseContext
|
class ParseContext
|
||||||
attr_accessor :locale, :line_number, :trim_whitespace, :depth
|
attr_accessor :locale, :line_number, :trim_whitespace, :depth
|
||||||
attr_reader :partial, :warnings, :error_mode, :environment
|
attr_reader :partial, :warnings, :error_mode
|
||||||
|
|
||||||
def initialize(options = Const::EMPTY_HASH)
|
def initialize(options = {})
|
||||||
@environment = options.fetch(:environment, Environment.default)
|
|
||||||
@template_options = options ? options.dup : {}
|
@template_options = options ? options.dup : {}
|
||||||
|
|
||||||
@locale = @template_options[:locale] ||= I18n.new
|
@locale = @template_options[:locale] ||= I18n.new
|
||||||
@@ -36,7 +35,7 @@ module Liquid
|
|||||||
@partial = value
|
@partial = value
|
||||||
@options = value ? partial_options : @template_options
|
@options = value ? partial_options : @template_options
|
||||||
|
|
||||||
@error_mode = @options[:error_mode] || @environment.error_mode
|
@error_mode = @options[:error_mode] || Template.error_mode
|
||||||
end
|
end
|
||||||
|
|
||||||
def partial_options
|
def partial_options
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ module Liquid
|
|||||||
str = consume
|
str = consume
|
||||||
str << variable_lookups
|
str << variable_lookups
|
||||||
when :open_square
|
when :open_square
|
||||||
str = consume.dup
|
str = consume
|
||||||
str << expression
|
str << expression
|
||||||
str << consume(:close_square)
|
str << consume(:close_square)
|
||||||
str << variable_lookups
|
str << variable_lookups
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ module Liquid
|
|||||||
class PartialCache
|
class PartialCache
|
||||||
def self.load(template_name, context:, parse_context:)
|
def self.load(template_name, context:, parse_context:)
|
||||||
cached_partials = context.registers[:cached_partials]
|
cached_partials = context.registers[:cached_partials]
|
||||||
cache_key = "#{template_name}:#{parse_context.error_mode}"
|
cached = cached_partials[template_name]
|
||||||
cached = cached_partials[cache_key]
|
|
||||||
return cached if cached
|
return cached if cached
|
||||||
|
|
||||||
file_system = context.registers[:file_system]
|
file_system = context.registers[:file_system]
|
||||||
@@ -16,16 +15,8 @@ module Liquid
|
|||||||
template_factory = context.registers[:template_factory]
|
template_factory = context.registers[:template_factory]
|
||||||
template = template_factory.for(template_name)
|
template = template_factory.for(template_name)
|
||||||
|
|
||||||
begin
|
partial = template.parse(source, parse_context)
|
||||||
partial = template.parse(source, parse_context)
|
cached_partials[template_name] = partial
|
||||||
rescue Liquid::Error => e
|
|
||||||
e.template_name = template&.name || template_name
|
|
||||||
raise e
|
|
||||||
end
|
|
||||||
|
|
||||||
partial.name ||= template_name
|
|
||||||
|
|
||||||
cached_partials[cache_key] = partial
|
|
||||||
ensure
|
ensure
|
||||||
parse_context.partial = false
|
parse_context.partial = false
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,14 +6,7 @@ require 'bigdecimal'
|
|||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
module StandardFilters
|
module StandardFilters
|
||||||
MAX_I32 = (1 << 31) - 1
|
MAX_INT = (1 << 31) - 1
|
||||||
private_constant :MAX_I32
|
|
||||||
|
|
||||||
MIN_I64 = -(1 << 63)
|
|
||||||
MAX_I64 = (1 << 63) - 1
|
|
||||||
I64_RANGE = MIN_I64..MAX_I64
|
|
||||||
private_constant :MIN_I64, :MAX_I64, :I64_RANGE
|
|
||||||
|
|
||||||
HTML_ESCAPE = {
|
HTML_ESCAPE = {
|
||||||
'&' => '&',
|
'&' => '&',
|
||||||
'>' => '>',
|
'>' => '>',
|
||||||
@@ -25,23 +18,10 @@ module Liquid
|
|||||||
STRIP_HTML_BLOCKS = Regexp.union(
|
STRIP_HTML_BLOCKS = Regexp.union(
|
||||||
%r{<script.*?</script>}m,
|
%r{<script.*?</script>}m,
|
||||||
/<!--.*?-->/m,
|
/<!--.*?-->/m,
|
||||||
%r{<style.*?</style>}m,
|
%r{<style.*?</style>}m
|
||||||
)
|
)
|
||||||
STRIP_HTML_TAGS = /<.*?>/m
|
STRIP_HTML_TAGS = /<.*?>/m
|
||||||
|
|
||||||
class << self
|
|
||||||
def try_coerce_encoding(input, encoding:)
|
|
||||||
original_encoding = input.encoding
|
|
||||||
if input.encoding != encoding
|
|
||||||
input.force_encoding(encoding)
|
|
||||||
unless input.valid_encoding?
|
|
||||||
input.force_encoding(original_encoding)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
input
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# @liquid_public_docs
|
# @liquid_public_docs
|
||||||
# @liquid_type filter
|
# @liquid_type filter
|
||||||
# @liquid_category array
|
# @liquid_category array
|
||||||
@@ -82,7 +62,7 @@ module Liquid
|
|||||||
# @liquid_type filter
|
# @liquid_type filter
|
||||||
# @liquid_category string
|
# @liquid_category string
|
||||||
# @liquid_summary
|
# @liquid_summary
|
||||||
# Capitalizes the first word in a string and downcases the remaining characters.
|
# Capitalizes the first word in a string.
|
||||||
# @liquid_syntax string | capitalize
|
# @liquid_syntax string | capitalize
|
||||||
# @liquid_return [string]
|
# @liquid_return [string]
|
||||||
def capitalize(input)
|
def capitalize(input)
|
||||||
@@ -93,7 +73,7 @@ module Liquid
|
|||||||
# @liquid_type filter
|
# @liquid_type filter
|
||||||
# @liquid_category string
|
# @liquid_category string
|
||||||
# @liquid_summary
|
# @liquid_summary
|
||||||
# Escapes special characters in HTML, such as `<>`, `'`, and `&`, and converts characters into escape sequences. The filter doesn't effect characters within the string that don’t have a corresponding escape sequence.".
|
# Escapes a string.
|
||||||
# @liquid_syntax string | escape
|
# @liquid_syntax string | escape
|
||||||
# @liquid_return [string]
|
# @liquid_return [string]
|
||||||
def escape(input)
|
def escape(input)
|
||||||
@@ -163,8 +143,7 @@ module Liquid
|
|||||||
# @liquid_syntax string | base64_decode
|
# @liquid_syntax string | base64_decode
|
||||||
# @liquid_return [string]
|
# @liquid_return [string]
|
||||||
def base64_decode(input)
|
def base64_decode(input)
|
||||||
input = input.to_s
|
Base64.strict_decode64(input.to_s)
|
||||||
StandardFilters.try_coerce_encoding(Base64.strict_decode64(input), encoding: input.encoding)
|
|
||||||
rescue ::ArgumentError
|
rescue ::ArgumentError
|
||||||
raise Liquid::ArgumentError, "invalid base64 provided to base64_decode"
|
raise Liquid::ArgumentError, "invalid base64 provided to base64_decode"
|
||||||
end
|
end
|
||||||
@@ -188,8 +167,7 @@ module Liquid
|
|||||||
# @liquid_syntax string | base64_url_safe_decode
|
# @liquid_syntax string | base64_url_safe_decode
|
||||||
# @liquid_return [string]
|
# @liquid_return [string]
|
||||||
def base64_url_safe_decode(input)
|
def base64_url_safe_decode(input)
|
||||||
input = input.to_s
|
Base64.urlsafe_decode64(input.to_s)
|
||||||
StandardFilters.try_coerce_encoding(Base64.urlsafe_decode64(input), encoding: input.encoding)
|
|
||||||
rescue ::ArgumentError
|
rescue ::ArgumentError
|
||||||
raise Liquid::ArgumentError, "invalid base64 provided to base64_url_safe_decode"
|
raise Liquid::ArgumentError, "invalid base64 provided to base64_url_safe_decode"
|
||||||
end
|
end
|
||||||
@@ -208,19 +186,10 @@ module Liquid
|
|||||||
offset = Utils.to_integer(offset)
|
offset = Utils.to_integer(offset)
|
||||||
length = length ? Utils.to_integer(length) : 1
|
length = length ? Utils.to_integer(length) : 1
|
||||||
|
|
||||||
begin
|
if input.is_a?(Array)
|
||||||
if input.is_a?(Array)
|
input.slice(offset, length) || []
|
||||||
input.slice(offset, length) || []
|
else
|
||||||
else
|
input.to_s.slice(offset, length) || ''
|
||||||
input.to_s.slice(offset, length) || ''
|
|
||||||
end
|
|
||||||
rescue RangeError
|
|
||||||
if I64_RANGE.cover?(length) && I64_RANGE.cover?(offset)
|
|
||||||
raise # unexpected error
|
|
||||||
end
|
|
||||||
offset = offset.clamp(I64_RANGE)
|
|
||||||
length = length.clamp(I64_RANGE)
|
|
||||||
retry
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -270,9 +239,9 @@ module Liquid
|
|||||||
wordlist = begin
|
wordlist = begin
|
||||||
input.split(" ", words + 1)
|
input.split(" ", words + 1)
|
||||||
rescue RangeError
|
rescue RangeError
|
||||||
# integer too big for String#split, but we can semantically assume no truncation is needed
|
raise if words + 1 < MAX_INT
|
||||||
return input if words + 1 > MAX_I32
|
# e.g. integer #{words} too big to convert to `int'
|
||||||
raise # unexpected error
|
raise Liquid::ArgumentError, "integer #{words} too big for truncatewords"
|
||||||
end
|
end
|
||||||
return input if wordlist.length <= words
|
return input if wordlist.length <= words
|
||||||
|
|
||||||
@@ -630,7 +599,7 @@ module Liquid
|
|||||||
# @liquid_description
|
# @liquid_description
|
||||||
# > Note:
|
# > Note:
|
||||||
# > The `concat` filter won't filter out duplicates. If you want to remove duplicates, then you need to use the
|
# > The `concat` filter won't filter out duplicates. If you want to remove duplicates, then you need to use the
|
||||||
# > [`uniq` filter](/docs/api/liquid/filters/uniq).
|
# > [`uniq` filter](/api/liquid/filters#uniq).
|
||||||
# @liquid_syntax array | concat: array
|
# @liquid_syntax array | concat: array
|
||||||
# @liquid_return [array[untyped]]
|
# @liquid_return [array[untyped]]
|
||||||
def concat(input, array)
|
def concat(input, array)
|
||||||
@@ -772,7 +741,7 @@ module Liquid
|
|||||||
# @liquid_type filter
|
# @liquid_type filter
|
||||||
# @liquid_category math
|
# @liquid_category math
|
||||||
# @liquid_summary
|
# @liquid_summary
|
||||||
# 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.
|
# Divides a number by a given number.
|
||||||
# @liquid_syntax number | divided_by: number
|
# @liquid_syntax number | divided_by: number
|
||||||
# @liquid_return [number]
|
# @liquid_return [number]
|
||||||
def divided_by(input, operand)
|
def divided_by(input, operand)
|
||||||
@@ -872,48 +841,18 @@ module Liquid
|
|||||||
# @liquid_summary
|
# @liquid_summary
|
||||||
# Sets a default value for any variable whose value is one of the following:
|
# Sets a default value for any variable whose value is one of the following:
|
||||||
#
|
#
|
||||||
# - [`empty`](/docs/api/liquid/basics#empty)
|
# - [`empty`](/api/liquid/basics#empty)
|
||||||
# - [`false`](/docs/api/liquid/basics#truthy-and-falsy)
|
# - [`false`](/api/liquid/basics#truthy-and-falsy)
|
||||||
# - [`nil`](/docs/api/liquid/basics#nil)
|
# - [`nil`](/api/liquid/basics#nil)
|
||||||
# @liquid_syntax variable | default: variable
|
# @liquid_syntax variable | default: variable
|
||||||
# @liquid_return [untyped]
|
# @liquid_return [untyped]
|
||||||
# @liquid_optional_param allow_false: [boolean] Whether to use false values instead of the default.
|
# @liquid_optional_param allow_false [boolean] Whether to use false values instead of the default.
|
||||||
def default(input, default_value = '', options = {})
|
def default(input, default_value = '', options = {})
|
||||||
options = {} unless options.is_a?(Hash)
|
options = {} unless options.is_a?(Hash)
|
||||||
false_check = options['allow_false'] ? input.nil? : !Liquid::Utils.to_liquid_value(input)
|
false_check = options['allow_false'] ? input.nil? : !Liquid::Utils.to_liquid_value(input)
|
||||||
false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input
|
false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input
|
||||||
end
|
end
|
||||||
|
|
||||||
# @liquid_public_docs
|
|
||||||
# @liquid_type filter
|
|
||||||
# @liquid_category array
|
|
||||||
# @liquid_summary
|
|
||||||
# Returns the sum of all elements in an array.
|
|
||||||
# @liquid_syntax array | sum
|
|
||||||
# @liquid_return [number]
|
|
||||||
def sum(input, property = nil)
|
|
||||||
ary = InputIterator.new(input, context)
|
|
||||||
return 0 if ary.empty?
|
|
||||||
|
|
||||||
values_for_sum = ary.map do |item|
|
|
||||||
if property.nil?
|
|
||||||
item
|
|
||||||
elsif item.respond_to?(:[])
|
|
||||||
item[property]
|
|
||||||
else
|
|
||||||
0
|
|
||||||
end
|
|
||||||
rescue TypeError
|
|
||||||
raise_property_error(property)
|
|
||||||
end
|
|
||||||
|
|
||||||
result = InputIterator.new(values_for_sum, context).sum do |item|
|
|
||||||
Utils.to_number(item)
|
|
||||||
end
|
|
||||||
|
|
||||||
result.is_a?(BigDecimal) ? result.to_f : result
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
attr_reader :context
|
attr_reader :context
|
||||||
@@ -944,8 +883,6 @@ module Liquid
|
|||||||
def nil_safe_casecmp(a, b)
|
def nil_safe_casecmp(a, b)
|
||||||
if !a.nil? && !b.nil?
|
if !a.nil? && !b.nil?
|
||||||
a.to_s.casecmp(b.to_s)
|
a.to_s.casecmp(b.to_s)
|
||||||
elsif a.nil? && b.nil?
|
|
||||||
0
|
|
||||||
else
|
else
|
||||||
a.nil? ? 1 : -1
|
a.nil? ? 1 : -1
|
||||||
end
|
end
|
||||||
@@ -1001,4 +938,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_filter(StandardFilters)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,19 +5,37 @@ module Liquid
|
|||||||
module StrainerFactory
|
module StrainerFactory
|
||||||
extend self
|
extend self
|
||||||
|
|
||||||
def add_global_filter(filter, environment = Environment.default)
|
def add_global_filter(filter)
|
||||||
Deprecations.warn("StrainerFactory.add_global_filter", "Environment#register_filter")
|
strainer_class_cache.clear
|
||||||
environment.register_filter(filter)
|
GlobalCache.add_filter(filter)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(context, filters = Const::EMPTY_ARRAY, environment = Environment.default)
|
def create(context, filters = [])
|
||||||
Deprecations.warn("StrainerFactory.create", "StrainerFactory.create_strainer")
|
strainer_from_cache(filters).new(context)
|
||||||
environment.create_strainer(context, filters)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def global_filter_names(environment = Environment.default)
|
def global_filter_names
|
||||||
Deprecations.warn("StrainerFactory.global_filter_names", "Environment#filter_method_names")
|
GlobalCache.filter_method_names
|
||||||
Environment.strainer_template.filter_method_names
|
end
|
||||||
|
|
||||||
|
GlobalCache = Class.new(StrainerTemplate)
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def strainer_from_cache(filters)
|
||||||
|
if filters.empty?
|
||||||
|
GlobalCache
|
||||||
|
else
|
||||||
|
strainer_class_cache[filters] ||= begin
|
||||||
|
klass = Class.new(GlobalCache)
|
||||||
|
filters.each { |f| klass.add_filter(f) }
|
||||||
|
klass
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def strainer_class_cache
|
||||||
|
@strainer_class_cache ||= {}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ module Liquid
|
|||||||
# @liquid_type object
|
# @liquid_type object
|
||||||
# @liquid_name tablerowloop
|
# @liquid_name tablerowloop
|
||||||
# @liquid_summary
|
# @liquid_summary
|
||||||
# Information about a parent [`tablerow` loop](/docs/api/liquid/tags/tablerow).
|
# Information about a parent [`tablerow` loop](/api/liquid/tags#tablerow).
|
||||||
class TablerowloopDrop < Drop
|
class TablerowloopDrop < Drop
|
||||||
def initialize(length, cols)
|
def initialize(length, cols)
|
||||||
@length = length
|
@length = length
|
||||||
|
|||||||
+3
-13
@@ -1,8 +1,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'liquid/tag/disabler'
|
|
||||||
require 'liquid/tag/disableable'
|
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
class Tag
|
class Tag
|
||||||
attr_reader :nodelist, :tag_name, :line_number, :parse_context
|
attr_reader :nodelist, :tag_name, :line_number, :parse_context
|
||||||
@@ -17,18 +14,12 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def disable_tags(*tag_names)
|
def disable_tags(*tag_names)
|
||||||
tag_names += disabled_tags
|
@disabled_tags ||= []
|
||||||
define_singleton_method(:disabled_tags) { tag_names }
|
@disabled_tags.concat(tag_names)
|
||||||
prepend(Disabler)
|
prepend(Disabler)
|
||||||
end
|
end
|
||||||
|
|
||||||
private :new
|
private :new
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def disabled_tags
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(tag_name, markup, parse_context)
|
def initialize(tag_name, markup, parse_context)
|
||||||
@@ -57,8 +48,7 @@ module Liquid
|
|||||||
# of the `render_to_output_buffer` method will become the default and the `render`
|
# of the `render_to_output_buffer` method will become the default and the `render`
|
||||||
# method will be removed.
|
# method will be removed.
|
||||||
def render_to_output_buffer(context, output)
|
def render_to_output_buffer(context, output)
|
||||||
render_result = render(context)
|
output << render(context)
|
||||||
output << render_result if render_result
|
|
||||||
output
|
output
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class Tag
|
class Tag
|
||||||
module Disabler
|
module Disabler
|
||||||
|
module ClassMethods
|
||||||
|
attr_reader :disabled_tags
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.prepended(base)
|
||||||
|
base.extend(ClassMethods)
|
||||||
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render_to_output_buffer(context, output)
|
||||||
context.with_disabled_tags(self.class.disabled_tags) do
|
context.with_disabled_tags(self.class.disabled_tags) do
|
||||||
super
|
super
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require_relative "tags/table_row"
|
|
||||||
require_relative "tags/echo"
|
|
||||||
require_relative "tags/if"
|
|
||||||
require_relative "tags/break"
|
|
||||||
require_relative "tags/inline_comment"
|
|
||||||
require_relative "tags/for"
|
|
||||||
require_relative "tags/assign"
|
|
||||||
require_relative "tags/ifchanged"
|
|
||||||
require_relative "tags/case"
|
|
||||||
require_relative "tags/include"
|
|
||||||
require_relative "tags/continue"
|
|
||||||
require_relative "tags/capture"
|
|
||||||
require_relative "tags/decrement"
|
|
||||||
require_relative "tags/unless"
|
|
||||||
require_relative "tags/increment"
|
|
||||||
require_relative "tags/comment"
|
|
||||||
require_relative "tags/raw"
|
|
||||||
require_relative "tags/render"
|
|
||||||
require_relative "tags/cycle"
|
|
||||||
|
|
||||||
module Liquid
|
|
||||||
module Tags
|
|
||||||
STANDARD_TAGS = {
|
|
||||||
'cycle' => Cycle,
|
|
||||||
'render' => Render,
|
|
||||||
'raw' => Raw,
|
|
||||||
'comment' => Comment,
|
|
||||||
'increment' => Increment,
|
|
||||||
'unless' => Unless,
|
|
||||||
'decrement' => Decrement,
|
|
||||||
'capture' => Capture,
|
|
||||||
'continue' => Continue,
|
|
||||||
'include' => Include,
|
|
||||||
'case' => Case,
|
|
||||||
'ifchanged' => Ifchanged,
|
|
||||||
'assign' => Assign,
|
|
||||||
'for' => For,
|
|
||||||
'#' => InlineComment,
|
|
||||||
'break' => Break,
|
|
||||||
'if' => If,
|
|
||||||
'echo' => Echo,
|
|
||||||
'tablerow' => TableRow,
|
|
||||||
}.freeze
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -8,7 +8,7 @@ module Liquid
|
|||||||
# @liquid_summary
|
# @liquid_summary
|
||||||
# Creates a new variable.
|
# Creates a new variable.
|
||||||
# @liquid_description
|
# @liquid_description
|
||||||
# You can create variables of any [basic type](/docs/api/liquid/basics#types), [object](/docs/api/liquid/objects), or object property.
|
# You can create variables of any [basic type](/api/liquid/basics#types), [object](/api/liquid/objects), or object property.
|
||||||
# @liquid_syntax
|
# @liquid_syntax
|
||||||
# {% assign variable_name = value %}
|
# {% assign variable_name = value %}
|
||||||
# @liquid_syntax_keyword variable_name The name of the variable being created.
|
# @liquid_syntax_keyword variable_name The name of the variable being created.
|
||||||
@@ -72,4 +72,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('assign', Assign)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ module Liquid
|
|||||||
# @liquid_category iteration
|
# @liquid_category iteration
|
||||||
# @liquid_name break
|
# @liquid_name break
|
||||||
# @liquid_summary
|
# @liquid_summary
|
||||||
# Stops a [`for` loop](/docs/api/liquid/tags/for) from iterating.
|
# Stops a [`for` loop](/api/liquid/tags#for) from iterating.
|
||||||
# @liquid_syntax
|
# @liquid_syntax
|
||||||
# {% break %}
|
# {% break %}
|
||||||
class Break < Tag
|
class Break < Tag
|
||||||
@@ -26,4 +26,6 @@ module Liquid
|
|||||||
output
|
output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('break', Break)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -39,4 +39,6 @@ module Liquid
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('capture', Capture)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
result = Liquid::Utils.to_liquid_value(
|
result = Liquid::Utils.to_liquid_value(
|
||||||
block.evaluate(context),
|
block.evaluate(context)
|
||||||
)
|
)
|
||||||
|
|
||||||
if result
|
if result
|
||||||
@@ -123,4 +123,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('case', Case)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ module Liquid
|
|||||||
# @liquid_summary
|
# @liquid_summary
|
||||||
# Prevents an expression from being rendered or output.
|
# Prevents an expression from being rendered or output.
|
||||||
# @liquid_description
|
# @liquid_description
|
||||||
# Any text inside `comment` tags won't be output, and any Liquid code will be parsed, but not executed.
|
# Any text inside `comment` tags won't be output, and any Liquid code won't be rendered.
|
||||||
# @liquid_syntax
|
# @liquid_syntax
|
||||||
# {% comment %}
|
# {% comment %}
|
||||||
# content
|
# content
|
||||||
@@ -25,64 +25,7 @@ module Liquid
|
|||||||
def blank?
|
def blank?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def parse_body(body, tokenizer)
|
|
||||||
if parse_context.depth >= MAX_DEPTH
|
|
||||||
raise StackLevelError, "Nesting too deep"
|
|
||||||
end
|
|
||||||
|
|
||||||
parse_context.depth += 1
|
|
||||||
comment_tag_depth = 1
|
|
||||||
|
|
||||||
begin
|
|
||||||
# Consume tokens without creating child nodes.
|
|
||||||
# The children tag doesn't require to be a valid Liquid except the comment and raw tag.
|
|
||||||
# The child comment and raw tag must be closed.
|
|
||||||
while (token = tokenizer.send(:shift))
|
|
||||||
tag_name = if tokenizer.for_liquid_tag
|
|
||||||
next if token.empty? || token.match?(BlockBody::WhitespaceOrNothing)
|
|
||||||
|
|
||||||
tag_name_match = BlockBody::LiquidTagToken.match(token)
|
|
||||||
|
|
||||||
next if tag_name_match.nil?
|
|
||||||
|
|
||||||
tag_name_match[1]
|
|
||||||
else
|
|
||||||
token =~ BlockBody::FullToken
|
|
||||||
Regexp.last_match(2)
|
|
||||||
end
|
|
||||||
|
|
||||||
case tag_name
|
|
||||||
when "raw"
|
|
||||||
parse_raw_tag_body(tokenizer)
|
|
||||||
when "comment"
|
|
||||||
comment_tag_depth += 1
|
|
||||||
when "endcomment"
|
|
||||||
comment_tag_depth -= 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if comment_tag_depth.zero?
|
|
||||||
parse_context.trim_whitespace = (token[-3] == WhitespaceControl) unless tokenizer.for_liquid_tag
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
raise_tag_never_closed(block_name)
|
|
||||||
ensure
|
|
||||||
parse_context.depth -= 1
|
|
||||||
end
|
|
||||||
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_raw_tag_body(tokenizer)
|
|
||||||
while (token = tokenizer.send(:shift))
|
|
||||||
return if token =~ BlockBody::FullTokenPossiblyInvalid && "endraw" == Regexp.last_match(2)
|
|
||||||
end
|
|
||||||
|
|
||||||
raise_tag_never_closed("raw")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('comment', Comment)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ module Liquid
|
|||||||
# @liquid_category iteration
|
# @liquid_category iteration
|
||||||
# @liquid_name continue
|
# @liquid_name continue
|
||||||
# @liquid_summary
|
# @liquid_summary
|
||||||
# Causes a [`for` loop](/docs/api/liquid/tags/for) to skip to the next iteration.
|
# Causes a [`for` loop](/api/liquid/tags#for) to skip to the next iteration.
|
||||||
# @liquid_syntax
|
# @liquid_syntax
|
||||||
# {% continue %}
|
# {% continue %}
|
||||||
class Continue < Tag
|
class Continue < Tag
|
||||||
@@ -17,4 +17,6 @@ module Liquid
|
|||||||
output
|
output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('continue', Continue)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ module Liquid
|
|||||||
# @liquid_category iteration
|
# @liquid_category iteration
|
||||||
# @liquid_name cycle
|
# @liquid_name cycle
|
||||||
# @liquid_summary
|
# @liquid_summary
|
||||||
# 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).
|
# Loops through a group of strings and outputs them one at a time for each iteration of a [`for` loop](/api/liquid/tags#for).
|
||||||
# @liquid_description
|
# @liquid_description
|
||||||
# The `cycle` tag must be used inside a `for` loop.
|
# The `cycle` tag must be used inside a `for` loop.
|
||||||
#
|
#
|
||||||
@@ -26,20 +26,14 @@ module Liquid
|
|||||||
when NamedSyntax
|
when NamedSyntax
|
||||||
@variables = variables_from_string(Regexp.last_match(2))
|
@variables = variables_from_string(Regexp.last_match(2))
|
||||||
@name = parse_expression(Regexp.last_match(1))
|
@name = parse_expression(Regexp.last_match(1))
|
||||||
@is_named = true
|
|
||||||
when SimpleSyntax
|
when SimpleSyntax
|
||||||
@variables = variables_from_string(markup)
|
@variables = variables_from_string(markup)
|
||||||
@name = @variables.to_s
|
@name = @variables.to_s
|
||||||
@is_named = !@name.match?(/\w+:0x\h{8}/)
|
|
||||||
else
|
else
|
||||||
raise SyntaxError, options[:locale].t("errors.syntax.cycle")
|
raise SyntaxError, options[:locale].t("errors.syntax.cycle")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def named?
|
|
||||||
@is_named
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render_to_output_buffer(context, output)
|
||||||
context.registers[:cycle] ||= {}
|
context.registers[:cycle] ||= {}
|
||||||
|
|
||||||
@@ -78,4 +72,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('cycle', Cycle)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ module Liquid
|
|||||||
# or [section](/themes/architecture/sections) file that they're created in. However, the variable is shared across
|
# 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.
|
# [snippets](/themes/architecture#snippets) included in the file.
|
||||||
#
|
#
|
||||||
# Similarly, variables that are created with `decrement` are independent from those created with [`assign`](/docs/api/liquid/tags/assign)
|
# Similarly, variables that are created with `decrement` are independent from those created with [`assign`](/api/liquid/tags#assign)
|
||||||
# and [`capture`](/docs/api/liquid/tags/capture). However, `decrement` and [`increment`](/docs/api/liquid/tags/increment) share
|
# and [`capture`](/api/liquid/tags#capture). However, `decrement` and [`increment`](/api/liquid/tags#increment) share
|
||||||
# variables.
|
# variables.
|
||||||
# @liquid_syntax
|
# @liquid_syntax
|
||||||
# {% decrement variable_name %}
|
# {% decrement variable_name %}
|
||||||
@@ -35,4 +35,6 @@ module Liquid
|
|||||||
output
|
output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('decrement', Decrement)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ module Liquid
|
|||||||
# Outputs an expression.
|
# Outputs an expression.
|
||||||
# @liquid_description
|
# @liquid_description
|
||||||
# Using the `echo` tag is the same as wrapping an expression in curly brackets (`{{` and `}}`). However, unlike the curly
|
# 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](/docs/api/liquid/tags/liquid).
|
# bracket method, you can use the `echo` tag inside [`liquid` tags](/api/liquid/tags#liquid).
|
||||||
#
|
#
|
||||||
# > Tip:
|
# > Tip:
|
||||||
# > You can use [filters](/docs/api/liquid/filters) on expressions inside `echo` tags.
|
# > You can use [filters](/api/liquid/filters) on expressions inside `echo` tags.
|
||||||
# @liquid_syntax
|
# @liquid_syntax
|
||||||
# {% liquid
|
# {% liquid
|
||||||
# echo expression
|
# echo expression
|
||||||
@@ -36,4 +36,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('echo', Echo)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ module Liquid
|
|||||||
# Renders an expression for every item in an array.
|
# Renders an expression for every item in an array.
|
||||||
# @liquid_description
|
# @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
|
# 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](/docs/api/liquid/tags/paginate) to split the items over multiple pages.
|
# [`paginate` tag](/api/liquid/tags#paginate) to split the items over multiple pages.
|
||||||
#
|
#
|
||||||
# > Tip:
|
# > Tip:
|
||||||
# > Every `for` loop has an associated [`forloop` object](/docs/api/liquid/objects/forloop) with information about the loop.
|
# > Every `for` loop has an associated [`forloop` object](/api/liquid/objects#forloop) with information about the loop.
|
||||||
# @liquid_syntax
|
# @liquid_syntax
|
||||||
# {% for variable in array %}
|
# {% for variable in array %}
|
||||||
# expression
|
# expression
|
||||||
@@ -98,12 +98,11 @@ module Liquid
|
|||||||
@name = "#{@variable_name}-#{collection_name}"
|
@name = "#{@variable_name}-#{collection_name}"
|
||||||
@reversed = p.id?('reversed')
|
@reversed = p.id?('reversed')
|
||||||
|
|
||||||
while p.look(:comma) || p.look(:id)
|
while p.look(:id) && p.look(:colon, 1)
|
||||||
p.consume?(:comma)
|
|
||||||
unless (attribute = p.id?('limit') || p.id?('offset'))
|
unless (attribute = p.id?('limit') || p.id?('offset'))
|
||||||
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_attribute")
|
raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_attribute")
|
||||||
end
|
end
|
||||||
p.consume(:colon)
|
p.consume
|
||||||
set_attribute(attribute, p.expression)
|
set_attribute(attribute, p.expression)
|
||||||
end
|
end
|
||||||
p.consume(:end_of_string)
|
p.consume(:end_of_string)
|
||||||
@@ -178,6 +177,7 @@ module Liquid
|
|||||||
case key
|
case key
|
||||||
when 'offset'
|
when 'offset'
|
||||||
@from = if expr == 'continue'
|
@from = if expr == 'continue'
|
||||||
|
Usage.increment('for_offset_continue')
|
||||||
:continue
|
:continue
|
||||||
else
|
else
|
||||||
parse_expression(expr)
|
parse_expression(expr)
|
||||||
@@ -201,4 +201,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('for', For)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ module Liquid
|
|||||||
def render_to_output_buffer(context, output)
|
def render_to_output_buffer(context, output)
|
||||||
@blocks.each do |block|
|
@blocks.each do |block|
|
||||||
result = Liquid::Utils.to_liquid_value(
|
result = Liquid::Utils.to_liquid_value(
|
||||||
block.evaluate(context),
|
block.evaluate(context)
|
||||||
)
|
)
|
||||||
|
|
||||||
if result
|
if result
|
||||||
@@ -111,7 +111,7 @@ module Liquid
|
|||||||
def parse_binary_comparisons(p)
|
def parse_binary_comparisons(p)
|
||||||
condition = parse_comparison(p)
|
condition = parse_comparison(p)
|
||||||
first_condition = condition
|
first_condition = condition
|
||||||
while (op = p.id?('and') || p.id?('or'))
|
while (op = (p.id?('and') || p.id?('or')))
|
||||||
child_condition = parse_comparison(p)
|
child_condition = parse_comparison(p)
|
||||||
condition.send(op, child_condition)
|
condition.send(op, child_condition)
|
||||||
condition = child_condition
|
condition = child_condition
|
||||||
@@ -135,4 +135,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('if', If)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -14,4 +14,6 @@ module Liquid
|
|||||||
output
|
output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('ifchanged', Ifchanged)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ module Liquid
|
|||||||
# @liquid_summary
|
# @liquid_summary
|
||||||
# Renders a [snippet](/themes/architecture#snippets).
|
# Renders a [snippet](/themes/architecture#snippets).
|
||||||
# @liquid_description
|
# @liquid_description
|
||||||
# Inside the snippet, you can access and alter variables that are [created](/docs/api/liquid/tags/variable-tags) outside of the
|
# Inside the snippet, you can access and alter variables that are [created](/api/liquid/tags#variable-tags) outside of the
|
||||||
# snippet.
|
# snippet.
|
||||||
# @liquid_syntax
|
# @liquid_syntax
|
||||||
# {% include 'filename' %}
|
# {% include 'filename' %}
|
||||||
@@ -16,7 +16,7 @@ module Liquid
|
|||||||
# @liquid_deprecated
|
# @liquid_deprecated
|
||||||
# Deprecated because the way that variables are handled reduces performance and makes code harder to both read and maintain.
|
# 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`](/docs/api/liquid/tags/render).
|
# The `include` tag has been replaced by [`render`](/api/liquid/tags#render).
|
||||||
class Include < Tag
|
class Include < Tag
|
||||||
prepend Tag::Disableable
|
prepend Tag::Disableable
|
||||||
|
|
||||||
@@ -52,12 +52,12 @@ module Liquid
|
|||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render_to_output_buffer(context, output)
|
||||||
template_name = context.evaluate(@template_name_expr)
|
template_name = context.evaluate(@template_name_expr)
|
||||||
raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name.is_a?(String)
|
raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name
|
||||||
|
|
||||||
partial = PartialCache.load(
|
partial = PartialCache.load(
|
||||||
template_name,
|
template_name,
|
||||||
context: context,
|
context: context,
|
||||||
parse_context: parse_context,
|
parse_context: parse_context
|
||||||
)
|
)
|
||||||
|
|
||||||
context_variable_name = @alias_name || template_name.split('/').last
|
context_variable_name = @alias_name || template_name.split('/').last
|
||||||
@@ -70,11 +70,9 @@ module Liquid
|
|||||||
|
|
||||||
old_template_name = context.template_name
|
old_template_name = context.template_name
|
||||||
old_partial = context.partial
|
old_partial = context.partial
|
||||||
|
|
||||||
begin
|
begin
|
||||||
context.template_name = partial.name
|
context.template_name = template_name
|
||||||
context.partial = true
|
context.partial = true
|
||||||
|
|
||||||
context.stack do
|
context.stack do
|
||||||
@attributes.each do |key, value|
|
@attributes.each do |key, value|
|
||||||
context[key] = context.evaluate(value)
|
context[key] = context.evaluate(value)
|
||||||
@@ -110,4 +108,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('include', Include)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ module Liquid
|
|||||||
# or [section](/themes/architecture/sections) file that they're created in. However, the variable is shared across
|
# 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.
|
# [snippets](/themes/architecture#snippets) included in the file.
|
||||||
#
|
#
|
||||||
# Similarly, variables that are created with `increment` are independent from those created with [`assign`](/docs/api/liquid/tags/assign)
|
# Similarly, variables that are created with `increment` are independent from those created with [`assign`](/api/liquid/tags#assign)
|
||||||
# and [`capture`](/docs/api/liquid/tags/capture). However, `increment` and [`decrement`](/docs/api/liquid/tags/decrement) share
|
# and [`capture`](/api/liquid/tags#capture). However, `increment` and [`decrement`](/api/liquid/tags#decrement) share
|
||||||
# variables.
|
# variables.
|
||||||
# @liquid_syntax
|
# @liquid_syntax
|
||||||
# {% increment variable_name %}
|
# {% increment variable_name %}
|
||||||
@@ -35,4 +35,6 @@ module Liquid
|
|||||||
output
|
output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('increment', Increment)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type tag
|
||||||
|
# @liquid_category syntax
|
||||||
|
# @liquid_name inline_comment
|
||||||
|
# @liquid_summary
|
||||||
|
# Prevents an expression from being rendered or output.
|
||||||
|
# @liquid_description
|
||||||
|
# Any text inside an `inline_comment` tag won't be rendered or output.
|
||||||
|
#
|
||||||
|
# You can create multi-line inline comments. However, each line must begin with a `#`.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% # content %}
|
||||||
|
# @liquid_syntax_keyword content The content of the comment.
|
||||||
class InlineComment < Tag
|
class InlineComment < Tag
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
@@ -25,4 +38,6 @@ module Liquid
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('#', InlineComment)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ module Liquid
|
|||||||
# @liquid_syntax_keyword expression The expression to be output without being rendered.
|
# @liquid_syntax_keyword expression The expression to be output without being rendered.
|
||||||
class Raw < Block
|
class Raw < Block
|
||||||
Syntax = /\A\s*\z/
|
Syntax = /\A\s*\z/
|
||||||
|
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om
|
||||||
|
|
||||||
def initialize(tag_name, markup, parse_context)
|
def initialize(tag_name, markup, parse_context)
|
||||||
super
|
super
|
||||||
@@ -24,8 +25,7 @@ module Liquid
|
|||||||
def parse(tokens)
|
def parse(tokens)
|
||||||
@body = +''
|
@body = +''
|
||||||
while (token = tokens.shift)
|
while (token = tokens.shift)
|
||||||
if token =~ BlockBody::FullTokenPossiblyInvalid && block_delimiter == Regexp.last_match(2)
|
if token =~ FullTokenPossiblyInvalid && block_delimiter == Regexp.last_match(2)
|
||||||
parse_context.trim_whitespace = (token[-3] == WhitespaceControl)
|
|
||||||
@body << Regexp.last_match(1) if Regexp.last_match(1) != ""
|
@body << Regexp.last_match(1) if Regexp.last_match(1) != ""
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -56,4 +56,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('raw', Raw)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,19 +8,19 @@ module Liquid
|
|||||||
# @liquid_summary
|
# @liquid_summary
|
||||||
# Renders a [snippet](/themes/architecture#snippets) or [app block](/themes/architecture/sections/section-schema#render-app-blocks).
|
# Renders a [snippet](/themes/architecture#snippets) or [app block](/themes/architecture/sections/section-schema#render-app-blocks).
|
||||||
# @liquid_description
|
# @liquid_description
|
||||||
# Inside snippets and app blocks, you can't directly access variables that are [created](/docs/api/liquid/tags/variable-tags) outside
|
# 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](/docs/api/liquid/tags/render#render-passing-variables-to-a-snippet)
|
# of the snippet or app block. However, you can [specify variables as parameters](/api/liquid/tags#render-passing-variables-to-snippets)
|
||||||
# to pass outside variables to snippets.
|
# 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
|
# 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)
|
# 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](/docs/api/liquid/objects/product), and a snippet or app block inside a [section](/themes/architecture/sections)
|
# 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](/docs/api/liquid/objects/section).
|
# can access the [`section` object](/api/liquid/objects#section).
|
||||||
#
|
#
|
||||||
# Outside a snippet or app block, you can't access variables created inside the snippet or app block.
|
# Outside a snippet or app block, you can't access variables created inside the snippet or app block.
|
||||||
#
|
#
|
||||||
# > Note:
|
# > Note:
|
||||||
# > When you render a snippet using the `render` tag, you can't use the [`include` tag](/docs/api/liquid/tags/include)
|
# > When you render a snippet using the `render` tag, you can't use the [`include` tag](/api/liquid/tags#include)
|
||||||
# > inside the snippet.
|
# > inside the snippet.
|
||||||
# @liquid_syntax
|
# @liquid_syntax
|
||||||
# {% render 'filename' %}
|
# {% render 'filename' %}
|
||||||
@@ -69,14 +69,14 @@ module Liquid
|
|||||||
partial = PartialCache.load(
|
partial = PartialCache.load(
|
||||||
template_name,
|
template_name,
|
||||||
context: context,
|
context: context,
|
||||||
parse_context: parse_context,
|
parse_context: parse_context
|
||||||
)
|
)
|
||||||
|
|
||||||
context_variable_name = @alias_name || template_name.split('/').last
|
context_variable_name = @alias_name || template_name.split('/').last
|
||||||
|
|
||||||
render_partial_func = ->(var, forloop) {
|
render_partial_func = ->(var, forloop) {
|
||||||
inner_context = context.new_isolated_subcontext
|
inner_context = context.new_isolated_subcontext
|
||||||
inner_context.template_name = partial.name
|
inner_context.template_name = template_name
|
||||||
inner_context.partial = true
|
inner_context.partial = true
|
||||||
inner_context['forloop'] = forloop if forloop
|
inner_context['forloop'] = forloop if forloop
|
||||||
|
|
||||||
@@ -108,4 +108,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('render', Render)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ module Liquid
|
|||||||
# The `tablerow` tag must be wrapped in HTML `<table>` and `</table>` tags.
|
# The `tablerow` tag must be wrapped in HTML `<table>` and `</table>` tags.
|
||||||
#
|
#
|
||||||
# > Tip:
|
# > Tip:
|
||||||
# > Every `tablerow` loop has an associated [`tablerowloop` object](/docs/api/liquid/objects/tablerowloop) with information about the loop.
|
# > Every `tablerow` loop has an associated [`tablerowloop` object](/api/liquid/objects#tablerowloop) with information about the loop.
|
||||||
# @liquid_syntax
|
# @liquid_syntax
|
||||||
# {% tablerow variable in array %}
|
# {% tablerow variable in array %}
|
||||||
# expression
|
# expression
|
||||||
@@ -45,13 +45,13 @@ module Liquid
|
|||||||
def render_to_output_buffer(context, output)
|
def render_to_output_buffer(context, output)
|
||||||
(collection = context.evaluate(@collection_name)) || (return '')
|
(collection = context.evaluate(@collection_name)) || (return '')
|
||||||
|
|
||||||
from = @attributes.key?('offset') ? to_integer(context.evaluate(@attributes['offset'])) : 0
|
from = @attributes.key?('offset') ? context.evaluate(@attributes['offset']).to_i : 0
|
||||||
to = @attributes.key?('limit') ? from + to_integer(context.evaluate(@attributes['limit'])) : nil
|
to = @attributes.key?('limit') ? from + context.evaluate(@attributes['limit']).to_i : nil
|
||||||
|
|
||||||
collection = Utils.slice_collection(collection, from, to)
|
collection = Utils.slice_collection(collection, from, to)
|
||||||
length = collection.length
|
length = collection.length
|
||||||
|
|
||||||
cols = @attributes.key?('cols') ? to_integer(context.evaluate(@attributes['cols'])) : length
|
cols = context.evaluate(@attributes['cols']).to_i
|
||||||
|
|
||||||
output << "<tr class=\"row1\">\n"
|
output << "<tr class=\"row1\">\n"
|
||||||
context.stack do
|
context.stack do
|
||||||
@@ -65,12 +65,6 @@ module Liquid
|
|||||||
super
|
super
|
||||||
output << '</td>'
|
output << '</td>'
|
||||||
|
|
||||||
# Handle any interrupts if they exist.
|
|
||||||
if context.interrupt?
|
|
||||||
interrupt = context.pop_interrupt
|
|
||||||
break if interrupt.is_a?(BreakInterrupt)
|
|
||||||
end
|
|
||||||
|
|
||||||
if tablerowloop.col_last && !tablerowloop.last
|
if tablerowloop.col_last && !tablerowloop.last
|
||||||
output << "</tr>\n<tr class=\"row#{tablerowloop.row + 1}\">"
|
output << "</tr>\n<tr class=\"row#{tablerowloop.row + 1}\">"
|
||||||
end
|
end
|
||||||
@@ -88,13 +82,7 @@ module Liquid
|
|||||||
super + @node.attributes.values + [@node.collection_name]
|
super + @node.attributes.values + [@node.collection_name]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def to_integer(value)
|
|
||||||
value.to_i
|
|
||||||
rescue NoMethodError
|
|
||||||
raise Liquid::ArgumentError, "invalid integer"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('tablerow', TableRow)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ module Liquid
|
|||||||
# Renders an expression unless a specific condition is `true`.
|
# Renders an expression unless a specific condition is `true`.
|
||||||
# @liquid_description
|
# @liquid_description
|
||||||
# > Tip:
|
# > Tip:
|
||||||
# > Similar to the [`if` tag](/docs/api/liquid/tags/if), you can use `elsif` to add more conditions to an `unless` tag.
|
# > Similar to the [`if` tag](/api/liquid/tags#if), you can use `elsif` to add more conditions to an `unless` tag.
|
||||||
# @liquid_syntax
|
# @liquid_syntax
|
||||||
# {% unless condition %}
|
# {% unless condition %}
|
||||||
# expression
|
# expression
|
||||||
@@ -23,7 +23,7 @@ module Liquid
|
|||||||
# First condition is interpreted backwards ( if not )
|
# First condition is interpreted backwards ( if not )
|
||||||
first_block = @blocks.first
|
first_block = @blocks.first
|
||||||
result = Liquid::Utils.to_liquid_value(
|
result = Liquid::Utils.to_liquid_value(
|
||||||
first_block.evaluate(context),
|
first_block.evaluate(context)
|
||||||
)
|
)
|
||||||
|
|
||||||
unless result
|
unless result
|
||||||
@@ -33,7 +33,7 @@ module Liquid
|
|||||||
# After the first condition unless works just like if
|
# After the first condition unless works just like if
|
||||||
@blocks[1..-1].each do |block|
|
@blocks[1..-1].each do |block|
|
||||||
result = Liquid::Utils.to_liquid_value(
|
result = Liquid::Utils.to_liquid_value(
|
||||||
block.evaluate(context),
|
block.evaluate(context)
|
||||||
)
|
)
|
||||||
|
|
||||||
if result
|
if result
|
||||||
@@ -44,4 +44,6 @@ module Liquid
|
|||||||
output
|
output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Template.register_tag('unless', Unless)
|
||||||
end
|
end
|
||||||
|
|||||||
+61
-64
@@ -15,9 +15,45 @@ module Liquid
|
|||||||
# template.render('user_name' => 'bob')
|
# template.render('user_name' => 'bob')
|
||||||
#
|
#
|
||||||
class Template
|
class Template
|
||||||
attr_accessor :root, :name
|
attr_accessor :root
|
||||||
attr_reader :resource_limits, :warnings
|
attr_reader :resource_limits, :warnings
|
||||||
|
|
||||||
|
class TagRegistry
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@tags = {}
|
||||||
|
@cache = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](tag_name)
|
||||||
|
return nil unless @tags.key?(tag_name)
|
||||||
|
return @cache[tag_name] if Liquid.cache_classes
|
||||||
|
|
||||||
|
lookup_class(@tags[tag_name]).tap { |o| @cache[tag_name] = o }
|
||||||
|
end
|
||||||
|
|
||||||
|
def []=(tag_name, klass)
|
||||||
|
@tags[tag_name] = klass.name
|
||||||
|
@cache[tag_name] = klass
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(tag_name)
|
||||||
|
@tags.delete(tag_name)
|
||||||
|
@cache.delete(tag_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def each(&block)
|
||||||
|
@tags.each(&block)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def lookup_class(name)
|
||||||
|
Object.const_get(name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
attr_reader :profiler
|
attr_reader :profiler
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
@@ -25,83 +61,52 @@ module Liquid
|
|||||||
# :lax acts like liquid 2.5 and silently ignores malformed tags in most cases.
|
# :lax acts like liquid 2.5 and silently ignores malformed tags in most cases.
|
||||||
# :warn is the default and will give deprecation warnings when invalid syntax is used.
|
# :warn is the default and will give deprecation warnings when invalid syntax is used.
|
||||||
# :strict will enforce correct syntax.
|
# :strict will enforce correct syntax.
|
||||||
def error_mode=(mode)
|
attr_accessor :error_mode
|
||||||
Deprecations.warn("Template.error_mode=", "Environment#error_mode=")
|
Template.error_mode = :lax
|
||||||
Environment.default.error_mode = mode
|
|
||||||
|
attr_accessor :default_exception_renderer
|
||||||
|
Template.default_exception_renderer = lambda do |exception|
|
||||||
|
exception
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_mode
|
attr_accessor :file_system
|
||||||
Environment.default.error_mode
|
Template.file_system = BlankFileSystem.new
|
||||||
end
|
|
||||||
|
|
||||||
def default_exception_renderer=(renderer)
|
attr_accessor :tags
|
||||||
Deprecations.warn("Template.default_exception_renderer=", "Environment#exception_renderer=")
|
Template.tags = TagRegistry.new
|
||||||
Environment.default.exception_renderer = renderer
|
private :tags=
|
||||||
end
|
|
||||||
|
|
||||||
def default_exception_renderer
|
|
||||||
Environment.default.exception_renderer
|
|
||||||
end
|
|
||||||
|
|
||||||
def file_system=(file_system)
|
|
||||||
Deprecations.warn("Template.file_system=", "Environment#file_system=")
|
|
||||||
Environment.default.file_system = file_system
|
|
||||||
end
|
|
||||||
|
|
||||||
def file_system
|
|
||||||
Environment.default.file_system
|
|
||||||
end
|
|
||||||
|
|
||||||
def tags
|
|
||||||
Environment.default.tags
|
|
||||||
end
|
|
||||||
|
|
||||||
def register_tag(name, klass)
|
def register_tag(name, klass)
|
||||||
Deprecations.warn("Template.register_tag", "Environment#register_tag")
|
tags[name.to_s] = klass
|
||||||
Environment.default.register_tag(name, klass)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Pass a module with filter methods which should be available
|
# Pass a module with filter methods which should be available
|
||||||
# to all liquid views. Good for registering the standard library
|
# to all liquid views. Good for registering the standard library
|
||||||
def register_filter(mod)
|
def register_filter(mod)
|
||||||
Deprecations.warn("Template.register_filter", "Environment#register_filter")
|
StrainerFactory.add_global_filter(mod)
|
||||||
Environment.default.register_filter(mod)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private def default_resource_limits=(limits)
|
attr_accessor :default_resource_limits
|
||||||
Deprecations.warn("Template.default_resource_limits=", "Environment#default_resource_limits=")
|
Template.default_resource_limits = {}
|
||||||
Environment.default.default_resource_limits = limits
|
private :default_resource_limits=
|
||||||
end
|
|
||||||
|
|
||||||
def default_resource_limits
|
|
||||||
Environment.default.default_resource_limits
|
|
||||||
end
|
|
||||||
|
|
||||||
# creates a new <tt>Template</tt> object from liquid source code
|
# creates a new <tt>Template</tt> object from liquid source code
|
||||||
# To enable profiling, pass in <tt>profile: true</tt> as an option.
|
# To enable profiling, pass in <tt>profile: true</tt> as an option.
|
||||||
# See Liquid::Profiler for more information
|
# See Liquid::Profiler for more information
|
||||||
def parse(source, options = {})
|
def parse(source, options = {})
|
||||||
environment = options[:environment] || Environment.default
|
new.parse(source, options)
|
||||||
new(environment: environment).parse(source, options)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(environment: Environment.default)
|
def initialize
|
||||||
@environment = environment
|
|
||||||
@rethrow_errors = false
|
@rethrow_errors = false
|
||||||
@resource_limits = ResourceLimits.new(environment.default_resource_limits)
|
@resource_limits = ResourceLimits.new(Template.default_resource_limits)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Parse source code.
|
# Parse source code.
|
||||||
# Returns self for easy chaining
|
# Returns self for easy chaining
|
||||||
def parse(source, options = {})
|
def parse(source, options = {})
|
||||||
parse_context = configure_options(options)
|
parse_context = configure_options(options)
|
||||||
source = source.to_s.to_str
|
|
||||||
|
|
||||||
unless source.valid_encoding?
|
|
||||||
raise TemplateEncodingError, parse_context.locale.t("errors.syntax.invalid_template_encoding")
|
|
||||||
end
|
|
||||||
|
|
||||||
tokenizer = parse_context.new_tokenizer(source, start_line_number: @line_numbers && 1)
|
tokenizer = parse_context.new_tokenizer(source, start_line_number: @line_numbers && 1)
|
||||||
@root = Document.parse(tokenizer, parse_context)
|
@root = Document.parse(tokenizer, parse_context)
|
||||||
self
|
self
|
||||||
@@ -151,11 +156,11 @@ module Liquid
|
|||||||
c
|
c
|
||||||
when Liquid::Drop
|
when Liquid::Drop
|
||||||
drop = args.shift
|
drop = args.shift
|
||||||
drop.context = Context.new([drop, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits, {}, @environment)
|
drop.context = Context.new([drop, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
|
||||||
when Hash
|
when Hash
|
||||||
Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits, {}, @environment)
|
Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
|
||||||
when nil
|
when nil
|
||||||
Context.new(assigns, instance_assigns, registers, @rethrow_errors, @resource_limits, {}, @environment)
|
Context.new(assigns, instance_assigns, registers, @rethrow_errors, @resource_limits)
|
||||||
else
|
else
|
||||||
raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
|
raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
|
||||||
end
|
end
|
||||||
@@ -184,8 +189,6 @@ module Liquid
|
|||||||
@profiler = context.profiler = Liquid::Profiler.new
|
@profiler = context.profiler = Liquid::Profiler.new
|
||||||
end
|
end
|
||||||
|
|
||||||
context.template_name ||= name
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
# render the nodelist.
|
# render the nodelist.
|
||||||
@root.render_to_output_buffer(context, output || +'')
|
@root.render_to_output_buffer(context, output || +'')
|
||||||
@@ -215,14 +218,8 @@ module Liquid
|
|||||||
@options = options
|
@options = options
|
||||||
@profiling = profiling
|
@profiling = profiling
|
||||||
@line_numbers = options[:line_numbers] || @profiling
|
@line_numbers = options[:line_numbers] || @profiling
|
||||||
parse_context = if options.is_a?(ParseContext)
|
parse_context = options.is_a?(ParseContext) ? options : ParseContext.new(options)
|
||||||
options
|
@warnings = parse_context.warnings
|
||||||
else
|
|
||||||
opts = options.key?(:environment) ? options : options.merge(environment: @environment)
|
|
||||||
ParseContext.new(opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
@warnings = parse_context.warnings
|
|
||||||
parse_context
|
parse_context
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Liquid
|
|
||||||
class Template
|
|
||||||
class TagRegistry
|
|
||||||
include Enumerable
|
|
||||||
|
|
||||||
def initialize(tags = nil)
|
|
||||||
@tags = {}
|
|
||||||
@cache = {}
|
|
||||||
tags.each { |tag_name, klass| self[tag_name] = klass }
|
|
||||||
Deprecations.warn("Template::TagRegistry", "Use a Environment instance with zeitwerk")
|
|
||||||
end
|
|
||||||
|
|
||||||
def [](tag_name)
|
|
||||||
return nil unless @tags.key?(tag_name)
|
|
||||||
return @cache[tag_name] if Liquid.cache_classes
|
|
||||||
|
|
||||||
lookup_class(@tags[tag_name]).tap { |o| @cache[tag_name] = o }
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete(tag_name)
|
|
||||||
Deprecations.warn("Template::TagRegistry#delete", "Use a Environment instance with immutable tags")
|
|
||||||
@tags.delete(tag_name)
|
|
||||||
@cache.delete(tag_name)
|
|
||||||
end
|
|
||||||
|
|
||||||
def []=(tag_name, klass)
|
|
||||||
@tags[tag_name] = klass.name
|
|
||||||
@cache[tag_name] = klass
|
|
||||||
end
|
|
||||||
|
|
||||||
def each(&block)
|
|
||||||
@tags.each(&block)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def lookup_class(name)
|
|
||||||
Object.const_get(name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -5,18 +5,14 @@ module Liquid
|
|||||||
attr_reader :line_number, :for_liquid_tag
|
attr_reader :line_number, :for_liquid_tag
|
||||||
|
|
||||||
def initialize(source, line_numbers = false, line_number: nil, for_liquid_tag: false)
|
def initialize(source, line_numbers = false, line_number: nil, for_liquid_tag: false)
|
||||||
@source = source
|
@source = source.to_s.to_str
|
||||||
@line_number = line_number || (line_numbers ? 1 : nil)
|
@line_number = line_number || (line_numbers ? 1 : nil)
|
||||||
@for_liquid_tag = for_liquid_tag
|
@for_liquid_tag = for_liquid_tag
|
||||||
@offset = 0
|
|
||||||
@tokens = tokenize
|
@tokens = tokenize
|
||||||
end
|
end
|
||||||
|
|
||||||
def shift
|
def shift
|
||||||
token = @tokens[@offset]
|
(token = @tokens.shift) || return
|
||||||
return nil unless token
|
|
||||||
|
|
||||||
@offset += 1
|
|
||||||
|
|
||||||
if @line_number
|
if @line_number
|
||||||
@line_number += @for_liquid_tag ? 1 : token.count("\n")
|
@line_number += @for_liquid_tag ? 1 : token.count("\n")
|
||||||
@@ -35,9 +31,7 @@ module Liquid
|
|||||||
tokens = @source.split(TemplateParser)
|
tokens = @source.split(TemplateParser)
|
||||||
|
|
||||||
# removes the rogue empty element at the beginning of the array
|
# removes the rogue empty element at the beginning of the array
|
||||||
if tokens[0]&.empty?
|
tokens.shift if tokens[0]&.empty?
|
||||||
@offset += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
tokens
|
tokens
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
VERSION = "5.6.0.alpha"
|
VERSION = "5.4.0"
|
||||||
end
|
end
|
||||||
|
|||||||
+1
-4
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
|
|||||||
s.license = "MIT"
|
s.license = "MIT"
|
||||||
# s.description = "A secure, non-evaling end user template engine with aesthetic markup."
|
# s.description = "A secure, non-evaling end user template engine with aesthetic markup."
|
||||||
|
|
||||||
s.required_ruby_version = ">= 3.0.0"
|
s.required_ruby_version = ">= 2.7.0"
|
||||||
s.required_rubygems_version = ">= 1.3.7"
|
s.required_rubygems_version = ">= 1.3.7"
|
||||||
|
|
||||||
s.metadata['allowed_push_host'] = 'https://rubygems.org'
|
s.metadata['allowed_push_host'] = 'https://rubygems.org'
|
||||||
@@ -28,9 +28,6 @@ Gem::Specification.new do |s|
|
|||||||
|
|
||||||
s.require_path = "lib"
|
s.require_path = "lib"
|
||||||
|
|
||||||
s.add_dependency("strscan")
|
|
||||||
s.add_dependency("bigdecimal")
|
|
||||||
|
|
||||||
s.add_development_dependency('rake', '~> 13.0')
|
s.add_development_dependency('rake', '~> 13.0')
|
||||||
s.add_development_dependency('minitest')
|
s.add_development_dependency('minitest')
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
require 'benchmark/ips'
|
require 'benchmark/ips'
|
||||||
require_relative 'theme_runner'
|
require_relative 'theme_runner'
|
||||||
|
|
||||||
RubyVM::YJIT.enable if defined?(RubyVM::YJIT)
|
|
||||||
Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first
|
Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first
|
||||||
profiler = ThemeRunner.new
|
profiler = ThemeRunner.new
|
||||||
|
|
||||||
|
|||||||
@@ -49,3 +49,8 @@ module Database
|
|||||||
end
|
end
|
||||||
end
|
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['items'] = collection_size
|
||||||
pagination['pages'] = page_count - 1
|
pagination['pages'] = page_count - 1
|
||||||
pagination['previous'] = link('« Previous', current_page - 1) if 1 < current_page
|
pagination['previous'] = link('« Previous', current_page - 1) unless 1 >= current_page
|
||||||
pagination['next'] = link('Next »', current_page + 1) if page_count > current_page + 1
|
pagination['next'] = link('Next »', current_page + 1) unless page_count <= current_page + 1
|
||||||
pagination['parts'] = []
|
pagination['parts'] = []
|
||||||
|
|
||||||
hellip_break = false
|
hellip_break = false
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require "benchmark/ips"
|
|
||||||
|
|
||||||
# benchmark liquid lexing
|
|
||||||
|
|
||||||
require 'liquid'
|
|
||||||
|
|
||||||
RubyVM::YJIT.enable
|
|
||||||
|
|
||||||
EXPRESSIONS = [
|
|
||||||
"foo[1..2].baz",
|
|
||||||
"12.0",
|
|
||||||
"foo.bar.based",
|
|
||||||
"21 - 62",
|
|
||||||
"foo.bar.baz",
|
|
||||||
"foo > 12",
|
|
||||||
"foo < 12",
|
|
||||||
"foo <= 12",
|
|
||||||
"foo >= 12",
|
|
||||||
"foo <> 12",
|
|
||||||
"foo == 12",
|
|
||||||
"foo != 12",
|
|
||||||
"foo contains 12",
|
|
||||||
"foo contains 'bar'",
|
|
||||||
"foo != 'bar'",
|
|
||||||
"'foo' contains 'bar'",
|
|
||||||
'234089',
|
|
||||||
"foo | default: -1",
|
|
||||||
]
|
|
||||||
|
|
||||||
EXPRESSIONS.each do |expr|
|
|
||||||
lexer_1_result = Liquid::Lexer1.new(expr).tokenize
|
|
||||||
lexer_2_result = Liquid::Lexer2.new(expr).tokenize
|
|
||||||
|
|
||||||
next if lexer_1_result == lexer_2_result
|
|
||||||
|
|
||||||
warn "Lexer1 and Lexer2 results are different for expression: #{expr}"
|
|
||||||
warn "expected: #{lexer_1_result}"
|
|
||||||
warn "got: #{lexer_2_result}"
|
|
||||||
abort
|
|
||||||
end
|
|
||||||
|
|
||||||
Benchmark.ips do |x|
|
|
||||||
x.config(time: 10, warmup: 5)
|
|
||||||
|
|
||||||
x.report("Liquid::Lexer1#tokenize") do
|
|
||||||
EXPRESSIONS.each do |expr|
|
|
||||||
l = Liquid::Lexer1.new(expr)
|
|
||||||
l.tokenize
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
x.report("Liquid::Lexer2#tokenize") do
|
|
||||||
EXPRESSIONS.each do |expr|
|
|
||||||
l = Liquid::Lexer2.new(expr)
|
|
||||||
l.tokenize
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
x.compare!
|
|
||||||
end
|
|
||||||
@@ -14,25 +14,19 @@ class AssignTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_assigned_variable
|
def test_assigned_variable
|
||||||
assert_template_result(
|
assert_template_result('.foo.',
|
||||||
'.foo.',
|
|
||||||
'{% assign foo = values %}.{{ foo[0] }}.',
|
'{% assign foo = values %}.{{ foo[0] }}.',
|
||||||
{ 'values' => %w(foo bar baz) },
|
{ 'values' => %w(foo bar baz) })
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result('.bar.',
|
||||||
'.bar.',
|
|
||||||
'{% assign foo = values %}.{{ foo[1] }}.',
|
'{% assign foo = values %}.{{ foo[1] }}.',
|
||||||
{ 'values' => %w(foo bar baz) },
|
{ 'values' => %w(foo bar baz) })
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_assign_with_filter
|
def test_assign_with_filter
|
||||||
assert_template_result(
|
assert_template_result('.bar.',
|
||||||
'.bar.',
|
|
||||||
'{% assign foo = values | split: "," %}.{{ foo[1] }}.',
|
'{% assign foo = values | split: "," %}.{{ foo[1] }}.',
|
||||||
{ 'values' => "foo,bar,baz" },
|
{ 'values' => "foo,bar,baz" })
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_assign_syntax_error
|
def test_assign_syntax_error
|
||||||
@@ -40,11 +34,8 @@ class AssignTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_assign_uses_error_mode
|
def test_assign_uses_error_mode
|
||||||
assert_match_syntax_error(
|
assert_match_syntax_error("Expected dotdot but found pipe in ",
|
||||||
"Expected dotdot but found pipe in ",
|
"{% assign foo = ('X' | downcase) %}", error_mode: :strict)
|
||||||
"{% assign foo = ('X' | downcase) %}",
|
|
||||||
error_mode: :strict,
|
|
||||||
)
|
|
||||||
assert_template_result("", "{% assign foo = ('X' | downcase) %}", error_mode: :lax)
|
assert_template_result("", "{% assign foo = ('X' | downcase) %}", error_mode: :lax)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class BlankTest < Minitest::Test
|
|||||||
|
|
||||||
def test_new_tags_are_not_blank_by_default
|
def test_new_tags_are_not_blank_by_default
|
||||||
with_custom_tag('foobar', FoobarTag) do
|
with_custom_tag('foobar', FoobarTag) do
|
||||||
assert_equal(" " * N, Liquid::Template.parse(wrap_in_for("{% foobar %}")).render!)
|
assert_template_result(" " * N, wrap_in_for("{% foobar %}"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -57,11 +57,9 @@ class BlankTest < Minitest::Test
|
|||||||
|
|
||||||
def test_nested_blocks_are_blank_but_only_if_all_children_are
|
def test_nested_blocks_are_blank_but_only_if_all_children_are
|
||||||
assert_template_result("", wrap(wrap(" ")))
|
assert_template_result("", wrap(wrap(" ")))
|
||||||
assert_template_result(
|
assert_template_result("\n but this is not " * (N + 1),
|
||||||
"\n but this is not " * (N + 1),
|
|
||||||
wrap('{% if true %} {% comment %} this is blank {% endcomment %} {% endif %}
|
wrap('{% if true %} {% comment %} this is blank {% endcomment %} {% endif %}
|
||||||
{% if true %} but this is not {% endif %}'),
|
{% if true %} but this is not {% endif %}'))
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_assigns_are_blank
|
def test_assigns_are_blank
|
||||||
@@ -91,21 +89,12 @@ class BlankTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_include_is_blank
|
def test_include_is_blank
|
||||||
assert_template_result(
|
assert_template_result("foobar" * (N + 1), wrap("{% include 'foobar' %}"),
|
||||||
"foobar" * (N + 1),
|
partials: { 'foobar' => 'foobar' })
|
||||||
wrap("{% include 'foobar' %}"),
|
assert_template_result(" foobar " * (N + 1), wrap("{% include ' foobar ' %}"),
|
||||||
partials: { 'foobar' => 'foobar' },
|
partials: { ' foobar ' => ' foobar ' })
|
||||||
)
|
assert_template_result(" " * (N + 1), wrap(" {% include ' ' %} "),
|
||||||
assert_template_result(
|
partials: { ' ' => ' ' })
|
||||||
" foobar " * (N + 1),
|
|
||||||
wrap("{% include ' foobar ' %}"),
|
|
||||||
partials: { ' foobar ' => ' foobar ' },
|
|
||||||
)
|
|
||||||
assert_template_result(
|
|
||||||
" " * (N + 1),
|
|
||||||
wrap(" {% include ' ' %} "),
|
|
||||||
partials: { ' ' => ' ' },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_case_is_blank
|
def test_case_is_blank
|
||||||
|
|||||||
@@ -36,24 +36,6 @@ class Category
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class ProductsDrop < Liquid::Drop
|
|
||||||
def initialize(products)
|
|
||||||
@products = products
|
|
||||||
end
|
|
||||||
|
|
||||||
def size
|
|
||||||
@products.size
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_liquid
|
|
||||||
if @context["forloop"]
|
|
||||||
@products.first(@context["forloop"].length)
|
|
||||||
else
|
|
||||||
@products
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class CategoryDrop < Liquid::Drop
|
class CategoryDrop < Liquid::Drop
|
||||||
attr_accessor :category, :context
|
attr_accessor :category, :context
|
||||||
|
|
||||||
@@ -139,23 +121,14 @@ class ContextTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_length_query
|
def test_length_query
|
||||||
assert_template_result(
|
assert_template_result("true", "{% if numbers.size == 4 %}true{% endif %}",
|
||||||
"true",
|
{ "numbers" => [1, 2, 3, 4] })
|
||||||
"{% if numbers.size == 4 %}true{% endif %}",
|
|
||||||
{ "numbers" => [1, 2, 3, 4] },
|
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result("true", "{% if numbers.size == 4 %}true{% endif %}",
|
||||||
"true",
|
{ "numbers" => { 1 => 1, 2 => 2, 3 => 3, 4 => 4 } })
|
||||||
"{% if numbers.size == 4 %}true{% endif %}",
|
|
||||||
{ "numbers" => { 1 => 1, 2 => 2, 3 => 3, 4 => 4 } },
|
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result("true", "{% if numbers.size == 1000 %}true{% endif %}",
|
||||||
"true",
|
{ "numbers" => { 1 => 1, 2 => 2, 3 => 3, 4 => 4, 'size' => 1000 } })
|
||||||
"{% if numbers.size == 1000 %}true{% endif %}",
|
|
||||||
{ "numbers" => { 1 => 1, 2 => 2, 3 => 3, 4 => 4, 'size' => 1000 } },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_hyphenated_variable
|
def test_hyphenated_variable
|
||||||
@@ -255,14 +228,12 @@ class ContextTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_hash_to_array_transition
|
def test_hash_to_array_transition
|
||||||
assigns = {
|
assigns = { 'colors' => {
|
||||||
'colors' => {
|
'Blue' => ['003366', '336699', '6699CC', '99CCFF'],
|
||||||
'Blue' => ['003366', '336699', '6699CC', '99CCFF'],
|
'Green' => ['003300', '336633', '669966', '99CC99'],
|
||||||
'Green' => ['003300', '336633', '669966', '99CC99'],
|
'Yellow' => ['CC9900', 'FFCC00', 'FFFF99', 'FFFFCC'],
|
||||||
'Yellow' => ['CC9900', 'FFCC00', 'FFFF99', 'FFFFCC'],
|
'Red' => ['660000', '993333', 'CC6666', 'FF9999'],
|
||||||
'Red' => ['660000', '993333', 'CC6666', 'FF9999'],
|
} }
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_template_result("003366", "{{ colors.Blue[0] }}", assigns)
|
assert_template_result("003366", "{{ colors.Blue[0] }}", assigns)
|
||||||
assert_template_result("FF9999", "{{ colors.Red[3] }}", assigns)
|
assert_template_result("FF9999", "{{ colors.Red[3] }}", assigns)
|
||||||
@@ -291,7 +262,7 @@ class ContextTest < Minitest::Test
|
|||||||
assigns = { 'product' => { 'variants' => [{ 'title' => 'draft151cm' }, { 'title' => 'element151cm' }] } }
|
assigns = { 'product' => { 'variants' => [{ 'title' => 'draft151cm' }, { 'title' => 'element151cm' }] } }
|
||||||
assert_template_result("draft151cm", '{{ product["variants"][0]["title"] }}', assigns)
|
assert_template_result("draft151cm", '{{ product["variants"][0]["title"] }}', assigns)
|
||||||
assert_template_result("element151cm", '{{ product["variants"][1]["title"] }}', assigns)
|
assert_template_result("element151cm", '{{ product["variants"][1]["title"] }}', assigns)
|
||||||
assert_template_result("draft151cm", '{{ product["variants"].first["title"] }}', assigns)
|
assert_template_result("draft151cm", '{{ product["variants"][0]["title"] }}', assigns)
|
||||||
assert_template_result("element151cm", '{{ product["variants"].last["title"] }}', assigns)
|
assert_template_result("element151cm", '{{ product["variants"].last["title"] }}', assigns)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -439,12 +410,10 @@ class ContextTest < Minitest::Test
|
|||||||
def test_nested_lambda_is_called_once
|
def test_nested_lambda_is_called_once
|
||||||
@global = 0
|
@global = 0
|
||||||
|
|
||||||
@context['callcount'] = {
|
@context['callcount'] = { "lambda" => proc {
|
||||||
"lambda" => proc {
|
@global += 1
|
||||||
@global += 1
|
@global.to_s
|
||||||
@global.to_s
|
} }
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_equal('1', @context['callcount.lambda'])
|
assert_equal('1', @context['callcount.lambda'])
|
||||||
assert_equal('1', @context['callcount.lambda'])
|
assert_equal('1', @context['callcount.lambda'])
|
||||||
@@ -454,11 +423,10 @@ class ContextTest < Minitest::Test
|
|||||||
def test_lambda_in_array_is_called_once
|
def test_lambda_in_array_is_called_once
|
||||||
@global = 0
|
@global = 0
|
||||||
|
|
||||||
p = proc {
|
@context['callcount'] = [1, 2, proc {
|
||||||
@global += 1
|
@global += 1
|
||||||
@global.to_s
|
@global.to_s
|
||||||
}
|
}, 4, 5]
|
||||||
@context['callcount'] = [1, 2, p, 4, 5]
|
|
||||||
|
|
||||||
assert_equal('1', @context['callcount[2]'])
|
assert_equal('1', @context['callcount[2]'])
|
||||||
assert_equal('1', @context['callcount[2]'])
|
assert_equal('1', @context['callcount[2]'])
|
||||||
@@ -505,7 +473,7 @@ class ContextTest < Minitest::Test
|
|||||||
def test_static_environments_are_read_with_lower_priority_than_environments
|
def test_static_environments_are_read_with_lower_priority_than_environments
|
||||||
context = Context.build(
|
context = Context.build(
|
||||||
static_environments: { 'shadowed' => 'static', 'unshadowed' => 'static' },
|
static_environments: { 'shadowed' => 'static', 'unshadowed' => 'static' },
|
||||||
environments: { 'shadowed' => 'dynamic' },
|
environments: { 'shadowed' => 'dynamic' }
|
||||||
)
|
)
|
||||||
|
|
||||||
assert_equal('dynamic', context['shadowed'])
|
assert_equal('dynamic', context['shadowed'])
|
||||||
@@ -653,25 +621,6 @@ class ContextTest < Minitest::Test
|
|||||||
assert_equal(:my_value, c.registers[:my_register])
|
assert_equal(:my_value, c.registers[:my_register])
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_variable_to_liquid_returns_contextual_drop
|
|
||||||
context = {
|
|
||||||
"products" => ProductsDrop.new(["A", "B", "C", "D", "E"]),
|
|
||||||
}
|
|
||||||
|
|
||||||
template = Liquid::Template.parse(<<~LIQUID)
|
|
||||||
{%- for i in (1..3) -%}
|
|
||||||
for_loop_products_count: {{ products | size }}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
unscoped_products_count: {{ products | size }}
|
|
||||||
LIQUID
|
|
||||||
|
|
||||||
result = template.render(context)
|
|
||||||
|
|
||||||
assert_includes(result, "for_loop_products_count: 3")
|
|
||||||
assert_includes(result, "unscoped_products_count: 5")
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def assert_no_object_allocations
|
def assert_no_object_allocations
|
||||||
|
|||||||
@@ -105,8 +105,7 @@ class ErrorHandlingTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_parsing_warn_with_line_numbers_adds_numbers_to_lexer_errors
|
def test_parsing_warn_with_line_numbers_adds_numbers_to_lexer_errors
|
||||||
template = Liquid::Template.parse(
|
template = Liquid::Template.parse('
|
||||||
'
|
|
||||||
foobar
|
foobar
|
||||||
|
|
||||||
{% if 1 =! 2 %}ok{% endif %}
|
{% if 1 =! 2 %}ok{% endif %}
|
||||||
@@ -114,19 +113,15 @@ class ErrorHandlingTest < Minitest::Test
|
|||||||
bla
|
bla
|
||||||
',
|
',
|
||||||
error_mode: :warn,
|
error_mode: :warn,
|
||||||
line_numbers: true,
|
line_numbers: true)
|
||||||
)
|
|
||||||
|
|
||||||
assert_equal(
|
assert_equal(['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
|
||||||
['Liquid syntax error (line 4): Unexpected character = in "1 =! 2"'],
|
template.warnings.map(&:message))
|
||||||
template.warnings.map(&:message),
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_parsing_strict_with_line_numbers_adds_numbers_to_lexer_errors
|
def test_parsing_strict_with_line_numbers_adds_numbers_to_lexer_errors
|
||||||
err = assert_raises(SyntaxError) do
|
err = assert_raises(SyntaxError) do
|
||||||
Liquid::Template.parse(
|
Liquid::Template.parse('
|
||||||
'
|
|
||||||
foobar
|
foobar
|
||||||
|
|
||||||
{% if 1 =! 2 %}ok{% endif %}
|
{% if 1 =! 2 %}ok{% endif %}
|
||||||
@@ -134,8 +129,7 @@ class ErrorHandlingTest < Minitest::Test
|
|||||||
bla
|
bla
|
||||||
',
|
',
|
||||||
error_mode: :strict,
|
error_mode: :strict,
|
||||||
line_numbers: true,
|
line_numbers: true)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_equal('Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message)
|
assert_equal('Liquid syntax error (line 4): Unexpected character = in "1 =! 2"', err.message)
|
||||||
@@ -219,21 +213,6 @@ class ErrorHandlingTest < Minitest::Test
|
|||||||
Liquid::Template.default_exception_renderer = old_exception_renderer if old_exception_renderer
|
Liquid::Template.default_exception_renderer = old_exception_renderer if old_exception_renderer
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_setting_exception_renderer_on_environment
|
|
||||||
exceptions = []
|
|
||||||
exception_renderer = ->(e) do
|
|
||||||
exceptions << e
|
|
||||||
''
|
|
||||||
end
|
|
||||||
|
|
||||||
environment = Liquid::Environment.build(exception_renderer: exception_renderer)
|
|
||||||
template = Liquid::Template.parse('This is a runtime error: {{ errors.argument_error }}', environment: environment)
|
|
||||||
output = template.render('errors' => ErrorDrop.new)
|
|
||||||
|
|
||||||
assert_equal('This is a runtime error: ', output)
|
|
||||||
assert_equal([Liquid::ArgumentError], template.errors.map(&:class))
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_exception_renderer_exposing_non_liquid_error
|
def test_exception_renderer_exposing_non_liquid_error
|
||||||
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
|
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
|
||||||
exceptions = []
|
exceptions = []
|
||||||
@@ -257,10 +236,16 @@ class ErrorHandlingTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_included_template_name_with_line_numbers
|
def test_included_template_name_with_line_numbers
|
||||||
environment = Liquid::Environment.build(file_system: TestFileSystem.new)
|
old_file_system = Liquid::Template.file_system
|
||||||
template = Liquid::Template.parse("Argument error:\n{% include 'product' %}", line_numbers: true, environment: environment)
|
|
||||||
page = template.render('errors' => ErrorDrop.new)
|
|
||||||
|
|
||||||
|
begin
|
||||||
|
Liquid::Template.file_system = TestFileSystem.new
|
||||||
|
|
||||||
|
template = Liquid::Template.parse("Argument error:\n{% include 'product' %}", line_numbers: true)
|
||||||
|
page = template.render('errors' => ErrorDrop.new)
|
||||||
|
ensure
|
||||||
|
Liquid::Template.file_system = old_file_system
|
||||||
|
end
|
||||||
assert_equal("Argument error:\nLiquid error (product line 1): argument error", page)
|
assert_equal("Argument error:\nLiquid error (product line 1): argument error", page)
|
||||||
assert_equal("product", template.errors.first.template_name)
|
assert_equal("product", template.errors.first.template_name)
|
||||||
end
|
end
|
||||||
@@ -272,81 +257,4 @@ class ErrorHandlingTest < Minitest::Test
|
|||||||
output = Liquid::Template.parse("{% assign x = 0 %}{% if 1 < '2' %}{% assign x = 3 %}{% endif %}{{ x }}").render
|
output = Liquid::Template.parse("{% assign x = 0 %}{% if 1 < '2' %}{% assign x = 3 %}{% endif %}{{ x }}").render
|
||||||
assert_equal("0", output)
|
assert_equal("0", output)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_syntax_error_is_raised_with_template_name
|
|
||||||
file_system = StubFileSystem.new("snippet" => "1\n2\n{{ 1")
|
|
||||||
|
|
||||||
context = Liquid::Context.build(
|
|
||||||
registers: { file_system: file_system },
|
|
||||||
)
|
|
||||||
|
|
||||||
template = Template.parse(
|
|
||||||
'{% render "snippet" %}',
|
|
||||||
line_numbers: true,
|
|
||||||
)
|
|
||||||
template.name = "template/index"
|
|
||||||
|
|
||||||
assert_equal(
|
|
||||||
"Liquid syntax error (snippet line 3): Variable '{{' was not properly terminated with regexp: /\\}\\}/",
|
|
||||||
template.render(context),
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_syntax_error_is_raised_with_template_name_from_template_factory
|
|
||||||
file_system = StubFileSystem.new("snippet" => "1\n2\n{{ 1")
|
|
||||||
|
|
||||||
context = Liquid::Context.build(
|
|
||||||
registers: {
|
|
||||||
file_system: file_system,
|
|
||||||
template_factory: StubTemplateFactory.new,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
template = Template.parse(
|
|
||||||
'{% render "snippet" %}',
|
|
||||||
line_numbers: true,
|
|
||||||
)
|
|
||||||
template.name = "template/index"
|
|
||||||
|
|
||||||
assert_equal(
|
|
||||||
"Liquid syntax error (some/path/snippet line 3): Variable '{{' was not properly terminated with regexp: /\\}\\}/",
|
|
||||||
template.render(context),
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_error_is_raised_during_parse_with_template_name
|
|
||||||
depth = Liquid::Block::MAX_DEPTH + 1
|
|
||||||
code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
|
|
||||||
|
|
||||||
template = Template.parse("{% render 'snippet' %}", line_numbers: true)
|
|
||||||
|
|
||||||
context = Liquid::Context.build(
|
|
||||||
registers: {
|
|
||||||
file_system: StubFileSystem.new("snippet" => code),
|
|
||||||
template_factory: StubTemplateFactory.new,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert_equal("Liquid error (some/path/snippet line 1): Nesting too deep", template.render(context))
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_internal_error_is_raised_with_template_name
|
|
||||||
template = Template.new
|
|
||||||
template.parse(
|
|
||||||
"{% render 'snippet' %}",
|
|
||||||
line_numbers: true,
|
|
||||||
)
|
|
||||||
template.name = "template/index"
|
|
||||||
|
|
||||||
context = Liquid::Context.build(
|
|
||||||
registers: {
|
|
||||||
file_system: StubFileSystem.new({}),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
assert_equal(
|
|
||||||
"Liquid error (template/index line 1): internal",
|
|
||||||
template.render(context),
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ class ExpressionTest < Minitest::Test
|
|||||||
|
|
||||||
assert_match_syntax_error(
|
assert_match_syntax_error(
|
||||||
"Liquid syntax error (line 1): Invalid expression type 'false' in range expression",
|
"Liquid syntax error (line 1): Invalid expression type 'false' in range expression",
|
||||||
"{{ (false..true) }}",
|
"{{ (false..true) }}"
|
||||||
)
|
)
|
||||||
assert_match_syntax_error(
|
assert_match_syntax_error(
|
||||||
"Liquid syntax error (line 1): Invalid expression type '(1..2)' in range expression",
|
"Liquid syntax error (line 1): Invalid expression type '(1..2)' in range expression",
|
||||||
"{{ ((1..2)..3) }}",
|
"{{ ((1..2)..3) }}"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class FilterKwargTest < Minitest::Test
|
|||||||
with_global_filter(KwargFilter) do
|
with_global_filter(KwargFilter) do
|
||||||
assert_equal(
|
assert_equal(
|
||||||
"data-src='src' data-widths='100, 200'",
|
"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
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -68,34 +68,22 @@ class FiltersTest < Minitest::Test
|
|||||||
|
|
||||||
def test_sort
|
def test_sort
|
||||||
assert_template_result("1 2 3 4", "{{numbers | sort | join}}", { "numbers" => [2, 1, 4, 3] })
|
assert_template_result("1 2 3 4", "{{numbers | sort | join}}", { "numbers" => [2, 1, 4, 3] })
|
||||||
assert_template_result(
|
assert_template_result("alphabetic as expected", "{{words | sort | join}}",
|
||||||
"alphabetic as expected",
|
{ "words" => ['expected', 'as', 'alphabetic'] })
|
||||||
"{{words | sort | join}}",
|
|
||||||
{ "words" => ['expected', 'as', 'alphabetic'] },
|
|
||||||
)
|
|
||||||
assert_template_result("3", "{{value | sort}}", { "value" => 3 })
|
assert_template_result("3", "{{value | sort}}", { "value" => 3 })
|
||||||
assert_template_result('are flower', "{{arrays | sort | join}}", { 'arrays' => ['flower', 'are'] })
|
assert_template_result('are flower', "{{arrays | sort | join}}", { 'arrays' => ['flower', 'are'] })
|
||||||
assert_template_result(
|
assert_template_result("Expected case sensitive", "{{case_sensitive | sort | join}}",
|
||||||
"Expected case sensitive",
|
{ "case_sensitive" => ["sensitive", "Expected", "case"] })
|
||||||
"{{case_sensitive | sort | join}}",
|
|
||||||
{ "case_sensitive" => ["sensitive", "Expected", "case"] },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_sort_natural
|
def test_sort_natural
|
||||||
# Test strings
|
# Test strings
|
||||||
assert_template_result(
|
assert_template_result("Assert case Insensitive", "{{words | sort_natural | join}}",
|
||||||
"Assert case Insensitive",
|
{ "words" => ["case", "Assert", "Insensitive"] })
|
||||||
"{{words | sort_natural | join}}",
|
|
||||||
{ "words" => ["case", "Assert", "Insensitive"] },
|
|
||||||
)
|
|
||||||
|
|
||||||
# Test hashes
|
# Test hashes
|
||||||
assert_template_result(
|
assert_template_result("A b C", "{{hashes | sort_natural: 'a' | map: 'a' | join}}",
|
||||||
"A b C",
|
{ "hashes" => [{ "a" => "A" }, { "a" => "b" }, { "a" => "C" }] })
|
||||||
"{{hashes | sort_natural: 'a' | map: 'a' | join}}",
|
|
||||||
{ "hashes" => [{ "a" => "A" }, { "a" => "b" }, { "a" => "C" }] },
|
|
||||||
)
|
|
||||||
|
|
||||||
# Test objects
|
# Test objects
|
||||||
@context['objects'] = [TestObject.new('A'), TestObject.new('b'), TestObject.new('C')]
|
@context['objects'] = [TestObject.new('A'), TestObject.new('b'), TestObject.new('C')]
|
||||||
@@ -104,18 +92,12 @@ class FiltersTest < Minitest::Test
|
|||||||
|
|
||||||
def test_compact
|
def test_compact
|
||||||
# Test strings
|
# Test strings
|
||||||
assert_template_result(
|
assert_template_result("a b c", "{{words | compact | join}}",
|
||||||
"a b c",
|
{ "words" => ['a', nil, 'b', nil, 'c'] })
|
||||||
"{{words | compact | join}}",
|
|
||||||
{ "words" => ['a', nil, 'b', nil, 'c'] },
|
|
||||||
)
|
|
||||||
|
|
||||||
# Test hashes
|
# Test hashes
|
||||||
assert_template_result(
|
assert_template_result("A C", "{{hashes | compact: 'a' | map: 'a' | join}}",
|
||||||
"A C",
|
{ "hashes" => [{ "a" => "A" }, { "a" => nil }, { "a" => "C" }] })
|
||||||
"{{hashes | compact: 'a' | map: 'a' | join}}",
|
|
||||||
{ "hashes" => [{ "a" => "A" }, { "a" => nil }, { "a" => "C" }] },
|
|
||||||
)
|
|
||||||
|
|
||||||
# Test objects
|
# Test objects
|
||||||
@context['objects'] = [TestObject.new('A'), TestObject.new(nil), TestObject.new('C')]
|
@context['objects'] = [TestObject.new('A'), TestObject.new(nil), TestObject.new('C')]
|
||||||
@@ -127,11 +109,8 @@ class FiltersTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_strip_html_ignore_comments_with_html
|
def test_strip_html_ignore_comments_with_html
|
||||||
assert_template_result(
|
assert_template_result("bla blub", "{{ var | strip_html }}",
|
||||||
"bla blub",
|
{ "var" => "<!-- split and some <ul> tag --><b>bla blub</a>" })
|
||||||
"{{ var | strip_html }}",
|
|
||||||
{ "var" => "<!-- split and some <ul> tag --><b>bla blub</a>" },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_capitalize
|
def test_capitalize
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class TestDrop < Liquid::Drop
|
|||||||
attr_reader :value
|
attr_reader :value
|
||||||
|
|
||||||
def registers
|
def registers
|
||||||
"{#{@value.inspect}=>#{@context.registers[@value].inspect}}"
|
{ @value => @context.registers[@value] }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -109,10 +109,6 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_raises(Liquid::ArgumentError) do
|
assert_raises(Liquid::ArgumentError) do
|
||||||
@filters.slice('foobar', 0, "")
|
@filters.slice('foobar', 0, "")
|
||||||
end
|
end
|
||||||
assert_equal("", @filters.slice("foobar", 0, -(1 << 64)))
|
|
||||||
assert_equal("foobar", @filters.slice("foobar", 0, 1 << 63))
|
|
||||||
assert_equal("", @filters.slice("foobar", 1 << 63, 6))
|
|
||||||
assert_equal("", @filters.slice("foobar", -(1 << 63), 6))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_slice_on_arrays
|
def test_slice_on_arrays
|
||||||
@@ -127,10 +123,6 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal(%w(r), @filters.slice(input, -1))
|
assert_equal(%w(r), @filters.slice(input, -1))
|
||||||
assert_equal(%w(), @filters.slice(input, 100, 10))
|
assert_equal(%w(), @filters.slice(input, 100, 10))
|
||||||
assert_equal(%w(), @filters.slice(input, -100, 10))
|
assert_equal(%w(), @filters.slice(input, -100, 10))
|
||||||
assert_equal([], @filters.slice(input, 0, -(1 << 64)))
|
|
||||||
assert_equal(input, @filters.slice(input, 0, 1 << 63))
|
|
||||||
assert_equal([], @filters.slice(input, 1 << 63, 6))
|
|
||||||
assert_equal([], @filters.slice(input, -(1 << 63), 6))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_truncate
|
def test_truncate
|
||||||
@@ -140,8 +132,6 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal('1234567890', @filters.truncate('1234567890'))
|
assert_equal('1234567890', @filters.truncate('1234567890'))
|
||||||
assert_equal("测试...", @filters.truncate("测试测试测试测试", 5))
|
assert_equal("测试...", @filters.truncate("测试测试测试测试", 5))
|
||||||
assert_equal('12341', @filters.truncate("1234567890", 5, 1))
|
assert_equal('12341', @filters.truncate("1234567890", 5, 1))
|
||||||
assert_equal("foobar", @filters.truncate("foobar", 1 << 63))
|
|
||||||
assert_equal("...", @filters.truncate("foobar", -(1 << 63)))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_split
|
def test_split
|
||||||
@@ -176,17 +166,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_base64_decode
|
def test_base64_decode
|
||||||
decoded = @filters.base64_decode('b25lIHR3byB0aHJlZQ==')
|
assert_equal('one two three', @filters.base64_decode('b25lIHR3byB0aHJlZQ=='))
|
||||||
assert_equal('one two three', decoded)
|
|
||||||
assert_equal(Encoding::UTF_8, decoded.encoding)
|
|
||||||
|
|
||||||
decoded = @filters.base64_decode('4pyF')
|
|
||||||
assert_equal('✅', decoded)
|
|
||||||
assert_equal(Encoding::UTF_8, decoded.encoding)
|
|
||||||
|
|
||||||
decoded = @filters.base64_decode("/w==")
|
|
||||||
assert_equal(Encoding::ASCII_8BIT, decoded.encoding)
|
|
||||||
assert_equal((+"\xFF").force_encoding(Encoding::ASCII_8BIT), decoded)
|
|
||||||
|
|
||||||
exception = assert_raises(Liquid::ArgumentError) do
|
exception = assert_raises(Liquid::ArgumentError) do
|
||||||
@filters.base64_decode("invalidbase64")
|
@filters.base64_decode("invalidbase64")
|
||||||
@@ -198,27 +178,16 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
def test_base64_url_safe_encode
|
def test_base64_url_safe_encode
|
||||||
assert_equal(
|
assert_equal(
|
||||||
'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMTIzNDU2Nzg5MCAhQCMkJV4mKigpLT1fKy8_Ljo7W117fVx8',
|
'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))
|
assert_equal('', @filters.base64_url_safe_encode(nil))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_base64_url_safe_decode
|
def test_base64_url_safe_decode
|
||||||
decoded = @filters.base64_url_safe_decode('YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMTIzNDU2Nzg5MCAhQCMkJV4mKigpLT1fKy8_Ljo7W117fVx8')
|
|
||||||
assert_equal(
|
assert_equal(
|
||||||
'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 !@#$%^&*()-=_+/?.:;[]{}\|',
|
'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 !@#$%^&*()-=_+/?.:;[]{}\|',
|
||||||
decoded,
|
@filters.base64_url_safe_decode('YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMTIzNDU2Nzg5MCAhQCMkJV4mKigpLT1fKy8_Ljo7W117fVx8')
|
||||||
)
|
)
|
||||||
assert_equal(Encoding::UTF_8, decoded.encoding)
|
|
||||||
|
|
||||||
decoded = @filters.base64_url_safe_decode('4pyF')
|
|
||||||
assert_equal('✅', decoded)
|
|
||||||
assert_equal(Encoding::UTF_8, decoded.encoding)
|
|
||||||
|
|
||||||
decoded = @filters.base64_url_safe_decode("_w==")
|
|
||||||
assert_equal(Encoding::ASCII_8BIT, decoded.encoding)
|
|
||||||
assert_equal((+"\xFF").force_encoding(Encoding::ASCII_8BIT), decoded)
|
|
||||||
|
|
||||||
exception = assert_raises(Liquid::ArgumentError) do
|
exception = assert_raises(Liquid::ArgumentError) do
|
||||||
@filters.base64_url_safe_decode("invalidbase64")
|
@filters.base64_url_safe_decode("invalidbase64")
|
||||||
end
|
end
|
||||||
@@ -251,15 +220,17 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal('one two three', @filters.truncatewords('one two three'))
|
assert_equal('one two three', @filters.truncatewords('one two three'))
|
||||||
assert_equal(
|
assert_equal(
|
||||||
'Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13”...',
|
'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("测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5))
|
||||||
assert_equal('one two1', @filters.truncatewords("one two three", 2, 1))
|
assert_equal('one two1', @filters.truncatewords("one two three", 2, 1))
|
||||||
assert_equal('one two three...', @filters.truncatewords("one two\tthree\nfour", 3))
|
assert_equal('one two three...', @filters.truncatewords("one two\tthree\nfour", 3))
|
||||||
assert_equal('one two...', @filters.truncatewords("one two three four", 2))
|
assert_equal('one two...', @filters.truncatewords("one two three four", 2))
|
||||||
assert_equal('one...', @filters.truncatewords("one two three four", 0))
|
assert_equal('one...', @filters.truncatewords("one two three four", 0))
|
||||||
assert_equal('one two three four', @filters.truncatewords("one two three four", 1 << 31))
|
exception = assert_raises(Liquid::ArgumentError) do
|
||||||
assert_equal('one...', @filters.truncatewords("one two three four", -(1 << 32)))
|
@filters.truncatewords("one two three four", 1 << 31)
|
||||||
|
end
|
||||||
|
assert_equal("Liquid error: integer #{1 << 31} too big for truncatewords", exception.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_strip_html
|
def test_strip_html
|
||||||
@@ -331,8 +302,8 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
{ "price" => "1", "handle" => "gamma" },
|
{ "price" => "1", "handle" => "gamma" },
|
||||||
{ "price" => 2, "handle" => "epsilon" },
|
{ "price" => 2, "handle" => "epsilon" },
|
||||||
{ "price" => "4", "handle" => "alpha" },
|
{ "price" => "4", "handle" => "alpha" },
|
||||||
{ "handle" => "beta" },
|
|
||||||
{ "handle" => "delta" },
|
{ "handle" => "delta" },
|
||||||
|
{ "handle" => "beta" },
|
||||||
]
|
]
|
||||||
assert_equal(expectation, @filters.sort_natural(input, "price"))
|
assert_equal(expectation, @filters.sort_natural(input, "price"))
|
||||||
end
|
end
|
||||||
@@ -454,11 +425,8 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
|
|
||||||
def test_map
|
def test_map
|
||||||
assert_equal([1, 2, 3, 4], @filters.map([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], 'a'))
|
assert_equal([1, 2, 3, 4], @filters.map([{ "a" => 1 }, { "a" => 2 }, { "a" => 3 }, { "a" => 4 }], 'a'))
|
||||||
assert_template_result(
|
assert_template_result('abc', "{{ ary | map:'foo' | map:'bar' }}",
|
||||||
'abc',
|
{ 'ary' => [{ 'foo' => { 'bar' => 'a' } }, { 'foo' => { 'bar' => 'b' } }, { 'foo' => { 'bar' => 'c' } }] })
|
||||||
"{{ ary | map:'foo' | map:'bar' }}",
|
|
||||||
{ 'ary' => [{ 'foo' => { 'bar' => 'a' } }, { 'foo' => { 'bar' => 'b' } }, { 'foo' => { 'bar' => 'c' } }] },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_map_doesnt_call_arbitrary_stuff
|
def test_map_doesnt_call_arbitrary_stuff
|
||||||
@@ -482,11 +450,8 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_map_on_hashes
|
def test_map_on_hashes
|
||||||
assert_template_result(
|
assert_template_result("4217", '{{ thing | map: "foo" | map: "bar" }}',
|
||||||
"4217",
|
{ "thing" => { "foo" => [{ "bar" => 42 }, { "bar" => 17 }] } })
|
||||||
'{{ thing | map: "foo" | map: "bar" }}',
|
|
||||||
{ "thing" => { "foo" => [{ "bar" => 42 }, { "bar" => 17 }] } },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_legacy_map_on_hashes_with_dynamic_key
|
def test_legacy_map_on_hashes_with_dynamic_key
|
||||||
@@ -504,8 +469,8 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
def test_map_over_proc
|
def test_map_over_proc
|
||||||
drop = TestDrop.new(value: "testfoo")
|
drop = TestDrop.new(value: "testfoo")
|
||||||
p = proc { drop }
|
p = proc { drop }
|
||||||
output = Liquid::Template.parse('{{ procs | map: "value" }}').render!({ "procs" => [p] })
|
templ = '{{ procs | map: "value" }}'
|
||||||
assert_equal("testfoo", output)
|
assert_template_result("testfoo", templ, { "procs" => [p] })
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_map_over_drops_returning_procs
|
def test_map_over_drops_returning_procs
|
||||||
@@ -517,13 +482,12 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
"proc" => -> { "bar" },
|
"proc" => -> { "bar" },
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
output = Liquid::Template.parse('{{ drops | map: "proc" }}').render!({ "drops" => drops })
|
templ = '{{ drops | map: "proc" }}'
|
||||||
assert_equal("foobar", output)
|
assert_template_result("foobar", templ, { "drops" => drops })
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_map_works_on_enumerables
|
def test_map_works_on_enumerables
|
||||||
output = Liquid::Template.parse('{{ foo | map: "foo" }}').render!({ "foo" => TestEnumerable.new })
|
assert_template_result("123", '{{ foo | map: "foo" }}', { "foo" => TestEnumerable.new })
|
||||||
assert_equal("123", output)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_map_returns_empty_on_2d_input_array
|
def test_map_returns_empty_on_2d_input_array
|
||||||
@@ -949,108 +913,6 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal([{ "foo" => true }, { "foo" => "for sure" }], @filters.where(input, "foo"))
|
assert_equal([{ "foo" => true }, { "foo" => "for sure" }], @filters.where(input, "foo"))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_sum_with_all_numbers
|
|
||||||
input = [1, 2]
|
|
||||||
|
|
||||||
assert_equal(3, @filters.sum(input))
|
|
||||||
assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do
|
|
||||||
@filters.sum(input, "quantity")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sum_with_numeric_strings
|
|
||||||
input = [1, 2, "3", "4"]
|
|
||||||
|
|
||||||
assert_equal(10, @filters.sum(input))
|
|
||||||
assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do
|
|
||||||
@filters.sum(input, "quantity")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sum_with_nested_arrays
|
|
||||||
input = [1, [2, [3, 4]]]
|
|
||||||
|
|
||||||
assert_equal(10, @filters.sum(input))
|
|
||||||
assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do
|
|
||||||
@filters.sum(input, "quantity")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sum_with_indexable_map_values
|
|
||||||
input = [{ "quantity" => 1 }, { "quantity" => 2, "weight" => 3 }, { "weight" => 4 }]
|
|
||||||
|
|
||||||
assert_equal(0, @filters.sum(input))
|
|
||||||
assert_equal(3, @filters.sum(input, "quantity"))
|
|
||||||
assert_equal(7, @filters.sum(input, "weight"))
|
|
||||||
assert_equal(0, @filters.sum(input, "subtotal"))
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sum_with_indexable_non_map_values
|
|
||||||
input = [1, [2], "foo", { "quantity" => 3 }]
|
|
||||||
|
|
||||||
assert_equal(3, @filters.sum(input))
|
|
||||||
assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do
|
|
||||||
@filters.sum(input, "quantity")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sum_with_unindexable_values
|
|
||||||
input = [1, true, nil, { "quantity" => 2 }]
|
|
||||||
|
|
||||||
assert_equal(1, @filters.sum(input))
|
|
||||||
assert_raises(Liquid::ArgumentError, "cannot select the property 'quantity'") do
|
|
||||||
@filters.sum(input, "quantity")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sum_without_property_calls_to_liquid
|
|
||||||
t = TestThing.new
|
|
||||||
Liquid::Template.parse('{{ foo | sum }}').render("foo" => [t])
|
|
||||||
assert(t.foo > 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sum_with_property_calls_to_liquid_on_property_values
|
|
||||||
t = TestThing.new
|
|
||||||
Liquid::Template.parse('{{ foo | sum: "quantity" }}').render("foo" => [{ "quantity" => t }])
|
|
||||||
assert(t.foo > 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sum_of_floats
|
|
||||||
input = [0.1, 0.2, 0.3]
|
|
||||||
assert_equal(0.6, @filters.sum(input))
|
|
||||||
assert_template_result("0.6", "{{ input | sum }}", { "input" => input })
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sum_of_negative_floats
|
|
||||||
input = [0.1, 0.2, -0.3]
|
|
||||||
assert_equal(0.0, @filters.sum(input))
|
|
||||||
assert_template_result("0.0", "{{ input | sum }}", { "input" => input })
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sum_with_float_strings
|
|
||||||
input = [0.1, "0.2", "0.3"]
|
|
||||||
assert_equal(0.6, @filters.sum(input))
|
|
||||||
assert_template_result("0.6", "{{ input | sum }}", { "input" => input })
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sum_resulting_in_negative_float
|
|
||||||
input = [0.1, -0.2, -0.3]
|
|
||||||
assert_equal(-0.4, @filters.sum(input))
|
|
||||||
assert_template_result("-0.4", "{{ input | sum }}", { "input" => input })
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_sum_with_floats_and_indexable_map_values
|
|
||||||
input = [{ "quantity" => 1 }, { "quantity" => 0.2, "weight" => -0.3 }, { "weight" => 0.4 }]
|
|
||||||
assert_equal(0.0, @filters.sum(input))
|
|
||||||
assert_equal(1.2, @filters.sum(input, "quantity"))
|
|
||||||
assert_equal(0.1, @filters.sum(input, "weight"))
|
|
||||||
assert_equal(0.0, @filters.sum(input, "subtotal"))
|
|
||||||
assert_template_result("0", "{{ input | sum }}", { "input" => input })
|
|
||||||
assert_template_result("1.2", "{{ input | sum: 'quantity' }}", { "input" => input })
|
|
||||||
assert_template_result("0.1", "{{ input | sum: 'weight' }}", { "input" => input })
|
|
||||||
assert_template_result("0", "{{ input | sum: 'subtotal' }}", { "input" => input })
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def with_timezone(tz)
|
def with_timezone(tz)
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ class BreakTagTest < Minitest::Test
|
|||||||
# block
|
# block
|
||||||
def test_break_with_no_block
|
def test_break_with_no_block
|
||||||
assigns = { 'i' => 1 }
|
assigns = { 'i' => 1 }
|
||||||
markup = 'before{% break %}after'
|
markup = '{% break %}'
|
||||||
expected = 'before'
|
expected = ''
|
||||||
|
|
||||||
assert_template_result(expected, markup, assigns)
|
assert_template_result(expected, markup, assigns)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -73,11 +73,9 @@ HERE
|
|||||||
|
|
||||||
def test_for_helpers
|
def test_for_helpers
|
||||||
assigns = { 'array' => [1, 2, 3] }
|
assigns = { 'array' => [1, 2, 3] }
|
||||||
assert_template_result(
|
assert_template_result(' 1/3 2/3 3/3 ',
|
||||||
' 1/3 2/3 3/3 ',
|
|
||||||
'{%for item in array%} {{forloop.index}}/{{forloop.length}} {%endfor%}',
|
'{%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(' 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(' 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)
|
assert_template_result(' 2 1 0 ', '{%for item in array%} {{forloop.rindex0}} {%endfor%}', assigns)
|
||||||
@@ -88,11 +86,9 @@ HERE
|
|||||||
|
|
||||||
def test_for_and_if
|
def test_for_and_if
|
||||||
assigns = { 'array' => [1, 2, 3] }
|
assigns = { 'array' => [1, 2, 3] }
|
||||||
assert_template_result(
|
assert_template_result('+--',
|
||||||
'+--',
|
|
||||||
'{%for item in array%}{% if forloop.first %}+{% else %}-{% endif %}{%endfor%}',
|
'{%for item in array%}{% if forloop.first %}+{% else %}-{% endif %}{%endfor%}',
|
||||||
assigns,
|
assigns)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_for_else
|
def test_for_else
|
||||||
@@ -107,7 +103,6 @@ HERE
|
|||||||
assert_template_result('1234', '{%for i in array limit:4 %}{{ i }}{%endfor%}', assigns)
|
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)
|
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
|
end
|
||||||
|
|
||||||
def test_limiting_with_invalid_limit
|
def test_limiting_with_invalid_limit
|
||||||
@@ -268,19 +263,6 @@ HERE
|
|||||||
assert_template_result(expected, markup, assigns)
|
assert_template_result(expected, markup, assigns)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_for_with_break_after_nested_loop
|
|
||||||
source = <<~LIQUID.chomp
|
|
||||||
{% for i in (1..2) -%}
|
|
||||||
{% for j in (1..2) -%}
|
|
||||||
{{ i }}-{{ j }},
|
|
||||||
{%- endfor -%}
|
|
||||||
{% break -%}
|
|
||||||
{% endfor -%}
|
|
||||||
after
|
|
||||||
LIQUID
|
|
||||||
assert_template_result("1-1,1-2,after", source)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_for_with_continue
|
def test_for_with_continue
|
||||||
assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5] } }
|
assigns = { 'array' => { 'items' => [1, 2, 3, 4, 5] } }
|
||||||
|
|
||||||
@@ -329,20 +311,15 @@ HERE
|
|||||||
# ruby 1.9.3 no longer supports .each on String though we mimic
|
# ruby 1.9.3 no longer supports .each on String though we mimic
|
||||||
# the functionality for backwards compatibility
|
# the functionality for backwards compatibility
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result('test string',
|
||||||
'test string',
|
|
||||||
'{%for val in string%}{{val}}{%endfor%}',
|
'{%for val in string%}{{val}}{%endfor%}',
|
||||||
{ 'string' => "test string" },
|
{ 'string' => "test string" })
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result('test string',
|
||||||
'test string',
|
|
||||||
'{%for val in string limit:1%}{{val}}{%endfor%}',
|
'{%for val in string limit:1%}{{val}}{%endfor%}',
|
||||||
{ 'string' => "test string" },
|
{ 'string' => "test string" })
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result('val-string-1-1-0-1-0-true-true-test string',
|
||||||
'val-string-1-1-0-1-0-true-true-test string',
|
|
||||||
'{%for val in string%}' \
|
'{%for val in string%}' \
|
||||||
'{{forloop.name}}-' \
|
'{{forloop.name}}-' \
|
||||||
'{{forloop.index}}-' \
|
'{{forloop.index}}-' \
|
||||||
@@ -353,28 +330,23 @@ HERE
|
|||||||
'{{forloop.first}}-' \
|
'{{forloop.first}}-' \
|
||||||
'{{forloop.last}}-' \
|
'{{forloop.last}}-' \
|
||||||
'{{val}}{%endfor%}',
|
'{{val}}{%endfor%}',
|
||||||
{ 'string' => "test string" },
|
{ 'string' => "test string" })
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_for_parentloop_references_parent_loop
|
def test_for_parentloop_references_parent_loop
|
||||||
assert_template_result(
|
assert_template_result('1.1 1.2 1.3 2.1 2.2 2.3 ',
|
||||||
'1.1 1.2 1.3 2.1 2.2 2.3 ',
|
|
||||||
'{% for inner in outer %}{% for k in inner %}' \
|
'{% for inner in outer %}{% for k in inner %}' \
|
||||||
'{{ forloop.parentloop.index }}.{{ forloop.index }} ' \
|
'{{ forloop.parentloop.index }}.{{ forloop.index }} ' \
|
||||||
'{% endfor %}{% endfor %}',
|
'{% endfor %}{% endfor %}',
|
||||||
{ 'outer' => [[1, 1, 1], [1, 1, 1]] },
|
{ 'outer' => [[1, 1, 1], [1, 1, 1]] })
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_for_parentloop_nil_when_not_present
|
def test_for_parentloop_nil_when_not_present
|
||||||
assert_template_result(
|
assert_template_result('.1 .2 ',
|
||||||
'.1 .2 ',
|
|
||||||
'{% for inner in outer %}' \
|
'{% for inner in outer %}' \
|
||||||
'{{ forloop.parentloop.index }}.{{ forloop.index }} ' \
|
'{{ forloop.parentloop.index }}.{{ forloop.index }} ' \
|
||||||
'{% endfor %}',
|
'{% endfor %}',
|
||||||
{ 'outer' => [[1, 1, 1], [1, 1, 1]] },
|
{ 'outer' => [[1, 1, 1], [1, 1, 1]] })
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_inner_for_over_empty_input
|
def test_inner_for_over_empty_input
|
||||||
@@ -465,4 +437,30 @@ HERE
|
|||||||
|
|
||||||
assert(context.registers[:for_stack].empty?)
|
assert(context.registers[:for_stack].empty?)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_instrument_for_offset_continue
|
||||||
|
assert_usage_increment('for_offset_continue') do
|
||||||
|
Template.parse('{% for item in items offset:continue %}{{item}}{% endfor %}')
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_usage_increment('for_offset_continue', times: 0) do
|
||||||
|
Template.parse('{% for item in items offset:2 %}{{item}}{% endfor %}')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_instrument_forloop_drop_name
|
||||||
|
assigns = { 'items' => [1, 2, 3, 4, 5] }
|
||||||
|
|
||||||
|
assert_usage_increment('forloop_drop_name', times: 5) do
|
||||||
|
Template.parse('{% for item in items %}{{forloop.name}}{% endfor %}').render!(assigns)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_usage_increment('forloop_drop_name', times: 0) do
|
||||||
|
Template.parse('{% for item in items %}{{forloop.index}}{% endfor %}').render!(assigns)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_usage_increment('forloop_drop_name', times: 0) do
|
||||||
|
Template.parse('{% for item in items %}{{item}}{% endfor %}').render!(assigns)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,10 +7,8 @@ class IfElseTagTest < Minitest::Test
|
|||||||
|
|
||||||
def test_if
|
def test_if
|
||||||
assert_template_result(' ', ' {% if false %} this text should not go into the output {% endif %} ')
|
assert_template_result(' ', ' {% if false %} this text should not go into the output {% endif %} ')
|
||||||
assert_template_result(
|
assert_template_result(' this text should go into the output ',
|
||||||
' this text should go into the output ',
|
' {% if true %} this text should go into the output {% endif %} ')
|
||||||
' {% 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 %}?')
|
assert_template_result(' you rock ?', '{% if false %} you suck {% endif %} {% if true %} you rock {% endif %}?')
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -53,22 +51,18 @@ class IfElseTagTest < Minitest::Test
|
|||||||
|
|
||||||
def test_comparison_of_expressions_starting_with_and_or_or
|
def test_comparison_of_expressions_starting_with_and_or_or
|
||||||
assigns = { 'order' => { 'items_count' => 0 }, 'android' => { 'name' => 'Roy' } }
|
assigns = { 'order' => { 'items_count' => 0 }, 'android' => { 'name' => 'Roy' } }
|
||||||
assert_template_result(
|
assert_template_result("YES",
|
||||||
"YES",
|
|
||||||
"{% if android.name == 'Roy' %}YES{% endif %}",
|
"{% if android.name == 'Roy' %}YES{% endif %}",
|
||||||
assigns,
|
assigns)
|
||||||
)
|
assert_template_result("YES",
|
||||||
assert_template_result(
|
|
||||||
"YES",
|
|
||||||
"{% if order.items_count == 0 %}YES{% endif %}",
|
"{% if order.items_count == 0 %}YES{% endif %}",
|
||||||
assigns,
|
assigns)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_if_and
|
def test_if_and
|
||||||
assert_template_result(' YES ', '{% if true and true %} YES {% endif %}')
|
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 %}')
|
assert_template_result('', '{% if false and true %} YES {% endif %}')
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_hash_miss_generates_false
|
def test_hash_miss_generates_false
|
||||||
@@ -161,10 +155,8 @@ class IfElseTagTest < Minitest::Test
|
|||||||
original_op = Condition.operators['contains']
|
original_op = Condition.operators['contains']
|
||||||
Condition.operators['contains'] = :[]
|
Condition.operators['contains'] = :[]
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result('yes',
|
||||||
'yes',
|
%({% if 'gnomeslab-and-or-liquid' contains 'gnomeslab-and-or-liquid' %}yes{% endif %}))
|
||||||
%({% if 'gnomeslab-and-or-liquid' contains 'gnomeslab-and-or-liquid' %}yes{% endif %}),
|
|
||||||
)
|
|
||||||
ensure
|
ensure
|
||||||
Condition.operators['contains'] = original_op
|
Condition.operators['contains'] = original_op
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,13 +3,44 @@
|
|||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
class TestFileSystem
|
class TestFileSystem
|
||||||
PARTIALS = {
|
|
||||||
"nested_template" => "{% include 'header' %} {% include 'body' %} {% include 'footer' %}",
|
|
||||||
"body" => "body {% include 'body_detail' %}",
|
|
||||||
}
|
|
||||||
|
|
||||||
def read_template_file(template_path)
|
def read_template_file(template_path)
|
||||||
PARTIALS[template_path] || template_path
|
case template_path
|
||||||
|
when "product"
|
||||||
|
"Product: {{ product.title }} "
|
||||||
|
|
||||||
|
when "product_alias"
|
||||||
|
"Product: {{ product.title }} "
|
||||||
|
|
||||||
|
when "locale_variables"
|
||||||
|
"Locale: {{echo1}} {{echo2}}"
|
||||||
|
|
||||||
|
when "variant"
|
||||||
|
"Variant: {{ variant.title }}"
|
||||||
|
|
||||||
|
when "nested_template"
|
||||||
|
"{% include 'header' %} {% include 'body' %} {% include 'footer' %}"
|
||||||
|
|
||||||
|
when "body"
|
||||||
|
"body {% include 'body_detail' %}"
|
||||||
|
|
||||||
|
when "nested_product_template"
|
||||||
|
"Product: {{ nested_product_template.title }} {%include 'details'%} "
|
||||||
|
|
||||||
|
when "recursively_nested_template"
|
||||||
|
"-{% include 'recursively_nested_template' %}"
|
||||||
|
|
||||||
|
when "pick_a_source"
|
||||||
|
"from TestFileSystem"
|
||||||
|
|
||||||
|
when 'assignments'
|
||||||
|
"{% assign foo = 'bar' %}"
|
||||||
|
|
||||||
|
when 'break'
|
||||||
|
"{% break %}"
|
||||||
|
|
||||||
|
else
|
||||||
|
template_path
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -49,122 +80,75 @@ end
|
|||||||
class IncludeTagTest < Minitest::Test
|
class IncludeTagTest < Minitest::Test
|
||||||
include Liquid
|
include Liquid
|
||||||
|
|
||||||
|
def setup
|
||||||
|
Liquid::Template.file_system = TestFileSystem.new
|
||||||
|
end
|
||||||
|
|
||||||
def test_include_tag_looks_for_file_system_in_registers_first
|
def test_include_tag_looks_for_file_system_in_registers_first
|
||||||
assert_equal(
|
assert_equal('from OtherFileSystem',
|
||||||
'from OtherFileSystem',
|
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: OtherFileSystem.new }))
|
||||||
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: OtherFileSystem.new }),
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include_tag_with
|
def test_include_tag_with
|
||||||
assert_template_result(
|
assert_template_result("Product: Draft 151cm ",
|
||||||
"Product: Draft 151cm ",
|
|
||||||
"{% include 'product' with products[0] %}",
|
"{% include 'product' with products[0] %}",
|
||||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||||
partials: { "product" => "Product: {{ product.title }} " },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include_tag_with_alias
|
def test_include_tag_with_alias
|
||||||
assert_template_result(
|
assert_template_result("Product: Draft 151cm ",
|
||||||
"Product: Draft 151cm ",
|
|
||||||
"{% include 'product_alias' with products[0] as product %}",
|
"{% include 'product_alias' with products[0] as product %}",
|
||||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||||
partials: { "product_alias" => "Product: {{ product.title }} " },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include_tag_for_alias
|
def test_include_tag_for_alias
|
||||||
assert_template_result(
|
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||||
"Product: Draft 151cm Product: Element 155cm ",
|
|
||||||
"{% include 'product_alias' for products as product %}",
|
"{% include 'product_alias' for products as product %}",
|
||||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||||
partials: { "product_alias" => "Product: {{ product.title }} " },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include_tag_with_default_name
|
def test_include_tag_with_default_name
|
||||||
assert_template_result(
|
assert_template_result("Product: Draft 151cm ",
|
||||||
"Product: Draft 151cm ",
|
"{% include 'product' %}", { "product" => { 'title' => 'Draft 151cm' } })
|
||||||
"{% include 'product' %}",
|
|
||||||
{ "product" => { 'title' => 'Draft 151cm' } },
|
|
||||||
partials: { "product" => "Product: {{ product.title }} " },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include_tag_for
|
def test_include_tag_for
|
||||||
assert_template_result(
|
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||||
"Product: Draft 151cm Product: Element 155cm ",
|
|
||||||
"{% include 'product' for products %}",
|
"{% include 'product' for products %}",
|
||||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||||
partials: { "product" => "Product: {{ product.title }} " },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include_tag_with_local_variables
|
def test_include_tag_with_local_variables
|
||||||
assert_template_result(
|
assert_template_result("Locale: test123 ", "{% include 'locale_variables' echo1: 'test123' %}")
|
||||||
"Locale: test123 ",
|
|
||||||
"{% include 'locale_variables' echo1: 'test123' %}",
|
|
||||||
partials: { "locale_variables" => "Locale: {{echo1}} {{echo2}}" },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include_tag_with_multiple_local_variables
|
def test_include_tag_with_multiple_local_variables
|
||||||
assert_template_result(
|
assert_template_result("Locale: test123 test321",
|
||||||
"Locale: test123 test321",
|
"{% include 'locale_variables' echo1: 'test123', echo2: 'test321' %}")
|
||||||
"{% include 'locale_variables' echo1: 'test123', echo2: 'test321' %}",
|
|
||||||
partials: { "locale_variables" => "Locale: {{echo1}} {{echo2}}" },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include_tag_with_multiple_local_variables_from_context
|
def test_include_tag_with_multiple_local_variables_from_context
|
||||||
assert_template_result(
|
assert_template_result("Locale: test123 test321",
|
||||||
"Locale: test123 test321",
|
|
||||||
"{% include 'locale_variables' echo1: echo1, echo2: more_echos.echo2 %}",
|
"{% include 'locale_variables' echo1: echo1, echo2: more_echos.echo2 %}",
|
||||||
{ 'echo1' => 'test123', 'more_echos' => { "echo2" => 'test321' } },
|
{ 'echo1' => 'test123', 'more_echos' => { "echo2" => 'test321' } })
|
||||||
partials: { "locale_variables" => "Locale: {{echo1}} {{echo2}}" },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_included_templates_assigns_variables
|
def test_included_templates_assigns_variables
|
||||||
assert_template_result(
|
assert_template_result("bar", "{% include 'assignments' %}{{ foo }}")
|
||||||
"bar",
|
|
||||||
"{% include 'assignments' %}{{ foo }}",
|
|
||||||
partials: { 'assignments' => "{% assign foo = 'bar' %}" },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_nested_include_tag
|
def test_nested_include_tag
|
||||||
partials = { "body" => "body {% include 'body_detail' %}", "body_detail" => "body_detail" }
|
assert_template_result("body body_detail", "{% include 'body' %}")
|
||||||
assert_template_result("body body_detail", "{% include 'body' %}", partials: partials)
|
|
||||||
|
|
||||||
partials = partials.merge({
|
assert_template_result("header body body_detail footer", "{% include 'nested_template' %}")
|
||||||
"nested_template" => "{% include 'header' %} {% include 'body' %} {% include 'footer' %}",
|
|
||||||
"header" => "header",
|
|
||||||
"footer" => "footer",
|
|
||||||
})
|
|
||||||
assert_template_result("header body body_detail footer", "{% include 'nested_template' %}", partials: partials)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_nested_include_with_variable
|
def test_nested_include_with_variable
|
||||||
partials = {
|
assert_template_result("Product: Draft 151cm details ",
|
||||||
"nested_product_template" => "Product: {{ nested_product_template.title }} {%include 'details'%} ",
|
"{% include 'nested_product_template' with product %}", { "product" => { "title" => 'Draft 151cm' } })
|
||||||
"details" => "details",
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result("Product: Draft 151cm details Product: Element 155cm details ",
|
||||||
"Product: Draft 151cm details ",
|
"{% include 'nested_product_template' for products %}", { "products" => [{ "title" => 'Draft 151cm' }, { "title" => 'Element 155cm' }] })
|
||||||
"{% 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,
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_recursively_included_template_does_not_produce_endless_loop
|
def test_recursively_included_template_does_not_produce_endless_loop
|
||||||
@@ -182,69 +166,41 @@ class IncludeTagTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_dynamically_choosen_template
|
def test_dynamically_choosen_template
|
||||||
assert_template_result(
|
assert_template_result("Test123", "{% include template %}", { "template" => 'Test123' })
|
||||||
"Test123",
|
assert_template_result("Test321", "{% include template %}", { "template" => 'Test321' })
|
||||||
"{% include template %}",
|
|
||||||
{ "template" => 'Test123' },
|
|
||||||
partials: { "Test123" => "Test123" },
|
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result("Product: Draft 151cm ", "{% include template for product %}",
|
||||||
"Test321",
|
{ "template" => 'product', 'product' => { 'title' => 'Draft 151cm' } })
|
||||||
"{% include template %}",
|
|
||||||
{ "template" => 'Test321' },
|
|
||||||
partials: { "Test321" => "Test321" },
|
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
|
||||||
"Product: Draft 151cm ",
|
|
||||||
"{% include template for product %}",
|
|
||||||
{ "template" => 'product', 'product' => { 'title' => 'Draft 151cm' } },
|
|
||||||
partials: { "product" => "Product: {{ product.title }} " },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include_tag_caches_second_read_of_same_partial
|
def test_include_tag_caches_second_read_of_same_partial
|
||||||
file_system = CountingFileSystem.new
|
file_system = CountingFileSystem.new
|
||||||
environment = Liquid::Environment.build(file_system: file_system)
|
assert_equal('from CountingFileSystemfrom CountingFileSystem',
|
||||||
assert_equal(
|
Template.parse("{% include 'pick_a_source' %}{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system }))
|
||||||
'from CountingFileSystemfrom CountingFileSystem',
|
|
||||||
Template.parse("{% include 'pick_a_source' %}{% include 'pick_a_source' %}", environment: environment).render!({}, registers: { file_system: file_system }),
|
|
||||||
)
|
|
||||||
assert_equal(1, file_system.count)
|
assert_equal(1, file_system.count)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include_tag_doesnt_cache_partials_across_renders
|
def test_include_tag_doesnt_cache_partials_across_renders
|
||||||
file_system = CountingFileSystem.new
|
file_system = CountingFileSystem.new
|
||||||
assert_equal(
|
assert_equal('from CountingFileSystem',
|
||||||
'from CountingFileSystem',
|
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system }))
|
||||||
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system }),
|
|
||||||
)
|
|
||||||
assert_equal(1, file_system.count)
|
assert_equal(1, file_system.count)
|
||||||
|
|
||||||
assert_equal(
|
assert_equal('from CountingFileSystem',
|
||||||
'from CountingFileSystem',
|
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system }))
|
||||||
Template.parse("{% include 'pick_a_source' %}").render!({}, registers: { file_system: file_system }),
|
|
||||||
)
|
|
||||||
assert_equal(2, file_system.count)
|
assert_equal(2, file_system.count)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include_tag_within_if_statement
|
def test_include_tag_within_if_statement
|
||||||
assert_template_result(
|
assert_template_result("foo_if_true", "{% if true %}{% include 'foo_if_true' %}{% endif %}")
|
||||||
"foo_if_true",
|
|
||||||
"{% if true %}{% include 'foo_if_true' %}{% endif %}",
|
|
||||||
partials: { "foo_if_true" => "foo_if_true" },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_custom_include_tag
|
def test_custom_include_tag
|
||||||
original_tag = Liquid::Template.tags['include']
|
original_tag = Liquid::Template.tags['include']
|
||||||
Liquid::Template.tags['include'] = CustomInclude
|
Liquid::Template.tags['include'] = CustomInclude
|
||||||
begin
|
begin
|
||||||
assert_equal(
|
assert_equal("custom_foo",
|
||||||
"custom_foo",
|
Template.parse("{% include 'custom_foo' %}").render!)
|
||||||
Template.parse("{% include 'custom_foo' %}").render!,
|
|
||||||
)
|
|
||||||
ensure
|
ensure
|
||||||
Liquid::Template.tags['include'] = original_tag
|
Liquid::Template.tags['include'] = original_tag
|
||||||
end
|
end
|
||||||
@@ -254,10 +210,8 @@ class IncludeTagTest < Minitest::Test
|
|||||||
original_tag = Liquid::Template.tags['include']
|
original_tag = Liquid::Template.tags['include']
|
||||||
Liquid::Template.tags['include'] = CustomInclude
|
Liquid::Template.tags['include'] = CustomInclude
|
||||||
begin
|
begin
|
||||||
assert_equal(
|
assert_equal("custom_foo_if_true",
|
||||||
"custom_foo_if_true",
|
Template.parse("{% if true %}{% include 'custom_foo_if_true' %}{% endif %}").render!)
|
||||||
Template.parse("{% if true %}{% include 'custom_foo_if_true' %}{% endif %}").render!,
|
|
||||||
)
|
|
||||||
ensure
|
ensure
|
||||||
Liquid::Template.tags['include'] = original_tag
|
Liquid::Template.tags['include'] = original_tag
|
||||||
end
|
end
|
||||||
@@ -272,7 +226,6 @@ class IncludeTagTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_passing_options_to_included_templates
|
def test_passing_options_to_included_templates
|
||||||
Liquid::Template.file_system = TestFileSystem.new
|
|
||||||
assert_raises(Liquid::SyntaxError) do
|
assert_raises(Liquid::SyntaxError) do
|
||||||
Template.parse("{% include template %}", error_mode: :strict).render!("template" => '{{ "X" || downcase }}')
|
Template.parse("{% include template %}", error_mode: :strict).render!("template" => '{{ "X" || downcase }}')
|
||||||
end
|
end
|
||||||
@@ -288,53 +241,27 @@ class IncludeTagTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_render_raise_argument_error_when_template_is_undefined
|
def test_render_raise_argument_error_when_template_is_undefined
|
||||||
assert_template_result(
|
assert_raises(Liquid::ArgumentError) do
|
||||||
"Liquid error (line 1): Argument error in tag 'include' - Illegal template name",
|
template = Liquid::Template.parse('{% include undefined_variable %}')
|
||||||
"{% include undefined_variable %}",
|
template.render!
|
||||||
render_errors: true,
|
end
|
||||||
)
|
assert_raises(Liquid::ArgumentError) do
|
||||||
|
template = Liquid::Template.parse('{% include nil %}')
|
||||||
assert_template_result(
|
template.render!
|
||||||
"Liquid error (line 1): Argument error in tag 'include' - Illegal template name",
|
end
|
||||||
"{% 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,
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_including_via_variable_value
|
def test_including_via_variable_value
|
||||||
assert_template_result(
|
assert_template_result("from TestFileSystem", "{% assign page = 'pick_a_source' %}{% include page %}")
|
||||||
"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 %}",
|
||||||
|
{ "product" => { 'title' => 'Draft 151cm' } })
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result("Product: Draft 151cm ", "{% assign page = 'product' %}{% include page for foo %}",
|
||||||
"Product: Draft 151cm ",
|
{ "foo" => { 'title' => 'Draft 151cm' } })
|
||||||
"{% assign page = 'product' %}{% include page %}",
|
|
||||||
{ "product" => { 'title' => 'Draft 151cm' } },
|
|
||||||
partials: partials,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
|
||||||
"Product: Draft 151cm ",
|
|
||||||
"{% assign page = 'product' %}{% include page for foo %}",
|
|
||||||
{ "foo" => { 'title' => 'Draft 151cm' } },
|
|
||||||
partials: partials,
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_including_with_strict_variables
|
def test_including_with_strict_variables
|
||||||
Liquid::Template.file_system = StubFileSystem.new({ "simple" => "simple" })
|
|
||||||
template = Liquid::Template.parse("{% include 'simple' %}", error_mode: :warn)
|
template = Liquid::Template.parse("{% include 'simple' %}", error_mode: :warn)
|
||||||
template.render(nil, strict_variables: true)
|
template.render(nil, strict_variables: true)
|
||||||
|
|
||||||
@@ -343,31 +270,6 @@ class IncludeTagTest < Minitest::Test
|
|||||||
|
|
||||||
def test_break_through_include
|
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 }}{% break %}{{ i }}{% endfor %}")
|
||||||
assert_template_result(
|
assert_template_result("1", "{% for i in (1..3) %}{{ i }}{% include 'break' %}{{ i }}{% endfor %}")
|
||||||
"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
|
||||||
end # IncludeTagTest
|
end # IncludeTagTest
|
||||||
|
|||||||
@@ -6,25 +6,20 @@ class IncrementTagTest < Minitest::Test
|
|||||||
include Liquid
|
include Liquid
|
||||||
|
|
||||||
def test_inc
|
def test_inc
|
||||||
assert_template_result('0 1', '{%increment port %} {{ port }}')
|
assert_template_result('0', '{%increment port %}', {})
|
||||||
assert_template_result(' 0 1 2', '{{port}} {%increment port %} {%increment port%} {{port}}')
|
assert_template_result('0 1', '{%increment port %} {%increment port%}', {})
|
||||||
assert_template_result(
|
assert_template_result('0 0 1 2 1',
|
||||||
'0 0 1 2 1',
|
|
||||||
'{%increment port %} {%increment starboard%} ' \
|
'{%increment port %} {%increment starboard%} ' \
|
||||||
'{%increment port %} {%increment port%} ' \
|
'{%increment port %} {%increment port%} ' \
|
||||||
'{%increment starboard %}',
|
'{%increment starboard %}', {})
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_dec
|
def test_dec
|
||||||
assert_template_result('-1 -1', '{%decrement port %} {{ port }}', { 'port' => 10 })
|
assert_template_result('9', '{%decrement port %}', { 'port' => 10 })
|
||||||
assert_template_result(' -1 -2 -2', '{{port}} {%decrement port %} {%decrement port%} {{port}}')
|
assert_template_result('-1 -2', '{%decrement port %} {%decrement port%}', {})
|
||||||
assert_template_result(
|
assert_template_result('1 5 2 2 5',
|
||||||
'0 1 2 0 3 1 1 3',
|
|
||||||
'{%increment starboard %} {%increment starboard%} {%increment starboard%} ' \
|
|
||||||
'{%increment port %} {%increment starboard%} ' \
|
'{%increment port %} {%increment starboard%} ' \
|
||||||
'{%increment port %} {%decrement port%} ' \
|
'{%increment port %} {%decrement port%} ' \
|
||||||
'{%decrement starboard %}',
|
'{%decrement starboard %}', { 'port' => 1, 'starboard' => 5 })
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -113,37 +113,4 @@ class LiquidTagTest < Minitest::Test
|
|||||||
{% raw %}{% liquid echo 'test' %}{% endraw %}
|
{% raw %}{% liquid echo 'test' %}{% endraw %}
|
||||||
LIQUID
|
LIQUID
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_nested_liquid_tags
|
|
||||||
assert_template_result('good', <<~LIQUID)
|
|
||||||
{%- liquid
|
|
||||||
liquid
|
|
||||||
if true
|
|
||||||
echo "good"
|
|
||||||
endif
|
|
||||||
-%}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_nested_liquid_tags_on_same_line
|
|
||||||
assert_template_result('good', <<~LIQUID)
|
|
||||||
{%- liquid liquid liquid echo "good" -%}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_nested_liquid_liquid_is_not_skipped_if_used_in_non_tag_position
|
|
||||||
assert_template_result('liquid', <<~LIQUID, { 'liquid' => 'liquid' })
|
|
||||||
{%- liquid liquid liquid echo liquid -%}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_next_liquid_with_unclosed_if_tag
|
|
||||||
assert_match_syntax_error("Liquid syntax error (line 2): 'if' tag was never closed", <<~LIQUID)
|
|
||||||
{%- liquid
|
|
||||||
liquid if true
|
|
||||||
echo "good"
|
|
||||||
endif
|
|
||||||
-%}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,16 +6,12 @@ class RawTagTest < Minitest::Test
|
|||||||
include Liquid
|
include Liquid
|
||||||
|
|
||||||
def test_tag_in_raw
|
def test_tag_in_raw
|
||||||
assert_template_result(
|
assert_template_result('{% comment %} test {% endcomment %}',
|
||||||
'{% comment %} test {% endcomment %}',
|
'{% raw %}{% comment %} test {% endcomment %}{% endraw %}')
|
||||||
'{% raw %}{% comment %} test {% endcomment %}{% endraw %}',
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_output_in_raw
|
def test_output_in_raw
|
||||||
assert_template_result('>{{ test }}<', '> {%- raw -%}{{ test }}{%- endraw -%} <')
|
assert_template_result('{{ test }}', '{% raw %}{{ test }}{% endraw %}')
|
||||||
assert_template_result("> inner <", "> {%- raw -%} inner {%- endraw %} <")
|
|
||||||
assert_template_result("> inner <", "> {%- raw -%} inner {%- endraw -%} <")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_open_tag_in_raw
|
def test_open_tag_in_raw
|
||||||
|
|||||||
@@ -6,79 +6,53 @@ class RenderTagTest < Minitest::Test
|
|||||||
include Liquid
|
include Liquid
|
||||||
|
|
||||||
def test_render_with_no_arguments
|
def test_render_with_no_arguments
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new('source' => 'rendered content')
|
||||||
'rendered content',
|
assert_template_result('rendered content', '{% render "source" %}')
|
||||||
'{% render "source" %}',
|
|
||||||
partials: { 'source' => 'rendered content' },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_tag_looks_for_file_system_in_registers_first
|
def test_render_tag_looks_for_file_system_in_registers_first
|
||||||
assert_template_result(
|
file_system = StubFileSystem.new('pick_a_source' => 'from register file system')
|
||||||
'from register file system',
|
assert_equal('from register file system',
|
||||||
'{% render "pick_a_source" %}',
|
Template.parse('{% render "pick_a_source" %}').render!({}, registers: { file_system: file_system }))
|
||||||
partials: { 'pick_a_source' => 'from register file system' },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_passes_named_arguments_into_inner_scope
|
def test_render_passes_named_arguments_into_inner_scope
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new('product' => '{{ inner_product.title }}')
|
||||||
'My Product',
|
assert_template_result('My Product', '{% render "product", inner_product: outer_product %}',
|
||||||
'{% render "product", inner_product: outer_product %}',
|
{ 'outer_product' => { 'title' => 'My Product' } })
|
||||||
{ 'outer_product' => { 'title' => 'My Product' } },
|
|
||||||
partials: { 'product' => '{{ inner_product.title }}' },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_accepts_literals_as_arguments
|
def test_render_accepts_literals_as_arguments
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ price }}')
|
||||||
'123',
|
assert_template_result('123', '{% render "snippet", price: 123 %}')
|
||||||
'{% render "snippet", price: 123 %}',
|
|
||||||
partials: { 'snippet' => '{{ price }}' },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_accepts_multiple_named_arguments
|
def test_render_accepts_multiple_named_arguments
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ one }} {{ two }}')
|
||||||
'1 2',
|
assert_template_result('1 2', '{% render "snippet", one: 1, two: 2 %}')
|
||||||
'{% render "snippet", one: 1, two: 2 %}',
|
|
||||||
partials: { 'snippet' => '{{ one }} {{ two }}' },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_does_not_inherit_parent_scope_variables
|
def test_render_does_not_inherit_parent_scope_variables
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ outer_variable }}')
|
||||||
'',
|
assert_template_result('', '{% assign outer_variable = "should not be visible" %}{% render "snippet" %}')
|
||||||
'{% assign outer_variable = "should not be visible" %}{% render "snippet" %}',
|
|
||||||
partials: { 'snippet' => '{{ outer_variable }}' },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_does_not_inherit_variable_with_same_name_as_snippet
|
def test_render_does_not_inherit_variable_with_same_name_as_snippet
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{{ snippet }}')
|
||||||
'',
|
assert_template_result('', "{% assign snippet = 'should not be visible' %}{% render 'snippet' %}")
|
||||||
"{% assign snippet = 'should not be visible' %}{% render 'snippet' %}",
|
|
||||||
partials: { 'snippet' => '{{ snippet }}' },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_does_not_mutate_parent_scope
|
def test_render_does_not_mutate_parent_scope
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new('snippet' => '{% assign inner = 1 %}')
|
||||||
'',
|
assert_template_result('', "{% render 'snippet' %}{{ inner }}")
|
||||||
"{% render 'snippet' %}{{ inner }}",
|
|
||||||
partials: { 'snippet' => '{% assign inner = 1 %}' },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_nested_render_tag
|
def test_nested_render_tag
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new(
|
||||||
'one two',
|
'one' => "one {% render 'two' %}",
|
||||||
"{% render 'one' %}",
|
'two' => 'two'
|
||||||
partials: {
|
|
||||||
'one' => "one {% render 'two' %}",
|
|
||||||
'two' => 'two',
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
assert_template_result('one two', "{% render 'one' %}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_recursively_rendered_template_does_not_produce_endless_loop
|
def test_recursively_rendered_template_does_not_produce_endless_loop
|
||||||
@@ -99,75 +73,64 @@ class RenderTagTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_dynamically_choosen_templates_are_not_allowed
|
def test_dynamically_choosen_templates_are_not_allowed
|
||||||
assert_syntax_error("{% assign name = 'snippet' %}{% render name %}")
|
Liquid::Template.file_system = StubFileSystem.new('snippet' => 'should not be rendered')
|
||||||
|
|
||||||
|
assert_raises(Liquid::SyntaxError) do
|
||||||
|
Liquid::Template.parse("{% assign name = 'snippet' %}{% render name %}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include_tag_caches_second_read_of_same_partial
|
def test_include_tag_caches_second_read_of_same_partial
|
||||||
file_system = StubFileSystem.new('snippet' => 'echo')
|
file_system = StubFileSystem.new('snippet' => 'echo')
|
||||||
assert_equal(
|
assert_equal('echoecho',
|
||||||
'echoecho',
|
|
||||||
Template.parse('{% render "snippet" %}{% render "snippet" %}')
|
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)
|
assert_equal(1, file_system.file_read_count)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_tag_doesnt_cache_partials_across_renders
|
def test_render_tag_doesnt_cache_partials_across_renders
|
||||||
file_system = StubFileSystem.new('snippet' => 'my message')
|
file_system = StubFileSystem.new('snippet' => 'my message')
|
||||||
|
|
||||||
assert_equal(
|
assert_equal('my message',
|
||||||
'my message',
|
Template.parse('{% include "snippet" %}').render!({}, registers: { file_system: file_system }))
|
||||||
Template.parse('{% include "snippet" %}').render!({}, registers: { file_system: file_system }),
|
|
||||||
)
|
|
||||||
assert_equal(1, file_system.file_read_count)
|
assert_equal(1, file_system.file_read_count)
|
||||||
|
|
||||||
assert_equal(
|
assert_equal('my message',
|
||||||
'my message',
|
Template.parse('{% include "snippet" %}').render!({}, registers: { file_system: file_system }))
|
||||||
Template.parse('{% include "snippet" %}').render!({}, registers: { file_system: file_system }),
|
|
||||||
)
|
|
||||||
assert_equal(2, file_system.file_read_count)
|
assert_equal(2, file_system.file_read_count)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_tag_within_if_statement
|
def test_render_tag_within_if_statement
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new('snippet' => 'my message')
|
||||||
'my message',
|
assert_template_result('my message', '{% if true %}{% render "snippet" %}{% endif %}')
|
||||||
'{% if true %}{% render "snippet" %}{% endif %}',
|
|
||||||
partials: { 'snippet' => 'my message' },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_break_through_render
|
def test_break_through_render
|
||||||
options = { partials: { 'break' => '{% break %}' } }
|
Liquid::Template.file_system = StubFileSystem.new('break' => '{% break %}')
|
||||||
assert_template_result('1', '{% for i in (1..3) %}{{ i }}{% break %}{{ i }}{% endfor %}', **options)
|
assert_template_result('1', '{% for i in (1..3) %}{{ i }}{% break %}{{ i }}{% endfor %}')
|
||||||
assert_template_result('112233', '{% for i in (1..3) %}{{ i }}{% render "break" %}{{ i }}{% endfor %}', **options)
|
assert_template_result('112233', '{% for i in (1..3) %}{{ i }}{% render "break" %}{{ i }}{% endfor %}')
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_increment_is_isolated_between_renders
|
def test_increment_is_isolated_between_renders
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new('incr' => '{% increment %}')
|
||||||
'010',
|
assert_template_result('010', '{% increment %}{% increment %}{% render "incr" %}')
|
||||||
'{% increment %}{% increment %}{% render "incr" %}',
|
|
||||||
partials: { 'incr' => '{% increment %}' },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_decrement_is_isolated_between_renders
|
def test_decrement_is_isolated_between_renders
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new('decr' => '{% decrement %}')
|
||||||
'-1-2-1',
|
assert_template_result('-1-2-1', '{% decrement %}{% decrement %}{% render "decr" %}')
|
||||||
'{% decrement %}{% decrement %}{% render "decr" %}',
|
|
||||||
partials: { 'decr' => '{% decrement %}' },
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_includes_will_not_render_inside_render_tag
|
def test_includes_will_not_render_inside_render_tag
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new(
|
||||||
'Liquid error (test_include line 1): include usage is not allowed in this context',
|
'foo' => 'bar',
|
||||||
'{% render "test_include" %}',
|
'test_include' => '{% include "foo" %}'
|
||||||
render_errors: true,
|
|
||||||
partials: {
|
|
||||||
'foo' => 'bar',
|
|
||||||
'test_include' => '{% include "foo" %}',
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
exc = assert_raises(Liquid::DisabledError) do
|
||||||
|
Liquid::Template.parse('{% render "test_include" %}').render!
|
||||||
|
end
|
||||||
|
assert_equal('Liquid error: include usage is not allowed in this context', exc.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_includes_will_not_render_inside_nested_sibling_tags
|
def test_includes_will_not_render_inside_nested_sibling_tags
|
||||||
@@ -180,109 +143,79 @@ class RenderTagTest < Minitest::Test
|
|||||||
'nested_render_with_sibling_include' => '{% render "test_include" %}{% include "foo" %}',
|
'nested_render_with_sibling_include' => '{% render "test_include" %}{% include "foo" %}',
|
||||||
'test_include' => '{% include "foo" %}',
|
'test_include' => '{% include "foo" %}',
|
||||||
},
|
},
|
||||||
render_errors: true,
|
render_errors: true
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_tag_with
|
def test_render_tag_with
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new(
|
||||||
"Product: Draft 151cm ",
|
'product' => "Product: {{ product.title }} ",
|
||||||
"{% render 'product' with products[0] %}",
|
'product_alias' => "Product: {{ product.title }} ",
|
||||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
|
||||||
partials: {
|
|
||||||
'product' => "Product: {{ product.title }} ",
|
|
||||||
'product_alias' => "Product: {{ product.title }} ",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert_template_result("Product: Draft 151cm ",
|
||||||
|
"{% render 'product' with products[0] %}",
|
||||||
|
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_tag_with_alias
|
def test_render_tag_with_alias
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new(
|
||||||
"Product: Draft 151cm ",
|
'product' => "Product: {{ product.title }} ",
|
||||||
"{% render 'product_alias' with products[0] as product %}",
|
'product_alias' => "Product: {{ product.title }} ",
|
||||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
|
||||||
partials: {
|
|
||||||
'product' => "Product: {{ product.title }} ",
|
|
||||||
'product_alias' => "Product: {{ product.title }} ",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert_template_result("Product: Draft 151cm ",
|
||||||
|
"{% render 'product_alias' with products[0] as product %}",
|
||||||
|
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_tag_for_alias
|
def test_render_tag_for_alias
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new(
|
||||||
"Product: Draft 151cm Product: Element 155cm ",
|
'product' => "Product: {{ product.title }} ",
|
||||||
"{% render 'product_alias' for products as product %}",
|
'product_alias' => "Product: {{ product.title }} ",
|
||||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
|
||||||
partials: {
|
|
||||||
'product' => "Product: {{ product.title }} ",
|
|
||||||
'product_alias' => "Product: {{ product.title }} ",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||||
|
"{% render 'product_alias' for products as product %}",
|
||||||
|
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_tag_for
|
def test_render_tag_for
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new(
|
||||||
"Product: Draft 151cm Product: Element 155cm ",
|
'product' => "Product: {{ product.title }} ",
|
||||||
"{% render 'product' for products %}",
|
'product_alias' => "Product: {{ product.title }} ",
|
||||||
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] },
|
|
||||||
partials: {
|
|
||||||
'product' => "Product: {{ product.title }} ",
|
|
||||||
'product_alias' => "Product: {{ product.title }} ",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||||
|
"{% render 'product' for products %}",
|
||||||
|
{ "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }] })
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_tag_forloop
|
def test_render_tag_forloop
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new(
|
||||||
"Product: Draft 151cm first index:1 Product: Element 155cm last index:2 ",
|
'product' => "Product: {{ product.title }} {% if forloop.first %}first{% endif %} {% if forloop.last %}last{% endif %} index:{{ forloop.index }} ",
|
||||||
"{% 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 }} ",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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' }] })
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_tag_for_drop
|
def test_render_tag_for_drop
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new(
|
||||||
"123",
|
'loop' => "{{ value.foo }}",
|
||||||
"{% render 'loop' for loop as value %}",
|
|
||||||
{ "loop" => TestEnumerable.new },
|
|
||||||
partials: {
|
|
||||||
'loop' => "{{ value.foo }}",
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert_template_result("123",
|
||||||
|
"{% render 'loop' for loop as value %}", { "loop" => TestEnumerable.new })
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_tag_with_drop
|
def test_render_tag_with_drop
|
||||||
assert_template_result(
|
Liquid::Template.file_system = StubFileSystem.new(
|
||||||
"TestEnumerable",
|
'loop' => "{{ value }}",
|
||||||
"{% 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("TestEnumerable",
|
||||||
assert_template_result(
|
"{% render 'loop' with loop as value %}", { "loop" => TestEnumerable.new })
|
||||||
'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
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ class StandardTagTest < Minitest::Test
|
|||||||
include Liquid
|
include Liquid
|
||||||
|
|
||||||
def test_no_transform
|
def test_no_transform
|
||||||
assert_template_result(
|
assert_template_result('this text should come out of the template without change...',
|
||||||
'this text should come out of the template without change...',
|
'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')
|
||||||
assert_template_result('<blah>', '<blah>')
|
assert_template_result('<blah>', '<blah>')
|
||||||
@@ -22,10 +20,8 @@ class StandardTagTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_has_a_block_which_does_nothing
|
def test_has_a_block_which_does_nothing
|
||||||
assert_template_result(
|
assert_template_result(%(the comment block should be removed .. right?),
|
||||||
%(the comment block should be removed .. right?),
|
%(the comment block should be removed {%comment%} be gone.. {%endcomment%} .. right?))
|
||||||
%(the comment block should be removed {%comment%} be gone.. {%endcomment%} .. right?),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result('', '{%comment%}{%endcomment%}')
|
assert_template_result('', '{%comment%}{%endcomment%}')
|
||||||
assert_template_result('', '{%comment%}{% endcomment %}')
|
assert_template_result('', '{%comment%}{% endcomment %}')
|
||||||
@@ -40,8 +36,6 @@ class StandardTagTest < Minitest::Test
|
|||||||
assert_template_result('', '{%comment%}{% endif %}{%endcomment%}')
|
assert_template_result('', '{%comment%}{% endif %}{%endcomment%}')
|
||||||
assert_template_result('', '{% comment %}{% endwhatever %}{% endcomment %}')
|
assert_template_result('', '{% comment %}{% endwhatever %}{% endcomment %}')
|
||||||
assert_template_result('', '{% comment %}{% raw %} {{%%%%}} }} { {% endcomment %} {% comment {% endraw %} {% endcomment %}')
|
assert_template_result('', '{% comment %}{% raw %} {{%%%%}} }} { {% endcomment %} {% comment {% endraw %} {% endcomment %}')
|
||||||
assert_template_result('', '{% comment %}{% " %}{% endcomment %}')
|
|
||||||
assert_template_result('', '{% comment %}{%%}{% endcomment %}')
|
|
||||||
|
|
||||||
assert_template_result('foobar', 'foo{%comment%}comment{%endcomment%}bar')
|
assert_template_result('foobar', 'foo{%comment%}comment{%endcomment%}bar')
|
||||||
assert_template_result('foobar', 'foo{% comment %}comment{% endcomment %}bar')
|
assert_template_result('foobar', 'foo{% comment %}comment{% endcomment %}bar')
|
||||||
@@ -68,81 +62,61 @@ class StandardTagTest < Minitest::Test
|
|||||||
|
|
||||||
def test_capture
|
def test_capture
|
||||||
assigns = { 'var' => 'content' }
|
assigns = { 'var' => 'content' }
|
||||||
assert_template_result(
|
assert_template_result('content foo content foo ',
|
||||||
'content foo content foo ',
|
|
||||||
'{{ var2 }}{% capture var2 %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}',
|
'{{ var2 }}{% capture var2 %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}',
|
||||||
assigns,
|
assigns)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_capture_detects_bad_syntax
|
def test_capture_detects_bad_syntax
|
||||||
assert_raises(SyntaxError) do
|
assert_raises(SyntaxError) do
|
||||||
assert_template_result(
|
assert_template_result('content foo content foo ',
|
||||||
'content foo content foo ',
|
|
||||||
'{{ var2 }}{% capture %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}',
|
'{{ var2 }}{% capture %}{{ var }} foo {% endcapture %}{{ var2 }}{{ var2 }}',
|
||||||
{ 'var' => 'content' },
|
{ 'var' => 'content' })
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_case
|
def test_case
|
||||||
assigns = { 'condition' => 2 }
|
assigns = { 'condition' => 2 }
|
||||||
assert_template_result(
|
assert_template_result(' its 2 ',
|
||||||
' its 2 ',
|
|
||||||
'{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}',
|
'{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}',
|
||||||
assigns,
|
assigns)
|
||||||
)
|
|
||||||
|
|
||||||
assigns = { 'condition' => 1 }
|
assigns = { 'condition' => 1 }
|
||||||
assert_template_result(
|
assert_template_result(' its 1 ',
|
||||||
' its 1 ',
|
|
||||||
'{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}',
|
'{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}',
|
||||||
assigns,
|
assigns)
|
||||||
)
|
|
||||||
|
|
||||||
assigns = { 'condition' => 3 }
|
assigns = { 'condition' => 3 }
|
||||||
assert_template_result(
|
assert_template_result('',
|
||||||
'',
|
|
||||||
'{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}',
|
'{% case condition %}{% when 1 %} its 1 {% when 2 %} its 2 {% endcase %}',
|
||||||
assigns,
|
assigns)
|
||||||
)
|
|
||||||
|
|
||||||
assigns = { 'condition' => "string here" }
|
assigns = { 'condition' => "string here" }
|
||||||
assert_template_result(
|
assert_template_result(' hit ',
|
||||||
' hit ',
|
|
||||||
'{% case condition %}{% when "string here" %} hit {% endcase %}',
|
'{% case condition %}{% when "string here" %} hit {% endcase %}',
|
||||||
assigns,
|
assigns)
|
||||||
)
|
|
||||||
|
|
||||||
assigns = { 'condition' => "bad string here" }
|
assigns = { 'condition' => "bad string here" }
|
||||||
assert_template_result(
|
assert_template_result('',
|
||||||
'',
|
|
||||||
'{% case condition %}{% when "string here" %} hit {% endcase %}',\
|
'{% case condition %}{% when "string here" %} hit {% endcase %}',\
|
||||||
assigns,
|
assigns)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_case_with_else
|
def test_case_with_else
|
||||||
assigns = { 'condition' => 5 }
|
assigns = { 'condition' => 5 }
|
||||||
assert_template_result(
|
assert_template_result(' hit ',
|
||||||
' hit ',
|
|
||||||
'{% case condition %}{% when 5 %} hit {% else %} else {% endcase %}',
|
'{% case condition %}{% when 5 %} hit {% else %} else {% endcase %}',
|
||||||
assigns,
|
assigns)
|
||||||
)
|
|
||||||
|
|
||||||
assigns = { 'condition' => 6 }
|
assigns = { 'condition' => 6 }
|
||||||
assert_template_result(
|
assert_template_result(' else ',
|
||||||
' else ',
|
|
||||||
'{% case condition %}{% when 5 %} hit {% else %} else {% endcase %}',
|
'{% case condition %}{% when 5 %} hit {% else %} else {% endcase %}',
|
||||||
assigns,
|
assigns)
|
||||||
)
|
|
||||||
|
|
||||||
assigns = { 'condition' => 6 }
|
assigns = { 'condition' => 6 }
|
||||||
assert_template_result(
|
assert_template_result(' else ',
|
||||||
' else ',
|
|
||||||
'{% case condition %} {% when 5 %} hit {% else %} else {% endcase %}',
|
'{% case condition %} {% when 5 %} hit {% else %} else {% endcase %}',
|
||||||
assigns,
|
assigns)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_case_on_size
|
def test_case_on_size
|
||||||
@@ -155,67 +129,47 @@ class StandardTagTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_case_on_size_with_else
|
def test_case_on_size_with_else
|
||||||
assert_template_result(
|
assert_template_result('else',
|
||||||
'else',
|
|
||||||
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
||||||
{ 'a' => [] },
|
{ 'a' => [] })
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result('1',
|
||||||
'1',
|
|
||||||
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
||||||
{ 'a' => [1] },
|
{ 'a' => [1] })
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result('2',
|
||||||
'2',
|
|
||||||
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
||||||
{ 'a' => [1, 1] },
|
{ 'a' => [1, 1] })
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result('else',
|
||||||
'else',
|
|
||||||
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
||||||
{ 'a' => [1, 1, 1] },
|
{ 'a' => [1, 1, 1] })
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result('else',
|
||||||
'else',
|
|
||||||
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
'{% 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(
|
assert_template_result('else',
|
||||||
'else',
|
|
||||||
'{% case a.size %}{% when 1 %}1{% when 2 %}2{% else %}else{% endcase %}',
|
'{% 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
|
end
|
||||||
|
|
||||||
def test_case_on_length_with_else
|
def test_case_on_length_with_else
|
||||||
assert_template_result(
|
assert_template_result('else',
|
||||||
'else',
|
|
||||||
'{% case a.empty? %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
|
'{% case a.empty? %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
|
||||||
{},
|
{})
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result('false',
|
||||||
'false',
|
|
||||||
'{% case false %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
|
'{% case false %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
|
||||||
{},
|
{})
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result('true',
|
||||||
'true',
|
|
||||||
'{% case true %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
|
'{% case true %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
|
||||||
{},
|
{})
|
||||||
)
|
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result('else',
|
||||||
'else',
|
|
||||||
'{% case NULL %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
|
'{% case NULL %}{% when true %}true{% when false %}false{% else %}else{% endcase %}',
|
||||||
{},
|
{})
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_assign_from_case
|
def test_assign_from_case
|
||||||
@@ -297,33 +251,24 @@ class StandardTagTest < Minitest::Test
|
|||||||
|
|
||||||
assert_template_result('one two one', '{%cycle "one", "two"%} {%cycle "one", "two"%} {%cycle "one", "two"%}')
|
assert_template_result('one two one', '{%cycle "one", "two"%} {%cycle "one", "two"%} {%cycle "one", "two"%}')
|
||||||
|
|
||||||
assert_template_result(
|
assert_template_result('text-align: left text-align: right',
|
||||||
'text-align: left text-align: right',
|
'{%cycle "text-align: left", "text-align: right" %} {%cycle "text-align: left", "text-align: right"%}')
|
||||||
'{%cycle "text-align: left", "text-align: right" %} {%cycle "text-align: left", "text-align: right"%}',
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_multiple_cycles
|
def test_multiple_cycles
|
||||||
assert_template_result(
|
assert_template_result('1 2 1 1 2 3 1',
|
||||||
'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%}')
|
||||||
'{%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
|
end
|
||||||
|
|
||||||
def test_multiple_named_cycles
|
def test_multiple_named_cycles
|
||||||
assert_template_result(
|
assert_template_result('one one two two one one',
|
||||||
'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" %}')
|
||||||
'{%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
|
end
|
||||||
|
|
||||||
def test_multiple_named_cycles_with_names_from_context
|
def test_multiple_named_cycles_with_names_from_context
|
||||||
assigns = { "var1" => 1, "var2" => 2 }
|
assigns = { "var1" => 1, "var2" => 2 }
|
||||||
assert_template_result(
|
assert_template_result('one one two two one one',
|
||||||
'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)
|
||||||
'{%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
|
end
|
||||||
|
|
||||||
def test_size_of_array
|
def test_size_of_array
|
||||||
|
|||||||
@@ -18,241 +18,51 @@ class TableRowTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_table_row
|
def test_table_row
|
||||||
assert_template_result(
|
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",
|
||||||
"<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 %}',
|
'{% 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(
|
assert_template_result("<tr class=\"row1\">\n</tr>\n",
|
||||||
"<tr class=\"row1\">\n</tr>\n",
|
|
||||||
'{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}',
|
'{% tablerow n in numbers cols:3%} {{n}} {% endtablerow %}',
|
||||||
{ 'numbers' => [] },
|
{ 'numbers' => [] })
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_table_row_with_different_cols
|
def test_table_row_with_different_cols
|
||||||
assert_template_result(
|
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",
|
||||||
"<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 %}',
|
'{% tablerow n in numbers cols:5%} {{n}} {% endtablerow %}',
|
||||||
{ 'numbers' => [1, 2, 3, 4, 5, 6] },
|
{ 'numbers' => [1, 2, 3, 4, 5, 6] })
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_table_col_counter
|
def test_table_col_counter
|
||||||
assert_template_result(
|
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",
|
||||||
"<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 %}',
|
'{% tablerow n in numbers cols:2%}{{tablerowloop.col}}{% endtablerow %}',
|
||||||
{ 'numbers' => [1, 2, 3, 4, 5, 6] },
|
{ 'numbers' => [1, 2, 3, 4, 5, 6] })
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_quoted_fragment
|
def test_quoted_fragment
|
||||||
assert_template_result(
|
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",
|
||||||
"<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 %}",
|
"{% 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] } })
|
||||||
)
|
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 %}",
|
"{% 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
|
end
|
||||||
|
|
||||||
def test_enumerable_drop
|
def test_enumerable_drop
|
||||||
assert_template_result(
|
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",
|
||||||
"<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 %}',
|
'{% 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
|
end
|
||||||
|
|
||||||
def test_offset_and_limit
|
def test_offset_and_limit
|
||||||
assert_template_result(
|
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",
|
||||||
"<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 %}',
|
'{% 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
|
end
|
||||||
|
|
||||||
def test_blank_string_not_iterable
|
def test_blank_string_not_iterable
|
||||||
assert_template_result(
|
assert_template_result("<tr class=\"row1\">\n</tr>\n",
|
||||||
"<tr class=\"row1\">\n</tr>\n",
|
|
||||||
"{% tablerow char in characters cols:3 %}I WILL NOT BE OUTPUT{% endtablerow %}",
|
"{% 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
|
|
||||||
expect = "<tr class=\"row1\">\n" \
|
|
||||||
"<td class=\"col1\">false</td>" \
|
|
||||||
"<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:var %}{{ tablerowloop.col_last }}{% endtablerow %}",
|
|
||||||
{ "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
|
|
||||||
template = <<~LIQUID.chomp
|
|
||||||
{% tablerow i in (1...2) %}
|
|
||||||
col: {{ tablerowloop.col }}
|
|
||||||
col0: {{ tablerowloop.col0 }}
|
|
||||||
col_first: {{ tablerowloop.col_first }}
|
|
||||||
col_last: {{ tablerowloop.col_last }}
|
|
||||||
first: {{ tablerowloop.first }}
|
|
||||||
index: {{ tablerowloop.index }}
|
|
||||||
index0: {{ tablerowloop.index0 }}
|
|
||||||
last: {{ tablerowloop.last }}
|
|
||||||
length: {{ tablerowloop.length }}
|
|
||||||
rindex: {{ tablerowloop.rindex }}
|
|
||||||
rindex0: {{ tablerowloop.rindex0 }}
|
|
||||||
row: {{ tablerowloop.row }}
|
|
||||||
{% endtablerow %}
|
|
||||||
LIQUID
|
|
||||||
|
|
||||||
expected_output = <<~OUTPUT
|
|
||||||
<tr class="row1">
|
|
||||||
<td class="col1">
|
|
||||||
col: 1
|
|
||||||
col0: 0
|
|
||||||
col_first: true
|
|
||||||
col_last: false
|
|
||||||
first: true
|
|
||||||
index: 1
|
|
||||||
index0: 0
|
|
||||||
last: false
|
|
||||||
length: 2
|
|
||||||
rindex: 2
|
|
||||||
rindex0: 1
|
|
||||||
row: 1
|
|
||||||
</td><td class="col2">
|
|
||||||
col: 2
|
|
||||||
col0: 1
|
|
||||||
col_first: false
|
|
||||||
col_last: true
|
|
||||||
first: false
|
|
||||||
index: 2
|
|
||||||
index0: 1
|
|
||||||
last: true
|
|
||||||
length: 2
|
|
||||||
rindex: 1
|
|
||||||
rindex0: 0
|
|
||||||
row: 1
|
|
||||||
</td></tr>
|
|
||||||
OUTPUT
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
def test_table_row_handles_interrupts
|
|
||||||
assert_template_result(
|
|
||||||
"<tr class=\"row1\">\n<td class=\"col1\"> 1 </td></tr>\n",
|
|
||||||
'{% tablerow n in (1...3) cols:2 %} {{n}} {% break %} {{n}} {% endtablerow %}',
|
|
||||||
)
|
|
||||||
|
|
||||||
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\"> 3 </td></tr>\n",
|
|
||||||
'{% tablerow n in (1...3) cols:2 %} {{n}} {% continue %} {{n}} {% endtablerow %}',
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_table_row_does_not_leak_interrupts
|
|
||||||
template = <<~LIQUID
|
|
||||||
{% for i in (1..2) -%}
|
|
||||||
{% for j in (1..2) -%}
|
|
||||||
{% tablerow k in (1..3) %}{% break %}{% endtablerow -%}
|
|
||||||
loop j={{ j }}
|
|
||||||
{% endfor -%}
|
|
||||||
loop i={{ i }}
|
|
||||||
{% endfor -%}
|
|
||||||
after loop
|
|
||||||
LIQUID
|
|
||||||
|
|
||||||
expected = <<~STR
|
|
||||||
<tr class="row1">
|
|
||||||
<td class="col1"></td></tr>
|
|
||||||
loop j=1
|
|
||||||
<tr class="row1">
|
|
||||||
<td class="col1"></td></tr>
|
|
||||||
loop j=2
|
|
||||||
loop i=1
|
|
||||||
<tr class="row1">
|
|
||||||
<td class="col1"></td></tr>
|
|
||||||
loop j=1
|
|
||||||
<tr class="row1">
|
|
||||||
<td class="col1"></td></tr>
|
|
||||||
loop j=2
|
|
||||||
loop i=2
|
|
||||||
after loop
|
|
||||||
STR
|
|
||||||
|
|
||||||
assert_template_result(
|
|
||||||
expected,
|
|
||||||
template,
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,10 +7,8 @@ class UnlessElseTagTest < Minitest::Test
|
|||||||
|
|
||||||
def test_unless
|
def test_unless
|
||||||
assert_template_result(' ', ' {% unless true %} this text should not go into the output {% endunless %} ')
|
assert_template_result(' ', ' {% unless true %} this text should not go into the output {% endunless %} ')
|
||||||
assert_template_result(
|
assert_template_result(' this text should go into the output ',
|
||||||
' this text should go into the output ',
|
' {% unless false %} this text should go into the output {% endunless %} ')
|
||||||
' {% 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 %}?')
|
assert_template_result(' you rock ?', '{% unless true %} you suck {% endunless %} {% unless false %} you rock {% endunless %}?')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -93,12 +93,10 @@ class TemplateTest < Minitest::Test
|
|||||||
|
|
||||||
def test_lambda_is_called_once_from_custom_assigns_over_multiple_parses_and_renders
|
def test_lambda_is_called_once_from_custom_assigns_over_multiple_parses_and_renders
|
||||||
t = Template.new
|
t = Template.new
|
||||||
assigns = {
|
assigns = { 'number' => -> {
|
||||||
'number' => -> {
|
@global ||= 0
|
||||||
@global ||= 0
|
@global += 1
|
||||||
@global += 1
|
} }
|
||||||
},
|
|
||||||
}
|
|
||||||
assert_equal('1', t.parse("{{number}}").render!(assigns))
|
assert_equal('1', t.parse("{{number}}").render!(assigns))
|
||||||
assert_equal('1', t.parse("{{number}}").render!(assigns))
|
assert_equal('1', t.parse("{{number}}").render!(assigns))
|
||||||
assert_equal('1', t.render!(assigns))
|
assert_equal('1', t.render!(assigns))
|
||||||
@@ -337,22 +335,4 @@ class TemplateTest < Minitest::Test
|
|||||||
assert_equal("x=2", output)
|
assert_equal("x=2", output)
|
||||||
assert_instance_of(String, output)
|
assert_instance_of(String, output)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_raises_error_with_invalid_utf8
|
|
||||||
e = assert_raises(TemplateEncodingError) do
|
|
||||||
Template.parse(<<~LIQUID)
|
|
||||||
{% comment %}
|
|
||||||
\xC0
|
|
||||||
{% endcomment %}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_equal('Liquid error: Invalid template encoding', e.message)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_allows_non_string_values_as_source
|
|
||||||
assert_equal('', Template.parse(nil).render)
|
|
||||||
assert_equal('1', Template.parse(1).render)
|
|
||||||
assert_equal('true', Template.parse(true).render)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
require 'timeout'
|
|
||||||
|
|
||||||
class VariableTest < Minitest::Test
|
class VariableTest < Minitest::Test
|
||||||
include Liquid
|
include Liquid
|
||||||
@@ -25,23 +24,17 @@ class VariableTest < Minitest::Test
|
|||||||
|
|
||||||
def test_if_tag_calls_to_liquid_value
|
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 == 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 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 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 %}true{% endif %}', { 'foo' => BooleanDrop.new(true) })
|
||||||
assert_template_result('true', '{% if foo %}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{% endif %}', { 'foo' => BooleanDrop.new(false) })
|
||||||
assert_template_result('', '{% if foo == true %}True{% endif %}', { 'foo' => BooleanDrop.new(false) })
|
assert_template_result('', '{% if foo == true %}True{% endif %}', { 'foo' => BooleanDrop.new(false) })
|
||||||
assert_template_result('', '{% if foo and true %}SHOULD NOT HAPPEN{% endif %}', { 'foo' => BooleanDrop.new(false) })
|
|
||||||
|
|
||||||
assert_template_result('one', '{% if a contains x %}one{% endif %}', { 'a' => [1], 'x' => IntegerDrop.new(1) })
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_unless_tag_calls_to_liquid_value
|
def test_unless_tag_calls_to_liquid_value
|
||||||
assert_template_result('', '{% unless foo %}true{% endunless %}', { 'foo' => BooleanDrop.new(true) })
|
assert_template_result('', '{% unless foo %}true{% endunless %}', { 'foo' => BooleanDrop.new(true) })
|
||||||
assert_template_result('true', '{% unless foo %}true{% endunless %}', { 'foo' => BooleanDrop.new(false) })
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_case_tag_calls_to_liquid_value
|
def test_case_tag_calls_to_liquid_value
|
||||||
@@ -137,72 +130,4 @@ class VariableTest < Minitest::Test
|
|||||||
def test_raw_value_variable
|
def test_raw_value_variable
|
||||||
assert_template_result('bar', '{{ [key] }}', { 'key' => 'foo', 'foo' => 'bar' })
|
assert_template_result('bar', '{{ [key] }}', { 'key' => 'foo', 'foo' => 'bar' })
|
||||||
end
|
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
|
end
|
||||||
|
|||||||
+42
-36
@@ -39,14 +39,12 @@ module Minitest
|
|||||||
|
|
||||||
def assert_template_result(
|
def assert_template_result(
|
||||||
expected, template, assigns = {},
|
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
|
|
||||||
)
|
)
|
||||||
file_system = StubFileSystem.new(partials || {})
|
template = Liquid::Template.parse(template, line_numbers: true, error_mode: error_mode&.to_sym)
|
||||||
environment = Liquid::Environment.build(file_system: file_system)
|
file_system = StubFileSystem.new(partials) if partials
|
||||||
template = Liquid::Template.parse(template, line_numbers: true, error_mode: error_mode&.to_sym, environment: environment)
|
registers = Liquid::Registers.new(file_system: file_system)
|
||||||
registers = Liquid::Registers.new(file_system: file_system, template_factory: template_factory)
|
context = Liquid::Context.build(environments: assigns, rethrow_errors: !render_errors, registers: registers)
|
||||||
context = Liquid::Context.build(static_environments: assigns, rethrow_errors: !render_errors, registers: registers, environment: environment)
|
|
||||||
output = template.render(context)
|
output = template.render(context)
|
||||||
assert_equal(expected, output, message)
|
assert_equal(expected, output, message)
|
||||||
end
|
end
|
||||||
@@ -58,10 +56,6 @@ module Minitest
|
|||||||
assert_match(match, exception.message)
|
assert_match(match, exception.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_syntax_error(template, error_mode: nil)
|
|
||||||
assert_match_syntax_error("", template, error_mode: error_mode)
|
|
||||||
end
|
|
||||||
|
|
||||||
def assert_usage_increment(name, times: 1)
|
def assert_usage_increment(name, times: 1)
|
||||||
old_method = Liquid::Usage.method(:increment)
|
old_method = Liquid::Usage.method(:increment)
|
||||||
calls = 0
|
calls = 0
|
||||||
@@ -79,12 +73,22 @@ module Minitest
|
|||||||
assert_equal(times, calls, "Number of calls to Usage.increment with #{name.inspect}")
|
assert_equal(times, calls, "Number of calls to Usage.increment with #{name.inspect}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_global_filter(*globals, &blk)
|
def with_global_filter(*globals)
|
||||||
environment = Liquid::Environment.build do |w|
|
original_global_cache = Liquid::StrainerFactory::GlobalCache
|
||||||
w.register_filters(globals)
|
Liquid::StrainerFactory.send(:remove_const, :GlobalCache)
|
||||||
end
|
Liquid::StrainerFactory.const_set(:GlobalCache, Class.new(Liquid::StrainerTemplate))
|
||||||
|
|
||||||
Environment.dangerously_override(environment, &blk)
|
globals.each do |global|
|
||||||
|
Liquid::Template.register_filter(global)
|
||||||
|
end
|
||||||
|
Liquid::StrainerFactory.send(:strainer_class_cache).clear
|
||||||
|
begin
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
Liquid::StrainerFactory.send(:remove_const, :GlobalCache)
|
||||||
|
Liquid::StrainerFactory.const_set(:GlobalCache, original_global_cache)
|
||||||
|
Liquid::StrainerFactory.send(:strainer_class_cache).clear
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_error_mode(mode)
|
def with_error_mode(mode)
|
||||||
@@ -95,11 +99,18 @@ module Minitest
|
|||||||
Liquid::Template.error_mode = old_mode
|
Liquid::Template.error_mode = old_mode
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_custom_tag(tag_name, tag_class, &block)
|
def with_custom_tag(tag_name, tag_class)
|
||||||
environment = Liquid::Environment.default.dup
|
old_tag = Liquid::Template.tags[tag_name]
|
||||||
environment.register_tag(tag_name, tag_class)
|
begin
|
||||||
|
Liquid::Template.register_tag(tag_name, tag_class)
|
||||||
Environment.dangerously_override(environment, &block)
|
yield
|
||||||
|
ensure
|
||||||
|
if old_tag
|
||||||
|
Liquid::Template.tags[tag_name] = old_tag
|
||||||
|
else
|
||||||
|
Liquid::Template.tags.delete(tag_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -110,23 +121,16 @@ class ThingWithToLiquid
|
|||||||
end
|
end
|
||||||
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
|
class IntegerDrop < Liquid::Drop
|
||||||
def initialize(value)
|
def initialize(value)
|
||||||
super()
|
super()
|
||||||
@value = value.to_i
|
@value = value.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
@value == other
|
||||||
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
@value.to_s
|
@value.to_s
|
||||||
end
|
end
|
||||||
@@ -142,6 +146,10 @@ class BooleanDrop < Liquid::Drop
|
|||||||
@value = value
|
@value = value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
@value == other
|
||||||
|
end
|
||||||
|
|
||||||
def to_liquid_value
|
def to_liquid_value
|
||||||
@value
|
@value
|
||||||
end
|
end
|
||||||
@@ -194,10 +202,8 @@ class StubTemplateFactory
|
|||||||
@count = 0
|
@count = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def for(template_name)
|
def for(_template_name)
|
||||||
@count += 1
|
@count += 1
|
||||||
template = Liquid::Template.new
|
Liquid::Template.new
|
||||||
template.name = "some/path/" + template_name
|
|
||||||
template
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -32,19 +32,11 @@ class BlockUnitTest < Minitest::Test
|
|||||||
assert_equal(String, template.root.nodelist[2].class)
|
assert_equal(String, template.root.nodelist[2].class)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_variable_with_multibyte_character
|
|
||||||
template = Liquid::Template.parse("{{ '❤️' }}")
|
|
||||||
assert_equal(1, template.root.nodelist.size)
|
|
||||||
assert_equal(Variable, template.root.nodelist[0].class)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_variable_many_embedded_fragments
|
def test_variable_many_embedded_fragments
|
||||||
template = Liquid::Template.parse(" {{funk}} {{so}} {{brother}} ")
|
template = Liquid::Template.parse(" {{funk}} {{so}} {{brother}} ")
|
||||||
assert_equal(7, template.root.nodelist.size)
|
assert_equal(7, template.root.nodelist.size)
|
||||||
assert_equal(
|
assert_equal([String, Variable, String, Variable, String, Variable, String],
|
||||||
[String, Variable, String, Variable, String, Variable, String],
|
block_types(template.root.nodelist))
|
||||||
block_types(template.root.nodelist),
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_with_block
|
def test_with_block
|
||||||
|
|||||||
@@ -55,11 +55,6 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
assert_evaluates_false('bob', 'contains', '---')
|
assert_evaluates_false('bob', 'contains', '---')
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_contains_binary_encoding_compatibility_with_utf8
|
|
||||||
assert_evaluates_true('🙈'.b, 'contains', '🙈')
|
|
||||||
assert_evaluates_true('🙈', 'contains', '🙈'.b)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_invalid_comparation_operator
|
def test_invalid_comparation_operator
|
||||||
assert_evaluates_argument_error(1, '~~', 0)
|
assert_evaluates_argument_error(1, '~~', 0)
|
||||||
end
|
end
|
||||||
@@ -169,17 +164,13 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
private
|
private
|
||||||
|
|
||||||
def assert_evaluates_true(left, op, right)
|
def assert_evaluates_true(left, op, right)
|
||||||
assert(
|
assert(Condition.new(left, op, right).evaluate(@context),
|
||||||
Condition.new(left, op, right).evaluate(@context),
|
"Evaluated false: #{left} #{op} #{right}")
|
||||||
"Evaluated false: #{left.inspect} #{op} #{right.inspect}",
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_evaluates_false(left, op, right)
|
def assert_evaluates_false(left, op, right)
|
||||||
assert(
|
assert(!Condition.new(left, op, right).evaluate(@context),
|
||||||
!Condition.new(left, op, right).evaluate(@context),
|
"Evaluated true: #{left} #{op} #{right}")
|
||||||
"Evaluated true: #{left.inspect} #{op} #{right.inspect}",
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_evaluates_argument_error(left, op, right)
|
def assert_evaluates_argument_error(left, op, right)
|
||||||
|
|||||||
@@ -50,14 +50,4 @@ class LexerUnitTest < Minitest::Test
|
|||||||
Lexer.new("%").tokenize
|
Lexer.new("%").tokenize
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_negative_numbers
|
|
||||||
tokens = Lexer.new("foo | default: -1").tokenize
|
|
||||||
assert_equal([[:id, 'foo'], [:pipe, '|'], [:id, 'default'], [:colon, ":"], [:number, '-1'], [:end_of_string]], tokens)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_greater_than_two_digits
|
|
||||||
tokens = Lexer.new("foo > 12").tokenize
|
|
||||||
assert_equal([[:id, 'foo'], [:comparison, '>'], [:number, '12'], [:end_of_string]], tokens)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,245 +8,245 @@ class ParseTreeVisitorTest < Minitest::Test
|
|||||||
def test_variable
|
def test_variable
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({{ test }})),
|
visit(%({{ test }}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_varible_with_filter
|
def test_varible_with_filter
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test", "infilter"],
|
["test", "infilter"],
|
||||||
visit(%({{ test | split: infilter }})),
|
visit(%({{ test | split: infilter }}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_dynamic_variable
|
def test_dynamic_variable
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test", "inlookup"],
|
["test", "inlookup"],
|
||||||
visit(%({{ test[inlookup] }})),
|
visit(%({{ test[inlookup] }}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_echo
|
def test_echo
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% echo test %})),
|
visit(%({% echo test %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_if_condition
|
def test_if_condition
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% if test %}{% endif %})),
|
visit(%({% if test %}{% endif %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_complex_if_condition
|
def test_complex_if_condition
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% if 1 == 1 and 2 == test %}{% endif %})),
|
visit(%({% if 1 == 1 and 2 == test %}{% endif %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_if_body
|
def test_if_body
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% if 1 == 1 %}{{ test }}{% endif %})),
|
visit(%({% if 1 == 1 %}{{ test }}{% endif %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_unless_condition
|
def test_unless_condition
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% unless test %}{% endunless %})),
|
visit(%({% unless test %}{% endunless %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_complex_unless_condition
|
def test_complex_unless_condition
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% unless 1 == 1 and 2 == test %}{% endunless %})),
|
visit(%({% unless 1 == 1 and 2 == test %}{% endunless %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_unless_body
|
def test_unless_body
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% unless 1 == 1 %}{{ test }}{% endunless %})),
|
visit(%({% unless 1 == 1 %}{{ test }}{% endunless %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_elsif_condition
|
def test_elsif_condition
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% if 1 == 1 %}{% elsif test %}{% endif %})),
|
visit(%({% if 1 == 1 %}{% elsif test %}{% endif %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_complex_elsif_condition
|
def test_complex_elsif_condition
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% if 1 == 1 %}{% elsif 1 == 1 and 2 == test %}{% endif %})),
|
visit(%({% if 1 == 1 %}{% elsif 1 == 1 and 2 == test %}{% endif %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_elsif_body
|
def test_elsif_body
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% if 1 == 1 %}{% elsif 2 == 2 %}{{ test }}{% endif %})),
|
visit(%({% if 1 == 1 %}{% elsif 2 == 2 %}{{ test }}{% endif %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_else_body
|
def test_else_body
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% if 1 == 1 %}{% else %}{{ test }}{% endif %})),
|
visit(%({% if 1 == 1 %}{% else %}{{ test }}{% endif %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_case_left
|
def test_case_left
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% case test %}{% endcase %})),
|
visit(%({% case test %}{% endcase %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_case_condition
|
def test_case_condition
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% case 1 %}{% when test %}{% endcase %})),
|
visit(%({% case 1 %}{% when test %}{% endcase %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_case_when_body
|
def test_case_when_body
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% case 1 %}{% when 2 %}{{ test }}{% endcase %})),
|
visit(%({% case 1 %}{% when 2 %}{{ test }}{% endcase %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_case_else_body
|
def test_case_else_body
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% case 1 %}{% else %}{{ test }}{% endcase %})),
|
visit(%({% case 1 %}{% else %}{{ test }}{% endcase %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_for_in
|
def test_for_in
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% for x in test %}{% endfor %})),
|
visit(%({% for x in test %}{% endfor %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_for_limit
|
def test_for_limit
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% for x in (1..5) limit: test %}{% endfor %})),
|
visit(%({% for x in (1..5) limit: test %}{% endfor %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_for_offset
|
def test_for_offset
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% for x in (1..5) offset: test %}{% endfor %})),
|
visit(%({% for x in (1..5) offset: test %}{% endfor %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_for_body
|
def test_for_body
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% for x in (1..5) %}{{ test }}{% endfor %})),
|
visit(%({% for x in (1..5) %}{{ test }}{% endfor %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_for_range
|
def test_for_range
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% for x in (1..test) %}{% endfor %})),
|
visit(%({% for x in (1..test) %}{% endfor %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_tablerow_in
|
def test_tablerow_in
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% tablerow x in test %}{% endtablerow %})),
|
visit(%({% tablerow x in test %}{% endtablerow %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_tablerow_limit
|
def test_tablerow_limit
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% tablerow x in (1..5) limit: test %}{% endtablerow %})),
|
visit(%({% tablerow x in (1..5) limit: test %}{% endtablerow %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_tablerow_offset
|
def test_tablerow_offset
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% tablerow x in (1..5) offset: test %}{% endtablerow %})),
|
visit(%({% tablerow x in (1..5) offset: test %}{% endtablerow %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_tablerow_body
|
def test_tablerow_body
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% tablerow x in (1..5) %}{{ test }}{% endtablerow %})),
|
visit(%({% tablerow x in (1..5) %}{{ test }}{% endtablerow %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_cycle
|
def test_cycle
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% cycle test %})),
|
visit(%({% cycle test %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_assign
|
def test_assign
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% assign x = test %})),
|
visit(%({% assign x = test %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_capture
|
def test_capture
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% capture x %}{{ test }}{% endcapture %})),
|
visit(%({% capture x %}{{ test }}{% endcapture %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include
|
def test_include
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% include test %})),
|
visit(%({% include test %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include_with
|
def test_include_with
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% include "hai" with test %})),
|
visit(%({% include "hai" with test %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_include_for
|
def test_include_for
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% include "hai" for test %})),
|
visit(%({% include "hai" for test %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_with
|
def test_render_with
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% render "hai" with test %})),
|
visit(%({% render "hai" with test %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_for
|
def test_render_for
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
visit(%({% render "hai" for test %})),
|
visit(%({% render "hai" for test %}))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -257,7 +257,7 @@ class ParseTreeVisitorTest < Minitest::Test
|
|||||||
["test", []],
|
["test", []],
|
||||||
["xs", []],
|
["xs", []],
|
||||||
]]],
|
]]],
|
||||||
traversal(%({% for x in xs offset: test %}{{ other }}{% endfor %})).visit,
|
traversal(%({% for x in xs offset: test %}{{ other }}{% endfor %})).visit
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ class PartialCacheUnitTest < Minitest::Test
|
|||||||
context = Liquid::Context.build(
|
context = Liquid::Context.build(
|
||||||
registers: {
|
registers: {
|
||||||
file_system: StubFileSystem.new('my_partial' => 'my partial body'),
|
file_system: StubFileSystem.new('my_partial' => 'my partial body'),
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
partial = Liquid::PartialCache.load(
|
partial = Liquid::PartialCache.load(
|
||||||
'my_partial',
|
'my_partial',
|
||||||
context: context,
|
context: context,
|
||||||
parse_context: Liquid::ParseContext.new,
|
parse_context: Liquid::ParseContext.new
|
||||||
)
|
)
|
||||||
|
|
||||||
assert_equal('my partial body', partial.render)
|
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
|
def test_reads_from_the_file_system_only_once_per_file
|
||||||
file_system = StubFileSystem.new('my_partial' => 'some partial body')
|
file_system = StubFileSystem.new('my_partial' => 'some partial body')
|
||||||
context = Liquid::Context.build(
|
context = Liquid::Context.build(
|
||||||
registers: { file_system: file_system },
|
registers: { file_system: file_system }
|
||||||
)
|
)
|
||||||
|
|
||||||
2.times do
|
2.times do
|
||||||
Liquid::PartialCache.load(
|
Liquid::PartialCache.load(
|
||||||
'my_partial',
|
'my_partial',
|
||||||
context: context,
|
context: context,
|
||||||
parse_context: Liquid::ParseContext.new,
|
parse_context: Liquid::ParseContext.new
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -39,31 +39,31 @@ class PartialCacheUnitTest < Minitest::Test
|
|||||||
def test_cache_state_is_stored_per_context
|
def test_cache_state_is_stored_per_context
|
||||||
parse_context = Liquid::ParseContext.new
|
parse_context = Liquid::ParseContext.new
|
||||||
shared_file_system = StubFileSystem.new(
|
shared_file_system = StubFileSystem.new(
|
||||||
'my_partial' => 'my shared value',
|
'my_partial' => 'my shared value'
|
||||||
)
|
)
|
||||||
context_one = Liquid::Context.build(
|
context_one = Liquid::Context.build(
|
||||||
registers: {
|
registers: {
|
||||||
file_system: shared_file_system,
|
file_system: shared_file_system,
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
context_two = Liquid::Context.build(
|
context_two = Liquid::Context.build(
|
||||||
registers: {
|
registers: {
|
||||||
file_system: shared_file_system,
|
file_system: shared_file_system,
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
2.times do
|
2.times do
|
||||||
Liquid::PartialCache.load(
|
Liquid::PartialCache.load(
|
||||||
'my_partial',
|
'my_partial',
|
||||||
context: context_one,
|
context: context_one,
|
||||||
parse_context: parse_context,
|
parse_context: parse_context
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
Liquid::PartialCache.load(
|
Liquid::PartialCache.load(
|
||||||
'my_partial',
|
'my_partial',
|
||||||
context: context_two,
|
context: context_two,
|
||||||
parse_context: parse_context,
|
parse_context: parse_context
|
||||||
)
|
)
|
||||||
|
|
||||||
assert_equal(2, shared_file_system.file_read_count)
|
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
|
def test_cache_is_not_broken_when_a_different_parse_context_is_used
|
||||||
file_system = StubFileSystem.new('my_partial' => 'some partial body')
|
file_system = StubFileSystem.new('my_partial' => 'some partial body')
|
||||||
context = Liquid::Context.build(
|
context = Liquid::Context.build(
|
||||||
registers: { file_system: file_system },
|
registers: { file_system: file_system }
|
||||||
)
|
)
|
||||||
|
|
||||||
Liquid::PartialCache.load(
|
Liquid::PartialCache.load(
|
||||||
'my_partial',
|
'my_partial',
|
||||||
context: context,
|
context: context,
|
||||||
parse_context: Liquid::ParseContext.new(my_key: 'value one'),
|
parse_context: Liquid::ParseContext.new(my_key: 'value one')
|
||||||
)
|
)
|
||||||
Liquid::PartialCache.load(
|
Liquid::PartialCache.load(
|
||||||
'my_partial',
|
'my_partial',
|
||||||
context: context,
|
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,
|
# Technically what we care about is that the file was parsed twice,
|
||||||
@@ -95,13 +95,13 @@ class PartialCacheUnitTest < Minitest::Test
|
|||||||
context = Liquid::Context.build(
|
context = Liquid::Context.build(
|
||||||
registers: {
|
registers: {
|
||||||
file_system: StubFileSystem.new('my_partial' => 'my partial body'),
|
file_system: StubFileSystem.new('my_partial' => 'my partial body'),
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
partial = Liquid::PartialCache.load(
|
partial = Liquid::PartialCache.load(
|
||||||
'my_partial',
|
'my_partial',
|
||||||
context: context,
|
context: context,
|
||||||
parse_context: Liquid::ParseContext.new,
|
parse_context: Liquid::ParseContext.new
|
||||||
)
|
)
|
||||||
|
|
||||||
assert_equal('my partial body', partial.render)
|
assert_equal('my partial body', partial.render)
|
||||||
@@ -113,13 +113,13 @@ class PartialCacheUnitTest < Minitest::Test
|
|||||||
registers: {
|
registers: {
|
||||||
file_system: StubFileSystem.new('my_partial' => 'my partial body'),
|
file_system: StubFileSystem.new('my_partial' => 'my partial body'),
|
||||||
template_factory: template_factory,
|
template_factory: template_factory,
|
||||||
},
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
partial = Liquid::PartialCache.load(
|
partial = Liquid::PartialCache.load(
|
||||||
'my_partial',
|
'my_partial',
|
||||||
context: context,
|
context: context,
|
||||||
parse_context: Liquid::ParseContext.new,
|
parse_context: Liquid::ParseContext.new
|
||||||
)
|
)
|
||||||
|
|
||||||
assert_equal('my partial body', partial.render)
|
assert_equal('my partial body', partial.render)
|
||||||
@@ -129,12 +129,12 @@ class PartialCacheUnitTest < Minitest::Test
|
|||||||
def test_cache_state_is_shared_for_subcontexts
|
def test_cache_state_is_shared_for_subcontexts
|
||||||
parse_context = Liquid::ParseContext.new
|
parse_context = Liquid::ParseContext.new
|
||||||
shared_file_system = StubFileSystem.new(
|
shared_file_system = StubFileSystem.new(
|
||||||
'my_partial' => 'my shared value',
|
'my_partial' => 'my shared value'
|
||||||
)
|
)
|
||||||
context = Liquid::Context.build(
|
context = Liquid::Context.build(
|
||||||
registers: Liquid::Registers.new(
|
registers: Liquid::Registers.new(
|
||||||
file_system: shared_file_system,
|
file_system: shared_file_system,
|
||||||
),
|
)
|
||||||
)
|
)
|
||||||
subcontext = context.new_isolated_subcontext
|
subcontext = context.new_isolated_subcontext
|
||||||
|
|
||||||
@@ -144,57 +144,16 @@ class PartialCacheUnitTest < Minitest::Test
|
|||||||
Liquid::PartialCache.load(
|
Liquid::PartialCache.load(
|
||||||
'my_partial',
|
'my_partial',
|
||||||
context: context,
|
context: context,
|
||||||
parse_context: parse_context,
|
parse_context: parse_context
|
||||||
)
|
)
|
||||||
|
|
||||||
Liquid::PartialCache.load(
|
Liquid::PartialCache.load(
|
||||||
'my_partial',
|
'my_partial',
|
||||||
context: subcontext,
|
context: subcontext,
|
||||||
parse_context: parse_context,
|
parse_context: parse_context
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_equal(1, shared_file_system.file_read_count)
|
assert_equal(1, shared_file_system.file_read_count)
|
||||||
end
|
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
|
|
||||||
|
|
||||||
def test_includes_error_mode_into_template_cache
|
|
||||||
template_factory = StubTemplateFactory.new
|
|
||||||
context = Liquid::Context.build(
|
|
||||||
registers: {
|
|
||||||
file_system: StubFileSystem.new('my_partial' => 'my partial body'),
|
|
||||||
template_factory: template_factory,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
[:lax, :warn, :strict].each do |error_mode|
|
|
||||||
Liquid::PartialCache.load(
|
|
||||||
'my_partial',
|
|
||||||
context: context,
|
|
||||||
parse_context: Liquid::ParseContext.new(error_mode: error_mode),
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_equal(
|
|
||||||
["my_partial:lax", "my_partial:warn", "my_partial:strict"],
|
|
||||||
context.registers[:cached_partials].keys,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
require 'timeout'
|
|
||||||
|
|
||||||
class RegexpUnitTest < Minitest::Test
|
class RegexpUnitTest < Minitest::Test
|
||||||
include Liquid
|
include Liquid
|
||||||
@@ -38,22 +37,10 @@ class RegexpUnitTest < Minitest::Test
|
|||||||
|
|
||||||
def test_variable_parser
|
def test_variable_parser
|
||||||
assert_equal(['var'], 'var'.scan(VariableParser))
|
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]'], '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]'], '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))
|
assert_equal(['var', '[method]', '[0]', 'method'], 'var[method][0].method'.scan(VariableParser))
|
||||||
end
|
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
|
end # RegexpTest
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class StrainerFactoryUnitTest < Minitest::Test
|
|||||||
|
|
||||||
assert_match(
|
assert_match(
|
||||||
/\ALiquid error: wrong number of arguments \((1 for 0|given 1, expected 0)\)\z/,
|
/\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
|
source = AccessScopeFilters.instance_method(:public_filter).source_location
|
||||||
assert_equal(source.map(&:to_s), exception.backtrace[0].split(':')[0..1])
|
assert_equal(source.map(&:to_s), exception.backtrace[0].split(':')[0..1])
|
||||||
|
|||||||
@@ -58,9 +58,8 @@ class StrainerTemplateUnitTest < Minitest::Test
|
|||||||
|
|
||||||
def test_add_filter_does_not_raise_when_module_overrides_previously_registered_method
|
def test_add_filter_does_not_raise_when_module_overrides_previously_registered_method
|
||||||
with_global_filter do
|
with_global_filter do
|
||||||
context = Context.new
|
strainer = Context.new.strainer
|
||||||
context.add_filters([PublicMethodOverrideFilter])
|
strainer.class.add_filter(PublicMethodOverrideFilter)
|
||||||
strainer = context.strainer
|
|
||||||
assert(strainer.class.send(:filter_methods).include?('public_filter'))
|
assert(strainer.class.send(:filter_methods).include?('public_filter'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -20,13 +20,4 @@ class TagUnitTest < Minitest::Test
|
|||||||
tag = Tag.parse("some_tag", "", Tokenizer.new(""), ParseContext.new)
|
tag = Tag.parse("some_tag", "", Tokenizer.new(""), ParseContext.new)
|
||||||
assert_equal('some_tag', tag.tag_name)
|
assert_equal('some_tag', tag.tag_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
class CustomTag < Liquid::Tag
|
|
||||||
def render(_context); end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_tag_render_to_output_buffer_nil_value
|
|
||||||
custom_tag = CustomTag.parse("some_tag", "", Tokenizer.new(""), ParseContext.new)
|
|
||||||
assert_equal('some string', custom_tag.render_to_output_buffer(Context.new, "some string"))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,202 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class CommentTagUnitTest < Minitest::Test
|
|
||||||
def test_comment_inside_liquid_tag
|
|
||||||
assert_template_result("", <<~LIQUID.chomp)
|
|
||||||
{% liquid
|
|
||||||
if 1 != 1
|
|
||||||
comment
|
|
||||||
else
|
|
||||||
echo 123
|
|
||||||
endcomment
|
|
||||||
endif
|
|
||||||
%}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_does_not_parse_nodes_inside_a_comment
|
|
||||||
assert_template_result("", <<~LIQUID.chomp)
|
|
||||||
{% comment %}
|
|
||||||
{% if true %}
|
|
||||||
{% if ... %}
|
|
||||||
{%- for ? -%}
|
|
||||||
{% while true %}
|
|
||||||
{%
|
|
||||||
unless if
|
|
||||||
%}
|
|
||||||
{% endcase %}
|
|
||||||
{% endcomment %}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_allows_unclosed_tags
|
|
||||||
assert_template_result('', <<~LIQUID.chomp)
|
|
||||||
{% comment %}
|
|
||||||
{% if true %}
|
|
||||||
{% endcomment %}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_open_tags_in_comment
|
|
||||||
assert_template_result('', <<~LIQUID.chomp)
|
|
||||||
{% comment %}
|
|
||||||
{% assign a = 123 {% comment %}
|
|
||||||
{% endcomment %}
|
|
||||||
LIQUID
|
|
||||||
|
|
||||||
assert_raises(Liquid::SyntaxError) do
|
|
||||||
assert_template_result("", <<~LIQUID.chomp)
|
|
||||||
{% comment %}
|
|
||||||
{% assign foo = "1"
|
|
||||||
{% endcomment %}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_raises(Liquid::SyntaxError) do
|
|
||||||
assert_template_result("", <<~LIQUID.chomp)
|
|
||||||
{% comment %}
|
|
||||||
{% comment %}
|
|
||||||
{% invalid
|
|
||||||
{% endcomment %}
|
|
||||||
{% endcomment %}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_raises(Liquid::SyntaxError) do
|
|
||||||
assert_template_result("", <<~LIQUID.chomp)
|
|
||||||
{% comment %}
|
|
||||||
{% {{ {%- endcomment %}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_child_comment_tags_need_to_be_closed
|
|
||||||
assert_template_result("", <<~LIQUID.chomp)
|
|
||||||
{% comment %}
|
|
||||||
{% comment %}
|
|
||||||
{% comment %}{% endcomment %}
|
|
||||||
{% endcomment %}
|
|
||||||
{% endcomment %}
|
|
||||||
LIQUID
|
|
||||||
|
|
||||||
assert_raises(Liquid::SyntaxError) do
|
|
||||||
assert_template_result("", <<~LIQUID.chomp)
|
|
||||||
{% comment %}
|
|
||||||
{% comment %}
|
|
||||||
{% comment %}
|
|
||||||
{% endcomment %}
|
|
||||||
{% endcomment %}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_child_raw_tags_need_to_be_closed
|
|
||||||
assert_template_result("", <<~LIQUID.chomp)
|
|
||||||
{% comment %}
|
|
||||||
{% raw %}
|
|
||||||
{% endcomment %}
|
|
||||||
{% endraw %}
|
|
||||||
{% endcomment %}
|
|
||||||
LIQUID
|
|
||||||
|
|
||||||
assert_raises(Liquid::SyntaxError) do
|
|
||||||
Liquid::Template.parse(<<~LIQUID.chomp)
|
|
||||||
{% comment %}
|
|
||||||
{% raw %}
|
|
||||||
{% endcomment %}
|
|
||||||
{% endcomment %}
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_error_line_number_is_correct
|
|
||||||
template = Liquid::Template.parse(<<~LIQUID.chomp, line_numbers: true)
|
|
||||||
{% comment %}
|
|
||||||
{% if true %}
|
|
||||||
{% endcomment %}
|
|
||||||
{{ errors.standard_error }}
|
|
||||||
LIQUID
|
|
||||||
|
|
||||||
output = template.render('errors' => ErrorDrop.new)
|
|
||||||
expected = <<~TEXT.chomp
|
|
||||||
|
|
||||||
Liquid error (line 4): standard error
|
|
||||||
TEXT
|
|
||||||
|
|
||||||
assert_equal(expected, output)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_comment_tag_delimiter_with_extra_strings
|
|
||||||
assert_template_result(
|
|
||||||
'',
|
|
||||||
<<~LIQUID.chomp,
|
|
||||||
{% comment %}
|
|
||||||
{% comment %}
|
|
||||||
{% endcomment
|
|
||||||
{% if true %}
|
|
||||||
{% endif %}
|
|
||||||
{% endcomment %}
|
|
||||||
LIQUID
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_nested_comment_tag_with_extra_strings
|
|
||||||
assert_template_result(
|
|
||||||
'',
|
|
||||||
<<~LIQUID.chomp,
|
|
||||||
{% comment %}
|
|
||||||
{% comment
|
|
||||||
{% assign foo = 1 %}
|
|
||||||
{% endcomment
|
|
||||||
{% assign foo = 1 %}
|
|
||||||
{% endcomment %}
|
|
||||||
LIQUID
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_ignores_delimiter_with_extra_strings
|
|
||||||
assert_template_result(
|
|
||||||
'',
|
|
||||||
<<~LIQUID.chomp,
|
|
||||||
{% if true %}
|
|
||||||
{% comment %}
|
|
||||||
{% commentXXXXX %}wut{% endcommentXXXXX %}
|
|
||||||
{% endcomment %}
|
|
||||||
{% endif %}
|
|
||||||
LIQUID
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_delimiter_can_have_extra_strings
|
|
||||||
assert_template_result('', "{% comment %}123{% endcomment xyz %}")
|
|
||||||
assert_template_result('', "{% comment %}123{% endcomment\txyz %}")
|
|
||||||
assert_template_result('', "{% comment %}123{% endcomment\nxyz %}")
|
|
||||||
assert_template_result('', "{% comment %}123{% endcomment\n xyz endcomment %}")
|
|
||||||
assert_template_result('', "{%comment}{% assign a = 1 %}{%endcomment}{% endif %}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_with_whitespace_control
|
|
||||||
assert_template_result("Hello!", " {%- comment -%}123{%- endcomment -%}Hello!")
|
|
||||||
assert_template_result("Hello!", "{%- comment -%}123{%- endcomment -%} Hello!")
|
|
||||||
assert_template_result("Hello!", " {%- comment -%}123{%- endcomment -%} Hello!")
|
|
||||||
|
|
||||||
assert_template_result("Hello!", <<~LIQUID.chomp)
|
|
||||||
{%- comment %}Whitespace control!{% endcomment -%}
|
|
||||||
Hello!
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_dont_override_liquid_tag_whitespace_control
|
|
||||||
assert_template_result("Hello!World!", <<~LIQUID.chomp)
|
|
||||||
Hello!
|
|
||||||
{%- liquid
|
|
||||||
comment
|
|
||||||
this is inside a liquid tag
|
|
||||||
endcomment
|
|
||||||
-%}
|
|
||||||
World!
|
|
||||||
LIQUID
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -36,6 +36,7 @@ class TemplateUnitTest < Minitest::Test
|
|||||||
assert(Template.tags['custom'].equal?(original_klass))
|
assert(Template.tags['custom'].equal?(original_klass))
|
||||||
ensure
|
ensure
|
||||||
Object.send(:remove_const, :CustomTag)
|
Object.send(:remove_const, :CustomTag)
|
||||||
|
Template.tags.delete('custom')
|
||||||
Liquid.cache_classes = original_cache_setting
|
Liquid.cache_classes = original_cache_setting
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -45,26 +46,36 @@ class TemplateUnitTest < Minitest::Test
|
|||||||
|
|
||||||
original_klass = Class.new
|
original_klass = Class.new
|
||||||
Object.send(:const_set, :CustomTag, original_klass)
|
Object.send(:const_set, :CustomTag, original_klass)
|
||||||
with_custom_tag('custom', CustomTag) do
|
Template.register_tag('custom', CustomTag)
|
||||||
Object.send(:remove_const, :CustomTag)
|
|
||||||
|
|
||||||
new_klass = Class.new
|
Object.send(:remove_const, :CustomTag)
|
||||||
Object.send(:const_set, :CustomTag, new_klass)
|
|
||||||
|
|
||||||
assert(Template.tags['custom'].equal?(new_klass))
|
new_klass = Class.new
|
||||||
end
|
Object.send(:const_set, :CustomTag, new_klass)
|
||||||
|
|
||||||
|
assert(Template.tags['custom'].equal?(new_klass))
|
||||||
ensure
|
ensure
|
||||||
Object.send(:remove_const, :CustomTag)
|
Object.send(:remove_const, :CustomTag)
|
||||||
|
Template.tags.delete('custom')
|
||||||
Liquid.cache_classes = original_cache_setting
|
Liquid.cache_classes = original_cache_setting
|
||||||
end
|
end
|
||||||
|
|
||||||
class FakeTag; end
|
class FakeTag; end
|
||||||
|
|
||||||
|
def test_tags_delete
|
||||||
|
Template.register_tag('fake', FakeTag)
|
||||||
|
assert_equal(FakeTag, Template.tags['fake'])
|
||||||
|
|
||||||
|
Template.tags.delete('fake')
|
||||||
|
assert_nil(Template.tags['fake'])
|
||||||
|
end
|
||||||
|
|
||||||
def test_tags_can_be_looped_over
|
def test_tags_can_be_looped_over
|
||||||
with_custom_tag('fake', FakeTag) do
|
Template.register_tag('fake', FakeTag)
|
||||||
result = Template.tags.map { |name, klass| [name, klass] }
|
result = Template.tags.map { |name, klass| [name, klass] }
|
||||||
assert(result.include?(["fake", "TemplateUnitTest::FakeTag"]))
|
assert(result.include?(["fake", "TemplateUnitTest::FakeTag"]))
|
||||||
end
|
ensure
|
||||||
|
Template.tags.delete('fake')
|
||||||
end
|
end
|
||||||
|
|
||||||
class TemplateSubclass < Liquid::Template
|
class TemplateSubclass < Liquid::Template
|
||||||
|
|||||||
Reference in New Issue
Block a user