mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-15 00:40:40 -07:00
Compare commits
30
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa3d4f838a | ||
|
|
d4c24f3ce2 | ||
|
|
3bbb7aa7ba | ||
|
|
102bac2e33 | ||
|
|
e69f729f76 | ||
|
|
4ec0b85d80 | ||
|
|
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:
|
||||
matrix:
|
||||
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: ruby-head, allowed-failure: true }
|
||||
name: test (${{ matrix.entry.ruby }})
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.entry.ruby }}
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
memory_profile:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 2.7
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ Performance:
|
||||
Enabled: true
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.5
|
||||
TargetRubyVersion: 2.7
|
||||
NewCops: disable
|
||||
Exclude:
|
||||
- 'vendor/bundle/**/*'
|
||||
|
||||
+137
-14
@@ -1,27 +1,74 @@
|
||||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config`
|
||||
# on 2020-12-11 18:53:41 UTC using RuboCop version 1.6.1.
|
||||
# on 2022-05-26 17:08:23 UTC using RuboCop version 1.29.1.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
# versions of RuboCop, may require this file to be generated again.
|
||||
|
||||
# Offense count: 2
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: runtime_error, standard_error
|
||||
Lint/InheritException:
|
||||
# Offense count: 1
|
||||
# This cop supports safe auto-correction (--auto-correct).
|
||||
# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
|
||||
# Include: **/*.gemspec
|
||||
Gemspec/OrderedDependencies:
|
||||
Exclude:
|
||||
- 'lib/liquid/interrupts.rb'
|
||||
- 'liquid.gemspec'
|
||||
|
||||
# Offense count: 113
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
||||
# URISchemes: http, https
|
||||
Layout/LineLength:
|
||||
Max: 260
|
||||
# Offense count: 6
|
||||
# This cop supports safe auto-correction (--auto-correct).
|
||||
Layout/ClosingHeredocIndentation:
|
||||
Exclude:
|
||||
- 'test/integration/tags/for_tag_test.rb'
|
||||
|
||||
# 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
|
||||
# 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:
|
||||
Exclude:
|
||||
- 'lib/liquid/forloop_drop.rb'
|
||||
@@ -33,7 +80,7 @@ Lint/MissingSuper:
|
||||
- 'test/integration/tags/for_tag_test.rb'
|
||||
- 'test/integration/tags/table_row_test.rb'
|
||||
|
||||
# Offense count: 43
|
||||
# Offense count: 44
|
||||
Naming/ConstantName:
|
||||
Exclude:
|
||||
- 'lib/liquid.rb'
|
||||
@@ -51,7 +98,83 @@ Naming/ConstantName:
|
||||
- 'performance/shopify/paginate.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
|
||||
Style/ClassVars:
|
||||
Exclude:
|
||||
- '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
|
||||
|
||||
group :test do
|
||||
gem 'rubocop', '~> 1.4', require: false
|
||||
gem 'rubocop-shopify', '~> 1.0.7', require: false
|
||||
gem 'rubocop-shopify', '~> 2.6', require: false
|
||||
gem 'rubocop-performance', require: false
|
||||
|
||||
platform :mri, :truffleruby do
|
||||
|
||||
+17
-1
@@ -1,9 +1,25 @@
|
||||
# Liquid Change Log
|
||||
|
||||
## 5.3.0 (unreleased)
|
||||
## 5.4.0 (unreleased)
|
||||
|
||||
### 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]
|
||||
|
||||
### Changed
|
||||
* Liquid::Context#registers now always returns a Liquid::StaticRegisters object, though supports the most used Hash functions for compatibility (#1553)
|
||||
|
||||
## 5.3.0 2022-03-22
|
||||
|
||||
### Fixes
|
||||
* StandardFilter: Fix missing @context on iterations (#1525) [Thierry Joyal]
|
||||
* Fix warning about block and default value in `static_registers.rb` (#1531) [Peter Zhu]
|
||||
|
||||
### Deprecation
|
||||
* 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
|
||||
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.
|
||||
```
|
||||
|
||||
If you want to set the error mode only on specific templates you can pass `:error_mode` as an option to `parse`:
|
||||
```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.
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ module Liquid
|
||||
WhitespaceControl = '-'
|
||||
TagStart = /\{\%/
|
||||
TagEnd = /\%\}/
|
||||
TagName = /#|\w+/
|
||||
VariableSignature = /\(?[\w\-\.\[\]]\)?/
|
||||
VariableSegment = /[\w\-]/
|
||||
VariableStart = /\{\{/
|
||||
|
||||
@@ -4,8 +4,8 @@ require 'English'
|
||||
|
||||
module Liquid
|
||||
class BlockBody
|
||||
LiquidTagToken = /\A\s*(\w+)\s*(.*?)\z/o
|
||||
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(\w+)(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
||||
LiquidTagToken = /\A\s*(#{TagName})\s*(.*?)\z/o
|
||||
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(#{TagName})(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
||||
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
|
||||
WhitespaceOrNothing = /\A\s*\z/
|
||||
TAGSTART = "{%"
|
||||
@@ -35,21 +35,9 @@ module Liquid
|
||||
super
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type tag
|
||||
# @category theme
|
||||
# @title liquid
|
||||
# @summary
|
||||
# Allows you to write multiple tags within one set of delimiters.
|
||||
# @description
|
||||
# Use the [`echo`](/api/liquid/tags/theme-tags#echo) tag to output an expression within a `liquid` tag.
|
||||
# @syntax
|
||||
# {% liquid
|
||||
# statement
|
||||
# %}
|
||||
private def parse_for_liquid_tag(tokenizer, parse_context)
|
||||
while (token = tokenizer.shift)
|
||||
unless token.empty? || token =~ WhitespaceOrNothing
|
||||
unless token.empty? || token.match?(WhitespaceOrNothing)
|
||||
unless token =~ LiquidTagToken
|
||||
# line isn't empty but didn't match tag syntax, yield and let the
|
||||
# caller raise a syntax error
|
||||
@@ -162,7 +150,7 @@ module Liquid
|
||||
end
|
||||
parse_context.trim_whitespace = false
|
||||
@nodelist << token
|
||||
@blank &&= !!(token =~ WhitespaceOrNothing)
|
||||
@blank &&= token.match?(WhitespaceOrNothing)
|
||||
end
|
||||
parse_context.line_number = tokenizer.line_number
|
||||
end
|
||||
|
||||
@@ -28,7 +28,7 @@ module Liquid
|
||||
|
||||
@static_environments = [static_environments].flat_map(&:freeze).freeze
|
||||
@scopes = [(outer_scope || {})]
|
||||
@registers = registers
|
||||
@registers = registers.is_a?(StaticRegisters) ? registers : StaticRegisters.new(registers)
|
||||
@errors = []
|
||||
@partial = false
|
||||
@strict_variables = false
|
||||
@@ -39,6 +39,10 @@ module Liquid
|
||||
@global_filter = nil
|
||||
@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
|
||||
if rethrow_errors
|
||||
self.exception_renderer = Liquid::RAISE_EXCEPTION_LAMBDA
|
||||
|
||||
@@ -13,15 +13,16 @@
|
||||
for_invalid_attribute: "Invalid attribute in for loop. Valid attributes are limit and offset"
|
||||
if: "Syntax Error in tag 'if' - Valid syntax: if [expression]"
|
||||
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}"
|
||||
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_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}"
|
||||
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:
|
||||
include: "Argument error in tag 'include' - Illegal template name"
|
||||
disabled:
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
module Liquid
|
||||
class PartialCache
|
||||
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]
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
partial = template.parse(source, parse_context)
|
||||
|
||||
+27
-298
@@ -28,47 +28,20 @@ module Liquid
|
||||
end
|
||||
|
||||
# convert an input string to DOWNCASE
|
||||
#
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @summary Converts a string into lowercase.
|
||||
# @category string
|
||||
# @syntax {{ string | downcase }}
|
||||
# @return string
|
||||
def downcase(input)
|
||||
input.to_s.downcase
|
||||
end
|
||||
|
||||
# convert an input string to UPCASE
|
||||
#
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @summary Converts a string into uppercase.
|
||||
# @category string
|
||||
# @syntax {{ string | upcase }}
|
||||
# @return string
|
||||
def upcase(input)
|
||||
input.to_s.upcase
|
||||
end
|
||||
|
||||
# capitalize words in the input sentence
|
||||
#
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @summary Capitalizes the first word in a string.
|
||||
# @category string
|
||||
# @syntax {{ string | capitalize }}
|
||||
# @return string
|
||||
# capitalize words in the input centence
|
||||
def capitalize(input)
|
||||
input.to_s.capitalize
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @summary Escapes a string.
|
||||
# @category string
|
||||
# @syntax {{ string | escape }}
|
||||
# @return string
|
||||
def escape(input)
|
||||
CGI.escapeHTML(input.to_s) unless input.nil?
|
||||
end
|
||||
@@ -91,47 +64,20 @@ module Liquid
|
||||
result
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @title base64_encode
|
||||
# @summary Encodes a string into Base64.
|
||||
# @category string
|
||||
# @syntax {{ string | base64_encode }}
|
||||
# @return string
|
||||
def base64_encode(input)
|
||||
Base64.strict_encode64(input.to_s)
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @summary Decodes a string from Base64.
|
||||
# @category string
|
||||
# @syntax {{ string | base64_decode }}
|
||||
# @return string
|
||||
def base64_decode(input)
|
||||
Base64.strict_decode64(input.to_s)
|
||||
rescue ::ArgumentError
|
||||
raise Liquid::ArgumentError, "invalid base64 provided to base64_decode"
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @summary Encodes a string into URL-safe Base64
|
||||
# @category string
|
||||
# @syntax {{ string | base64_url_safe_encode }}
|
||||
# @return string
|
||||
# @description
|
||||
# To produce URL-safe Base64, this filter uses `-`` and `_`` in place of `+`` and `/``.
|
||||
def base64_url_safe_encode(input)
|
||||
Base64.urlsafe_encode64(input.to_s)
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @summary Decodes a string from URL-safe Base64.
|
||||
# @category string
|
||||
# @syntax {{ string | base64_url_safe_decode }}
|
||||
# @return string
|
||||
def base64_url_safe_decode(input)
|
||||
Base64.urlsafe_decode64(input.to_s)
|
||||
rescue ::ArgumentError
|
||||
@@ -149,34 +95,7 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @category string
|
||||
# @summary
|
||||
# Truncates a string down to a specified number of characters.
|
||||
# @description
|
||||
# By default, an ellipsis (`...`)
|
||||
# is appended to the truncated string.
|
||||
#
|
||||
# > Tip:
|
||||
# > The number of characters in both default and custom ellipses is included in the
|
||||
# > character count for the truncated string. For example, if you want to truncate a string
|
||||
# > down to ten characters and use the default ellipsis (`...`), then you should set the
|
||||
# > character count parameter to `13`.
|
||||
#
|
||||
# ### Custom ellipsis
|
||||
#
|
||||
# The `truncate` filter accepts an optional parameter to specify a custom ellipsis to be
|
||||
# appended to the truncated string.
|
||||
#
|
||||
# ### No ellipsis
|
||||
#
|
||||
# If you don't want an ellipsis appended to your truncated string, then you can set the
|
||||
# ellipsis parameter to a blank string (`''`).
|
||||
# @syntax {{ string | truncate: character_count, ellipsis }}
|
||||
# @required_param character_count [number] The number of characters to include in the truncated string. Includes the characters in the ellipsis.
|
||||
# @optional_param ellipsis [string] The format of the ellipsis appended to the truncated string. Default is `...`. Pass a blank string (`''`) to remove the ellipsis completely.
|
||||
# @return string
|
||||
# Truncate a string down to x characters
|
||||
def truncate(input, length = 50, truncate_string = "...")
|
||||
return if input.nil?
|
||||
input_str = input.to_s
|
||||
@@ -190,27 +109,6 @@ module Liquid
|
||||
input_str.length > length ? input_str[0...l].concat(truncate_string_str) : input_str
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @category string
|
||||
# @summary
|
||||
# Truncates a string down to a specified number of words.
|
||||
# @description
|
||||
# By default, an ellipsis (`...`) is appended to the truncated string.
|
||||
#
|
||||
# ### Custom ellipsis
|
||||
#
|
||||
# The `truncate` filter accepts an optional parameter to specify a custom ellipsis to be
|
||||
# appended to the truncated string.
|
||||
#
|
||||
# ### No ellipsis
|
||||
#
|
||||
# If you don't want an ellipsis appended to your truncated string, then you can set the
|
||||
# ellipsis parameter to a blank string (`''`).
|
||||
# @syntax {{ string | truncatewords: word_count, ellipsis }}
|
||||
# @required_param word_count [number] The number of words to include in the truncated string. Includes the characters in the ellipsis.
|
||||
# @optional_param ellipsis [string] The format of the ellipsis appended to the truncated string. Default is `...`. Pass a blank string (`''`) to remove the ellipsis completely.
|
||||
# @return string
|
||||
def truncatewords(input, words = 15, truncate_string = "...")
|
||||
return if input.nil?
|
||||
input = input.to_s
|
||||
@@ -239,46 +137,18 @@ module Liquid
|
||||
input.to_s.split(pattern.to_s)
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @category string
|
||||
# @summary
|
||||
# Strips all whitespace, such as tabs, spaces, and newlines, from the left and right sides of a string.
|
||||
# @syntax {{ string | strip }}
|
||||
# @return string
|
||||
def strip(input)
|
||||
input.to_s.strip
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @category string
|
||||
# @summary
|
||||
# Strips all whitespace, such as tabs, spaces, and newlines, from the left side of a string.
|
||||
# @syntax {{ string | lstrip }}
|
||||
# @return string
|
||||
def lstrip(input)
|
||||
input.to_s.lstrip
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @category string
|
||||
# @summary
|
||||
# Strips all whitespace, such as tabs, spaces, and newlines, from the right side of a string.
|
||||
# @syntax {{ string | rstrip }}
|
||||
# @return string
|
||||
def rstrip(input)
|
||||
input.to_s.rstrip
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @category string
|
||||
# @summary
|
||||
# Strips all HTML tags from a string.
|
||||
# @syntax {{ string | strip_html }}
|
||||
# @return string
|
||||
def strip_html(input)
|
||||
empty = ''
|
||||
result = input.to_s.gsub(STRIP_HTML_BLOCKS, empty)
|
||||
@@ -286,36 +156,18 @@ module Liquid
|
||||
result
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @category string
|
||||
# @summary
|
||||
# Strips all line breaks and newlines from a string.
|
||||
# @syntax {{ string | strip_newlines }}
|
||||
# @return string
|
||||
# Remove all newlines from the string
|
||||
def strip_newlines(input)
|
||||
input.to_s.gsub(/\r?\n/, '')
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @category array
|
||||
# @summary Joins the elements in an array. The result is a single string.
|
||||
# @syntax {{ array | join: ', ' }}
|
||||
# @required_param delimiter [string] The separator to join the array elements with.
|
||||
# @return string
|
||||
# Join elements of the array with certain character between them
|
||||
def join(input, glue = ' ')
|
||||
InputIterator.new(input, context).join(glue)
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @category array
|
||||
# @summary Sorts the elements of an array.
|
||||
# @description The order of the sorted array is case-sensitive.
|
||||
# @syntax {{ array | sort: property }}
|
||||
# @optional_param property [string] The property of the element to sort by.
|
||||
# @return array
|
||||
# Sort elements of the array
|
||||
# provide optional property with which to sort an array of hashes or drops
|
||||
def sort(input, property = nil)
|
||||
ary = InputIterator.new(input, context)
|
||||
|
||||
@@ -403,24 +255,13 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type tag
|
||||
# @category array
|
||||
# @summary Reverses the order of the items in an array.
|
||||
# @syntax {{ array | reverse }}
|
||||
# @return array
|
||||
# Reverse the elements of an array
|
||||
def reverse(input)
|
||||
ary = InputIterator.new(input, context)
|
||||
ary.reverse
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type tag
|
||||
# @category array
|
||||
# @summary Accepts an array element's property as a parameter and creates an array out of the property value for each array element.
|
||||
# @required_param property [string] The property to extract from the element.
|
||||
# @syntax {{ array | map: 'property' }}
|
||||
# @return array
|
||||
# map/collect on a given property
|
||||
def map(input, property)
|
||||
InputIterator.new(input, context).map do |e|
|
||||
e = e.call if e.is_a?(Proc)
|
||||
@@ -457,14 +298,7 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @summary Replaces all occurrences of a string with a substring.
|
||||
# @type filter
|
||||
# @category string
|
||||
# @syntax {{ string | replace: original_string, replacement_string }}
|
||||
# @required_param original_string [string] The string to replace.
|
||||
# @required_param replacement_string [string] The replacement string.
|
||||
# @return string
|
||||
# Replace occurrences of a string with another
|
||||
def replace(input, string, replacement = '')
|
||||
input.to_s.gsub(string.to_s, replacement.to_s)
|
||||
end
|
||||
@@ -489,24 +323,12 @@ module Liquid
|
||||
output
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @summary Removes a substring from a string.
|
||||
# @type filter
|
||||
# @category string
|
||||
# @syntax {{ string | remove: substring }}
|
||||
# @required_param substring [string] The substring to remove from the string.
|
||||
# @return string
|
||||
# remove a substring
|
||||
def remove(input, string)
|
||||
replace(input, string, '')
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @summary Removes the first occurrences of a substring.
|
||||
# @type filter
|
||||
# @category string
|
||||
# @syntax {{ string | remove_first: substring }}
|
||||
# @required_param substring [string] The substring to remove from the string.
|
||||
# @return string
|
||||
# remove the first occurrences of a substring
|
||||
def remove_first(input, string)
|
||||
replace_first(input, string, '')
|
||||
end
|
||||
@@ -517,28 +339,10 @@ module Liquid
|
||||
end
|
||||
|
||||
# add one string to another
|
||||
#
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @summary Appends characters to a string.
|
||||
# @category string
|
||||
# @syntax {{ string | append: to_append }}
|
||||
# @required_param to_append [string] characters to append to the original string
|
||||
# @return string
|
||||
def append(input, string)
|
||||
input.to_s + string.to_s
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @category array
|
||||
# @summary Concatenates (combines) an array with another array.
|
||||
# @description
|
||||
# The resulting array contains all the elements of the original arrays.
|
||||
# `concat` won't remove duplicate entries from the concatenated array unless you also use the [`uniq`](/api/liquid/filters/array-filters#uniq) filter.
|
||||
# @syntax {{ first_array | concat: second_array }}
|
||||
# @required_param second_array [array] The array to concatenate with the primary array.
|
||||
# @return array
|
||||
def concat(input, array)
|
||||
unless array.respond_to?(:to_ary)
|
||||
raise ArgumentError, "concat filter requires an array argument"
|
||||
@@ -546,13 +350,7 @@ module Liquid
|
||||
InputIterator.new(input, context).concat(array)
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @summary Prepends a string to another string.
|
||||
# @type filter
|
||||
# @category string
|
||||
# @syntax {{ string | prepend: additional_string }}
|
||||
# @required_param additional_string [string] The string to prepend to the original string.
|
||||
# @return string
|
||||
# prepend a string to another
|
||||
def prepend(input, string)
|
||||
string.to_s + input.to_s
|
||||
end
|
||||
@@ -601,101 +399,58 @@ module Liquid
|
||||
date.strftime(format.to_s)
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @category array
|
||||
# @summary Returns the first element of an array.
|
||||
# @syntax {{ array | first }}
|
||||
# Get the first element of the passed in array
|
||||
#
|
||||
# Example:
|
||||
# {{ product.images | first | to_img }}
|
||||
#
|
||||
def first(array)
|
||||
array.first if array.respond_to?(:first)
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @type filter
|
||||
# @category array
|
||||
# @summary Returns the last element of an array, or the last character inside of a string.
|
||||
# @syntax {{ array | last }}
|
||||
# Get the last element of the passed in array
|
||||
#
|
||||
# Example:
|
||||
# {{ product.images | last | to_img }}
|
||||
#
|
||||
def last(array)
|
||||
array.last if array.respond_to?(:last)
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @syntax {{ number | abs }}
|
||||
# @summary Returns the absolute value of a number.
|
||||
# @type filter
|
||||
# @category math
|
||||
# @return number
|
||||
# @description
|
||||
# `abs` will also work on a string if the string only contains a number.
|
||||
# absolute value
|
||||
def abs(input)
|
||||
result = Utils.to_number(input).abs
|
||||
result.is_a?(BigDecimal) ? result.to_f : result
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @summary Adds a number to an output.
|
||||
# @type filter
|
||||
# @category math
|
||||
# @syntax {{ number | plus: number }}
|
||||
# @required_param number [number] The number to add to the original number.
|
||||
# @return number
|
||||
# addition
|
||||
def plus(input, operand)
|
||||
apply_operation(input, operand, :+)
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @summary Subtracts a number from an output.
|
||||
# @type filter
|
||||
# @category math
|
||||
# @syntax {{ number | minus: number }}
|
||||
# @return number
|
||||
# subtraction
|
||||
def minus(input, operand)
|
||||
apply_operation(input, operand, :-)
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @summary Multiplies an output by a number.
|
||||
# @type filter
|
||||
# @category math
|
||||
# @syntax {{ number | times: number }}
|
||||
# @required_param number [number] The number to multiply the original number by.
|
||||
# @return number
|
||||
# multiplication
|
||||
def times(input, operand)
|
||||
apply_operation(input, operand, :*)
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @summary Divides an output by a number. The output is rounded down to the nearest integer.
|
||||
# @type filter
|
||||
# @category math
|
||||
# @syntax {{ number | divided_by: number }}
|
||||
# @return number
|
||||
# division
|
||||
def divided_by(input, operand)
|
||||
apply_operation(input, operand, :/)
|
||||
rescue ::ZeroDivisionError => e
|
||||
raise Liquid::ZeroDivisionError, e.message
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @summary Divides an output by a number and returns the remainder.
|
||||
# @type filter
|
||||
# @category math
|
||||
# @syntax {{ number | modulo: number }}
|
||||
# @required_param number [number] The number to divide the original number by.
|
||||
# @return number
|
||||
def modulo(input, operand)
|
||||
apply_operation(input, operand, :%)
|
||||
rescue ::ZeroDivisionError => e
|
||||
raise Liquid::ZeroDivisionError, e.message
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @summary Rounds the output to the nearest integer or specified number of decimals.
|
||||
# @type filter
|
||||
# @category math
|
||||
# @syntax {{ number | round }}
|
||||
# @optional_param decimals [number] The number of decimal places to round to.
|
||||
# @return number
|
||||
def round(input, n = 0)
|
||||
result = Utils.to_number(input).round(Utils.to_number(n))
|
||||
result = result.to_f if result.is_a?(BigDecimal)
|
||||
@@ -705,37 +460,18 @@ module Liquid
|
||||
raise Liquid::FloatDomainError, e.message
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @summary Rounds an output up to the nearest integer.
|
||||
# @type filter
|
||||
# @category math
|
||||
# @syntax {{ number | ceil }}
|
||||
# @return number
|
||||
def ceil(input)
|
||||
Utils.to_number(input).ceil.to_i
|
||||
rescue ::FloatDomainError => e
|
||||
raise Liquid::FloatDomainError, e.message
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @summary Rounds an output down to the nearest integer.
|
||||
# @type filter
|
||||
# @category math
|
||||
# @syntax {{ number | floor }}
|
||||
# @return number
|
||||
def floor(input)
|
||||
Utils.to_number(input).floor.to_i
|
||||
rescue ::FloatDomainError => e
|
||||
raise Liquid::FloatDomainError, e.message
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @summary Limits a number to a minimum value.
|
||||
# @type filter
|
||||
# @category math
|
||||
# @syntax {{ number | at_least: number }}
|
||||
# @required_param number [number] The minimum valid value.
|
||||
# @return number
|
||||
def at_least(input, n)
|
||||
min_value = Utils.to_number(n)
|
||||
|
||||
@@ -744,13 +480,6 @@ module Liquid
|
||||
result.is_a?(BigDecimal) ? result.to_f : result
|
||||
end
|
||||
|
||||
# @public_docs
|
||||
# @summary Limits a number to a maximum value.
|
||||
# @type filter
|
||||
# @category math
|
||||
# @syntax {{ number | at_most: number }}
|
||||
# @required_param number [number] The maximum valid value.
|
||||
# @return number
|
||||
def at_most(input, n)
|
||||
max_value = Utils.to_number(n)
|
||||
|
||||
|
||||
@@ -1,27 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# @public_docs
|
||||
# @title case
|
||||
# @type tag
|
||||
# @category controlflow
|
||||
# @summary Creates a switch statement to execute a particular block of code when a variable has a specified value.
|
||||
# @description
|
||||
# `case` initializes the switch statement, and `when` statements define the various conditions.
|
||||
#
|
||||
# An optional `else` statement at the end of the case provides code to execute if none of the conditions are met.
|
||||
# @syntax
|
||||
# {% case variable %}
|
||||
# {% when value1 %}
|
||||
# statement1
|
||||
# {% when value2 %}
|
||||
# statement2
|
||||
# {% when value3 %}
|
||||
# statement3
|
||||
# ...
|
||||
# {% else %}
|
||||
# else_statement
|
||||
# {% endcase %}
|
||||
class Case < Block
|
||||
Syntax = /(#{QuotedFragment})/o
|
||||
WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/om
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# @public_docs
|
||||
# @type tag
|
||||
# @category theme
|
||||
# @title comment
|
||||
# @summary
|
||||
# Allows you to comment out parts of a Liquid file.
|
||||
# Any text within the opening and closing `comment` blocks won't be output,
|
||||
# and any Liquid code won't be executed.
|
||||
# @syntax
|
||||
# {% comment %}
|
||||
# statement
|
||||
# {% endcomment %}
|
||||
class Comment < Block
|
||||
def render_to_output_buffer(_context, output)
|
||||
output
|
||||
|
||||
@@ -13,19 +13,6 @@ module Liquid
|
||||
# <div class="red"> Item four </div>
|
||||
# <div class="green"> Item five</div>
|
||||
#
|
||||
# @public_docs
|
||||
# @title cycle
|
||||
# @type tag
|
||||
# @category iteration
|
||||
# @summary Loops through a group of strings and prints them in the order that they were passed as parameters.
|
||||
# @description
|
||||
# Each time `cycle` is called, the next string that was passed as a parameter is printed.
|
||||
#
|
||||
# `cycle` must be used within a `for` loop block.
|
||||
# @syntax
|
||||
# {% for condition %}
|
||||
# {% cycle value1, value2 ... %}
|
||||
# {% endfor %}
|
||||
class Cycle < Tag
|
||||
SimpleSyntax = /\A#{QuotedFragment}+/o
|
||||
NamedSyntax = /\A(#{QuotedFragment})\s*\:\s*(.*)/om
|
||||
|
||||
+10
-10
@@ -1,16 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# @public_docs
|
||||
# @type tag
|
||||
# @category theme
|
||||
# @title echo
|
||||
# @summary
|
||||
# Outputs an expression, or Liquid object, in the rendered HTML.
|
||||
# Works the same as wrapping an expression in double curly brace delimiters `{{ }}`.
|
||||
# Works inside the [`liquid`](/api/liquid/tags/theme-tags#liquid) tag and supports [filters](/api/liquid/filters).
|
||||
# @syntax
|
||||
# {% echo 'string' %}
|
||||
# Echo outputs an expression
|
||||
#
|
||||
# {% echo monkey %}
|
||||
# {% echo user.name %}
|
||||
#
|
||||
# This is identical to variable output syntax, like {{ foo }}, but works
|
||||
# inside {% liquid %} tags. The full syntax is supported, including filters:
|
||||
#
|
||||
# {% echo user | link %}
|
||||
#
|
||||
class Echo < Tag
|
||||
attr_reader :variable
|
||||
|
||||
|
||||
@@ -45,20 +45,6 @@ module Liquid
|
||||
# forloop.last:: Returns true if the item is the last item.
|
||||
# forloop.parentloop:: Provides access to the parent loop, if present.
|
||||
#
|
||||
# @public_docs
|
||||
# @title for
|
||||
# @type tag
|
||||
# @category iteration
|
||||
# @summary Repeatedly executes a block of code.
|
||||
# @description
|
||||
# You can output a maximum of 50 results per page with `for` loops. In cases where there are more than 50 results,
|
||||
# use the [`paginate`](/api/liquid/tags/theme-tags#paginate) tag to split them across multiple pages.
|
||||
#
|
||||
# For a full list of attributes available within a `for` loop, refer to the [`forloop`](/api/liquid/objects/for-loops) object.
|
||||
# @syntax
|
||||
# {% for variable in iterable parameters %}
|
||||
# @optional_param limit [number] Exit the `for` loop at the specified index.
|
||||
# @optional_param offset [number] Start the `for` loop at the specified index.
|
||||
class For < Block
|
||||
Syntax = /\A(#{VariableSegment}+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
|
||||
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
module Liquid
|
||||
# If is the conditional block
|
||||
#
|
||||
# @public_docs
|
||||
# @title if
|
||||
# @type tag
|
||||
# @category controlflow
|
||||
# @summary Executes a block of code only if a certain condition is met (if the result is `truthy`).
|
||||
# @syntax
|
||||
# {% if variable operator value %}
|
||||
# statement
|
||||
# {% if user.admin %}
|
||||
# Admin user!
|
||||
# {% else %}
|
||||
# Not admin user
|
||||
# {% endif %}
|
||||
#
|
||||
# There are {% if count < 5 %} less {% else %} more {% endif %} items than you need.
|
||||
#
|
||||
class If < Block
|
||||
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
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class InlineComment < Tag
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
|
||||
# Semantically, a comment should only ignore everything after it on the line.
|
||||
# Currently, this implementation doesn't support mixing a comment with another tag
|
||||
# but we need to reserve future support for this 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,16 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# @public_docs
|
||||
# @type tag
|
||||
# @category theme
|
||||
# @title raw
|
||||
# @summary
|
||||
# Allows you to output Liquid code on a page without it being parsed.
|
||||
# @syntax
|
||||
# {% raw %}
|
||||
# liquid
|
||||
# {% endraw %}
|
||||
class Raw < Block
|
||||
Syntax = /\A\s*\z/
|
||||
FullTokenPossiblyInvalid = /\A(.*)#{TagStart}\s*(\w+)\s*(.*)?#{TagEnd}\z/om
|
||||
|
||||
@@ -1,28 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# @public_docs
|
||||
# @type tag
|
||||
# @category theme
|
||||
# @title render
|
||||
# @summary
|
||||
# Renders a snippet from the **snippets** folder of a theme, or [code for an app block](/themes/architecture/sections/section-schema#render-app-blocks).
|
||||
# You don't need to write the file's `.liquid` extension.
|
||||
# @description
|
||||
# When a snippet is rendered, the code inside it doesn't automatically have access to the variables assigned
|
||||
# using [variable tags](/api/liquid/tags/variable-tags) within the snippet's parent template.
|
||||
# Similarly, variables assigned within the snippet can't be accessed by the code outside of the snippet.
|
||||
# This encapsulation increases performance and helps make theme code easier to understand and maintain.
|
||||
#
|
||||
# You can't use an [`include`](/api/liquid/tags/theme-tags#include) tag inside of a snippet rendered using the `render` tag.
|
||||
#
|
||||
# > Tip:
|
||||
# > This tag replaces the deprecated [`include`](/api/liquid/tags/theme-tags#include) tag.
|
||||
# @syntax
|
||||
# {% render reference %}
|
||||
# @optional_param with [string] Pass an object to use in the snippet. Use with the `as` parameter.
|
||||
# @optional_param for [string] Render the snippet once for each value of an enumerable object. Use with the `as` parameter. When using the `for` parameter, the [`forloop`](/api/liquid/objects/for-loops) object is accessible within the snippet.
|
||||
# @optional_param as [string] The variable that the object referenced by a `with` or `for` parameter represents within the snippet.
|
||||
class Render < Tag
|
||||
FOR = 'for'
|
||||
SYNTAX = /(#{QuotedString}+)(\s+(with|#{FOR})\s+(#{QuotedFragment}+))?(\s+(?:as)\s+(#{VariableSegment}+))?/o
|
||||
@@ -56,9 +34,9 @@ module Liquid
|
||||
end
|
||||
|
||||
def render_tag(context, output)
|
||||
# Though we evaluate this here we will only ever parse it as a string literal.
|
||||
template_name = context.evaluate(@template_name_expr)
|
||||
raise ArgumentError, options[:locale].t("errors.argument.include") unless template_name
|
||||
# The expression should be a String literal, which parses to a String object
|
||||
template_name = @template_name_expr
|
||||
raise ::ArgumentError unless template_name.is_a?(String)
|
||||
|
||||
partial = PartialCache.load(
|
||||
template_name,
|
||||
|
||||
@@ -1,23 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
# @public_docs
|
||||
# @title tablerow
|
||||
# @type tag
|
||||
# @category iteration
|
||||
# @summary Generates rows for an HTML table.
|
||||
# @description
|
||||
# Must be wrapped in opening `<table>` and closing `</table>` HTML tags.
|
||||
# For a full list of attributes available within a `tablerow` loop, refer to the [`tablerow`](/api/liquid/objects/tablerow) object.
|
||||
# @syntax
|
||||
# <table>
|
||||
# {% tablerow variable in interable parameters %}
|
||||
# content
|
||||
# {% endtablerow %}
|
||||
# </table>
|
||||
# @optional_param cols [number] The number of columns the table should have.
|
||||
# @optional_param limit [number] Exit the `tablerow` loop at the specified index.
|
||||
# @optional_param offset [number] Start the `tablerow` loop at the specified index.
|
||||
class TableRow < Block
|
||||
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)/o
|
||||
|
||||
|
||||
@@ -3,15 +3,10 @@
|
||||
require_relative 'if'
|
||||
|
||||
module Liquid
|
||||
# @public_docs
|
||||
# @title unless
|
||||
# @type tag
|
||||
# @category controlflow
|
||||
# @summary Executes a block of code only if a certain condition is not met (if the result is `falsy`).
|
||||
# @syntax
|
||||
# {% unless variable operator value %}
|
||||
# statement
|
||||
# {% endunless %}
|
||||
# Unless is a conditional just like 'if' but works on the inverse logic.
|
||||
#
|
||||
# {% unless x < 0 %} x is greater than zero {% endunless %}
|
||||
#
|
||||
class Unless < If
|
||||
def render_to_output_buffer(context, output)
|
||||
# First condition is interpreted backwards ( if not )
|
||||
|
||||
@@ -167,15 +167,14 @@ module Liquid
|
||||
|
||||
output = nil
|
||||
|
||||
context_register = context.registers.is_a?(StaticRegisters) ? context.registers.static : context.registers
|
||||
|
||||
case args.last
|
||||
when Hash
|
||||
options = args.pop
|
||||
output = options[:output] if options[:output]
|
||||
static_registers = context.registers.static
|
||||
|
||||
options[:registers]&.each do |key, register|
|
||||
context_register[key] = register
|
||||
static_registers[key] = register
|
||||
end
|
||||
|
||||
apply_options_to_context(context, options)
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
module Liquid
|
||||
class VariableLookup
|
||||
SQUARE_BRACKETED = /\A\[(.*)\]\z/m
|
||||
COMMAND_METHODS = ['size', 'first', 'last'].freeze
|
||||
COMMAND_METHODS = ['size', 'first', 'last'].freeze
|
||||
|
||||
attr_reader :name, :lookups
|
||||
|
||||
@@ -15,8 +14,8 @@ module Liquid
|
||||
lookups = markup.scan(VariableParser)
|
||||
|
||||
name = lookups.shift
|
||||
if name =~ SQUARE_BRACKETED
|
||||
name = Expression.parse(Regexp.last_match(1))
|
||||
if name&.start_with?('[') && name&.end_with?(']')
|
||||
name = Expression.parse(name[1..-2])
|
||||
end
|
||||
@name = name
|
||||
|
||||
@@ -25,8 +24,8 @@ module Liquid
|
||||
|
||||
@lookups.each_index do |i|
|
||||
lookup = lookups[i]
|
||||
if lookup =~ SQUARE_BRACKETED
|
||||
lookups[i] = Expression.parse(Regexp.last_match(1))
|
||||
if lookup&.start_with?('[') && lookup&.end_with?(']')
|
||||
lookups[i] = Expression.parse(lookup[1..-2])
|
||||
elsif COMMAND_METHODS.include?(lookup)
|
||||
@command_flags |= 1 << i
|
||||
end
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
VERSION = "5.3.0.alpha"
|
||||
VERSION = "5.4.0.alpha"
|
||||
end
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
|
||||
s.license = "MIT"
|
||||
# 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.metadata['allowed_push_host'] = 'https://rubygems.org'
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# This profiler run simulates Shopify.
|
||||
# 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.
|
||||
# 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
|
||||
# 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.
|
||||
|
||||
@@ -618,6 +618,20 @@ class ContextTest < Minitest::Test
|
||||
end
|
||||
end
|
||||
|
||||
def test_context_always_uses_static_registers
|
||||
registers = {
|
||||
my_register: :my_value,
|
||||
}
|
||||
c = Context.new({}, {}, registers)
|
||||
assert_instance_of(StaticRegisters, c.registers)
|
||||
assert_equal(:my_value, c.registers[:my_register])
|
||||
|
||||
r = StaticRegisters.new(registers)
|
||||
c = Context.new({}, {}, r)
|
||||
assert_instance_of(StaticRegisters, c.registers)
|
||||
assert_equal(:my_value, c.registers[:my_register])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assert_no_object_allocations
|
||||
|
||||
@@ -32,7 +32,7 @@ class TestDrop < Liquid::Drop
|
||||
attr_reader :value
|
||||
|
||||
def registers
|
||||
@context.registers
|
||||
{ @value => @context.registers[@value] }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -440,7 +440,7 @@ class StandardFiltersTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_map_calls_context=
|
||||
model = TestModel.new(value: "test")
|
||||
model = TestModel.new(value: :test)
|
||||
|
||||
template = Template.parse('{{ foo | map: "registers" }}')
|
||||
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
|
||||
@@ -125,4 +125,35 @@ class PartialCacheUnitTest < Minitest::Test
|
||||
assert_equal('my partial body', partial.render)
|
||||
assert_equal(1, template_factory.count)
|
||||
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::StaticRegisters.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
|
||||
|
||||
Reference in New Issue
Block a user