mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-13 07:50:43 -07:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fc45ca3af | ||
|
|
ab455480fa | ||
|
|
5e8e5e89b1 | ||
|
|
6ddfaec3f9 | ||
|
|
fff6c565c1 | ||
|
|
31f7be8a6d |
@@ -7,7 +7,7 @@ jobs:
|
||||
matrix:
|
||||
entry:
|
||||
- { ruby: 2.5, allowed-failure: false } # minimum supported
|
||||
- { ruby: 3.1, allowed-failure: false } # latest
|
||||
- { ruby: 3.0, allowed-failure: false } # latest
|
||||
- { ruby: ruby-head, allowed-failure: true }
|
||||
name: test (${{ matrix.entry.ruby }})
|
||||
steps:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+2
-8
@@ -1,7 +1,5 @@
|
||||
inherit_gem:
|
||||
rubocop-shopify: rubocop.yml
|
||||
|
||||
inherit_from:
|
||||
- 'https://shopify.github.io/ruby-style-guide/rubocop.yml'
|
||||
- .rubocop_todo.yml
|
||||
|
||||
require: rubocop-performance
|
||||
@@ -10,7 +8,7 @@ Performance:
|
||||
Enabled: true
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.5
|
||||
TargetRubyVersion: 2.4
|
||||
NewCops: disable
|
||||
Exclude:
|
||||
- 'vendor/bundle/**/*'
|
||||
@@ -18,7 +16,3 @@ AllCops:
|
||||
Naming/MethodName:
|
||||
Exclude:
|
||||
- 'example/server/liquid_servlet.rb'
|
||||
|
||||
# Backport https://github.com/Shopify/ruby-style-guide/pull/258
|
||||
Layout/BeginEndAlignment:
|
||||
Enabled: true
|
||||
|
||||
+1
-4
@@ -5,7 +5,6 @@
|
||||
* Bugfixes
|
||||
* Performance improvements
|
||||
* Features that are likely to be useful to the majority of Liquid users
|
||||
* Documentation updates that are concise and likely to be useful to the majority of Liquid users
|
||||
|
||||
## Things we won't merge
|
||||
|
||||
@@ -15,14 +14,12 @@
|
||||
* Features that can easily be implemented on top of Liquid (for example as a custom filter or custom filesystem)
|
||||
* Code that does not include tests
|
||||
* Code that breaks existing tests
|
||||
* Documentation changes that are verbose, incorrect or not important to most people (we want to keep it simple and easy to understand)
|
||||
|
||||
## Workflow
|
||||
|
||||
* [Sign the CLA](https://cla.shopify.com/) if you haven't already
|
||||
* Fork the Liquid repository
|
||||
* Create a new branch in your fork
|
||||
* For updating [Liquid documentation](https://shopify.github.io/liquid/), create it from `gh-pages` branch. (You can skip tests.)
|
||||
* If it makes sense, add tests for your code and/or run a performance benchmark
|
||||
* Make sure all tests pass (`bundle exec rake`)
|
||||
* Create a pull request
|
||||
|
||||
|
||||
@@ -19,10 +19,9 @@ end
|
||||
|
||||
group :test do
|
||||
gem 'rubocop', '~> 1.4', require: false
|
||||
gem 'rubocop-shopify', '~> 1.0.7', require: false
|
||||
gem 'rubocop-performance', require: false
|
||||
|
||||
platform :mri, :truffleruby do
|
||||
gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'master'
|
||||
gem 'liquid-c', github: 'Shopify/liquid-c', ref: 'inline-comment'
|
||||
end
|
||||
end
|
||||
|
||||
+2
-36
@@ -1,43 +1,9 @@
|
||||
# Liquid Change Log
|
||||
|
||||
## 5.3.0 (unreleased)
|
||||
|
||||
### Fixes
|
||||
* StandardFilter: Fix missing @context on iterations (#1525) [Thierry Joyal]
|
||||
|
||||
### Deprecation
|
||||
* Condition#evaluate to require mandatory context argument in Liquid 6.0.0 (#1527) [Thierry Joyal]
|
||||
|
||||
## 5.2.0 2022-03-01
|
||||
## Unreleased
|
||||
|
||||
### Features
|
||||
* Add `remove_last`, and `replace_last` filters (#1422) [Anders Hagbard]
|
||||
* Eagerly cache global filters (#1524) [Jean Boussier]
|
||||
|
||||
### Fixes
|
||||
* Fix some internal errors in filters from invalid input (#1476) [Dylan Thacker-Smith]
|
||||
* Allow dash in filter kwarg name for consistency with Liquid::C (#1518) [CP Clermont]
|
||||
|
||||
## 5.1.0 / 2021-09-09
|
||||
|
||||
### Features
|
||||
* Add `base64_encode`, `base64_decode`, `base64_url_safe_encode`, and `base64_url_safe_decode` filters (#1450) [Daniel Insley]
|
||||
* Introduce `to_liquid_value` in `Liquid::Drop` (#1441) [Michael Go]
|
||||
|
||||
### Fixes
|
||||
* Fix support for using a String subclass for the liquid source (#1421) [Dylan Thacker-Smith]
|
||||
* Add `ParseTreeVisitor` to `RangeLookup` (#1470) [CP Clermont]
|
||||
* Translate `RangeError` to `Liquid::Error` for `truncatewords` with large int (#1431) [Dylan Thacker-Smith]
|
||||
|
||||
## 5.0.1 / 2021-03-24
|
||||
|
||||
### Fixes
|
||||
* Add ParseTreeVisitor to Echo tag (#1414) [CP Clermont]
|
||||
* Test with ruby 3.0 as the latest ruby version (#1398) [Dylan Thacker-Smith]
|
||||
* Handle carriage return in newlines_to_br (#1391) [Unending]
|
||||
|
||||
### Performance Improvements
|
||||
* Use split limit in truncatewords (#1361) [Dylan Thacker-Smith]
|
||||
* Allow `#` to be used as an inline comment tag (#1401) [Dylan Thacker-Smith]
|
||||
|
||||
## 5.0.0 / 2021-01-06
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
* [Contributing guidelines](CONTRIBUTING.md)
|
||||
* [Version history](History.md)
|
||||
* [Liquid documentation from Shopify](https://shopify.dev/api/liquid)
|
||||
* [Liquid documentation from Shopify](http://docs.shopify.com/themes/liquid-basics)
|
||||
* [Liquid Wiki at GitHub](https://github.com/Shopify/liquid/wiki)
|
||||
* [Website](http://liquidmarkup.org/)
|
||||
|
||||
@@ -56,7 +56,7 @@ For standard use you can just pass it the content of a file and call render with
|
||||
|
||||
Setting the error mode of Liquid lets you specify how strictly you want your templates to be interpreted.
|
||||
Normally the parser is very lax and will accept almost anything without error. Unfortunately this can make
|
||||
it very hard to debug and can lead to unexpected behaviour.
|
||||
it very hard to debug and can lead to unexpected behaviour.
|
||||
|
||||
Liquid also comes with a stricter parser that can be used when editing templates to give better error messages
|
||||
when templates are invalid. You can enable this new parser like this:
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ module Liquid
|
||||
VariableIncompleteEnd = /\}\}?/
|
||||
QuotedString = /"[^"]*"|'[^']*'/
|
||||
QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/o
|
||||
TagAttributes = /(\w[\w-]*)\s*\:\s*(#{QuotedFragment})/o
|
||||
TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/o
|
||||
AnyStartingTag = /#{TagStart}|#{VariableStart}/o
|
||||
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om
|
||||
TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
|
||||
@@ -59,8 +59,8 @@ require 'liquid/forloop_drop'
|
||||
require 'liquid/extensions'
|
||||
require 'liquid/errors'
|
||||
require 'liquid/interrupts'
|
||||
require 'liquid/strainer_template'
|
||||
require 'liquid/strainer_factory'
|
||||
require 'liquid/strainer_template'
|
||||
require 'liquid/expression'
|
||||
require 'liquid/context'
|
||||
require 'liquid/parser_switching'
|
||||
|
||||
@@ -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*(\w+|#)\s*(.*?)\z/o
|
||||
FullToken = /\A#{TagStart}#{WhitespaceControl}?(\s*)(\w+|#)(\s*)(.*?)#{WhitespaceControl}?#{TagEnd}\z/om
|
||||
ContentOfVariable = /\A#{VariableStart}#{WhitespaceControl}?(.*?)#{WhitespaceControl}?#{VariableEnd}\z/om
|
||||
WhitespaceOrNothing = /\A\s*\z/
|
||||
TAGSTART = "{%"
|
||||
@@ -35,18 +35,6 @@ 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
|
||||
@@ -243,8 +231,8 @@ module Liquid
|
||||
end
|
||||
|
||||
def create_variable(token, parse_context)
|
||||
if token =~ ContentOfVariable
|
||||
markup = Regexp.last_match(1)
|
||||
token.scan(ContentOfVariable) do |content|
|
||||
markup = content.first
|
||||
return Variable.new(markup, parse_context)
|
||||
end
|
||||
BlockBody.raise_missing_variable_terminator(token, parse_context)
|
||||
|
||||
+4
-10
@@ -8,7 +8,7 @@ module Liquid
|
||||
# c = Condition.new(1, '==', 1)
|
||||
# c.evaluate #=> true
|
||||
#
|
||||
class Condition # :nodoc:
|
||||
class Condition #:nodoc:
|
||||
@@operators = {
|
||||
'==' => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
|
||||
'!=' => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
|
||||
@@ -61,7 +61,7 @@ module Liquid
|
||||
@child_condition = nil
|
||||
end
|
||||
|
||||
def evaluate(context = deprecated_default_context)
|
||||
def evaluate(context = Context.new)
|
||||
condition = self
|
||||
result = nil
|
||||
loop do
|
||||
@@ -134,8 +134,8 @@ module Liquid
|
||||
# return this as the result.
|
||||
return context.evaluate(left) if op.nil?
|
||||
|
||||
left = Liquid::Utils.to_liquid_value(context.evaluate(left))
|
||||
right = Liquid::Utils.to_liquid_value(context.evaluate(right))
|
||||
left = context.evaluate(left)
|
||||
right = context.evaluate(right)
|
||||
|
||||
operation = self.class.operators[op] || raise(Liquid::ArgumentError, "Unknown operator #{op}")
|
||||
|
||||
@@ -150,12 +150,6 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
def deprecated_default_context
|
||||
warn("DEPRECATION WARNING: Condition#evaluate without a context argument is deprecated" \
|
||||
" and will be removed from Liquid 6.0.0.")
|
||||
Context.new
|
||||
end
|
||||
|
||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
||||
def children
|
||||
[
|
||||
|
||||
@@ -124,7 +124,7 @@ module Liquid
|
||||
# context['var'] = 'hi'
|
||||
# end
|
||||
#
|
||||
# context['var'] #=> nil
|
||||
# context['var] #=> nil
|
||||
def stack(new_scope = {})
|
||||
push(new_scope)
|
||||
yield
|
||||
|
||||
+10
-11
@@ -10,23 +10,21 @@ module Liquid
|
||||
'empty' => ''
|
||||
}.freeze
|
||||
|
||||
INTEGERS_REGEX = /\A(-?\d+)\z/
|
||||
FLOATS_REGEX = /\A(-?\d[\d\.]+)\z/
|
||||
SINGLE_QUOTED_STRING = /\A\s*'(.*)'\s*\z/m
|
||||
DOUBLE_QUOTED_STRING = /\A\s*"(.*)"\s*\z/m
|
||||
INTEGERS_REGEX = /\A\s*(-?\d+)\s*\z/
|
||||
FLOATS_REGEX = /\A\s*(-?\d[\d\.]+)\s*\z/
|
||||
|
||||
# Use an atomic group (?>...) to avoid pathological backtracing from
|
||||
# malicious input as described in https://github.com/Shopify/liquid/issues/1357
|
||||
RANGES_REGEX = /\A\(\s*(?>(\S+)\s*\.\.)\s*(\S+)\s*\)\z/
|
||||
RANGES_REGEX = /\A\s*\(\s*(?>(\S+)\s*\.\.)\s*(\S+)\s*\)\s*\z/
|
||||
|
||||
def self.parse(markup)
|
||||
return nil unless markup
|
||||
|
||||
markup = markup.strip
|
||||
if (markup.start_with?('"') && markup.end_with?('"')) ||
|
||||
(markup.start_with?("'") && markup.end_with?("'"))
|
||||
return markup[1..-2]
|
||||
end
|
||||
|
||||
case markup
|
||||
when nil
|
||||
nil
|
||||
when SINGLE_QUOTED_STRING, DOUBLE_QUOTED_STRING
|
||||
Regexp.last_match(1)
|
||||
when INTEGERS_REGEX
|
||||
Regexp.last_match(1).to_i
|
||||
when RANGES_REGEX
|
||||
@@ -34,6 +32,7 @@ module Liquid
|
||||
when FLOATS_REGEX
|
||||
Regexp.last_match(1).to_f
|
||||
else
|
||||
markup = markup.strip
|
||||
if LITERALS.key?(markup)
|
||||
LITERALS[markup]
|
||||
else
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
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"
|
||||
inline_comment_invalid: "Syntax error in tag '#' - Each line of comments must be prefixed by the '#' character"
|
||||
argument:
|
||||
include: "Argument error in tag 'include' - Illegal template name"
|
||||
disabled:
|
||||
|
||||
@@ -12,8 +12,6 @@ module Liquid
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :start_obj, :end_obj
|
||||
|
||||
def initialize(start_obj, end_obj)
|
||||
@start_obj = start_obj
|
||||
@end_obj = end_obj
|
||||
@@ -37,11 +35,5 @@ module Liquid
|
||||
Utils.to_integer(input)
|
||||
end
|
||||
end
|
||||
|
||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
||||
def children
|
||||
[@node.start_obj, @node.end_obj]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+47
-385
@@ -1,12 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'cgi'
|
||||
require 'base64'
|
||||
require 'bigdecimal'
|
||||
|
||||
module Liquid
|
||||
module StandardFilters
|
||||
MAX_INT = (1 << 31) - 1
|
||||
HTML_ESCAPE = {
|
||||
'&' => '&',
|
||||
'>' => '>',
|
||||
@@ -28,47 +26,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,53 +62,6 @@ 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
|
||||
raise Liquid::ArgumentError, "invalid base64 provided to base64_url_safe_decode"
|
||||
end
|
||||
|
||||
def slice(input, offset, length = nil)
|
||||
offset = Utils.to_integer(offset)
|
||||
length = length ? Utils.to_integer(length) : 1
|
||||
@@ -149,34 +73,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,40 +87,13 @@ 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
|
||||
words = Utils.to_integer(words)
|
||||
words = 1 if words <= 0
|
||||
|
||||
wordlist = begin
|
||||
input.split(" ", words + 1)
|
||||
rescue RangeError
|
||||
raise if words + 1 < MAX_INT
|
||||
# e.g. integer #{words} too big to convert to `int'
|
||||
raise Liquid::ArgumentError, "integer #{words} too big for truncatewords"
|
||||
end
|
||||
wordlist = input.split(" ", words + 1)
|
||||
return input if wordlist.length <= words
|
||||
|
||||
wordlist.pop
|
||||
@@ -239,46 +109,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 +128,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)
|
||||
|
||||
@@ -361,23 +185,17 @@ module Liquid
|
||||
|
||||
if ary.empty?
|
||||
[]
|
||||
elsif target_value.nil?
|
||||
ary.select do |item|
|
||||
item[property]
|
||||
elsif ary.first.respond_to?(:[]) && target_value.nil?
|
||||
begin
|
||||
ary.select { |item| item[property] }
|
||||
rescue TypeError
|
||||
raise_property_error(property)
|
||||
rescue NoMethodError
|
||||
return nil unless item.respond_to?(:[])
|
||||
raise
|
||||
end
|
||||
else
|
||||
ary.select do |item|
|
||||
item[property] == target_value
|
||||
elsif ary.first.respond_to?(:[])
|
||||
begin
|
||||
ary.select { |item| item[property] == target_value }
|
||||
rescue TypeError
|
||||
raise_property_error(property)
|
||||
rescue NoMethodError
|
||||
return nil unless item.respond_to?(:[])
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -391,36 +209,22 @@ module Liquid
|
||||
ary.uniq
|
||||
elsif ary.empty? # The next two cases assume a non-empty array.
|
||||
[]
|
||||
else
|
||||
ary.uniq do |item|
|
||||
item[property]
|
||||
elsif ary.first.respond_to?(:[])
|
||||
begin
|
||||
ary.uniq { |a| a[property] }
|
||||
rescue TypeError
|
||||
raise_property_error(property)
|
||||
rescue NoMethodError
|
||||
return nil unless item.respond_to?(:[])
|
||||
raise
|
||||
end
|
||||
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)
|
||||
@@ -445,26 +249,16 @@ module Liquid
|
||||
ary.compact
|
||||
elsif ary.empty? # The next two cases assume a non-empty array.
|
||||
[]
|
||||
else
|
||||
ary.reject do |item|
|
||||
item[property].nil?
|
||||
elsif ary.first.respond_to?(:[])
|
||||
begin
|
||||
ary.reject { |a| a[property].nil? }
|
||||
rescue TypeError
|
||||
raise_property_error(property)
|
||||
rescue NoMethodError
|
||||
return nil unless item.respond_to?(:[])
|
||||
raise
|
||||
end
|
||||
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
|
||||
@@ -474,71 +268,21 @@ module Liquid
|
||||
input.to_s.sub(string.to_s, replacement.to_s)
|
||||
end
|
||||
|
||||
# Replace the last occurrences of a string with another
|
||||
def replace_last(input, string, replacement)
|
||||
input = input.to_s
|
||||
string = string.to_s
|
||||
replacement = replacement.to_s
|
||||
|
||||
start_index = input.rindex(string)
|
||||
|
||||
return input unless start_index
|
||||
|
||||
output = input.dup
|
||||
output[start_index, string.length] = replacement
|
||||
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, '')
|
||||
input.to_s.gsub(string.to_s, '')
|
||||
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
|
||||
|
||||
# remove the last occurences of a substring
|
||||
def remove_last(input, string)
|
||||
replace_last(input, string, '')
|
||||
input.to_s.sub(string.to_s, '')
|
||||
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 +290,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 +339,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 +400,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 +420,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)
|
||||
|
||||
@@ -771,7 +440,7 @@ module Liquid
|
||||
#
|
||||
def default(input, default_value = '', options = {})
|
||||
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? : !input
|
||||
false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input
|
||||
end
|
||||
|
||||
@@ -789,16 +458,10 @@ module Liquid
|
||||
end
|
||||
|
||||
def nil_safe_compare(a, b)
|
||||
result = a <=> b
|
||||
|
||||
if result
|
||||
result
|
||||
elsif a.nil?
|
||||
1
|
||||
elsif b.nil?
|
||||
-1
|
||||
if !a.nil? && !b.nil?
|
||||
a <=> b
|
||||
else
|
||||
raise Liquid::ArgumentError, "cannot sort values of incompatible types"
|
||||
a.nil? ? 1 : -1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -853,9 +516,8 @@ module Liquid
|
||||
|
||||
def each
|
||||
@input.each do |e|
|
||||
e = e.respond_to?(:to_liquid) ? e.to_liquid : e
|
||||
e.context = @context if e.respond_to?(:context=)
|
||||
yield(e)
|
||||
yield(e.respond_to?(:to_liquid) ? e.to_liquid : e)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -31,11 +31,7 @@ module Liquid
|
||||
if @registers.key?(key)
|
||||
@registers.fetch(key)
|
||||
elsif default != UNDEFINED
|
||||
if block_given?
|
||||
@static.fetch(key, &block)
|
||||
else
|
||||
@static.fetch(key, default)
|
||||
end
|
||||
@static.fetch(key, default, &block)
|
||||
else
|
||||
@static.fetch(key, &block)
|
||||
end
|
||||
|
||||
@@ -7,26 +7,25 @@ module Liquid
|
||||
|
||||
def add_global_filter(filter)
|
||||
strainer_class_cache.clear
|
||||
GlobalCache.add_filter(filter)
|
||||
global_filters << filter
|
||||
end
|
||||
|
||||
def create(context, filters = [])
|
||||
strainer_from_cache(filters).new(context)
|
||||
end
|
||||
|
||||
GlobalCache = Class.new(StrainerTemplate)
|
||||
|
||||
private
|
||||
|
||||
def global_filters
|
||||
@global_filters ||= []
|
||||
end
|
||||
|
||||
def strainer_from_cache(filters)
|
||||
if filters.empty?
|
||||
GlobalCache
|
||||
else
|
||||
strainer_class_cache[filters] ||= begin
|
||||
klass = Class.new(GlobalCache)
|
||||
filters.each { |f| klass.add_filter(f) }
|
||||
klass
|
||||
end
|
||||
strainer_class_cache[filters] ||= begin
|
||||
klass = Class.new(StrainerTemplate)
|
||||
global_filters.each { |f| klass.add_filter(f) }
|
||||
filters.each { |f| klass.add_filter(f) }
|
||||
klass
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -31,11 +31,6 @@ module Liquid
|
||||
filter_methods.include?(method.to_s)
|
||||
end
|
||||
|
||||
def inherited(subclass)
|
||||
super
|
||||
subclass.instance_variable_set(:@filter_methods, @filter_methods.dup)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filter_methods
|
||||
|
||||
+1
-29
@@ -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
|
||||
@@ -73,14 +52,7 @@ module Liquid
|
||||
@blocks.each do |block|
|
||||
if block.else?
|
||||
block.attachment.render_to_output_buffer(context, output) if execute_else_block
|
||||
next
|
||||
end
|
||||
|
||||
result = Liquid::Utils.to_liquid_value(
|
||||
block.evaluate(context)
|
||||
)
|
||||
|
||||
if result
|
||||
elsif block.evaluate(context)
|
||||
execute_else_block = false
|
||||
block.attachment.render_to_output_buffer(context, output)
|
||||
end
|
||||
|
||||
@@ -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
-18
@@ -1,19 +1,17 @@
|
||||
# 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
|
||||
|
||||
def initialize(tag_name, markup, parse_context)
|
||||
super
|
||||
@variable = Variable.new(markup, parse_context)
|
||||
@@ -22,12 +20,6 @@ module Liquid
|
||||
def render(context)
|
||||
@variable.render_to_output_buffer(context, +'')
|
||||
end
|
||||
|
||||
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
||||
def children
|
||||
[@node.variable]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('echo', Echo)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+8
-13
@@ -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
|
||||
@@ -51,11 +50,7 @@ module Liquid
|
||||
|
||||
def render_to_output_buffer(context, output)
|
||||
@blocks.each do |block|
|
||||
result = Liquid::Utils.to_liquid_value(
|
||||
block.evaluate(context)
|
||||
)
|
||||
|
||||
if result
|
||||
if block.evaluate(context)
|
||||
return block.attachment.render_to_output_buffer(context, output)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
class InlineComment < Tag
|
||||
def initialize(tag_name, markup, options)
|
||||
super
|
||||
# Semantically, a comment should only ignore everything after it on the line.
|
||||
# Currently, this implementation doesn't support mixing a comment with another tag
|
||||
# but we need to reserve future support for this.
|
||||
if markup.match?(/\n\s*[^#]/)
|
||||
raise SyntaxError, options[:locale].t("errors.syntax.inline_comment_invalid")
|
||||
end
|
||||
end
|
||||
|
||||
def render_to_output_buffer(_context, output)
|
||||
output
|
||||
end
|
||||
|
||||
def blank?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
Template.register_tag('#', InlineComment)
|
||||
end
|
||||
@@ -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
|
||||
|
||||
@@ -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,34 +3,21 @@
|
||||
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 )
|
||||
first_block = @blocks.first
|
||||
result = Liquid::Utils.to_liquid_value(
|
||||
first_block.evaluate(context)
|
||||
)
|
||||
|
||||
unless result
|
||||
unless first_block.evaluate(context)
|
||||
return first_block.attachment.render_to_output_buffer(context, output)
|
||||
end
|
||||
|
||||
# After the first condition unless works just like if
|
||||
@blocks[1..-1].each do |block|
|
||||
result = Liquid::Utils.to_liquid_value(
|
||||
block.evaluate(context)
|
||||
)
|
||||
|
||||
if result
|
||||
if block.evaluate(context)
|
||||
return block.attachment.render_to_output_buffer(context, output)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@ module Liquid
|
||||
attr_reader :line_number, :for_liquid_tag
|
||||
|
||||
def initialize(source, line_numbers = false, line_number: nil, for_liquid_tag: false)
|
||||
@source = source.to_s.to_str
|
||||
@source = source
|
||||
@line_number = line_number || (line_numbers ? 1 : nil)
|
||||
@for_liquid_tag = for_liquid_tag
|
||||
@tokens = tokenize
|
||||
@@ -24,7 +24,7 @@ module Liquid
|
||||
private
|
||||
|
||||
def tokenize
|
||||
return [] if @source.empty?
|
||||
return [] if @source.to_s.empty?
|
||||
|
||||
return @source.split("\n") if @for_liquid_tag
|
||||
|
||||
|
||||
@@ -81,13 +81,5 @@ module Liquid
|
||||
rescue ::ArgumentError
|
||||
nil
|
||||
end
|
||||
|
||||
def self.to_liquid_value(obj)
|
||||
# Enable "obj" to represent itself as a primitive value like integer, string, or boolean
|
||||
return obj.to_liquid_value if obj.respond_to?(:to_liquid_value)
|
||||
|
||||
# Otherwise return the object itself
|
||||
obj
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,9 +40,6 @@ module Liquid
|
||||
@lookups.each_index do |i|
|
||||
key = context.evaluate(@lookups[i])
|
||||
|
||||
# Cast "key" to its liquid value to enable it to act as a primitive value
|
||||
key = Liquid::Utils.to_liquid_value(key)
|
||||
|
||||
# If object is a hash- or array-like object we look for the
|
||||
# presence of the key and if its available we return it
|
||||
if object.respond_to?(:[]) &&
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Liquid
|
||||
VERSION = "5.3.0.alpha"
|
||||
VERSION = "5.0.0"
|
||||
end
|
||||
|
||||
@@ -3,19 +3,11 @@
|
||||
require 'yaml'
|
||||
|
||||
module Database
|
||||
DATABASE_FILE_PATH = "#{__dir__}/vision.database.yml"
|
||||
|
||||
# Load the standard vision toolkit database and re-arrage it to be simply exportable
|
||||
# to liquid as assigns. All this is based on Shopify
|
||||
def self.tables
|
||||
@tables ||= begin
|
||||
db =
|
||||
if YAML.respond_to?(:unsafe_load_file) # Only Psych 4+ can use unsafe_load_file
|
||||
# unsafe_load_file is needed for YAML references
|
||||
YAML.unsafe_load_file(DATABASE_FILE_PATH)
|
||||
else
|
||||
YAML.load_file(DATABASE_FILE_PATH)
|
||||
end
|
||||
db = YAML.load_file("#{__dir__}/vision.database.yml")
|
||||
|
||||
# From vision source
|
||||
db['products'].each do |product|
|
||||
|
||||
@@ -24,7 +24,7 @@ class ContextSensitiveDrop < Liquid::Drop
|
||||
end
|
||||
end
|
||||
|
||||
class Category
|
||||
class Category < Liquid::Drop
|
||||
attr_accessor :name
|
||||
|
||||
def initialize(name)
|
||||
@@ -36,9 +36,8 @@ class Category
|
||||
end
|
||||
end
|
||||
|
||||
class CategoryDrop < Liquid::Drop
|
||||
class CategoryDrop
|
||||
attr_accessor :category, :context
|
||||
|
||||
def initialize(category)
|
||||
@category = category
|
||||
end
|
||||
@@ -406,42 +405,45 @@ class ContextTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_lambda_is_called_once
|
||||
@global = 0
|
||||
|
||||
@context['callcount'] = proc {
|
||||
@global += 1
|
||||
@global ||= 0
|
||||
@global += 1
|
||||
@global.to_s
|
||||
}
|
||||
|
||||
assert_equal('1', @context['callcount'])
|
||||
assert_equal('1', @context['callcount'])
|
||||
assert_equal('1', @context['callcount'])
|
||||
|
||||
@global = nil
|
||||
end
|
||||
|
||||
def test_nested_lambda_is_called_once
|
||||
@global = 0
|
||||
|
||||
@context['callcount'] = { "lambda" => proc {
|
||||
@global += 1
|
||||
@global ||= 0
|
||||
@global += 1
|
||||
@global.to_s
|
||||
} }
|
||||
|
||||
assert_equal('1', @context['callcount.lambda'])
|
||||
assert_equal('1', @context['callcount.lambda'])
|
||||
assert_equal('1', @context['callcount.lambda'])
|
||||
|
||||
@global = nil
|
||||
end
|
||||
|
||||
def test_lambda_in_array_is_called_once
|
||||
@global = 0
|
||||
|
||||
@context['callcount'] = [1, 2, proc {
|
||||
@global += 1
|
||||
@global ||= 0
|
||||
@global += 1
|
||||
@global.to_s
|
||||
}, 4, 5]
|
||||
|
||||
assert_equal('1', @context['callcount[2]'])
|
||||
assert_equal('1', @context['callcount[2]'])
|
||||
assert_equal('1', @context['callcount[2]'])
|
||||
|
||||
@global = nil
|
||||
end
|
||||
|
||||
def test_access_to_context_from_proc
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class FilterKwargTest < Minitest::Test
|
||||
module KwargFilter
|
||||
def html_tag(_tag, attributes)
|
||||
attributes
|
||||
.map { |key, value| "#{key}='#{value}'" }
|
||||
.join(' ')
|
||||
end
|
||||
end
|
||||
|
||||
include Liquid
|
||||
|
||||
def test_can_parse_data_kwargs
|
||||
with_global_filter(KwargFilter) do
|
||||
assert_equal(
|
||||
"data-src='src' data-widths='100, 200'",
|
||||
Template.parse("{{ 'img' | html_tag: data-src: 'src', data-widths: '100, 200' }}").render(nil, nil)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,27 +3,6 @@
|
||||
require 'test_helper'
|
||||
|
||||
class ProfilerTest < Minitest::Test
|
||||
class TestDrop < Liquid::Drop
|
||||
def initialize(value)
|
||||
super()
|
||||
@value = value
|
||||
end
|
||||
|
||||
def to_s
|
||||
artificial_execution_time
|
||||
|
||||
@value
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Monotonic clock precision fluctuate based on the operating system
|
||||
# By introducing a small sleep we ensure ourselves to register a non zero unit of time
|
||||
def artificial_execution_time
|
||||
sleep(Process.clock_getres(Process::CLOCK_MONOTONIC))
|
||||
end
|
||||
end
|
||||
|
||||
include Liquid
|
||||
|
||||
class ProfilingFileSystem
|
||||
@@ -219,22 +198,16 @@ class ProfilerTest < Minitest::Test
|
||||
|
||||
def test_profiling_supports_self_time
|
||||
t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true)
|
||||
collection = [
|
||||
TestDrop.new("one"),
|
||||
TestDrop.new("two"),
|
||||
]
|
||||
output = t.render!("collection" => collection)
|
||||
assert_equal(" one two ", output)
|
||||
|
||||
t.render!("collection" => ["one", "two"])
|
||||
leaf = t.profiler[0].children[0]
|
||||
assert_operator(leaf.self_time, :>, 0.0)
|
||||
|
||||
assert_operator(leaf.self_time, :>, 0)
|
||||
end
|
||||
|
||||
def test_profiling_supports_total_time
|
||||
t = Template.parse("{% if true %} {{ test }} {% endif %}", profile: true)
|
||||
output = t.render!("test" => TestDrop.new("one"))
|
||||
assert_equal(" one ", output)
|
||||
t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", profile: true)
|
||||
t.render!
|
||||
|
||||
assert_operator(t.profiler[0].total_time, :>, 0.0)
|
||||
assert_operator(t.profiler[0].total_time, :>, 0)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class Filters
|
||||
include Liquid::StandardFilters
|
||||
end
|
||||
|
||||
class TestThing
|
||||
attr_reader :foo
|
||||
|
||||
@@ -25,24 +29,8 @@ class TestThing
|
||||
end
|
||||
|
||||
class TestDrop < Liquid::Drop
|
||||
def initialize(value:)
|
||||
@value = value
|
||||
end
|
||||
|
||||
attr_reader :value
|
||||
|
||||
def registers
|
||||
@context.registers
|
||||
end
|
||||
end
|
||||
|
||||
class TestModel
|
||||
def initialize(value:)
|
||||
@value = value
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
TestDrop.new(value: @value)
|
||||
def test
|
||||
"testfoo"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -65,13 +53,10 @@ class NumberLikeThing < Liquid::Drop
|
||||
end
|
||||
|
||||
class StandardFiltersTest < Minitest::Test
|
||||
Filters = Class.new(Liquid::StrainerTemplate)
|
||||
Filters.add_filter(Liquid::StandardFilters)
|
||||
|
||||
include Liquid
|
||||
|
||||
def setup
|
||||
@filters = Filters.new(Context.new)
|
||||
@filters = Filters.new
|
||||
end
|
||||
|
||||
def test_size
|
||||
@@ -160,40 +145,6 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal('<strong>Hulk</strong>', @filters.escape_once('<strong>Hulk</strong>'))
|
||||
end
|
||||
|
||||
def test_base64_encode
|
||||
assert_equal('b25lIHR3byB0aHJlZQ==', @filters.base64_encode('one two three'))
|
||||
assert_equal('', @filters.base64_encode(nil))
|
||||
end
|
||||
|
||||
def test_base64_decode
|
||||
assert_equal('one two three', @filters.base64_decode('b25lIHR3byB0aHJlZQ=='))
|
||||
|
||||
exception = assert_raises(Liquid::ArgumentError) do
|
||||
@filters.base64_decode("invalidbase64")
|
||||
end
|
||||
|
||||
assert_equal('Liquid error: invalid base64 provided to base64_decode', exception.message)
|
||||
end
|
||||
|
||||
def test_base64_url_safe_encode
|
||||
assert_equal(
|
||||
'YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMTIzNDU2Nzg5MCAhQCMkJV4mKigpLT1fKy8_Ljo7W117fVx8',
|
||||
@filters.base64_url_safe_encode('abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 !@#$%^&*()-=_+/?.:;[]{}\|')
|
||||
)
|
||||
assert_equal('', @filters.base64_url_safe_encode(nil))
|
||||
end
|
||||
|
||||
def test_base64_url_safe_decode
|
||||
assert_equal(
|
||||
'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 !@#$%^&*()-=_+/?.:;[]{}\|',
|
||||
@filters.base64_url_safe_decode('YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXogQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMTIzNDU2Nzg5MCAhQCMkJV4mKigpLT1fKy8_Ljo7W117fVx8')
|
||||
)
|
||||
exception = assert_raises(Liquid::ArgumentError) do
|
||||
@filters.base64_url_safe_decode("invalidbase64")
|
||||
end
|
||||
assert_equal('Liquid error: invalid base64 provided to base64_url_safe_decode', exception.message)
|
||||
end
|
||||
|
||||
def test_url_encode
|
||||
assert_equal('foo%2B1%40example.com', @filters.url_encode('[email protected]'))
|
||||
assert_equal('1', @filters.url_encode(1))
|
||||
@@ -220,17 +171,13 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal('one two three', @filters.truncatewords('one two three'))
|
||||
assert_equal(
|
||||
'Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13”...',
|
||||
@filters.truncatewords('Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13” x 16” x 10.5” high) with cover.', 15)
|
||||
@filters.truncatewords('Two small (13” x 5.5” x 10” high) baskets fit inside one large basket (13” x 16” x 10.5” high) with cover.', 15)
|
||||
)
|
||||
assert_equal("测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5))
|
||||
assert_equal('one two1', @filters.truncatewords("one two three", 2, 1))
|
||||
assert_equal('one two three...', @filters.truncatewords("one two\tthree\nfour", 3))
|
||||
assert_equal('one two...', @filters.truncatewords("one two three four", 2))
|
||||
assert_equal('one...', @filters.truncatewords("one two three four", 0))
|
||||
exception = assert_raises(Liquid::ArgumentError) do
|
||||
@filters.truncatewords("one two three four", 1 << 31)
|
||||
end
|
||||
assert_equal("Liquid error: integer #{1 << 31} too big for truncatewords", exception.message)
|
||||
end
|
||||
|
||||
def test_strip_html
|
||||
@@ -274,8 +221,8 @@ class StandardFiltersTest < Minitest::Test
|
||||
{ "price" => 1, "handle" => "gamma" },
|
||||
{ "price" => 2, "handle" => "epsilon" },
|
||||
{ "price" => 4, "handle" => "alpha" },
|
||||
{ "handle" => "beta" },
|
||||
{ "handle" => "delta" },
|
||||
{ "handle" => "beta" },
|
||||
]
|
||||
assert_equal(expectation, @filters.sort(input, "price"))
|
||||
end
|
||||
@@ -378,9 +325,8 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal(["foo"], @filters.uniq("foo"))
|
||||
assert_equal([1, 3, 2, 4], @filters.uniq([1, 1, 3, 2, 3, 1, 4, 3, 2, 1]))
|
||||
assert_equal([{ "a" => 1 }, { "a" => 3 }, { "a" => 2 }], @filters.uniq([{ "a" => 1 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a"))
|
||||
test_drop = TestDrop.new(value: "test")
|
||||
test_drop_alternate = TestDrop.new(value: "test")
|
||||
assert_equal([test_drop], @filters.uniq([test_drop, test_drop_alternate], 'value'))
|
||||
testdrop = TestDrop.new
|
||||
assert_equal([testdrop], @filters.uniq([testdrop, TestDrop.new], 'test'))
|
||||
end
|
||||
|
||||
def test_uniq_empty_array
|
||||
@@ -439,16 +385,6 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_template_result("woot: 1", '{{ foo | map: "whatever" }}', "foo" => [t])
|
||||
end
|
||||
|
||||
def test_map_calls_context=
|
||||
model = TestModel.new(value: "test")
|
||||
|
||||
template = Template.parse('{{ foo | map: "registers" }}')
|
||||
template.registers[:test] = 1234
|
||||
template.assigns['foo'] = [model]
|
||||
|
||||
assert_template_result("{:test=>1234}", template.render!)
|
||||
end
|
||||
|
||||
def test_map_on_hashes
|
||||
assert_template_result("4217", '{{ thing | map: "foo" | map: "bar" }}',
|
||||
"thing" => { "foo" => [{ "bar" => 42 }, { "bar" => 17 }] })
|
||||
@@ -467,9 +403,9 @@ class StandardFiltersTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_map_over_proc
|
||||
drop = TestDrop.new(value: "testfoo")
|
||||
drop = TestDrop.new
|
||||
p = proc { drop }
|
||||
templ = '{{ procs | map: "value" }}'
|
||||
templ = '{{ procs | map: "test" }}'
|
||||
assert_template_result("testfoo", templ, "procs" => [p])
|
||||
end
|
||||
|
||||
@@ -565,31 +501,19 @@ class StandardFiltersTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_replace
|
||||
assert_equal('b b b b', @filters.replace('a a a a', 'a', 'b'))
|
||||
assert_equal('2 2 2 2', @filters.replace('1 1 1 1', '1', 2))
|
||||
assert_equal('2 2 2 2', @filters.replace('1 1 1 1', 1, 2))
|
||||
assert_equal('1 1 1 1', @filters.replace('1 1 1 1', 2, 3))
|
||||
assert_template_result('2 2 2 2', "{{ '1 1 1 1' | replace: '1', 2 }}")
|
||||
|
||||
assert_equal('b a a a', @filters.replace_first('a a a a', 'a', 'b'))
|
||||
assert_equal('2 1 1 1', @filters.replace_first('1 1 1 1', '1', 2))
|
||||
assert_equal('2 1 1 1', @filters.replace_first('1 1 1 1', 1, 2))
|
||||
assert_equal('1 1 1 1', @filters.replace_first('1 1 1 1', 2, 3))
|
||||
assert_template_result('2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}")
|
||||
|
||||
assert_equal('a a a b', @filters.replace_last('a a a a', 'a', 'b'))
|
||||
assert_equal('1 1 1 2', @filters.replace_last('1 1 1 1', 1, 2))
|
||||
assert_equal('1 1 1 1', @filters.replace_last('1 1 1 1', 2, 3))
|
||||
assert_template_result('1 1 1 2', "{{ '1 1 1 1' | replace_last: '1', 2 }}")
|
||||
end
|
||||
|
||||
def test_remove
|
||||
assert_equal(' ', @filters.remove("a a a a", 'a'))
|
||||
assert_template_result(' ', "{{ '1 1 1 1' | remove: 1 }}")
|
||||
|
||||
assert_equal('b a a', @filters.remove_first("a b a a", 'a '))
|
||||
assert_template_result(' 1 1 1', "{{ '1 1 1 1' | remove_first: 1 }}")
|
||||
|
||||
assert_equal('a a b', @filters.remove_last("a a b a", ' a'))
|
||||
assert_template_result('1 1 1 ', "{{ '1 1 1 1' | remove_last: 1 }}")
|
||||
assert_equal(' ', @filters.remove("1 1 1 1", 1))
|
||||
assert_equal('a a a', @filters.remove_first("a a a a", 'a '))
|
||||
assert_equal(' 1 1 1', @filters.remove_first("1 1 1 1", 1))
|
||||
assert_template_result('a a a', "{{ 'a a a a' | remove_first: 'a ' }}")
|
||||
end
|
||||
|
||||
def test_pipes_in_string_arguments
|
||||
@@ -766,8 +690,6 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal("bar", @filters.default([], "bar"))
|
||||
assert_equal("bar", @filters.default({}, "bar"))
|
||||
assert_template_result('bar', "{{ false | default: 'bar' }}")
|
||||
assert_template_result('bar', "{{ drop | default: 'bar' }}", 'drop' => BooleanDrop.new(false))
|
||||
assert_template_result('Yay', "{{ drop | default: 'bar' }}", 'drop' => BooleanDrop.new(true))
|
||||
end
|
||||
|
||||
def test_default_handle_false
|
||||
@@ -778,8 +700,6 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal("bar", @filters.default([], "bar", "allow_false" => true))
|
||||
assert_equal("bar", @filters.default({}, "bar", "allow_false" => true))
|
||||
assert_template_result('false', "{{ false | default: 'bar', allow_false: true }}")
|
||||
assert_template_result('Nay', "{{ drop | default: 'bar', allow_false: true }}", 'drop' => BooleanDrop.new(false))
|
||||
assert_template_result('Yay', "{{ drop | default: 'bar', allow_false: true }}", 'drop' => BooleanDrop.new(true))
|
||||
end
|
||||
|
||||
def test_cannot_access_private_methods
|
||||
@@ -808,18 +728,6 @@ class StandardFiltersTest < Minitest::Test
|
||||
assert_equal(expectation, @filters.where(input, "ok"))
|
||||
end
|
||||
|
||||
def test_where_string_keys
|
||||
input = [
|
||||
"alpha", "beta", "gamma", "delta"
|
||||
]
|
||||
|
||||
expectation = [
|
||||
"beta",
|
||||
]
|
||||
|
||||
assert_equal(expectation, @filters.where(input, "be"))
|
||||
end
|
||||
|
||||
def test_where_no_key_set
|
||||
input = [
|
||||
{ "handle" => "alpha", "ok" => true },
|
||||
@@ -865,7 +773,7 @@ class StandardFiltersTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_all_filters_never_raise_non_liquid_exception
|
||||
test_drop = TestDrop.new(value: "test")
|
||||
test_drop = TestDrop.new
|
||||
test_drop.context = Context.new
|
||||
test_enum = TestEnumerable.new
|
||||
test_enum.context = Context.new
|
||||
@@ -890,14 +798,19 @@ class StandardFiltersTest < Minitest::Test
|
||||
{ 1 => "bar" },
|
||||
["foo", 123, nil, true, false, Drop, ["foo"], { foo: "bar" }],
|
||||
]
|
||||
StandardFilters.public_instance_methods(false).each do |method|
|
||||
arg_count = @filters.method(method).arity
|
||||
arg_count *= -1 if arg_count < 0
|
||||
|
||||
test_types.repeated_permutation(arg_count) do |args|
|
||||
@filters.send(method, *args)
|
||||
rescue Liquid::Error
|
||||
nil
|
||||
test_types.each do |first|
|
||||
test_types.each do |other|
|
||||
(@filters.methods - Object.methods).each do |method|
|
||||
arg_count = @filters.method(method).arity
|
||||
arg_count *= -1 if arg_count < 0
|
||||
inputs = [first]
|
||||
inputs << ([other] * (arg_count - 1)) if arg_count > 1
|
||||
begin
|
||||
@filters.send(method, *inputs)
|
||||
rescue Liquid::ArgumentError, Liquid::ZeroDivisionError
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -96,12 +96,12 @@ class IncludeTagTest < Minitest::Test
|
||||
|
||||
def test_include_tag_with_alias
|
||||
assert_template_result("Product: Draft 151cm ",
|
||||
"{% include 'product_alias' with products[0] as product %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
|
||||
"{% include 'product_alias' with products[0] as product %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
|
||||
end
|
||||
|
||||
def test_include_tag_for_alias
|
||||
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||
"{% include 'product_alias' for products as product %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
|
||||
"{% include 'product_alias' for products as product %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
|
||||
end
|
||||
|
||||
def test_include_tag_with_default_name
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
|
||||
class InlineCommentTest < Minitest::Test
|
||||
include Liquid
|
||||
|
||||
def test_tag_in_different_styles
|
||||
assert_template_result('', '{% # This text gets ignored %}')
|
||||
assert_template_result('', '{%# This text gets ignored #%}')
|
||||
assert_template_result('', '{%# This text gets ignored %}')
|
||||
assert_template_result('', '{%#- This text gets ignored -#%}')
|
||||
end
|
||||
|
||||
def test_test_syntax_error
|
||||
assert_template_result('fail', '{% #This doesnt work %}')
|
||||
|
||||
assert false
|
||||
rescue
|
||||
# ok good
|
||||
end
|
||||
|
||||
def test_tag_ws_stripping
|
||||
assert_template_result('', ' {%#- This text gets ignored -#%} ')
|
||||
end
|
||||
|
||||
def test_comment_inline_tag
|
||||
assert_template_result('ok', '{% echo "ok" # output something from a tag %}')
|
||||
end
|
||||
|
||||
def test_comment_line_before_tag
|
||||
assert_template_result('ok', '{% # this sort of comment also
|
||||
echo "ok" %}')
|
||||
end
|
||||
|
||||
def test_comment_inline_variable
|
||||
assert_template_result('ok', '{{ "ok" # output something from a variable }}')
|
||||
assert_template_result('ok', '{{ "OK" | downcase # output something from a variable }}')
|
||||
end
|
||||
|
||||
def test_inside_liquid_tag
|
||||
source = <<~LIQUID
|
||||
{%- liquid
|
||||
echo "before("
|
||||
# This text gets ignored
|
||||
echo ")after"
|
||||
-%}
|
||||
LIQUID
|
||||
assert_template_result('before()after', source)
|
||||
end
|
||||
|
||||
def test_multiline
|
||||
assert_template_result('', '{% # this sort of comment also
|
||||
# will just work, because it parses
|
||||
# as a single call to the "#" tag %}')
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -151,7 +151,7 @@ class RenderTagTest < Minitest::Test
|
||||
)
|
||||
|
||||
assert_template_result("Product: Draft 151cm ",
|
||||
"{% render 'product' with products[0] %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
|
||||
"{% render 'product' with products[0] %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
|
||||
end
|
||||
|
||||
def test_render_tag_with_alias
|
||||
@@ -161,7 +161,7 @@ class RenderTagTest < Minitest::Test
|
||||
)
|
||||
|
||||
assert_template_result("Product: Draft 151cm ",
|
||||
"{% render 'product_alias' with products[0] as product %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
|
||||
"{% render 'product_alias' with products[0] as product %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
|
||||
end
|
||||
|
||||
def test_render_tag_for_alias
|
||||
@@ -171,7 +171,7 @@ class RenderTagTest < Minitest::Test
|
||||
)
|
||||
|
||||
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||
"{% render 'product_alias' for products as product %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
|
||||
"{% render 'product_alias' for products as product %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
|
||||
end
|
||||
|
||||
def test_render_tag_for
|
||||
@@ -181,7 +181,7 @@ class RenderTagTest < Minitest::Test
|
||||
)
|
||||
|
||||
assert_template_result("Product: Draft 151cm Product: Element 155cm ",
|
||||
"{% render 'product' for products %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
|
||||
"{% render 'product' for products %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
|
||||
end
|
||||
|
||||
def test_render_tag_forloop
|
||||
@@ -190,7 +190,7 @@ class RenderTagTest < Minitest::Test
|
||||
)
|
||||
|
||||
assert_template_result("Product: Draft 151cm first index:1 Product: Element 155cm last index:2 ",
|
||||
"{% render 'product' for products %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
|
||||
"{% render 'product' for products %}", "products" => [{ 'title' => 'Draft 151cm' }, { 'title' => 'Element 155cm' }])
|
||||
end
|
||||
|
||||
def test_render_tag_for_drop
|
||||
@@ -199,7 +199,7 @@ class RenderTagTest < Minitest::Test
|
||||
)
|
||||
|
||||
assert_template_result("123",
|
||||
"{% render 'loop' for loop as value %}", "loop" => TestEnumerable.new)
|
||||
"{% render 'loop' for loop as value %}", "loop" => TestEnumerable.new)
|
||||
end
|
||||
|
||||
def test_render_tag_with_drop
|
||||
@@ -208,6 +208,6 @@ class RenderTagTest < Minitest::Test
|
||||
)
|
||||
|
||||
assert_template_result("TestEnumerable",
|
||||
"{% render 'loop' with loop as value %}", "loop" => TestEnumerable.new)
|
||||
"{% render 'loop' with loop as value %}", "loop" => TestEnumerable.new)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -323,18 +323,4 @@ class TemplateTest < Minitest::Test
|
||||
result = t.render('x' => 1, 'y' => 5)
|
||||
assert_equal('12345', result)
|
||||
end
|
||||
|
||||
def test_source_string_subclass
|
||||
string_subclass = Class.new(String) do
|
||||
# E.g. ActiveSupport::SafeBuffer does this, so don't just rely on to_s to return a String
|
||||
def to_s
|
||||
self
|
||||
end
|
||||
end
|
||||
source = string_subclass.new("{% assign x = 2 -%} x= {{- x }}")
|
||||
assert_instance_of(string_subclass, source)
|
||||
output = Template.parse(source).render!
|
||||
assert_equal("x=2", output)
|
||||
assert_instance_of(String, output)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,33 +15,6 @@ class VariableTest < Minitest::Test
|
||||
assert_template_result('foobar', '{{ foo }}', 'foo' => ThingWithToLiquid.new)
|
||||
end
|
||||
|
||||
def test_variable_lookup_calls_to_liquid_value
|
||||
assert_template_result('1', '{{ foo }}', 'foo' => IntegerDrop.new('1'))
|
||||
assert_template_result('2', '{{ list[foo] }}', 'foo' => IntegerDrop.new('1'), 'list' => [1, 2, 3])
|
||||
assert_template_result('one', '{{ list[foo] }}', 'foo' => IntegerDrop.new('1'), 'list' => { 1 => 'one' })
|
||||
assert_template_result('Yay', '{{ foo }}', 'foo' => BooleanDrop.new(true))
|
||||
assert_template_result('YAY', '{{ foo | upcase }}', 'foo' => BooleanDrop.new(true))
|
||||
end
|
||||
|
||||
def test_if_tag_calls_to_liquid_value
|
||||
assert_template_result('one', '{% if foo == 1 %}one{% endif %}', 'foo' => IntegerDrop.new('1'))
|
||||
assert_template_result('one', '{% if 0 < foo %}one{% endif %}', 'foo' => IntegerDrop.new('1'))
|
||||
assert_template_result('one', '{% if foo > 0 %}one{% endif %}', 'foo' => IntegerDrop.new('1'))
|
||||
assert_template_result('true', '{% if foo == true %}true{% endif %}', 'foo' => BooleanDrop.new(true))
|
||||
assert_template_result('true', '{% if foo %}true{% endif %}', 'foo' => BooleanDrop.new(true))
|
||||
|
||||
assert_template_result('', '{% if foo %}true{% endif %}', 'foo' => BooleanDrop.new(false))
|
||||
assert_template_result('', '{% if foo == true %}True{% endif %}', 'foo' => BooleanDrop.new(false))
|
||||
end
|
||||
|
||||
def test_unless_tag_calls_to_liquid_value
|
||||
assert_template_result('', '{% unless foo %}true{% endunless %}', 'foo' => BooleanDrop.new(true))
|
||||
end
|
||||
|
||||
def test_case_tag_calls_to_liquid_value
|
||||
assert_template_result('One', '{% case foo %}{% when 1 %}One{% endcase %}', 'foo' => IntegerDrop.new('1'))
|
||||
end
|
||||
|
||||
def test_simple_with_whitespaces
|
||||
template = Template.parse(%( {{ test }} ))
|
||||
assert_equal(' worked ', template.render!('test' => 'worked'))
|
||||
@@ -131,8 +104,4 @@ class VariableTest < Minitest::Test
|
||||
def test_dynamic_find_var
|
||||
assert_template_result('bar', '{{ [key] }}', 'key' => 'foo', 'foo' => 'bar')
|
||||
end
|
||||
|
||||
def test_raw_value_variable
|
||||
assert_template_result('bar', '{{ [key] }}', 'key' => 'foo', 'foo' => 'bar')
|
||||
end
|
||||
end
|
||||
|
||||
+10
-48
@@ -72,21 +72,21 @@ module Minitest
|
||||
end
|
||||
|
||||
def with_global_filter(*globals)
|
||||
original_global_cache = Liquid::StrainerFactory::GlobalCache
|
||||
Liquid::StrainerFactory.send(:remove_const, :GlobalCache)
|
||||
Liquid::StrainerFactory.const_set(:GlobalCache, Class.new(Liquid::StrainerTemplate))
|
||||
original_global_filters = Liquid::StrainerFactory.instance_variable_get(:@global_filters)
|
||||
Liquid::StrainerFactory.instance_variable_set(:@global_filters, [])
|
||||
globals.each do |global|
|
||||
Liquid::StrainerFactory.add_global_filter(global)
|
||||
end
|
||||
|
||||
Liquid::StrainerFactory.send(:strainer_class_cache).clear
|
||||
|
||||
globals.each do |global|
|
||||
Liquid::Template.register_filter(global)
|
||||
end
|
||||
yield
|
||||
ensure
|
||||
Liquid::StrainerFactory.send(:strainer_class_cache).clear
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
Liquid::StrainerFactory.send(:remove_const, :GlobalCache)
|
||||
Liquid::StrainerFactory.const_set(:GlobalCache, original_global_cache)
|
||||
Liquid::StrainerFactory.send(:strainer_class_cache).clear
|
||||
end
|
||||
Liquid::StrainerFactory.instance_variable_set(:@global_filters, original_global_filters)
|
||||
end
|
||||
|
||||
def with_error_mode(mode)
|
||||
@@ -119,44 +119,6 @@ class ThingWithToLiquid
|
||||
end
|
||||
end
|
||||
|
||||
class IntegerDrop < Liquid::Drop
|
||||
def initialize(value)
|
||||
super()
|
||||
@value = value.to_i
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
@value == other
|
||||
end
|
||||
|
||||
def to_s
|
||||
@value.to_s
|
||||
end
|
||||
|
||||
def to_liquid_value
|
||||
@value
|
||||
end
|
||||
end
|
||||
|
||||
class BooleanDrop < Liquid::Drop
|
||||
def initialize(value)
|
||||
super()
|
||||
@value = value
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
@value == other
|
||||
end
|
||||
|
||||
def to_liquid_value
|
||||
@value
|
||||
end
|
||||
|
||||
def to_s
|
||||
@value ? "Yay" : "Nay"
|
||||
end
|
||||
end
|
||||
|
||||
class ErrorDrop < Liquid::Drop
|
||||
def standard_error
|
||||
raise Liquid::StandardError, 'standard error'
|
||||
|
||||
@@ -10,8 +10,8 @@ class ConditionUnitTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_basic_condition
|
||||
assert_equal(false, Condition.new(1, '==', 2).evaluate(Context.new))
|
||||
assert_equal(true, Condition.new(1, '==', 1).evaluate(Context.new))
|
||||
assert_equal(false, Condition.new(1, '==', 2).evaluate)
|
||||
assert_equal(true, Condition.new(1, '==', 1).evaluate)
|
||||
end
|
||||
|
||||
def test_default_operators_evalute_true
|
||||
@@ -67,11 +67,11 @@ class ConditionUnitTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_hash_compare_backwards_compatibility
|
||||
assert_nil(Condition.new({}, '>', 2).evaluate(Context.new))
|
||||
assert_nil(Condition.new(2, '>', {}).evaluate(Context.new))
|
||||
assert_equal(false, Condition.new({}, '==', 2).evaluate(Context.new))
|
||||
assert_equal(true, Condition.new({ 'a' => 1 }, '==', 'a' => 1).evaluate(Context.new))
|
||||
assert_equal(true, Condition.new({ 'a' => 2 }, 'contains', 'a').evaluate(Context.new))
|
||||
assert_nil(Condition.new({}, '>', 2).evaluate)
|
||||
assert_nil(Condition.new(2, '>', {}).evaluate)
|
||||
assert_equal(false, Condition.new({}, '==', 2).evaluate)
|
||||
assert_equal(true, Condition.new({ 'a' => 1 }, '==', 'a' => 1).evaluate)
|
||||
assert_equal(true, Condition.new({ 'a' => 2 }, 'contains', 'a').evaluate)
|
||||
end
|
||||
|
||||
def test_contains_works_on_arrays
|
||||
@@ -106,29 +106,30 @@ class ConditionUnitTest < Minitest::Test
|
||||
|
||||
def test_or_condition
|
||||
condition = Condition.new(1, '==', 2)
|
||||
assert_equal(false, condition.evaluate(Context.new))
|
||||
|
||||
assert_equal(false, condition.evaluate)
|
||||
|
||||
condition.or(Condition.new(2, '==', 1))
|
||||
|
||||
assert_equal(false, condition.evaluate(Context.new))
|
||||
assert_equal(false, condition.evaluate)
|
||||
|
||||
condition.or(Condition.new(1, '==', 1))
|
||||
|
||||
assert_equal(true, condition.evaluate(Context.new))
|
||||
assert_equal(true, condition.evaluate)
|
||||
end
|
||||
|
||||
def test_and_condition
|
||||
condition = Condition.new(1, '==', 1)
|
||||
|
||||
assert_equal(true, condition.evaluate(Context.new))
|
||||
assert_equal(true, condition.evaluate)
|
||||
|
||||
condition.and(Condition.new(2, '==', 2))
|
||||
|
||||
assert_equal(true, condition.evaluate(Context.new))
|
||||
assert_equal(true, condition.evaluate)
|
||||
|
||||
condition.and(Condition.new(2, '==', 1))
|
||||
|
||||
assert_equal(false, condition.evaluate(Context.new))
|
||||
assert_equal(false, condition.evaluate)
|
||||
end
|
||||
|
||||
def test_should_allow_custom_proc_operator
|
||||
@@ -147,20 +148,6 @@ class ConditionUnitTest < Minitest::Test
|
||||
assert_evaluates_true(VariableLookup.new("one"), '==', VariableLookup.new("another"))
|
||||
end
|
||||
|
||||
def test_default_context_is_deprecated
|
||||
if Gem::Version.new(Liquid::VERSION) >= Gem::Version.new('6.0.0')
|
||||
flunk("Condition#evaluate without a context argument is to be removed")
|
||||
end
|
||||
|
||||
_out, err = capture_io do
|
||||
assert_equal(true, Condition.new(1, '==', 1).evaluate)
|
||||
end
|
||||
|
||||
expected = "DEPRECATION WARNING: Condition#evaluate without a context argument is deprecated" \
|
||||
" and will be removed from Liquid 6.0.0."
|
||||
assert_includes(err.lines.map(&:strip), expected)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assert_evaluates_true(left, op, right)
|
||||
|
||||
@@ -26,13 +26,6 @@ class ParseTreeVisitorTest < Minitest::Test
|
||||
)
|
||||
end
|
||||
|
||||
def test_echo
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% echo test %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_if_condition
|
||||
assert_equal(
|
||||
["test"],
|
||||
@@ -159,13 +152,6 @@ class ParseTreeVisitorTest < Minitest::Test
|
||||
)
|
||||
end
|
||||
|
||||
def test_for_range
|
||||
assert_equal(
|
||||
["test"],
|
||||
visit(%({% for x in (1..test) %}{% endfor %}))
|
||||
)
|
||||
end
|
||||
|
||||
def test_tablerow_in
|
||||
assert_equal(
|
||||
["test"],
|
||||
|
||||
@@ -52,8 +52,7 @@ class StrainerFactoryUnitTest < Minitest::Test
|
||||
/\ALiquid error: wrong number of arguments \((1 for 0|given 1, expected 0)\)\z/,
|
||||
exception.message
|
||||
)
|
||||
source = AccessScopeFilters.instance_method(:public_filter).source_location
|
||||
assert_equal(source.map(&:to_s), exception.backtrace[0].split(':')[0..1])
|
||||
assert_equal(exception.backtrace[0].split(':')[0], __FILE__)
|
||||
end
|
||||
|
||||
def test_strainer_only_invokes_public_filter_methods
|
||||
|
||||
@@ -57,8 +57,8 @@ class StrainerTemplateUnitTest < Minitest::Test
|
||||
end
|
||||
|
||||
def test_add_filter_does_not_raise_when_module_overrides_previously_registered_method
|
||||
strainer = Context.new.strainer
|
||||
with_global_filter do
|
||||
strainer = Context.new.strainer
|
||||
strainer.class.add_filter(PublicMethodOverrideFilter)
|
||||
assert(strainer.class.send(:filter_methods).include?('public_filter'))
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user