mirror of
https://github.com/Shopify/liquid.git
synced 2026-10-02 00:25:12 -07:00
Compare commits
64
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f8722a6d3 | ||
|
|
81f44e36be | ||
|
|
c9ec8f4635 | ||
|
|
3fb467f069 | ||
|
|
992e15a173 | ||
|
|
1fdc577246 | ||
|
|
74245cd396 | ||
|
|
86605016e1 | ||
|
|
6981305736 | ||
|
|
6d3c5ef3d3 | ||
|
|
150ddf4c3b | ||
|
|
a3e9088a0e | ||
|
|
b05393884d | ||
|
|
ef9db08642 | ||
|
|
5dd8c84b47 | ||
|
|
1af12c74cc | ||
|
|
72bbbda022 | ||
|
|
489e3ca7bf | ||
|
|
65542f9e4f | ||
|
|
d497bfffe9 | ||
|
|
ebafb0a3fe | ||
|
|
4ec9db3f99 | ||
|
|
95eb5d6036 | ||
|
|
7ba40d48c0 | ||
|
|
9020dbcd41 | ||
|
|
73f7467258 | ||
|
|
f3aa5fbd7c | ||
|
|
eb70bb9b87 | ||
|
|
4d8e55dbc4 | ||
|
|
f697093e94 | ||
|
|
7594bed88a | ||
|
|
f64471eb4e | ||
|
|
d4c24f3ce2 | ||
|
|
3bbb7aa7ba | ||
|
|
102bac2e33 | ||
|
|
e69f729f76 | ||
|
|
4ec0b85d80 | ||
|
|
f77c766262 | ||
|
|
7bc14ae2be | ||
|
|
41c19929ad | ||
|
|
05d768c6ab | ||
|
|
54414dfd83 | ||
|
|
23a8438fa6 | ||
|
|
21f3337dec | ||
|
|
1f0a0ad55c | ||
|
|
36dce29776 | ||
|
|
8b68630a11 | ||
|
|
8882338aa1 | ||
|
|
6c2c621712 | ||
|
|
1cae1e497f | ||
|
|
ed7dae50aa | ||
|
|
7e99432bd1 | ||
|
|
6c187b8470 | ||
|
|
6e07f73f68 | ||
|
|
f64af57b7b | ||
|
|
c60c3c7802 | ||
|
|
11625b1bc9 | ||
|
|
ec6fb4d5fa | ||
|
|
fad58ef436 | ||
|
|
22568080b1 | ||
|
|
dd7ed00ec4 | ||
|
|
1667c1180e | ||
|
|
0e14d539a3 | ||
|
|
db106ae058 |
@@ -6,12 +6,12 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
entry:
|
entry:
|
||||||
- { ruby: 2.5, allowed-failure: false } # minimum supported
|
- { ruby: 2.7, allowed-failure: false } # minimum supported
|
||||||
- { ruby: 3.1, allowed-failure: false } # latest
|
- { ruby: 3.1, allowed-failure: false } # latest
|
||||||
- { ruby: ruby-head, allowed-failure: true }
|
- { ruby: ruby-head, allowed-failure: true }
|
||||||
name: test (${{ matrix.entry.ruby }})
|
name: test (${{ matrix.entry.ruby }})
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- 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 }}
|
||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
memory_profile:
|
memory_profile:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: ruby/setup-ruby@v1
|
- uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 2.7
|
ruby-version: 2.7
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ Performance:
|
|||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
TargetRubyVersion: 2.5
|
TargetRubyVersion: 2.7
|
||||||
NewCops: disable
|
NewCops: disable
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'vendor/bundle/**/*'
|
- 'vendor/bundle/**/*'
|
||||||
|
|||||||
+137
-14
@@ -1,27 +1,74 @@
|
|||||||
# This configuration was generated by
|
# This configuration was generated by
|
||||||
# `rubocop --auto-gen-config`
|
# `rubocop --auto-gen-config`
|
||||||
# on 2020-12-11 18:53:41 UTC using RuboCop version 1.6.1.
|
# on 2022-05-18 19:25:47 UTC using RuboCop version 1.29.1.
|
||||||
# The point is for the user to remove these configuration records
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the offenses are removed from the code base.
|
# one by one as the offenses are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# Note that changes in the inspected code, or installation of new
|
||||||
# versions of RuboCop, may require this file to be generated again.
|
# versions of RuboCop, may require this file to be generated again.
|
||||||
|
|
||||||
# Offense count: 2
|
# Offense count: 1
|
||||||
# Cop supports --auto-correct.
|
# This cop supports safe auto-correction (--auto-correct).
|
||||||
# Configuration parameters: EnforcedStyle.
|
# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
|
||||||
# SupportedStyles: runtime_error, standard_error
|
# Include: **/*.gemspec
|
||||||
Lint/InheritException:
|
Gemspec/OrderedDependencies:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/liquid/interrupts.rb'
|
- 'liquid.gemspec'
|
||||||
|
|
||||||
# Offense count: 113
|
# Offense count: 6
|
||||||
# Cop supports --auto-correct.
|
# This cop supports safe auto-correction (--auto-correct).
|
||||||
# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
Layout/ClosingHeredocIndentation:
|
||||||
# URISchemes: http, https
|
Exclude:
|
||||||
Layout/LineLength:
|
- 'test/integration/tags/for_tag_test.rb'
|
||||||
Max: 260
|
|
||||||
|
# Offense count: 34
|
||||||
|
# This cop supports safe auto-correction (--auto-correct).
|
||||||
|
Layout/EmptyLineAfterGuardClause:
|
||||||
|
Exclude:
|
||||||
|
- 'lib/liquid/block.rb'
|
||||||
|
- 'lib/liquid/block_body.rb'
|
||||||
|
- 'lib/liquid/context.rb'
|
||||||
|
- 'lib/liquid/drop.rb'
|
||||||
|
- 'lib/liquid/lexer.rb'
|
||||||
|
- 'lib/liquid/parser.rb'
|
||||||
|
- 'lib/liquid/profiler/hooks.rb'
|
||||||
|
- 'lib/liquid/standardfilters.rb'
|
||||||
|
- 'lib/liquid/tags/for.rb'
|
||||||
|
- 'lib/liquid/tags/if.rb'
|
||||||
|
- 'lib/liquid/utils.rb'
|
||||||
|
- 'lib/liquid/variable.rb'
|
||||||
|
- 'lib/liquid/variable_lookup.rb'
|
||||||
|
- 'performance/shopify/money_filter.rb'
|
||||||
|
- 'performance/shopify/paginate.rb'
|
||||||
|
|
||||||
# Offense count: 8
|
# Offense count: 8
|
||||||
|
# This cop supports safe auto-correction (--auto-correct).
|
||||||
|
# Configuration parameters: AllowAliasSyntax, AllowedMethods.
|
||||||
|
# AllowedMethods: alias_method, public, protected, private
|
||||||
|
Layout/EmptyLinesAroundAttributeAccessor:
|
||||||
|
Exclude:
|
||||||
|
- 'lib/liquid/template.rb'
|
||||||
|
- 'test/integration/filter_test.rb'
|
||||||
|
- 'test/integration/tags/include_tag_test.rb'
|
||||||
|
- 'test/unit/strainer_template_unit_test.rb'
|
||||||
|
|
||||||
|
# Offense count: 17
|
||||||
|
# This cop supports safe auto-correction (--auto-correct).
|
||||||
|
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
||||||
|
# SupportedStyles: aligned, indented
|
||||||
|
Layout/LineEndStringConcatenationIndentation:
|
||||||
|
Exclude:
|
||||||
|
- 'test/integration/tags/for_tag_test.rb'
|
||||||
|
- 'test/integration/tags/increment_tag_test.rb'
|
||||||
|
|
||||||
|
# Offense count: 1
|
||||||
|
# This cop supports safe auto-correction (--auto-correct).
|
||||||
|
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
||||||
|
# SupportedStyles: aligned, indented
|
||||||
|
Layout/MultilineOperationIndentation:
|
||||||
|
Exclude:
|
||||||
|
- 'lib/liquid/expression.rb'
|
||||||
|
|
||||||
|
# Offense count: 9
|
||||||
Lint/MissingSuper:
|
Lint/MissingSuper:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/liquid/forloop_drop.rb'
|
- 'lib/liquid/forloop_drop.rb'
|
||||||
@@ -33,7 +80,7 @@ Lint/MissingSuper:
|
|||||||
- 'test/integration/tags/for_tag_test.rb'
|
- 'test/integration/tags/for_tag_test.rb'
|
||||||
- 'test/integration/tags/table_row_test.rb'
|
- 'test/integration/tags/table_row_test.rb'
|
||||||
|
|
||||||
# Offense count: 43
|
# Offense count: 44
|
||||||
Naming/ConstantName:
|
Naming/ConstantName:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/liquid.rb'
|
- 'lib/liquid.rb'
|
||||||
@@ -51,7 +98,83 @@ Naming/ConstantName:
|
|||||||
- 'performance/shopify/paginate.rb'
|
- 'performance/shopify/paginate.rb'
|
||||||
- 'test/integration/tags/include_tag_test.rb'
|
- 'test/integration/tags/include_tag_test.rb'
|
||||||
|
|
||||||
|
# Offense count: 9
|
||||||
|
# Configuration parameters: CheckIdentifiers, CheckConstants, CheckVariables, CheckStrings, CheckSymbols, CheckComments, CheckFilepaths, FlaggedTerms.
|
||||||
|
Naming/InclusiveLanguage:
|
||||||
|
Exclude:
|
||||||
|
- 'lib/liquid/drop.rb'
|
||||||
|
- 'lib/liquid/parse_context.rb'
|
||||||
|
- 'test/integration/drop_test.rb'
|
||||||
|
- 'test/integration/tags/if_else_tag_test.rb'
|
||||||
|
|
||||||
# Offense count: 2
|
# Offense count: 2
|
||||||
Style/ClassVars:
|
Style/ClassVars:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'lib/liquid/condition.rb'
|
- 'lib/liquid/condition.rb'
|
||||||
|
|
||||||
|
# Offense count: 3
|
||||||
|
# This cop supports safe auto-correction (--auto-correct).
|
||||||
|
Style/ExplicitBlockArgument:
|
||||||
|
Exclude:
|
||||||
|
- 'test/integration/context_test.rb'
|
||||||
|
- 'test/integration/tag/disableable_test.rb'
|
||||||
|
- 'test/integration/tags/for_tag_test.rb'
|
||||||
|
|
||||||
|
# Offense count: 2982
|
||||||
|
# This cop supports safe auto-correction (--auto-correct).
|
||||||
|
# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
|
||||||
|
# SupportedStyles: single_quotes, double_quotes
|
||||||
|
Style/StringLiterals:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Offense count: 20
|
||||||
|
# This cop supports safe auto-correction (--auto-correct).
|
||||||
|
# Configuration parameters: EnforcedStyle.
|
||||||
|
# SupportedStyles: single_quotes, double_quotes
|
||||||
|
Style/StringLiteralsInInterpolation:
|
||||||
|
Exclude:
|
||||||
|
- 'lib/liquid/condition.rb'
|
||||||
|
- 'lib/liquid/strainer_template.rb'
|
||||||
|
- 'lib/liquid/tag/disableable.rb'
|
||||||
|
- 'performance/shopify/shop_filter.rb'
|
||||||
|
- 'performance/shopify/tag_filter.rb'
|
||||||
|
|
||||||
|
# Offense count: 6
|
||||||
|
# This cop supports safe auto-correction (--auto-correct).
|
||||||
|
# Configuration parameters: EnforcedStyleForMultiline.
|
||||||
|
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
|
||||||
|
Style/TrailingCommaInArrayLiteral:
|
||||||
|
Exclude:
|
||||||
|
- 'example/server/example_servlet.rb'
|
||||||
|
- 'lib/liquid/condition.rb'
|
||||||
|
- 'test/integration/context_test.rb'
|
||||||
|
- 'test/integration/standard_filter_test.rb'
|
||||||
|
- 'test/unit/parse_tree_visitor_test.rb'
|
||||||
|
|
||||||
|
# Offense count: 1
|
||||||
|
# This cop supports safe auto-correction (--auto-correct).
|
||||||
|
# Configuration parameters: EnforcedStyleForMultiline.
|
||||||
|
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
|
||||||
|
Style/TrailingCommaInHashLiteral:
|
||||||
|
Exclude:
|
||||||
|
- 'lib/liquid/expression.rb'
|
||||||
|
|
||||||
|
# Offense count: 19
|
||||||
|
# This cop supports safe auto-correction (--auto-correct).
|
||||||
|
# Configuration parameters: EnforcedStyle, MinSize, WordRegex.
|
||||||
|
# SupportedStyles: percent, brackets
|
||||||
|
Style/WordArray:
|
||||||
|
Exclude:
|
||||||
|
- 'lib/liquid/tags/if.rb'
|
||||||
|
- 'liquid.gemspec'
|
||||||
|
- 'test/integration/assign_test.rb'
|
||||||
|
- 'test/integration/context_test.rb'
|
||||||
|
- 'test/integration/drop_test.rb'
|
||||||
|
- 'test/integration/standard_filter_test.rb'
|
||||||
|
|
||||||
|
# Offense count: 117
|
||||||
|
# This cop supports safe auto-correction (--auto-correct).
|
||||||
|
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, IgnoredPatterns.
|
||||||
|
# URISchemes: http, https
|
||||||
|
Layout/LineLength:
|
||||||
|
Max: 260
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ group :benchmark, :test do
|
|||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'rubocop', '~> 1.4', require: false
|
gem 'rubocop-shopify', '~> 2.7.0', require: false
|
||||||
gem 'rubocop-shopify', '~> 1.0.7', require: false
|
|
||||||
gem 'rubocop-performance', require: false
|
gem 'rubocop-performance', require: false
|
||||||
|
|
||||||
platform :mri, :truffleruby do
|
platform :mri, :truffleruby do
|
||||||
|
|||||||
+18
-1
@@ -1,9 +1,26 @@
|
|||||||
# Liquid Change Log
|
# Liquid Change Log
|
||||||
|
|
||||||
## 5.3.0 (unreleased)
|
## 5.4.0 2022-07-29
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
* Drop support for end-of-life Ruby versions (2.5 and 2.6) (#1578) [Andy Waite]
|
||||||
|
|
||||||
|
### Features
|
||||||
|
* Allow `#` to be used as an inline comment tag (#1498) [CP Clermont]
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
* `PartialCache` now shares snippet cache with subcontexts by default (#1553) [Chris AtLee]
|
||||||
|
* Hash registers no longer leak into subcontexts as static registers (#1564) [Chris AtLee]
|
||||||
|
* Fix `ParseTreeVisitor` for `with` variable expressions in `Render` tag (#1596) [CP Clermont]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* Liquid::Context#registers now always returns a Liquid::Registers object, though supports the most used Hash functions for compatibility (#1553)
|
||||||
|
|
||||||
|
## 5.3.0 2022-03-22
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
* StandardFilter: Fix missing @context on iterations (#1525) [Thierry Joyal]
|
* StandardFilter: Fix missing @context on iterations (#1525) [Thierry Joyal]
|
||||||
|
* Fix warning about block and default value in `static_registers.rb` (#1531) [Peter Zhu]
|
||||||
|
|
||||||
### Deprecation
|
### Deprecation
|
||||||
* Condition#evaluate to require mandatory context argument in Liquid 6.0.0 (#1527) [Thierry Joyal]
|
* Condition#evaluate to require mandatory context argument in Liquid 6.0.0 (#1527) [Thierry Joyal]
|
||||||
|
|||||||
@@ -63,13 +63,13 @@ when templates are invalid. You can enable this new parser like this:
|
|||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
Liquid::Template.error_mode = :strict # Raises a SyntaxError when invalid syntax is used
|
Liquid::Template.error_mode = :strict # Raises a SyntaxError when invalid syntax is used
|
||||||
Liquid::Template.error_mode = :warn # Adds errors to template.errors but continues as normal
|
Liquid::Template.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::Template.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`:
|
||||||
```ruby
|
```ruby
|
||||||
Liquid::Template.parse(source, :error_mode => :strict)
|
Liquid::Template.parse(source, error_mode: :strict)
|
||||||
```
|
```
|
||||||
This is useful for doing things like enabling strict mode only in the theme editor.
|
This is useful for doing things like enabling strict mode only in the theme editor.
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -29,6 +29,7 @@ module Liquid
|
|||||||
WhitespaceControl = '-'
|
WhitespaceControl = '-'
|
||||||
TagStart = /\{\%/
|
TagStart = /\{\%/
|
||||||
TagEnd = /\%\}/
|
TagEnd = /\%\}/
|
||||||
|
TagName = /#|\w+/
|
||||||
VariableSignature = /\(?[\w\-\.\[\]]\)?/
|
VariableSignature = /\(?[\w\-\.\[\]]\)?/
|
||||||
VariableSegment = /[\w\-]/
|
VariableSegment = /[\w\-]/
|
||||||
VariableStart = /\{\{/
|
VariableStart = /\{\{/
|
||||||
@@ -83,8 +84,7 @@ require 'liquid/tokenizer'
|
|||||||
require 'liquid/parse_context'
|
require 'liquid/parse_context'
|
||||||
require 'liquid/partial_cache'
|
require 'liquid/partial_cache'
|
||||||
require 'liquid/usage'
|
require 'liquid/usage'
|
||||||
require 'liquid/register'
|
require 'liquid/registers'
|
||||||
require 'liquid/static_registers'
|
|
||||||
require 'liquid/template_factory'
|
require 'liquid/template_factory'
|
||||||
|
|
||||||
# Load all the tags of the standard library
|
# Load all the tags of the standard library
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ require 'English'
|
|||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
class BlockBody
|
class BlockBody
|
||||||
LiquidTagToken = /\A\s*(\w+)\s*(.*?)\z/o
|
LiquidTagToken = /\A\s*(#{TagName})\s*(.*?)\z/o
|
||||||
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(\w+)(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(#{TagName})(\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 = "{%"
|
||||||
@@ -37,7 +37,7 @@ module Liquid
|
|||||||
|
|
||||||
private def parse_for_liquid_tag(tokenizer, parse_context)
|
private def parse_for_liquid_tag(tokenizer, parse_context)
|
||||||
while (token = tokenizer.shift)
|
while (token = tokenizer.shift)
|
||||||
unless token.empty? || token =~ WhitespaceOrNothing
|
unless token.empty? || token.match?(WhitespaceOrNothing)
|
||||||
unless token =~ LiquidTagToken
|
unless token =~ LiquidTagToken
|
||||||
# line isn't empty but didn't match tag syntax, yield and let the
|
# line isn't empty but didn't match tag syntax, yield and let the
|
||||||
# caller raise a syntax error
|
# caller raise a syntax error
|
||||||
@@ -150,7 +150,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
parse_context.trim_whitespace = false
|
parse_context.trim_whitespace = false
|
||||||
@nodelist << token
|
@nodelist << token
|
||||||
@blank &&= !!(token =~ WhitespaceOrNothing)
|
@blank &&= token.match?(WhitespaceOrNothing)
|
||||||
end
|
end
|
||||||
parse_context.line_number = tokenizer.line_number
|
parse_context.line_number = tokenizer.line_number
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ module Liquid
|
|||||||
|
|
||||||
@static_environments = [static_environments].flat_map(&:freeze).freeze
|
@static_environments = [static_environments].flat_map(&:freeze).freeze
|
||||||
@scopes = [(outer_scope || {})]
|
@scopes = [(outer_scope || {})]
|
||||||
@registers = registers
|
@registers = registers.is_a?(Registers) ? registers : Registers.new(registers)
|
||||||
@errors = []
|
@errors = []
|
||||||
@partial = false
|
@partial = false
|
||||||
@strict_variables = false
|
@strict_variables = false
|
||||||
@@ -39,6 +39,10 @@ module Liquid
|
|||||||
@global_filter = nil
|
@global_filter = nil
|
||||||
@disabled_tags = {}
|
@disabled_tags = {}
|
||||||
|
|
||||||
|
@registers.static[:cached_partials] ||= {}
|
||||||
|
@registers.static[:file_system] ||= Liquid::Template.file_system
|
||||||
|
@registers.static[:template_factory] ||= Liquid::TemplateFactory.new
|
||||||
|
|
||||||
self.exception_renderer = Template.default_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
|
||||||
@@ -140,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: StaticRegisters.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
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type object
|
||||||
|
# @liquid_name forloop
|
||||||
|
# @liquid_summary
|
||||||
|
# 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
|
||||||
@@ -9,33 +14,71 @@ module Liquid
|
|||||||
@index = 0
|
@index = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :length, :parentloop
|
# @liquid_public_docs
|
||||||
|
# @liquid_name length
|
||||||
|
# @liquid_summary
|
||||||
|
# The total number of iterations in the loop.
|
||||||
|
# @liquid_return [number]
|
||||||
|
attr_reader :length
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_name parentloop
|
||||||
|
# @liquid_summary
|
||||||
|
# The parent `forloop` object.
|
||||||
|
# @liquid_description
|
||||||
|
# If the current `for` loop isn't nested inside another `for` loop, then `nil` is returned.
|
||||||
|
# @liquid_return [forloop]
|
||||||
|
attr_reader :parentloop
|
||||||
|
|
||||||
def name
|
def name
|
||||||
Usage.increment('forloop_drop_name')
|
Usage.increment('forloop_drop_name')
|
||||||
@name
|
@name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# The 1-based index of the current iteration.
|
||||||
|
# @liquid_return [number]
|
||||||
def index
|
def index
|
||||||
@index + 1
|
@index + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# The 0-based index of the current iteration.
|
||||||
|
# @liquid_return [number]
|
||||||
def index0
|
def index0
|
||||||
@index
|
@index
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# The 1-based index of the current iteration, in reverse order.
|
||||||
|
# @liquid_return [number]
|
||||||
def rindex
|
def rindex
|
||||||
@length - @index
|
@length - @index
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# The 0-based index of the current iteration, in reverse order.
|
||||||
|
# @liquid_return [number]
|
||||||
def rindex0
|
def rindex0
|
||||||
@length - @index - 1
|
@length - @index - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# Returns `true` if the current iteration is the first. Returns `false` if not.
|
||||||
|
# @liquid_return [boolean]
|
||||||
def first
|
def first
|
||||||
@index == 0
|
@index == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# Returns `true` if the current iteration is the last. Returns `false` if not.
|
||||||
|
# @liquid_return [boolean]
|
||||||
def last
|
def last
|
||||||
@index == @length - 1
|
@index == @length - 1
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,15 +13,16 @@
|
|||||||
for_invalid_attribute: "Invalid attribute in for loop. Valid attributes are limit and offset"
|
for_invalid_attribute: "Invalid attribute in for loop. Valid attributes are limit and offset"
|
||||||
if: "Syntax Error in tag 'if' - Valid syntax: if [expression]"
|
if: "Syntax Error in tag 'if' - Valid syntax: if [expression]"
|
||||||
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]"
|
||||||
unknown_tag: "Unknown tag '%{tag}'"
|
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}"
|
||||||
|
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"
|
||||||
|
tag_never_closed: "'%{block_name}' tag was never closed"
|
||||||
|
tag_termination: "Tag '%{token}' was not properly terminated with regexp: %{tag_end}"
|
||||||
unexpected_else: "%{block_name} tag does not expect 'else' tag"
|
unexpected_else: "%{block_name} tag does not expect 'else' tag"
|
||||||
unexpected_outer_tag: "Unexpected outer '%{tag}' tag"
|
unexpected_outer_tag: "Unexpected outer '%{tag}' tag"
|
||||||
tag_termination: "Tag '%{token}' was not properly terminated with regexp: %{tag_end}"
|
unknown_tag: "Unknown tag '%{tag}'"
|
||||||
variable_termination: "Variable '%{token}' was not properly terminated with regexp: %{tag_end}"
|
variable_termination: "Variable '%{token}' was not properly terminated with regexp: %{tag_end}"
|
||||||
tag_never_closed: "'%{block_name}' tag was never closed"
|
|
||||||
table_row: "Syntax Error in 'table_row loop' - Valid syntax: table_row [item] in [collection] cols=3"
|
|
||||||
render: "Syntax error in tag 'render' - Template name must be a quoted string"
|
|
||||||
argument:
|
argument:
|
||||||
include: "Argument error in tag 'include' - Illegal template name"
|
include: "Argument error in tag 'include' - Illegal template name"
|
||||||
disabled:
|
disabled:
|
||||||
|
|||||||
@@ -3,16 +3,16 @@
|
|||||||
module Liquid
|
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]
|
||||||
cached = cached_partials[template_name]
|
cached = cached_partials[template_name]
|
||||||
return cached if cached
|
return cached if cached
|
||||||
|
|
||||||
file_system = (context.registers[:file_system] ||= Liquid::Template.file_system)
|
file_system = context.registers[:file_system]
|
||||||
source = file_system.read_template_file(template_name)
|
source = file_system.read_template_file(template_name)
|
||||||
|
|
||||||
parse_context.partial = true
|
parse_context.partial = true
|
||||||
|
|
||||||
template_factory = (context.registers[:template_factory] ||= Liquid::TemplateFactory.new)
|
template_factory = context.registers[:template_factory]
|
||||||
template = template_factory.for(template_name)
|
template = template_factory.for(template_name)
|
||||||
|
|
||||||
partial = template.parse(source, parse_context)
|
partial = template.parse(source, parse_context)
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Liquid
|
|
||||||
class Register
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,35 +1,35 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
class StaticRegisters
|
class Registers
|
||||||
attr_reader :static
|
attr_reader :static
|
||||||
|
|
||||||
def initialize(registers = {})
|
def initialize(registers = {})
|
||||||
@static = registers.is_a?(StaticRegisters) ? registers.static : registers
|
@static = registers.is_a?(Registers) ? registers.static : registers
|
||||||
@registers = {}
|
@changes = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
def []=(key, value)
|
def []=(key, value)
|
||||||
@registers[key] = value
|
@changes[key] = value
|
||||||
end
|
end
|
||||||
|
|
||||||
def [](key)
|
def [](key)
|
||||||
if @registers.key?(key)
|
if @changes.key?(key)
|
||||||
@registers[key]
|
@changes[key]
|
||||||
else
|
else
|
||||||
@static[key]
|
@static[key]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(key)
|
def delete(key)
|
||||||
@registers.delete(key)
|
@changes.delete(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
UNDEFINED = Object.new
|
UNDEFINED = Object.new
|
||||||
|
|
||||||
def fetch(key, default = UNDEFINED, &block)
|
def fetch(key, default = UNDEFINED, &block)
|
||||||
if @registers.key?(key)
|
if @changes.key?(key)
|
||||||
@registers.fetch(key)
|
@changes.fetch(key)
|
||||||
elsif default != UNDEFINED
|
elsif default != UNDEFINED
|
||||||
if block_given?
|
if block_given?
|
||||||
@static.fetch(key, &block)
|
@static.fetch(key, &block)
|
||||||
@@ -42,7 +42,10 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def key?(key)
|
def key?(key)
|
||||||
@registers.key?(key) || @static.key?(key)
|
@changes.key?(key) || @static.key?(key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Alias for backwards compatibility
|
||||||
|
StaticRegisters = Registers
|
||||||
end
|
end
|
||||||
+406
-64
@@ -22,46 +22,99 @@ module Liquid
|
|||||||
)
|
)
|
||||||
STRIP_HTML_TAGS = /<.*?>/m
|
STRIP_HTML_TAGS = /<.*?>/m
|
||||||
|
|
||||||
# @public_docs
|
# @liquid_public_docs
|
||||||
# @title Size
|
# @liquid_type filter
|
||||||
# @syntax The syntax
|
# @liquid_category array
|
||||||
# @summary The summary
|
# @liquid_summary
|
||||||
# @type filter
|
# Returns the size of a string or array.
|
||||||
# @category My category
|
# @liquid_description
|
||||||
# @description
|
# The size of a string is the number of characters that the string includes. The size of an array is the number of items
|
||||||
# Return the size of an array or of an string
|
# in the array.
|
||||||
|
# @liquid_syntax variable | size
|
||||||
|
# @liquid_return [number]
|
||||||
def size(input)
|
def size(input)
|
||||||
input.respond_to?(:size) ? input.size : 0
|
input.respond_to?(:size) ? input.size : 0
|
||||||
end
|
end
|
||||||
|
|
||||||
# convert an input string to DOWNCASE
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Converts a string to all lowercase characters.
|
||||||
|
# @liquid_syntax string | downcase
|
||||||
|
# @liquid_return [string]
|
||||||
def downcase(input)
|
def downcase(input)
|
||||||
input.to_s.downcase
|
input.to_s.downcase
|
||||||
end
|
end
|
||||||
|
|
||||||
# convert an input string to UPCASE
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Converts a string to all uppercase characters.
|
||||||
|
# @liquid_syntax string | upcase
|
||||||
|
# @liquid_return [string]
|
||||||
def upcase(input)
|
def upcase(input)
|
||||||
input.to_s.upcase
|
input.to_s.upcase
|
||||||
end
|
end
|
||||||
|
|
||||||
# capitalize words in the input centence
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Capitalizes the first word in a string.
|
||||||
|
# @liquid_syntax string | capitalize
|
||||||
|
# @liquid_return [string]
|
||||||
def capitalize(input)
|
def capitalize(input)
|
||||||
input.to_s.capitalize
|
input.to_s.capitalize
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Escapes a string.
|
||||||
|
# @liquid_syntax string | escape
|
||||||
|
# @liquid_return [string]
|
||||||
def escape(input)
|
def escape(input)
|
||||||
CGI.escapeHTML(input.to_s) unless input.nil?
|
CGI.escapeHTML(input.to_s) unless input.nil?
|
||||||
end
|
end
|
||||||
alias_method :h, :escape
|
alias_method :h, :escape
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Escapes a string without changing characters that have already been escaped.
|
||||||
|
# @liquid_syntax string | escape_once
|
||||||
|
# @liquid_return [string]
|
||||||
def escape_once(input)
|
def escape_once(input)
|
||||||
input.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
|
input.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Converts any URL-unsafe characters in a string to the
|
||||||
|
# [percent-encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding) equivalent.
|
||||||
|
# @liquid_description
|
||||||
|
# > Note:
|
||||||
|
# > Spaces are converted to a `+` character, instead of a percent-encoded character.
|
||||||
|
# @liquid_syntax string | url_encode
|
||||||
|
# @liquid_return [string]
|
||||||
def url_encode(input)
|
def url_encode(input)
|
||||||
CGI.escape(input.to_s) unless input.nil?
|
CGI.escape(input.to_s) unless input.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Decodes any [percent-encoded](https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding) characters
|
||||||
|
# in a string.
|
||||||
|
# @liquid_syntax string | url_decode
|
||||||
|
# @liquid_return [string]
|
||||||
def url_decode(input)
|
def url_decode(input)
|
||||||
return if input.nil?
|
return if input.nil?
|
||||||
|
|
||||||
@@ -71,26 +124,64 @@ module Liquid
|
|||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Encodes a string to [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
|
||||||
|
# @liquid_syntax string | base64_encode
|
||||||
|
# @liquid_return [string]
|
||||||
def base64_encode(input)
|
def base64_encode(input)
|
||||||
Base64.strict_encode64(input.to_s)
|
Base64.strict_encode64(input.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Decodes a string in [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
|
||||||
|
# @liquid_syntax string | base64_decode
|
||||||
|
# @liquid_return [string]
|
||||||
def base64_decode(input)
|
def base64_decode(input)
|
||||||
Base64.strict_decode64(input.to_s)
|
Base64.strict_decode64(input.to_s)
|
||||||
rescue ::ArgumentError
|
rescue ::ArgumentError
|
||||||
raise Liquid::ArgumentError, "invalid base64 provided to base64_decode"
|
raise Liquid::ArgumentError, "invalid base64 provided to base64_decode"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Encodes a string to URL-safe [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
|
||||||
|
# @liquid_syntax string | base64_url_safe_encode
|
||||||
|
# @liquid_return [string]
|
||||||
def base64_url_safe_encode(input)
|
def base64_url_safe_encode(input)
|
||||||
Base64.urlsafe_encode64(input.to_s)
|
Base64.urlsafe_encode64(input.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Decodes a string in URL-safe [Base64 format](https://developer.mozilla.org/en-US/docs/Glossary/Base64).
|
||||||
|
# @liquid_syntax string | base64_url_safe_decode
|
||||||
|
# @liquid_return [string]
|
||||||
def base64_url_safe_decode(input)
|
def base64_url_safe_decode(input)
|
||||||
Base64.urlsafe_decode64(input.to_s)
|
Base64.urlsafe_decode64(input.to_s)
|
||||||
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
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Returns a substring or series of array items, starting at a given 0-based index.
|
||||||
|
# @liquid_description
|
||||||
|
# By default, the substring has a length of one character, and the array series has one array item. However, you can
|
||||||
|
# provide a second parameter to specify the number of characters or array items.
|
||||||
|
# @liquid_syntax string | slice
|
||||||
|
# @liquid_return [string]
|
||||||
def slice(input, offset, length = nil)
|
def slice(input, offset, length = nil)
|
||||||
offset = Utils.to_integer(offset)
|
offset = Utils.to_integer(offset)
|
||||||
length = length ? Utils.to_integer(length) : 1
|
length = length ? Utils.to_integer(length) : 1
|
||||||
@@ -102,7 +193,16 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Truncate a string down to x characters
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Truncates a string down to a given number of characters.
|
||||||
|
# @liquid_description
|
||||||
|
# If the specified number of characters is less than the length of the string, then an ellipsis (`...`) is appended to
|
||||||
|
# the truncated string. The ellipsis is included in the character count of the truncated string.
|
||||||
|
# @liquid_syntax string | truncate: number
|
||||||
|
# @liquid_return [string]
|
||||||
def truncate(input, length = 50, truncate_string = "...")
|
def truncate(input, length = 50, truncate_string = "...")
|
||||||
return if input.nil?
|
return if input.nil?
|
||||||
input_str = input.to_s
|
input_str = input.to_s
|
||||||
@@ -116,6 +216,20 @@ module Liquid
|
|||||||
input_str.length > length ? input_str[0...l].concat(truncate_string_str) : input_str
|
input_str.length > length ? input_str[0...l].concat(truncate_string_str) : input_str
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Truncates a string down to a given number of words.
|
||||||
|
# @liquid_description
|
||||||
|
# If the specified number of words is less than the number of words in the string, then an ellipsis (`...`) is appended to
|
||||||
|
# the truncated string.
|
||||||
|
#
|
||||||
|
# > Caution:
|
||||||
|
# > HTML tags are treated as words, so you should strip any HTML from truncated content. If you don't strip HTML, then
|
||||||
|
# > closing HTML tags can be removed, which can result in unexpected behavior.
|
||||||
|
# @liquid_syntax string | truncatewords: number
|
||||||
|
# @liquid_return [string]
|
||||||
def truncatewords(input, words = 15, truncate_string = "...")
|
def truncatewords(input, words = 15, truncate_string = "...")
|
||||||
return if input.nil?
|
return if input.nil?
|
||||||
input = input.to_s
|
input = input.to_s
|
||||||
@@ -135,27 +249,57 @@ module Liquid
|
|||||||
wordlist.join(" ").concat(truncate_string.to_s)
|
wordlist.join(" ").concat(truncate_string.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Split input string into an array of substrings separated by given pattern.
|
# @liquid_public_docs
|
||||||
#
|
# @liquid_type filter
|
||||||
# Example:
|
# @liquid_category string
|
||||||
# <div class="summary">{{ post | split '//' | first }}</div>
|
# @liquid_summary
|
||||||
#
|
# Splits a string into an array of substrings based on a given separator.
|
||||||
|
# @liquid_syntax string | split: string
|
||||||
|
# @liquid_return [array[string]]
|
||||||
def split(input, pattern)
|
def split(input, pattern)
|
||||||
input.to_s.split(pattern.to_s)
|
input.to_s.split(pattern.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Strips all whitespace from the left and right of a string.
|
||||||
|
# @liquid_syntax string | strip
|
||||||
|
# @liquid_return [string]
|
||||||
def strip(input)
|
def strip(input)
|
||||||
input.to_s.strip
|
input.to_s.strip
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Strips all whitespace from the left of a string.
|
||||||
|
# @liquid_syntax string | lstrip
|
||||||
|
# @liquid_return [string]
|
||||||
def lstrip(input)
|
def lstrip(input)
|
||||||
input.to_s.lstrip
|
input.to_s.lstrip
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Strips all whitespace from the right of a string.
|
||||||
|
# @liquid_syntax string | rstrip
|
||||||
|
# @liquid_return [string]
|
||||||
def rstrip(input)
|
def rstrip(input)
|
||||||
input.to_s.rstrip
|
input.to_s.rstrip
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Strips all HTML tags from a string.
|
||||||
|
# @liquid_syntax string | strip_html
|
||||||
|
# @liquid_return [string]
|
||||||
def strip_html(input)
|
def strip_html(input)
|
||||||
empty = ''
|
empty = ''
|
||||||
result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
|
result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
|
||||||
@@ -163,18 +307,35 @@ module Liquid
|
|||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
# Remove all newlines from the string
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Strips all newline characters (line breaks) from a string.
|
||||||
|
# @liquid_syntax string | strip_newlines
|
||||||
|
# @liquid_return [string]
|
||||||
def strip_newlines(input)
|
def strip_newlines(input)
|
||||||
input.to_s.gsub(/\r?\n/, '')
|
input.to_s.gsub(/\r?\n/, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
# Join elements of the array with certain character between them
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category array
|
||||||
|
# @liquid_summary
|
||||||
|
# Combines all of the items in an array into a single string, separated by a space.
|
||||||
|
# @liquid_syntax array | join
|
||||||
|
# @liquid_return [string]
|
||||||
def join(input, glue = ' ')
|
def join(input, glue = ' ')
|
||||||
InputIterator.new(input, context).join(glue)
|
InputIterator.new(input, context).join(glue)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sort elements of the array
|
# @liquid_public_docs
|
||||||
# provide optional property with which to sort an array of hashes or drops
|
# @liquid_type filter
|
||||||
|
# @liquid_category array
|
||||||
|
# @liquid_summary
|
||||||
|
# Sorts the items in an array in case-sensitive alphabetical, or numerical, order.
|
||||||
|
# @liquid_syntax array | sort
|
||||||
|
# @liquid_return [array[untyped]]
|
||||||
def sort(input, property = nil)
|
def sort(input, property = nil)
|
||||||
ary = InputIterator.new(input, context)
|
ary = InputIterator.new(input, context)
|
||||||
|
|
||||||
@@ -193,8 +354,17 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sort elements of an array ignoring case if strings
|
# @liquid_public_docs
|
||||||
# provide optional property with which to sort an array of hashes or drops
|
# @liquid_type filter
|
||||||
|
# @liquid_category array
|
||||||
|
# @liquid_summary
|
||||||
|
# Sorts the items in an array in case-insensitive alphabetical order.
|
||||||
|
# @liquid_description
|
||||||
|
# > Caution:
|
||||||
|
# > You shouldn't use the `sort_natural` filter to sort numerical values. When comparing items an array, each item is converted to a
|
||||||
|
# > string, so sorting on numerical values can lead to unexpected results.
|
||||||
|
# @liquid_syntax array | sort_natural
|
||||||
|
# @liquid_return [array[untyped]]
|
||||||
def sort_natural(input, property = nil)
|
def sort_natural(input, property = nil)
|
||||||
ary = InputIterator.new(input, context)
|
ary = InputIterator.new(input, context)
|
||||||
|
|
||||||
@@ -213,8 +383,15 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Filter the elements of an array to those with a certain property value.
|
# @liquid_public_docs
|
||||||
# By default the target is any truthy value.
|
# @liquid_type filter
|
||||||
|
# @liquid_category array
|
||||||
|
# @liquid_summary
|
||||||
|
# Filters an array to include only items with a specific property value.
|
||||||
|
# @liquid_description
|
||||||
|
# This requires you to provide both the property name and the associated value.
|
||||||
|
# @liquid_syntax array | where: string, string
|
||||||
|
# @liquid_return [array[untyped]]
|
||||||
def where(input, property, target_value = nil)
|
def where(input, property, target_value = nil)
|
||||||
ary = InputIterator.new(input, context)
|
ary = InputIterator.new(input, context)
|
||||||
|
|
||||||
@@ -241,8 +418,13 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Remove duplicate elements from an array
|
# @liquid_public_docs
|
||||||
# provide optional property with which to determine uniqueness
|
# @liquid_type filter
|
||||||
|
# @liquid_category array
|
||||||
|
# @liquid_summary
|
||||||
|
# Removes any duplicate items in an array.
|
||||||
|
# @liquid_syntax array | uniq
|
||||||
|
# @liquid_return [array[untyped]]
|
||||||
def uniq(input, property = nil)
|
def uniq(input, property = nil)
|
||||||
ary = InputIterator.new(input, context)
|
ary = InputIterator.new(input, context)
|
||||||
|
|
||||||
@@ -262,13 +444,25 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Reverse the elements of an array
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category array
|
||||||
|
# @liquid_summary
|
||||||
|
# Reverses the order of the items in an array.
|
||||||
|
# @liquid_syntax array | reverse
|
||||||
|
# @liquid_return [array[untyped]]
|
||||||
def reverse(input)
|
def reverse(input)
|
||||||
ary = InputIterator.new(input, context)
|
ary = InputIterator.new(input, context)
|
||||||
ary.reverse
|
ary.reverse
|
||||||
end
|
end
|
||||||
|
|
||||||
# map/collect on a given property
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category array
|
||||||
|
# @liquid_summary
|
||||||
|
# Creates an array of values from a specific property of the items in an array.
|
||||||
|
# @liquid_syntax array | map: string
|
||||||
|
# @liquid_return [array[untyped]]
|
||||||
def map(input, property)
|
def map(input, property)
|
||||||
InputIterator.new(input, context).map do |e|
|
InputIterator.new(input, context).map do |e|
|
||||||
e = e.call if e.is_a?(Proc)
|
e = e.call if e.is_a?(Proc)
|
||||||
@@ -284,8 +478,13 @@ module Liquid
|
|||||||
raise_property_error(property)
|
raise_property_error(property)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Remove nils within an array
|
# @liquid_public_docs
|
||||||
# provide optional property with which to check for nil
|
# @liquid_type filter
|
||||||
|
# @liquid_category array
|
||||||
|
# @liquid_summary
|
||||||
|
# Removes any `nil` items from an array.
|
||||||
|
# @liquid_syntax array | compact
|
||||||
|
# @liquid_return [array[untyped]]
|
||||||
def compact(input, property = nil)
|
def compact(input, property = nil)
|
||||||
ary = InputIterator.new(input, context)
|
ary = InputIterator.new(input, context)
|
||||||
|
|
||||||
@@ -305,17 +504,35 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Replace occurrences of a string with another
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Replaces any instance of a substring inside a string with a given string.
|
||||||
|
# @liquid_syntax string | replace: string, string
|
||||||
|
# @liquid_return [string]
|
||||||
def replace(input, string, replacement = '')
|
def replace(input, string, replacement = '')
|
||||||
input.to_s.gsub(string.to_s, replacement.to_s)
|
input.to_s.gsub(string.to_s, replacement.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Replace the first occurrences of a string with another
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Replaces the first instance of a substring inside a string with a given string.
|
||||||
|
# @liquid_syntax string | replace_first: string, string
|
||||||
|
# @liquid_return [string]
|
||||||
def replace_first(input, string, replacement = '')
|
def replace_first(input, string, replacement = '')
|
||||||
input.to_s.sub(string.to_s, replacement.to_s)
|
input.to_s.sub(string.to_s, replacement.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Replace the last occurrences of a string with another
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Replaces the last instance of a substring inside a string with a given string.
|
||||||
|
# @liquid_syntax string | replace_last: string, string
|
||||||
|
# @liquid_return [string]
|
||||||
def replace_last(input, string, replacement)
|
def replace_last(input, string, replacement)
|
||||||
input = input.to_s
|
input = input.to_s
|
||||||
string = string.to_s
|
string = string.to_s
|
||||||
@@ -330,26 +547,61 @@ module Liquid
|
|||||||
output
|
output
|
||||||
end
|
end
|
||||||
|
|
||||||
# remove a substring
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Removes any instance of a substring inside a string.
|
||||||
|
# @liquid_syntax string | remove: string
|
||||||
|
# @liquid_return [string]
|
||||||
def remove(input, string)
|
def remove(input, string)
|
||||||
replace(input, string, '')
|
replace(input, string, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
# remove the first occurrences of a substring
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Removes the first instance of a substring inside a string.
|
||||||
|
# @liquid_syntax string | remove_first: string
|
||||||
|
# @liquid_return [string]
|
||||||
def remove_first(input, string)
|
def remove_first(input, string)
|
||||||
replace_first(input, string, '')
|
replace_first(input, string, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
# remove the last occurences of a substring
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Removes the last instance of a substring inside a string.
|
||||||
|
# @liquid_syntax string | remove_last: string
|
||||||
|
# @liquid_return [string]
|
||||||
def remove_last(input, string)
|
def remove_last(input, string)
|
||||||
replace_last(input, string, '')
|
replace_last(input, string, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
# add one string to another
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Adds a given string to the end of a string.
|
||||||
|
# @liquid_syntax string | append: string
|
||||||
|
# @liquid_return [string]
|
||||||
def append(input, string)
|
def append(input, string)
|
||||||
input.to_s + string.to_s
|
input.to_s + string.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category array
|
||||||
|
# @liquid_summary
|
||||||
|
# Concatenates (combines) two arrays.
|
||||||
|
# @liquid_description
|
||||||
|
# > Note:
|
||||||
|
# > The `concat` filter won't filter out duplicates. If you want to remove duplicates, then you need to use the
|
||||||
|
# > [`uniq` filter](/api/liquid/filters#uniq).
|
||||||
|
# @liquid_syntax array | concat: array
|
||||||
|
# @liquid_return [array[untyped]]
|
||||||
def concat(input, array)
|
def concat(input, array)
|
||||||
unless array.respond_to?(:to_ary)
|
unless array.respond_to?(:to_ary)
|
||||||
raise ArgumentError, "concat filter requires an array argument"
|
raise ArgumentError, "concat filter requires an array argument"
|
||||||
@@ -357,12 +609,24 @@ module Liquid
|
|||||||
InputIterator.new(input, context).concat(array)
|
InputIterator.new(input, context).concat(array)
|
||||||
end
|
end
|
||||||
|
|
||||||
# prepend a string to another
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Adds a given string to the beginning of a string.
|
||||||
|
# @liquid_syntax string | prepend: string
|
||||||
|
# @liquid_return [string]
|
||||||
def prepend(input, string)
|
def prepend(input, string)
|
||||||
string.to_s + input.to_s
|
string.to_s + input.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add <br /> tags in front of all newlines in input string
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category string
|
||||||
|
# @liquid_summary
|
||||||
|
# Converts newlines (`\n`) in a string to HTML line breaks (`<br>`).
|
||||||
|
# @liquid_syntax string | newline_to_br
|
||||||
|
# @liquid_return [string]
|
||||||
def newline_to_br(input)
|
def newline_to_br(input)
|
||||||
input.to_s.gsub(/\r?\n/, "<br />\n")
|
input.to_s.gsub(/\r?\n/, "<br />\n")
|
||||||
end
|
end
|
||||||
@@ -406,58 +670,106 @@ module Liquid
|
|||||||
date.strftime(format.to_s)
|
date.strftime(format.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get the first element of the passed in array
|
# @liquid_public_docs
|
||||||
#
|
# @liquid_type filter
|
||||||
# Example:
|
# @liquid_category array
|
||||||
# {{ product.images | first | to_img }}
|
# @liquid_summary
|
||||||
#
|
# Returns the first item in an array.
|
||||||
|
# @liquid_syntax array | first
|
||||||
|
# @liquid_return [untyped]
|
||||||
def first(array)
|
def first(array)
|
||||||
array.first if array.respond_to?(:first)
|
array.first if array.respond_to?(:first)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get the last element of the passed in array
|
# @liquid_public_docs
|
||||||
#
|
# @liquid_type filter
|
||||||
# Example:
|
# @liquid_category array
|
||||||
# {{ product.images | last | to_img }}
|
# @liquid_summary
|
||||||
#
|
# Returns the last item in an array.
|
||||||
|
# @liquid_syntax array | last
|
||||||
|
# @liquid_return [untyped]
|
||||||
def last(array)
|
def last(array)
|
||||||
array.last if array.respond_to?(:last)
|
array.last if array.respond_to?(:last)
|
||||||
end
|
end
|
||||||
|
|
||||||
# absolute value
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category math
|
||||||
|
# @liquid_summary
|
||||||
|
# Returns the absolute value of a number.
|
||||||
|
# @liquid_syntax number | abs
|
||||||
|
# @liquid_return [number]
|
||||||
def abs(input)
|
def abs(input)
|
||||||
result = Utils.to_number(input).abs
|
result = Utils.to_number(input).abs
|
||||||
result.is_a?(BigDecimal) ? result.to_f : result
|
result.is_a?(BigDecimal) ? result.to_f : result
|
||||||
end
|
end
|
||||||
|
|
||||||
# addition
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category math
|
||||||
|
# @liquid_summary
|
||||||
|
# Adds two numbers.
|
||||||
|
# @liquid_syntax number | plus: number
|
||||||
|
# @liquid_return [number]
|
||||||
def plus(input, operand)
|
def plus(input, operand)
|
||||||
apply_operation(input, operand, :+)
|
apply_operation(input, operand, :+)
|
||||||
end
|
end
|
||||||
|
|
||||||
# subtraction
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category math
|
||||||
|
# @liquid_summary
|
||||||
|
# Subtracts a given number from another number.
|
||||||
|
# @liquid_syntax number | minus: number
|
||||||
|
# @liquid_return [number]
|
||||||
def minus(input, operand)
|
def minus(input, operand)
|
||||||
apply_operation(input, operand, :-)
|
apply_operation(input, operand, :-)
|
||||||
end
|
end
|
||||||
|
|
||||||
# multiplication
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category math
|
||||||
|
# @liquid_summary
|
||||||
|
# Multiplies a number by a given number.
|
||||||
|
# @liquid_syntax number | times: number
|
||||||
|
# @liquid_return [number]
|
||||||
def times(input, operand)
|
def times(input, operand)
|
||||||
apply_operation(input, operand, :*)
|
apply_operation(input, operand, :*)
|
||||||
end
|
end
|
||||||
|
|
||||||
# division
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category math
|
||||||
|
# @liquid_summary
|
||||||
|
# Divides a number by a given number.
|
||||||
|
# @liquid_syntax number | divided_by: number
|
||||||
|
# @liquid_return [number]
|
||||||
def divided_by(input, operand)
|
def divided_by(input, operand)
|
||||||
apply_operation(input, operand, :/)
|
apply_operation(input, operand, :/)
|
||||||
rescue ::ZeroDivisionError => e
|
rescue ::ZeroDivisionError => e
|
||||||
raise Liquid::ZeroDivisionError, e.message
|
raise Liquid::ZeroDivisionError, e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category math
|
||||||
|
# @liquid_summary
|
||||||
|
# Returns the remainder of dividing a number by a given number.
|
||||||
|
# @liquid_syntax number | modulo: number
|
||||||
|
# @liquid_return [number]
|
||||||
def modulo(input, operand)
|
def modulo(input, operand)
|
||||||
apply_operation(input, operand, :%)
|
apply_operation(input, operand, :%)
|
||||||
rescue ::ZeroDivisionError => e
|
rescue ::ZeroDivisionError => e
|
||||||
raise Liquid::ZeroDivisionError, e.message
|
raise Liquid::ZeroDivisionError, e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category math
|
||||||
|
# @liquid_summary
|
||||||
|
# Rounds a number to the nearest integer.
|
||||||
|
# @liquid_syntax number | round
|
||||||
|
# @liquid_return [number]
|
||||||
def round(input, n = 0)
|
def round(input, n = 0)
|
||||||
result = Utils.to_number(input).round(Utils.to_number(n))
|
result = Utils.to_number(input).round(Utils.to_number(n))
|
||||||
result = result.to_f if result.is_a?(BigDecimal)
|
result = result.to_f if result.is_a?(BigDecimal)
|
||||||
@@ -467,18 +779,39 @@ module Liquid
|
|||||||
raise Liquid::FloatDomainError, e.message
|
raise Liquid::FloatDomainError, e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category math
|
||||||
|
# @liquid_summary
|
||||||
|
# Rounds a number up to the nearest integer.
|
||||||
|
# @liquid_syntax number | ceil
|
||||||
|
# @liquid_return [number]
|
||||||
def ceil(input)
|
def ceil(input)
|
||||||
Utils.to_number(input).ceil.to_i
|
Utils.to_number(input).ceil.to_i
|
||||||
rescue ::FloatDomainError => e
|
rescue ::FloatDomainError => e
|
||||||
raise Liquid::FloatDomainError, e.message
|
raise Liquid::FloatDomainError, e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category math
|
||||||
|
# @liquid_summary
|
||||||
|
# Rounds a number down to the nearest integer.
|
||||||
|
# @liquid_syntax number | floor
|
||||||
|
# @liquid_return [number]
|
||||||
def floor(input)
|
def floor(input)
|
||||||
Utils.to_number(input).floor.to_i
|
Utils.to_number(input).floor.to_i
|
||||||
rescue ::FloatDomainError => e
|
rescue ::FloatDomainError => e
|
||||||
raise Liquid::FloatDomainError, e.message
|
raise Liquid::FloatDomainError, e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category math
|
||||||
|
# @liquid_summary
|
||||||
|
# Limits a number to a minimum value.
|
||||||
|
# @liquid_syntax number | at_least
|
||||||
|
# @liquid_return [number]
|
||||||
def at_least(input, n)
|
def at_least(input, n)
|
||||||
min_value = Utils.to_number(n)
|
min_value = Utils.to_number(n)
|
||||||
|
|
||||||
@@ -487,6 +820,13 @@ module Liquid
|
|||||||
result.is_a?(BigDecimal) ? result.to_f : result
|
result.is_a?(BigDecimal) ? result.to_f : result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type filter
|
||||||
|
# @liquid_category math
|
||||||
|
# @liquid_summary
|
||||||
|
# Limits a number to a maximum value.
|
||||||
|
# @liquid_syntax number | at_most
|
||||||
|
# @liquid_return [number]
|
||||||
def at_most(input, n)
|
def at_most(input, n)
|
||||||
max_value = Utils.to_number(n)
|
max_value = Utils.to_number(n)
|
||||||
|
|
||||||
@@ -495,16 +835,18 @@ module Liquid
|
|||||||
result.is_a?(BigDecimal) ? result.to_f : result
|
result.is_a?(BigDecimal) ? result.to_f : result
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set a default value when the input is nil, false or empty
|
# @liquid_public_docs
|
||||||
#
|
# @liquid_type filter
|
||||||
# Example:
|
# @liquid_category default
|
||||||
# {{ product.title | default: "No Title" }}
|
# @liquid_summary
|
||||||
#
|
# Sets a default value for any variable whose value is one of the following:
|
||||||
# Use `allow_false` when an input should only be tested against nil or empty and not false.
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# {{ product.title | default: "No Title", allow_false: true }}
|
|
||||||
#
|
#
|
||||||
|
# - [`empty`](/api/liquid/basics#empty)
|
||||||
|
# - [`false`](/api/liquid/basics#truthy-and-falsy)
|
||||||
|
# - [`nil`](/api/liquid/basics#nil)
|
||||||
|
# @liquid_syntax variable | default: variable
|
||||||
|
# @liquid_return [untyped]
|
||||||
|
# @liquid_optional_param allow_false [boolean] Whether to use false values instead of the default.
|
||||||
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)
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ module Liquid
|
|||||||
strainer_from_cache(filters).new(context)
|
strainer_from_cache(filters).new(context)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def global_filter_names
|
||||||
|
GlobalCache.filter_method_names
|
||||||
|
end
|
||||||
|
|
||||||
GlobalCache = Class.new(StrainerTemplate)
|
GlobalCache = Class.new(StrainerTemplate)
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ module Liquid
|
|||||||
subclass.instance_variable_set(:@filter_methods, @filter_methods.dup)
|
subclass.instance_variable_set(:@filter_methods, @filter_methods.dup)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filter_method_names
|
||||||
|
filter_methods.map(&:to_s).to_a
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def filter_methods
|
def filter_methods
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type object
|
||||||
|
# @liquid_name tablerowloop
|
||||||
|
# @liquid_summary
|
||||||
|
# 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
|
||||||
@@ -10,40 +15,92 @@ module Liquid
|
|||||||
@index = 0
|
@index = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :length, :col, :row
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# The total number of iterations in the loop.
|
||||||
|
# @liquid_return [number]
|
||||||
|
attr_reader :length
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# The 1-based index of the current column.
|
||||||
|
# @liquid_return [number]
|
||||||
|
attr_reader :col
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# The 1-based index of current row.
|
||||||
|
# @liquid_return [number]
|
||||||
|
attr_reader :row
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# The 1-based index of the current iteration.
|
||||||
|
# @liquid_return [number]
|
||||||
def index
|
def index
|
||||||
@index + 1
|
@index + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# The 0-based index of the current iteration.
|
||||||
|
# @liquid_return [number]
|
||||||
def index0
|
def index0
|
||||||
@index
|
@index
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# The 0-based index of the current column.
|
||||||
|
# @liquid_return [number]
|
||||||
def col0
|
def col0
|
||||||
@col - 1
|
@col - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# The 1-based index of the current iteration, in reverse order.
|
||||||
|
# @liquid_return [number]
|
||||||
def rindex
|
def rindex
|
||||||
@length - @index
|
@length - @index
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# The 0-based index of the current iteration, in reverse order.
|
||||||
|
# @liquid_return [number]
|
||||||
def rindex0
|
def rindex0
|
||||||
@length - @index - 1
|
@length - @index - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# Returns `true` if the current iteration is the first. Returns `false` if not.
|
||||||
|
# @liquid_return [boolean]
|
||||||
def first
|
def first
|
||||||
@index == 0
|
@index == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# Returns `true` if the current iteration is the last. Returns `false` if not.
|
||||||
|
# @liquid_return [boolean]
|
||||||
def last
|
def last
|
||||||
@index == @length - 1
|
@index == @length - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# Returns `true` if the current column is the first in the row. Returns `false` if not.
|
||||||
|
# @liquid_return [boolean]
|
||||||
def col_first
|
def col_first
|
||||||
@col == 1
|
@col == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_summary
|
||||||
|
# Returns `true` if the current column is the last in the row. Returns `false` if not.
|
||||||
|
# @liquid_return [boolean]
|
||||||
def col_last
|
def col_last
|
||||||
@col == @cols
|
@col == @cols
|
||||||
end
|
end
|
||||||
|
|||||||
+12
-14
@@ -1,20 +1,18 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# @public_docs
|
|
||||||
module Liquid
|
module Liquid
|
||||||
# @public_docs
|
# @liquid_public_docs
|
||||||
# @title Assign
|
# @liquid_type tag
|
||||||
# @syntax The syntax
|
# @liquid_category variable
|
||||||
# @summary The summary
|
# @liquid_name assign
|
||||||
# @type tag
|
# @liquid_summary
|
||||||
# @description
|
# Creates a new variable.
|
||||||
# Assign sets a variable in your template.
|
# @liquid_description
|
||||||
#
|
# You can create variables of any [basic type](/api/liquid/basics#types), [object](/api/liquid/objects), or object property.
|
||||||
# {% assign foo = 'monkey' %}
|
# @liquid_syntax
|
||||||
#
|
# {% assign variable_name = value %}
|
||||||
# You can then use the variable later in the page.
|
# @liquid_syntax_keyword variable_name The name of the variable being created.
|
||||||
#
|
# @liquid_syntax_keyword value The value you want to assign to the variable.
|
||||||
# {{ foo }}
|
|
||||||
class Assign < Tag
|
class Assign < Tag
|
||||||
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
|
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ module Liquid
|
|||||||
# {% endif %}
|
# {% endif %}
|
||||||
# {% endfor %}
|
# {% endfor %}
|
||||||
#
|
#
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type tag
|
||||||
|
# @liquid_category iteration
|
||||||
|
# @liquid_name break
|
||||||
|
# @liquid_summary
|
||||||
|
# Stops a [`for` loop](/api/liquid/tags#for) from iterating.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% break %}
|
||||||
class Break < Tag
|
class Break < Tag
|
||||||
INTERRUPT = BreakInterrupt.new.freeze
|
INTERRUPT = BreakInterrupt.new.freeze
|
||||||
|
|
||||||
|
|||||||
+13
-10
@@ -1,17 +1,20 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
# Capture stores the result of a block into a variable without rendering it inplace.
|
# @liquid_public_docs
|
||||||
#
|
# @liquid_type tag
|
||||||
# {% capture heading %}
|
# @liquid_category variable
|
||||||
# Monkeys!
|
# @liquid_name capture
|
||||||
|
# @liquid_summary
|
||||||
|
# Creates a new variable with a string value.
|
||||||
|
# @liquid_description
|
||||||
|
# You can create complex strings with Liquid logic and variables.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% capture variable %}
|
||||||
|
# value
|
||||||
# {% endcapture %}
|
# {% endcapture %}
|
||||||
# ...
|
# @liquid_syntax_keyword variable The name of the variable being created.
|
||||||
# <h1>{{ heading }}</h1>
|
# @liquid_syntax_keyword value The value you want to assign to the variable.
|
||||||
#
|
|
||||||
# Capture is useful for saving content for use later in your template, such as
|
|
||||||
# in a sidebar or footer.
|
|
||||||
#
|
|
||||||
class Capture < Block
|
class Capture < Block
|
||||||
Syntax = /(#{VariableSignature}+)/o
|
Syntax = /(#{VariableSignature}+)/o
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,27 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type tag
|
||||||
|
# @liquid_category conditional
|
||||||
|
# @liquid_name case
|
||||||
|
# @liquid_summary
|
||||||
|
# Renders a specific expression depending on the value of a specific variable.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% case variable %}
|
||||||
|
# {% when first_value %}
|
||||||
|
# first_expression
|
||||||
|
# {% when second_value %}
|
||||||
|
# second_expression
|
||||||
|
# {% else %}
|
||||||
|
# third_expression
|
||||||
|
# {% endcase %}
|
||||||
|
# @liquid_syntax_keyword variable The name of the variable you want to base your case statement on.
|
||||||
|
# @liquid_syntax_keyword first_value A specific value to check for.
|
||||||
|
# @liquid_syntax_keyword second_value A specific value to check for.
|
||||||
|
# @liquid_syntax_keyword first_expression An expression to be rendered when the variable's value matches `first_value`.
|
||||||
|
# @liquid_syntax_keyword second_expression An expression to be rendered when the variable's value matches `second_value`.
|
||||||
|
# @liquid_syntax_keyword third_expression An expression to be rendered when the variable's value has no match.
|
||||||
class Case < Block
|
class Case < Block
|
||||||
Syntax = /(#{QuotedFragment})/o
|
Syntax = /(#{QuotedFragment})/o
|
||||||
WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/om
|
WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/om
|
||||||
|
|||||||
@@ -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 comment
|
||||||
|
# @liquid_summary
|
||||||
|
# Prevents an expression from being rendered or output.
|
||||||
|
# @liquid_description
|
||||||
|
# Any text inside `comment` tags won't be output, and any Liquid code won't be rendered.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% comment %}
|
||||||
|
# content
|
||||||
|
# {% endcomment %}
|
||||||
|
# @liquid_syntax_keyword content The content of the comment.
|
||||||
class Comment < Block
|
class Comment < Block
|
||||||
def render_to_output_buffer(_context, output)
|
def render_to_output_buffer(_context, output)
|
||||||
output
|
output
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
# Continue tag to be used to break out of a for loop.
|
# @liquid_public_docs
|
||||||
#
|
# @liquid_type tag
|
||||||
# == Basic Usage:
|
# @liquid_category iteration
|
||||||
# {% for item in collection %}
|
# @liquid_name continue
|
||||||
# {% if item.condition %}
|
# @liquid_summary
|
||||||
# {% continue %}
|
# Causes a [`for` loop](/api/liquid/tags#for) to skip to the next iteration.
|
||||||
# {% endif %}
|
# @liquid_syntax
|
||||||
# {% endfor %}
|
# {% continue %}
|
||||||
#
|
|
||||||
class Continue < Tag
|
class Continue < Tag
|
||||||
INTERRUPT = ContinueInterrupt.new.freeze
|
INTERRUPT = ContinueInterrupt.new.freeze
|
||||||
|
|
||||||
|
|||||||
+12
-11
@@ -1,18 +1,19 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
# Cycle is usually used within a loop to alternate between values, like colors or DOM classes.
|
# @liquid_public_docs
|
||||||
#
|
# @liquid_type tag
|
||||||
# {% for item in items %}
|
# @liquid_category iteration
|
||||||
# <div class="{% cycle 'red', 'green', 'blue' %}"> {{ item }} </div>
|
# @liquid_name cycle
|
||||||
# {% end %}
|
# @liquid_summary
|
||||||
#
|
# Loops through a group of strings and outputs them one at a time for each iteration of a [`for` loop](/api/liquid/tags#for).
|
||||||
# <div class="red"> Item one </div>
|
# @liquid_description
|
||||||
# <div class="green"> Item two </div>
|
# The `cycle` tag must be used inside a `for` loop.
|
||||||
# <div class="blue"> Item three </div>
|
|
||||||
# <div class="red"> Item four </div>
|
|
||||||
# <div class="green"> Item five</div>
|
|
||||||
#
|
#
|
||||||
|
# > Tip:
|
||||||
|
# > Use the `cycle` tag to output text in a predictable pattern. For example, to apply odd/even classes to rows in a table.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% cycle string, string, ... %}
|
||||||
class Cycle < Tag
|
class Cycle < Tag
|
||||||
SimpleSyntax = /\A#{QuotedFragment}+/o
|
SimpleSyntax = /\A#{QuotedFragment}+/o
|
||||||
NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
|
NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
# decrement is used in a place where one needs to insert a counter
|
# @liquid_public_docs
|
||||||
# into a template, and needs the counter to survive across
|
# @liquid_type tag
|
||||||
# multiple instantiations of the template.
|
# @liquid_category variable
|
||||||
# NOTE: decrement is a pre-decrement, --i,
|
# @liquid_name decrement
|
||||||
# while increment is post: i++.
|
# @liquid_summary
|
||||||
#
|
# Creates a new variable, with a default value of -1, that's decreased by 1 with each subsequent call.
|
||||||
# (To achieve the survival, the application must keep the context)
|
# @liquid_description
|
||||||
#
|
# Variables that are declared with `decrement` are unique to the [layout](/themes/architecture/layouts), [template](/themes/architecture/templates),
|
||||||
# if the variable does not exist, it is created with value 0.
|
# 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.
|
||||||
# Hello: {% decrement variable %}
|
|
||||||
#
|
|
||||||
# gives you:
|
|
||||||
#
|
|
||||||
# Hello: -1
|
|
||||||
# Hello: -2
|
|
||||||
# Hello: -3
|
|
||||||
#
|
#
|
||||||
|
# Similarly, variables that are created with `decrement` are independent from those created with [`assign`](/api/liquid/tags#assign)
|
||||||
|
# and [`capture`](/api/liquid/tags#capture). However, `decrement` and [`increment`](/api/liquid/tags#increment) share
|
||||||
|
# variables.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% decrement variable_name %}
|
||||||
|
# @liquid_syntax_keyword variable_name The name of the variable being decremented.
|
||||||
class Decrement < Tag
|
class Decrement < Tag
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
|
|||||||
+16
-9
@@ -1,16 +1,23 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
# Echo outputs an expression
|
# @liquid_public_docs
|
||||||
#
|
# @liquid_type tag
|
||||||
# {% echo monkey %}
|
# @liquid_category syntax
|
||||||
# {% echo user.name %}
|
# @liquid_name echo
|
||||||
#
|
# @liquid_summary
|
||||||
# This is identical to variable output syntax, like {{ foo }}, but works
|
# Outputs an expression.
|
||||||
# inside {% liquid %} tags. The full syntax is supported, including filters:
|
# @liquid_description
|
||||||
#
|
# Using the `echo` tag is the same as wrapping an expression in curly brackets (`{{` and `}}`). However, unlike the curly
|
||||||
# {% echo user | link %}
|
# bracket method, you can use the `echo` tag inside [`liquid` tags](/api/liquid/tags#liquid).
|
||||||
#
|
#
|
||||||
|
# > Tip:
|
||||||
|
# > You can use [filters](/api/liquid/filters) on expressions inside `echo` tags.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% liquid
|
||||||
|
# echo expression
|
||||||
|
# %}
|
||||||
|
# @liquid_syntax_keyword expression The expression to be output.
|
||||||
class Echo < Tag
|
class Echo < Tag
|
||||||
attr_reader :variable
|
attr_reader :variable
|
||||||
|
|
||||||
|
|||||||
+22
-43
@@ -1,50 +1,29 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
# "For" iterates over an array or collection.
|
# @liquid_public_docs
|
||||||
# Several useful variables are available to you within the loop.
|
# @liquid_type tag
|
||||||
#
|
# @liquid_category iteration
|
||||||
# == Basic usage:
|
# @liquid_name for
|
||||||
# {% for item in collection %}
|
# @liquid_summary
|
||||||
# {{ forloop.index }}: {{ item.name }}
|
# Renders an expression for every item in an array.
|
||||||
# {% endfor %}
|
# @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
|
||||||
# == Advanced usage:
|
# [`paginate` tag](/api/liquid/tags#paginate) to split the items over multiple pages.
|
||||||
# {% for item in collection %}
|
|
||||||
# <div {% if forloop.first %}class="first"{% endif %}>
|
|
||||||
# Item {{ forloop.index }}: {{ item.name }}
|
|
||||||
# </div>
|
|
||||||
# {% else %}
|
|
||||||
# There is nothing in the collection.
|
|
||||||
# {% endfor %}
|
|
||||||
#
|
|
||||||
# You can also define a limit and offset much like SQL. Remember
|
|
||||||
# that offset starts at 0 for the first item.
|
|
||||||
#
|
|
||||||
# {% for item in collection limit:5 offset:10 %}
|
|
||||||
# {{ item.name }}
|
|
||||||
# {% end %}
|
|
||||||
#
|
|
||||||
# To reverse the for loop simply use {% for item in collection reversed %} (note that the flag's spelling is different to the filter `reverse`)
|
|
||||||
#
|
|
||||||
# == Available variables:
|
|
||||||
#
|
|
||||||
# forloop.name:: 'item-collection'
|
|
||||||
# forloop.length:: Length of the loop
|
|
||||||
# forloop.index:: The current item's position in the collection;
|
|
||||||
# forloop.index starts at 1.
|
|
||||||
# This is helpful for non-programmers who start believe
|
|
||||||
# the first item in an array is 1, not 0.
|
|
||||||
# forloop.index0:: The current item's position in the collection
|
|
||||||
# where the first item is 0
|
|
||||||
# forloop.rindex:: Number of items remaining in the loop
|
|
||||||
# (length - index) where 1 is the last item.
|
|
||||||
# forloop.rindex0:: Number of items remaining in the loop
|
|
||||||
# where 0 is the last item.
|
|
||||||
# forloop.first:: Returns true if the item is the first item.
|
|
||||||
# forloop.last:: Returns true if the item is the last item.
|
|
||||||
# forloop.parentloop:: Provides access to the parent loop, if present.
|
|
||||||
#
|
#
|
||||||
|
# > Tip:
|
||||||
|
# > Every `for` loop has an associated [`forloop` object](/api/liquid/objects#forloop) with information about the loop.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% for variable in array %}
|
||||||
|
# expression
|
||||||
|
# {% endfor %}
|
||||||
|
# @liquid_syntax_keyword variable The current item in the array.
|
||||||
|
# @liquid_syntax_keyword array The array to iterate over.
|
||||||
|
# @liquid_syntax_keyword expression The expression to render for each iteration.
|
||||||
|
# @liquid_optional_param limit [number] The number of iterations to perform.
|
||||||
|
# @liquid_optional_param offset [number] The 1-based index to start iterating at.
|
||||||
|
# @liquid_optional_param range [untyped] A custom numeric range to iterate over.
|
||||||
|
# @liquid_optional_param reversed [untyped] Iterate in reverse order.
|
||||||
class For < Block
|
class For < Block
|
||||||
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
|
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
|
||||||
|
|
||||||
|
|||||||
+11
-9
@@ -1,16 +1,18 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
# If is the conditional block
|
# @liquid_public_docs
|
||||||
#
|
# @liquid_type tag
|
||||||
# {% if user.admin %}
|
# @liquid_category conditional
|
||||||
# Admin user!
|
# @liquid_name if
|
||||||
# {% else %}
|
# @liquid_summary
|
||||||
# Not admin user
|
# Renders an expression if a specific condition is `true`.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% if condition %}
|
||||||
|
# expression
|
||||||
# {% endif %}
|
# {% endif %}
|
||||||
#
|
# @liquid_syntax_keyword condition The condition to evaluate.
|
||||||
# There are {% if count < 5 %} less {% else %} more {% endif %} items than you need.
|
# @liquid_syntax_keyword expression The expression to render if the condition is met.
|
||||||
#
|
|
||||||
class If < Block
|
class If < Block
|
||||||
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o
|
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o
|
||||||
ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o
|
ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o
|
||||||
|
|||||||
+15
-13
@@ -1,20 +1,22 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
# Include allows templates to relate with other templates
|
# @liquid_public_docs
|
||||||
#
|
# @liquid_type tag
|
||||||
# Simply include another template:
|
# @liquid_category theme
|
||||||
#
|
# @liquid_name include
|
||||||
# {% include 'product' %}
|
# @liquid_summary
|
||||||
#
|
# Renders a [snippet](/themes/architecture#snippets).
|
||||||
# Include a template with a local variable:
|
# @liquid_description
|
||||||
#
|
# Inside the snippet, you can access and alter variables that are [created](/api/liquid/tags#variable-tags) outside of the
|
||||||
# {% include 'product' with products[0] %}
|
# snippet.
|
||||||
#
|
# @liquid_syntax
|
||||||
# Include a template for a collection:
|
# {% include 'filename' %}
|
||||||
#
|
# @liquid_syntax_keyword filename The name of the snippet to render, without the `.liquid` extension.
|
||||||
# {% include 'product' for products %}
|
# @liquid_deprecated
|
||||||
|
# Deprecated because the way that variables are handled reduces performance and makes code harder to both read and maintain.
|
||||||
#
|
#
|
||||||
|
# The `include` tag has been replaced by [`render`](/api/liquid/tags#render).
|
||||||
class Include < Tag
|
class Include < Tag
|
||||||
prepend Tag::Disableable
|
prepend Tag::Disableable
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
# increment is used in a place where one needs to insert a counter
|
# @liquid_public_docs
|
||||||
# into a template, and needs the counter to survive across
|
# @liquid_type tag
|
||||||
# multiple instantiations of the template.
|
# @liquid_category variable
|
||||||
# (To achieve the survival, the application must keep the context)
|
# @liquid_name increment
|
||||||
#
|
# @liquid_summary
|
||||||
# if the variable does not exist, it is created with value 0.
|
# Creates a new variable, with a default value of 0, that's increased by 1 with each subsequent call.
|
||||||
#
|
# @liquid_description
|
||||||
# Hello: {% increment variable %}
|
# Variables that are declared with `increment` are unique to the [layout](/themes/architecture/layouts), [template](/themes/architecture/templates),
|
||||||
#
|
# or [section](/themes/architecture/sections) file that they're created in. However, the variable is shared across
|
||||||
# gives you:
|
# [snippets](/themes/architecture#snippets) included in the file.
|
||||||
#
|
|
||||||
# Hello: 0
|
|
||||||
# Hello: 1
|
|
||||||
# Hello: 2
|
|
||||||
#
|
#
|
||||||
|
# Similarly, variables that are created with `increment` are independent from those created with [`assign`](/api/liquid/tags#assign)
|
||||||
|
# and [`capture`](/api/liquid/tags#capture). However, `increment` and [`decrement`](/api/liquid/tags#decrement) share
|
||||||
|
# variables.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% increment variable_name %}
|
||||||
|
# @liquid_syntax_keyword variable_name The name of the variable being incremented.
|
||||||
class Increment < Tag
|
class Increment < Tag
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
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
|
||||||
|
def initialize(tag_name, markup, options)
|
||||||
|
super
|
||||||
|
|
||||||
|
# Semantically, a comment should only ignore everything after it on the line.
|
||||||
|
# Currently, this implementation doesn't support mixing a comment with another tag
|
||||||
|
# but we need to reserve future support for this and prevent the introduction
|
||||||
|
# of inline comments from being backward incompatible change.
|
||||||
|
#
|
||||||
|
# As such, we're forcing users to put a # symbol on every line otherwise this
|
||||||
|
# tag will throw an error.
|
||||||
|
if markup.match?(/\n\s*[^#\s]/)
|
||||||
|
raise SyntaxError, options[:locale].t("errors.syntax.inline_comment_invalid")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_to_output_buffer(_context, output)
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
def blank?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Template.register_tag('#', InlineComment)
|
||||||
|
end
|
||||||
@@ -1,6 +1,17 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type tag
|
||||||
|
# @liquid_category syntax
|
||||||
|
# @liquid_name raw
|
||||||
|
# @liquid_summary
|
||||||
|
# Outputs any Liquid code as text instead of rendering it.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% raw %}
|
||||||
|
# expression
|
||||||
|
# {% endraw %}
|
||||||
|
# @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
|
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om
|
||||||
|
|||||||
@@ -1,13 +1,37 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type tag
|
||||||
|
# @liquid_category theme
|
||||||
|
# @liquid_name render
|
||||||
|
# @liquid_summary
|
||||||
|
# Renders a [snippet](/themes/architecture#snippets) or [app block](/themes/architecture/sections/section-schema#render-app-blocks).
|
||||||
|
# @liquid_description
|
||||||
|
# Inside snippets and app blocks, you can't directly access variables that are [created](/api/liquid/tags#variable-tags) outside
|
||||||
|
# of the snippet or app block. However, you can [specify variables as parameters](/api/liquid/tags#render-passing-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
|
||||||
|
# directly accessible outside the snippet or app block. For example, a snippet or app block inside the [product template](/themes/architecture/templates/product)
|
||||||
|
# can access the [`product` object](/api/liquid/objects#product), and a snippet or app block inside a [section](/themes/architecture/sections)
|
||||||
|
# can access the [`section` object](/api/liquid/objects#section).
|
||||||
|
#
|
||||||
|
# Outside a snippet or app block, you can't access variables created inside the snippet or app block.
|
||||||
|
#
|
||||||
|
# > Note:
|
||||||
|
# > When you render a snippet using the `render` tag, you can't use the [`include` tag](/api/liquid/tags#include)
|
||||||
|
# > inside the snippet.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% render 'filename' %}
|
||||||
|
# @liquid_syntax_keyword filename The name of the snippet to render, without the `.liquid` extension.
|
||||||
class Render < Tag
|
class Render < Tag
|
||||||
FOR = 'for'
|
FOR = 'for'
|
||||||
SYNTAX = /(#{QuotedString}+)(\s+(with|#{FOR})\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
|
SYNTAX = /(#{QuotedString}+)(\s+(with|#{FOR})\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
|
||||||
|
|
||||||
disable_tags "include"
|
disable_tags "include"
|
||||||
|
|
||||||
attr_reader :template_name_expr, :attributes
|
attr_reader :template_name_expr, :variable_name_expr, :attributes
|
||||||
|
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
@@ -34,9 +58,9 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render_tag(context, output)
|
def render_tag(context, output)
|
||||||
# Though we evaluate this here we will only ever parse it as a string literal.
|
# The expression should be a String literal, which parses to a String object
|
||||||
template_name = context.evaluate(@template_name_expr)
|
template_name = @template_name_expr
|
||||||
raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name
|
raise ::ArgumentError unless template_name.is_a?(String)
|
||||||
|
|
||||||
partial = PartialCache.load(
|
partial = PartialCache.load(
|
||||||
template_name,
|
template_name,
|
||||||
@@ -75,6 +99,7 @@ module Liquid
|
|||||||
def children
|
def children
|
||||||
[
|
[
|
||||||
@node.template_name_expr,
|
@node.template_name_expr,
|
||||||
|
@node.variable_name_expr,
|
||||||
] + @node.attributes.values
|
] + @node.attributes.values
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,6 +1,28 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
|
# @liquid_public_docs
|
||||||
|
# @liquid_type tag
|
||||||
|
# @liquid_category iteration
|
||||||
|
# @liquid_name tablerow
|
||||||
|
# @liquid_summary
|
||||||
|
# Generates HTML table rows for every item in an array.
|
||||||
|
# @liquid_description
|
||||||
|
# The `tablerow` tag must be wrapped in HTML `<table>` and `</table>` tags.
|
||||||
|
#
|
||||||
|
# > Tip:
|
||||||
|
# > Every `tablerow` loop has an associated [`tablerowloop` object](/api/liquid/objects#tablerowloop) with information about the loop.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% tablerow variable in array %}
|
||||||
|
# expression
|
||||||
|
# {% endtablerow %}
|
||||||
|
# @liquid_syntax_keyword variable The current item in the array.
|
||||||
|
# @liquid_syntax_keyword array The array to iterate over.
|
||||||
|
# @liquid_syntax_keyword expression The expression to render.
|
||||||
|
# @liquid_optional_param cols [number] The number of columns that the table should have.
|
||||||
|
# @liquid_optional_param limit [number] The number of iterations to perform.
|
||||||
|
# @liquid_optional_param offset [number] The 1-based index to start iterating at.
|
||||||
|
# @liquid_optional_param range [untyped] A custom numeric range to iterate over.
|
||||||
class TableRow < Block
|
class TableRow < Block
|
||||||
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
|
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,21 @@
|
|||||||
require_relative 'if'
|
require_relative 'if'
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
# Unless is a conditional just like 'if' but works on the inverse logic.
|
# @liquid_public_docs
|
||||||
#
|
# @liquid_type tag
|
||||||
# {% unless x < 0 %} x is greater than zero {% endunless %}
|
# @liquid_category conditional
|
||||||
#
|
# @liquid_name unless
|
||||||
|
# @liquid_summary
|
||||||
|
# Renders an expression unless a specific condition is `true`.
|
||||||
|
# @liquid_description
|
||||||
|
# > Tip:
|
||||||
|
# > Similar to the [`if` tag](/api/liquid/tags#if), you can use `elsif` to add more conditions to an `unless` tag.
|
||||||
|
# @liquid_syntax
|
||||||
|
# {% unless condition %}
|
||||||
|
# expression
|
||||||
|
# {% endunless %}
|
||||||
|
# @liquid_syntax_keyword condition The condition to evaluate.
|
||||||
|
# @liquid_syntax_keyword expression The expression to render unless the condition is met.
|
||||||
class Unless < If
|
class Unless < If
|
||||||
def render_to_output_buffer(context, output)
|
def render_to_output_buffer(context, output)
|
||||||
# First condition is interpreted backwards ( if not )
|
# First condition is interpreted backwards ( if not )
|
||||||
|
|||||||
@@ -167,15 +167,14 @@ module Liquid
|
|||||||
|
|
||||||
output = nil
|
output = nil
|
||||||
|
|
||||||
context_register = context.registers.is_a?(StaticRegisters) ? context.registers.static : context.registers
|
|
||||||
|
|
||||||
case args.last
|
case args.last
|
||||||
when Hash
|
when Hash
|
||||||
options = args.pop
|
options = args.pop
|
||||||
output = options[:output] if options[:output]
|
output = options[:output] if options[:output]
|
||||||
|
static_registers = context.registers.static
|
||||||
|
|
||||||
options[:registers]&.each do |key, register|
|
options[:registers]&.each do |key, register|
|
||||||
context_register[key] = register
|
static_registers[key] = register
|
||||||
end
|
end
|
||||||
|
|
||||||
apply_options_to_context(context, options)
|
apply_options_to_context(context, options)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ module Liquid
|
|||||||
|
|
||||||
name_markup = Regexp.last_match(1)
|
name_markup = Regexp.last_match(1)
|
||||||
filter_markup = Regexp.last_match(2)
|
filter_markup = Regexp.last_match(2)
|
||||||
@name = Expression.parse(name_markup)
|
@name = parse_context.parse_expression(name_markup)
|
||||||
if filter_markup =~ FilterMarkupRegex
|
if filter_markup =~ FilterMarkupRegex
|
||||||
filters = Regexp.last_match(1).scan(FilterParser)
|
filters = Regexp.last_match(1).scan(FilterParser)
|
||||||
filters.each do |f|
|
filters.each do |f|
|
||||||
@@ -65,7 +65,7 @@ module Liquid
|
|||||||
|
|
||||||
return if p.look(:end_of_string)
|
return if p.look(:end_of_string)
|
||||||
|
|
||||||
@name = Expression.parse(p.expression)
|
@name = parse_context.parse_expression(p.expression)
|
||||||
while p.consume?(:pipe)
|
while p.consume?(:pipe)
|
||||||
filtername = p.consume(:id)
|
filtername = p.consume(:id)
|
||||||
filterargs = p.consume?(:colon) ? parse_filterargs(p) : []
|
filterargs = p.consume?(:colon) ? parse_filterargs(p) : []
|
||||||
@@ -122,9 +122,9 @@ module Liquid
|
|||||||
unparsed_args.each do |a|
|
unparsed_args.each do |a|
|
||||||
if (matches = a.match(JustTagAttributes))
|
if (matches = a.match(JustTagAttributes))
|
||||||
keyword_args ||= {}
|
keyword_args ||= {}
|
||||||
keyword_args[matches[1]] = Expression.parse(matches[2])
|
keyword_args[matches[1]] = parse_context.parse_expression(matches[2])
|
||||||
else
|
else
|
||||||
filter_args << Expression.parse(a)
|
filter_args << parse_context.parse_expression(a)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
result = [filter_name, filter_args]
|
result = [filter_name, filter_args]
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
class VariableLookup
|
class VariableLookup
|
||||||
SQUARE_BRACKETED = /\A\[(.*)\]\z/m
|
COMMAND_METHODS = ['size', 'first', 'last'].freeze
|
||||||
COMMAND_METHODS = ['size', 'first', 'last'].freeze
|
|
||||||
|
|
||||||
attr_reader :name, :lookups
|
attr_reader :name, :lookups
|
||||||
|
|
||||||
@@ -15,8 +14,8 @@ module Liquid
|
|||||||
lookups = markup.scan(VariableParser)
|
lookups = markup.scan(VariableParser)
|
||||||
|
|
||||||
name = lookups.shift
|
name = lookups.shift
|
||||||
if name =~ SQUARE_BRACKETED
|
if name&.start_with?('[') && name&.end_with?(']')
|
||||||
name = Expression.parse(Regexp.last_match(1))
|
name = Expression.parse(name[1..-2])
|
||||||
end
|
end
|
||||||
@name = name
|
@name = name
|
||||||
|
|
||||||
@@ -25,14 +24,18 @@ module Liquid
|
|||||||
|
|
||||||
@lookups.each_index do |i|
|
@lookups.each_index do |i|
|
||||||
lookup = lookups[i]
|
lookup = lookups[i]
|
||||||
if lookup =~ SQUARE_BRACKETED
|
if lookup&.start_with?('[') && lookup&.end_with?(']')
|
||||||
lookups[i] = Expression.parse(Regexp.last_match(1))
|
lookups[i] = Expression.parse(lookup[1..-2])
|
||||||
elsif COMMAND_METHODS.include?(lookup)
|
elsif COMMAND_METHODS.include?(lookup)
|
||||||
@command_flags |= 1 << i
|
@command_flags |= 1 << i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def lookup_command?(lookup_index)
|
||||||
|
@command_flags & (1 << lookup_index) != 0
|
||||||
|
end
|
||||||
|
|
||||||
def evaluate(context)
|
def evaluate(context)
|
||||||
name = context.evaluate(@name)
|
name = context.evaluate(@name)
|
||||||
object = context.find_variable(name)
|
object = context.find_variable(name)
|
||||||
@@ -56,7 +59,7 @@ module Liquid
|
|||||||
# Some special cases. If the part wasn't in square brackets and
|
# Some special cases. If the part wasn't in square brackets and
|
||||||
# no key with the same name was found we interpret following calls
|
# no key with the same name was found we interpret following calls
|
||||||
# as commands and call them on the current object
|
# as commands and call them on the current object
|
||||||
elsif @command_flags & (1 << i) != 0 && object.respond_to?(key)
|
elsif lookup_command?(i) && object.respond_to?(key)
|
||||||
object = object.send(key).to_liquid
|
object = object.send(key).to_liquid
|
||||||
|
|
||||||
# No key was present with the desired value and it wasn't one of the directly supported
|
# No key was present with the desired value and it wasn't one of the directly supported
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
VERSION = "5.3.0.alpha"
|
VERSION = "5.4.0"
|
||||||
end
|
end
|
||||||
|
|||||||
+2
-3
@@ -17,13 +17,12 @@ 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 = ">= 2.5.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'
|
||||||
|
|
||||||
s.test_files = Dir.glob("{test}/**/*")
|
s.files = Dir.glob("{lib}/**/*") + %w(LICENSE README.md)
|
||||||
s.files = Dir.glob("{lib}/**/*") + %w(LICENSE README.md)
|
|
||||||
|
|
||||||
s.extra_rdoc_files = ["History.md", "README.md"]
|
s.extra_rdoc_files = ["History.md", "README.md"]
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# This profiler run simulates Shopify.
|
# This profiler run simulates Shopify.
|
||||||
# We are looking in the tests directory for liquid files and render them within the designated layout file.
|
# We are looking in the tests directory for liquid files and render them within the designated layout file.
|
||||||
# We will also export a substantial database to liquid which the templates can render values of.
|
# We will also export a substantial database to liquid which the templates can render values of.
|
||||||
# All this is to make the benchmark as non syntetic as possible. All templates and tests are lifted from
|
# All this is to make the benchmark as non synthetic as possible. All templates and tests are lifted from
|
||||||
# direct real-world usage and the profiler measures code that looks very similar to the way it looks in
|
# direct real-world usage and the profiler measures code that looks very similar to the way it looks in
|
||||||
# Shopify which is likely the biggest user of liquid in the world which something to the tune of several
|
# Shopify which is likely the biggest user of liquid in the world which something to the tune of several
|
||||||
# million Template#render calls a day.
|
# million Template#render calls a day.
|
||||||
|
|||||||
@@ -529,7 +529,7 @@ class ContextTest < Minitest::Test
|
|||||||
registers = {
|
registers = {
|
||||||
my_register: :my_value,
|
my_register: :my_value,
|
||||||
}
|
}
|
||||||
super_context = Context.new({}, {}, StaticRegisters.new(registers))
|
super_context = Context.new({}, {}, Registers.new(registers))
|
||||||
super_context.registers[:my_register] = :my_alt_value
|
super_context.registers[:my_register] = :my_alt_value
|
||||||
subcontext = super_context.new_isolated_subcontext
|
subcontext = super_context.new_isolated_subcontext
|
||||||
assert_equal(:my_value, subcontext.registers[:my_register])
|
assert_equal(:my_value, subcontext.registers[:my_register])
|
||||||
@@ -618,6 +618,20 @@ class ContextTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_context_always_uses_static_registers
|
||||||
|
registers = {
|
||||||
|
my_register: :my_value,
|
||||||
|
}
|
||||||
|
c = Context.new({}, {}, registers)
|
||||||
|
assert_instance_of(Registers, c.registers)
|
||||||
|
assert_equal(:my_value, c.registers[:my_register])
|
||||||
|
|
||||||
|
r = Registers.new(registers)
|
||||||
|
c = Context.new({}, {}, r)
|
||||||
|
assert_instance_of(Registers, c.registers)
|
||||||
|
assert_equal(:my_value, c.registers[:my_register])
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def assert_no_object_allocations
|
def assert_no_object_allocations
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class TestDrop < Liquid::Drop
|
|||||||
attr_reader :value
|
attr_reader :value
|
||||||
|
|
||||||
def registers
|
def registers
|
||||||
@context.registers
|
{ @value => @context.registers[@value] }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -440,7 +440,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_map_calls_context=
|
def test_map_calls_context=
|
||||||
model = TestModel.new(value: "test")
|
model = TestModel.new(value: :test)
|
||||||
|
|
||||||
template = Template.parse('{{ foo | map: "registers" }}')
|
template = Template.parse('{{ foo | map: "registers" }}')
|
||||||
template.registers[:test] = 1234
|
template.registers[:test] = 1234
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'test_helper'
|
||||||
|
|
||||||
|
class InlineCommentTest < Minitest::Test
|
||||||
|
include Liquid
|
||||||
|
|
||||||
|
def test_inline_comment_returns_nothing
|
||||||
|
assert_template_result('', '{%- # this is an inline comment -%}')
|
||||||
|
assert_template_result('', '{%-# this is an inline comment -%}')
|
||||||
|
assert_template_result('', '{% # this is an inline comment %}')
|
||||||
|
assert_template_result('', '{%# this is an inline comment %}')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_inline_comment_does_not_require_a_space_after_the_pound_sign
|
||||||
|
assert_template_result('', '{%#this is an inline comment%}')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_liquid_inline_comment_returns_nothing
|
||||||
|
assert_template_result('Hey there, how are you doing today?', <<~LIQUID)
|
||||||
|
{%- liquid
|
||||||
|
# This is how you'd write a block comment in a liquid tag.
|
||||||
|
# It looks a lot like what you'd have in ruby.
|
||||||
|
|
||||||
|
# You can use it as inline documentation in your
|
||||||
|
# liquid blocks to explain why you're doing something.
|
||||||
|
echo "Hey there, "
|
||||||
|
|
||||||
|
# It won't affect the output.
|
||||||
|
echo "how are you doing today?"
|
||||||
|
-%}
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_inline_comment_can_be_written_on_multiple_lines
|
||||||
|
assert_template_result('', <<~LIQUID)
|
||||||
|
{%-
|
||||||
|
# That kind of block comment is also allowed.
|
||||||
|
# It would only be a stylistic difference.
|
||||||
|
|
||||||
|
# Much like JavaScript's /* */ comments and their
|
||||||
|
# leading * on new lines.
|
||||||
|
-%}
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_inline_comment_multiple_pound_signs
|
||||||
|
assert_template_result('', <<~LIQUID)
|
||||||
|
{%- liquid
|
||||||
|
######################################
|
||||||
|
# We support comments like this too. #
|
||||||
|
######################################
|
||||||
|
-%}
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_inline_comments_require_the_pound_sign_on_every_new_line
|
||||||
|
assert_match_syntax_error("Each line of comments must be prefixed by the '#' character", <<~LIQUID)
|
||||||
|
{%-
|
||||||
|
# some comment
|
||||||
|
echo 'hello world'
|
||||||
|
-%}
|
||||||
|
LIQUID
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_inline_comment_does_not_support_nested_tags
|
||||||
|
assert_template_result(' -%}', "{%- # {% echo 'hello world' %} -%}")
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -236,6 +236,20 @@ class ParseTreeVisitorTest < Minitest::Test
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_render_with
|
||||||
|
assert_equal(
|
||||||
|
["test"],
|
||||||
|
visit(%({% render "hai" with test %}))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_render_for
|
||||||
|
assert_equal(
|
||||||
|
["test"],
|
||||||
|
visit(%({% render "hai" for test %}))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def test_preserve_tree_structure
|
def test_preserve_tree_structure
|
||||||
assert_equal(
|
assert_equal(
|
||||||
[[nil, [
|
[[nil, [
|
||||||
|
|||||||
@@ -125,4 +125,35 @@ class PartialCacheUnitTest < Minitest::Test
|
|||||||
assert_equal('my partial body', partial.render)
|
assert_equal('my partial body', partial.render)
|
||||||
assert_equal(1, template_factory.count)
|
assert_equal(1, template_factory.count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_cache_state_is_shared_for_subcontexts
|
||||||
|
parse_context = Liquid::ParseContext.new
|
||||||
|
shared_file_system = StubFileSystem.new(
|
||||||
|
'my_partial' => 'my shared value'
|
||||||
|
)
|
||||||
|
context = Liquid::Context.build(
|
||||||
|
registers: Liquid::Registers.new(
|
||||||
|
file_system: shared_file_system,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
subcontext = context.new_isolated_subcontext
|
||||||
|
|
||||||
|
assert_equal(subcontext.registers[:cached_partials].object_id, context.registers[:cached_partials].object_id)
|
||||||
|
|
||||||
|
2.times do
|
||||||
|
Liquid::PartialCache.load(
|
||||||
|
'my_partial',
|
||||||
|
context: context,
|
||||||
|
parse_context: parse_context
|
||||||
|
)
|
||||||
|
|
||||||
|
Liquid::PartialCache.load(
|
||||||
|
'my_partial',
|
||||||
|
context: subcontext,
|
||||||
|
parse_context: parse_context
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal(1, shared_file_system.file_read_count)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
class StaticRegistersUnitTest < Minitest::Test
|
class RegistersUnitTest < Minitest::Test
|
||||||
include Liquid
|
include Liquid
|
||||||
|
|
||||||
def test_set
|
def test_set
|
||||||
static_register = StaticRegisters.new(a: 1, b: 2)
|
static_register = Registers.new(a: 1, b: 2)
|
||||||
static_register[:b] = 22
|
static_register[:b] = 22
|
||||||
static_register[:c] = 33
|
static_register[:c] = 33
|
||||||
|
|
||||||
@@ -16,13 +16,13 @@ class StaticRegistersUnitTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_get_missing_key
|
def test_get_missing_key
|
||||||
static_register = StaticRegisters.new
|
static_register = Registers.new
|
||||||
|
|
||||||
assert_nil(static_register[:missing])
|
assert_nil(static_register[:missing])
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_delete
|
def test_delete
|
||||||
static_register = StaticRegisters.new(a: 1, b: 2)
|
static_register = Registers.new(a: 1, b: 2)
|
||||||
static_register[:b] = 22
|
static_register[:b] = 22
|
||||||
static_register[:c] = 33
|
static_register[:c] = 33
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ class StaticRegistersUnitTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_fetch
|
def test_fetch
|
||||||
static_register = StaticRegisters.new(a: 1, b: 2)
|
static_register = Registers.new(a: 1, b: 2)
|
||||||
static_register[:b] = 22
|
static_register[:b] = 22
|
||||||
static_register[:c] = 33
|
static_register[:c] = 33
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ class StaticRegistersUnitTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_key
|
def test_key
|
||||||
static_register = StaticRegisters.new(a: 1, b: 2)
|
static_register = Registers.new(a: 1, b: 2)
|
||||||
static_register[:b] = 22
|
static_register[:b] = 22
|
||||||
static_register[:c] = 33
|
static_register[:c] = 33
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ class StaticRegistersUnitTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_static_register_can_be_frozen
|
def test_static_register_can_be_frozen
|
||||||
static_register = StaticRegisters.new(a: 1)
|
static_register = Registers.new(a: 1)
|
||||||
|
|
||||||
static_register.static.freeze
|
static_register.static.freeze
|
||||||
|
|
||||||
@@ -94,14 +94,14 @@ class StaticRegistersUnitTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_new_static_retains_static
|
def test_new_static_retains_static
|
||||||
static_register = StaticRegisters.new(a: 1, b: 2)
|
static_register = Registers.new(a: 1, b: 2)
|
||||||
static_register[:b] = 22
|
static_register[:b] = 22
|
||||||
static_register[:c] = 33
|
static_register[:c] = 33
|
||||||
|
|
||||||
new_static_register = StaticRegisters.new(static_register)
|
new_static_register = Registers.new(static_register)
|
||||||
new_static_register[:b] = 222
|
new_static_register[:b] = 222
|
||||||
|
|
||||||
newest_static_register = StaticRegisters.new(new_static_register)
|
newest_static_register = Registers.new(new_static_register)
|
||||||
newest_static_register[:c] = 333
|
newest_static_register[:c] = 333
|
||||||
|
|
||||||
assert_equal(1, static_register[:a])
|
assert_equal(1, static_register[:a])
|
||||||
@@ -118,11 +118,11 @@ class StaticRegistersUnitTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_multiple_instances_are_unique
|
def test_multiple_instances_are_unique
|
||||||
static_register_1 = StaticRegisters.new(a: 1, b: 2)
|
static_register_1 = Registers.new(a: 1, b: 2)
|
||||||
static_register_1[:b] = 22
|
static_register_1[:b] = 22
|
||||||
static_register_1[:c] = 33
|
static_register_1[:c] = 33
|
||||||
|
|
||||||
static_register_2 = StaticRegisters.new(a: 10, b: 20)
|
static_register_2 = Registers.new(a: 10, b: 20)
|
||||||
static_register_2[:b] = 220
|
static_register_2[:b] = 220
|
||||||
static_register_2[:c] = 330
|
static_register_2[:c] = 330
|
||||||
|
|
||||||
@@ -138,11 +138,11 @@ class StaticRegistersUnitTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_initialization_reused_static_same_memory_object
|
def test_initialization_reused_static_same_memory_object
|
||||||
static_register_1 = StaticRegisters.new(a: 1, b: 2)
|
static_register_1 = Registers.new(a: 1, b: 2)
|
||||||
static_register_1[:b] = 22
|
static_register_1[:b] = 22
|
||||||
static_register_1[:c] = 33
|
static_register_1[:c] = 33
|
||||||
|
|
||||||
static_register_2 = StaticRegisters.new(static_register_1)
|
static_register_2 = Registers.new(static_register_1)
|
||||||
|
|
||||||
assert_equal(1, static_register_2[:a])
|
assert_equal(1, static_register_2[:a])
|
||||||
assert_equal(2, static_register_2[:b])
|
assert_equal(2, static_register_2[:b])
|
||||||
Reference in New Issue
Block a user