mirror of
https://github.com/Shopify/liquid.git
synced 2026-10-03 00:55:11 -07:00
Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
965ea3cfb1 | ||
|
|
7357dcf185 | ||
|
|
68c3827ef2 | ||
|
|
4af38bc549 | ||
|
|
df241abf70 | ||
|
|
10f8337209 | ||
|
|
5ed0410a8b | ||
|
|
0f5220c391 | ||
|
|
7a23f46fab | ||
|
|
3f7edf00b9 | ||
|
|
b4a2a79e26 | ||
|
|
1d2bee1f60 | ||
|
|
01e6eec97a | ||
|
|
fbdab19358 | ||
|
|
ce85ac5d3d | ||
|
|
c0ffee16a3 | ||
|
|
a7eb33fa39 | ||
|
|
1a85e98793 | ||
|
|
c588337aac | ||
|
|
97f7922457 | ||
|
|
0d83e64cfe | ||
|
|
0d5e01ae98 | ||
|
|
15eaa49e48 | ||
|
|
91c54c579d | ||
|
|
1310c4978d | ||
|
|
3de1db3c3a | ||
|
|
03522caaf8 | ||
|
|
7acea2a9c9 | ||
|
|
d8ef698539 | ||
|
|
ebdfdb80e5 | ||
|
|
95e9fa5010 | ||
|
|
db3999a008 | ||
|
|
10e2aa8d5b | ||
|
|
7c4114671b | ||
|
|
b01de9d325 | ||
|
|
a5369c26a8 | ||
|
|
a03de8f9ea | ||
|
|
e86fe27259 | ||
|
|
c8906d05b9 | ||
|
|
50d1a2ffc9 | ||
|
|
aa8ce87b96 | ||
|
|
698f5e0d96 | ||
|
|
996bfe0c82 | ||
|
|
be81c9ae5a | ||
|
|
edd4d70aee | ||
|
|
ac66dbbafe |
@@ -7,7 +7,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
entry:
|
entry:
|
||||||
- { ruby: 2.5, allowed-failure: false } # minimum supported
|
- { ruby: 2.5, allowed-failure: false } # minimum supported
|
||||||
- { ruby: 3.0, allowed-failure: false } # latest
|
- { ruby: 3.1, allowed-failure: false } # latest
|
||||||
- { ruby: ruby-head, allowed-failure: true }
|
- { ruby: ruby-head, allowed-failure: true }
|
||||||
name: test (${{ matrix.entry.ruby }})
|
name: test (${{ matrix.entry.ruby }})
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
+22
-1
@@ -1,12 +1,33 @@
|
|||||||
# Liquid Change Log
|
# Liquid Change Log
|
||||||
|
|
||||||
## 5.0.2 (unreleased)
|
## 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
|
||||||
|
|
||||||
|
### 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
|
### Features
|
||||||
* Add `base64_encode`, `base64_decode`, `base64_url_safe_encode`, and `base64_url_safe_decode` filters (#1450) [Daniel Insley]
|
* 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
|
### Fixes
|
||||||
* Fix support for using a String subclass for the liquid source (#1421) [Dylan Thacker-Smith]
|
* 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
|
## 5.0.1 / 2021-03-24
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
* [Contributing guidelines](CONTRIBUTING.md)
|
* [Contributing guidelines](CONTRIBUTING.md)
|
||||||
* [Version history](History.md)
|
* [Version history](History.md)
|
||||||
* [Liquid documentation from Shopify](http://docs.shopify.com/themes/liquid-basics)
|
* [Liquid documentation from Shopify](https://shopify.dev/api/liquid)
|
||||||
* [Liquid Wiki at GitHub](https://github.com/Shopify/liquid/wiki)
|
* [Liquid Wiki at GitHub](https://github.com/Shopify/liquid/wiki)
|
||||||
* [Website](http://liquidmarkup.org/)
|
* [Website](http://liquidmarkup.org/)
|
||||||
|
|
||||||
@@ -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.
|
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
|
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
|
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:
|
when templates are invalid. You can enable this new parser like this:
|
||||||
|
|||||||
+2
-2
@@ -36,7 +36,7 @@ module Liquid
|
|||||||
VariableIncompleteEnd = /\}\}?/
|
VariableIncompleteEnd = /\}\}?/
|
||||||
QuotedString = /"[^"]*"|'[^']*'/
|
QuotedString = /"[^"]*"|'[^']*'/
|
||||||
QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/o
|
QuotedFragment = /#{QuotedString}|(?:[^\s,\|'"]|#{QuotedString})+/o
|
||||||
TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/o
|
TagAttributes = /(\w[\w-]*)\s*\:\s*(#{QuotedFragment})/o
|
||||||
AnyStartingTag = /#{TagStart}|#{VariableStart}/o
|
AnyStartingTag = /#{TagStart}|#{VariableStart}/o
|
||||||
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om
|
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om
|
||||||
TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
|
TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
|
||||||
@@ -59,8 +59,8 @@ require 'liquid/forloop_drop'
|
|||||||
require 'liquid/extensions'
|
require 'liquid/extensions'
|
||||||
require 'liquid/errors'
|
require 'liquid/errors'
|
||||||
require 'liquid/interrupts'
|
require 'liquid/interrupts'
|
||||||
require 'liquid/strainer_factory'
|
|
||||||
require 'liquid/strainer_template'
|
require 'liquid/strainer_template'
|
||||||
|
require 'liquid/strainer_factory'
|
||||||
require 'liquid/expression'
|
require 'liquid/expression'
|
||||||
require 'liquid/context'
|
require 'liquid/context'
|
||||||
require 'liquid/parser_switching'
|
require 'liquid/parser_switching'
|
||||||
|
|||||||
@@ -231,8 +231,8 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def create_variable(token, parse_context)
|
def create_variable(token, parse_context)
|
||||||
token.scan(ContentOfVariable) do |content|
|
if token =~ ContentOfVariable
|
||||||
markup = content.first
|
markup = Regexp.last_match(1)
|
||||||
return Variable.new(markup, parse_context)
|
return Variable.new(markup, parse_context)
|
||||||
end
|
end
|
||||||
BlockBody.raise_missing_variable_terminator(token, parse_context)
|
BlockBody.raise_missing_variable_terminator(token, parse_context)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ module Liquid
|
|||||||
# c = Condition.new(1, '==', 1)
|
# c = Condition.new(1, '==', 1)
|
||||||
# c.evaluate #=> true
|
# c.evaluate #=> true
|
||||||
#
|
#
|
||||||
class Condition #:nodoc:
|
class Condition # :nodoc:
|
||||||
@@operators = {
|
@@operators = {
|
||||||
'==' => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
|
'==' => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
|
||||||
'!=' => ->(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
|
@child_condition = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def evaluate(context = Context.new)
|
def evaluate(context = deprecated_default_context)
|
||||||
condition = self
|
condition = self
|
||||||
result = nil
|
result = nil
|
||||||
loop do
|
loop do
|
||||||
@@ -150,6 +150,12 @@ module Liquid
|
|||||||
end
|
end
|
||||||
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
|
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
||||||
def children
|
def children
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ module Liquid
|
|||||||
# context['var'] = 'hi'
|
# context['var'] = 'hi'
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# context['var] #=> nil
|
# context['var'] #=> nil
|
||||||
def stack(new_scope = {})
|
def stack(new_scope = {})
|
||||||
push(new_scope)
|
push(new_scope)
|
||||||
yield
|
yield
|
||||||
|
|||||||
+11
-10
@@ -10,21 +10,23 @@ module Liquid
|
|||||||
'empty' => ''
|
'empty' => ''
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
SINGLE_QUOTED_STRING = /\A\s*'(.*)'\s*\z/m
|
INTEGERS_REGEX = /\A(-?\d+)\z/
|
||||||
DOUBLE_QUOTED_STRING = /\A\s*"(.*)"\s*\z/m
|
FLOATS_REGEX = /\A(-?\d[\d\.]+)\z/
|
||||||
INTEGERS_REGEX = /\A\s*(-?\d+)\s*\z/
|
|
||||||
FLOATS_REGEX = /\A\s*(-?\d[\d\.]+)\s*\z/
|
|
||||||
|
|
||||||
# Use an atomic group (?>...) to avoid pathological backtracing from
|
# Use an atomic group (?>...) to avoid pathological backtracing from
|
||||||
# malicious input as described in https://github.com/Shopify/liquid/issues/1357
|
# malicious input as described in https://github.com/Shopify/liquid/issues/1357
|
||||||
RANGES_REGEX = /\A\s*\(\s*(?>(\S+)\s*\.\.)\s*(\S+)\s*\)\s*\z/
|
RANGES_REGEX = /\A\(\s*(?>(\S+)\s*\.\.)\s*(\S+)\s*\)\z/
|
||||||
|
|
||||||
def self.parse(markup)
|
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
|
case markup
|
||||||
when nil
|
|
||||||
nil
|
|
||||||
when SINGLE_QUOTED_STRING, DOUBLE_QUOTED_STRING
|
|
||||||
Regexp.last_match(1)
|
|
||||||
when INTEGERS_REGEX
|
when INTEGERS_REGEX
|
||||||
Regexp.last_match(1).to_i
|
Regexp.last_match(1).to_i
|
||||||
when RANGES_REGEX
|
when RANGES_REGEX
|
||||||
@@ -32,7 +34,6 @@ module Liquid
|
|||||||
when FLOATS_REGEX
|
when FLOATS_REGEX
|
||||||
Regexp.last_match(1).to_f
|
Regexp.last_match(1).to_f
|
||||||
else
|
else
|
||||||
markup = markup.strip
|
|
||||||
if LITERALS.key?(markup)
|
if LITERALS.key?(markup)
|
||||||
LITERALS[markup]
|
LITERALS[markup]
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr_reader :start_obj, :end_obj
|
||||||
|
|
||||||
def initialize(start_obj, end_obj)
|
def initialize(start_obj, end_obj)
|
||||||
@start_obj = start_obj
|
@start_obj = start_obj
|
||||||
@end_obj = end_obj
|
@end_obj = end_obj
|
||||||
@@ -35,5 +37,11 @@ module Liquid
|
|||||||
Utils.to_integer(input)
|
Utils.to_integer(input)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class ParseTreeVisitor < Liquid::ParseTreeVisitor
|
||||||
|
def children
|
||||||
|
[@node.start_obj, @node.end_obj]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -22,7 +22,14 @@ module Liquid
|
|||||||
)
|
)
|
||||||
STRIP_HTML_TAGS = /<.*?>/m
|
STRIP_HTML_TAGS = /<.*?>/m
|
||||||
|
|
||||||
# Return the size of an array or of an string
|
# @public_docs
|
||||||
|
# @title Size
|
||||||
|
# @syntax The syntax
|
||||||
|
# @summary The summary
|
||||||
|
# @type filter
|
||||||
|
# @category My category
|
||||||
|
# @description
|
||||||
|
# Return the size of an array or of an string
|
||||||
def size(input)
|
def size(input)
|
||||||
input.respond_to?(:size) ? input.size : 0
|
input.respond_to?(:size) ? input.size : 0
|
||||||
end
|
end
|
||||||
@@ -213,17 +220,23 @@ module Liquid
|
|||||||
|
|
||||||
if ary.empty?
|
if ary.empty?
|
||||||
[]
|
[]
|
||||||
elsif ary.first.respond_to?(:[]) && target_value.nil?
|
elsif target_value.nil?
|
||||||
begin
|
ary.select do |item|
|
||||||
ary.select { |item| item[property] }
|
item[property]
|
||||||
rescue TypeError
|
rescue TypeError
|
||||||
raise_property_error(property)
|
raise_property_error(property)
|
||||||
|
rescue NoMethodError
|
||||||
|
return nil unless item.respond_to?(:[])
|
||||||
|
raise
|
||||||
end
|
end
|
||||||
elsif ary.first.respond_to?(:[])
|
else
|
||||||
begin
|
ary.select do |item|
|
||||||
ary.select { |item| item[property] == target_value }
|
item[property] == target_value
|
||||||
rescue TypeError
|
rescue TypeError
|
||||||
raise_property_error(property)
|
raise_property_error(property)
|
||||||
|
rescue NoMethodError
|
||||||
|
return nil unless item.respond_to?(:[])
|
||||||
|
raise
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -237,11 +250,14 @@ module Liquid
|
|||||||
ary.uniq
|
ary.uniq
|
||||||
elsif ary.empty? # The next two cases assume a non-empty array.
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
||||||
[]
|
[]
|
||||||
elsif ary.first.respond_to?(:[])
|
else
|
||||||
begin
|
ary.uniq do |item|
|
||||||
ary.uniq { |a| a[property] }
|
item[property]
|
||||||
rescue TypeError
|
rescue TypeError
|
||||||
raise_property_error(property)
|
raise_property_error(property)
|
||||||
|
rescue NoMethodError
|
||||||
|
return nil unless item.respond_to?(:[])
|
||||||
|
raise
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -277,11 +293,14 @@ module Liquid
|
|||||||
ary.compact
|
ary.compact
|
||||||
elsif ary.empty? # The next two cases assume a non-empty array.
|
elsif ary.empty? # The next two cases assume a non-empty array.
|
||||||
[]
|
[]
|
||||||
elsif ary.first.respond_to?(:[])
|
else
|
||||||
begin
|
ary.reject do |item|
|
||||||
ary.reject { |a| a[property].nil? }
|
item[property].nil?
|
||||||
rescue TypeError
|
rescue TypeError
|
||||||
raise_property_error(property)
|
raise_property_error(property)
|
||||||
|
rescue NoMethodError
|
||||||
|
return nil unless item.respond_to?(:[])
|
||||||
|
raise
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -296,14 +315,34 @@ module Liquid
|
|||||||
input.to_s.sub(string.to_s, replacement.to_s)
|
input.to_s.sub(string.to_s, replacement.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Replace the last occurrences of a string with another
|
||||||
|
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
|
||||||
|
|
||||||
# remove a substring
|
# remove a substring
|
||||||
def remove(input, string)
|
def remove(input, string)
|
||||||
input.to_s.gsub(string.to_s, '')
|
replace(input, string, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
# remove the first occurrences of a substring
|
# remove the first occurrences of a substring
|
||||||
def remove_first(input, string)
|
def remove_first(input, string)
|
||||||
input.to_s.sub(string.to_s, '')
|
replace_first(input, string, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
# remove the last occurences of a substring
|
||||||
|
def remove_last(input, string)
|
||||||
|
replace_last(input, string, '')
|
||||||
end
|
end
|
||||||
|
|
||||||
# add one string to another
|
# add one string to another
|
||||||
@@ -468,7 +507,7 @@ module Liquid
|
|||||||
#
|
#
|
||||||
def default(input, default_value = '', options = {})
|
def default(input, default_value = '', options = {})
|
||||||
options = {} unless options.is_a?(Hash)
|
options = {} unless options.is_a?(Hash)
|
||||||
false_check = options['allow_false'] ? input.nil? : !input
|
false_check = options['allow_false'] ? input.nil? : !Liquid::Utils.to_liquid_value(input)
|
||||||
false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input
|
false_check || (input.respond_to?(:empty?) && input.empty?) ? default_value : input
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -486,10 +525,16 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def nil_safe_compare(a, b)
|
def nil_safe_compare(a, b)
|
||||||
if !a.nil? && !b.nil?
|
result = a <=> b
|
||||||
a <=> b
|
|
||||||
|
if result
|
||||||
|
result
|
||||||
|
elsif a.nil?
|
||||||
|
1
|
||||||
|
elsif b.nil?
|
||||||
|
-1
|
||||||
else
|
else
|
||||||
a.nil? ? 1 : -1
|
raise Liquid::ArgumentError, "cannot sort values of incompatible types"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -544,8 +589,9 @@ module Liquid
|
|||||||
|
|
||||||
def each
|
def each
|
||||||
@input.each do |e|
|
@input.each do |e|
|
||||||
|
e = e.respond_to?(:to_liquid) ? e.to_liquid : e
|
||||||
e.context = @context if e.respond_to?(:context=)
|
e.context = @context if e.respond_to?(:context=)
|
||||||
yield(e.respond_to?(:to_liquid) ? e.to_liquid : e)
|
yield(e)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -31,7 +31,11 @@ module Liquid
|
|||||||
if @registers.key?(key)
|
if @registers.key?(key)
|
||||||
@registers.fetch(key)
|
@registers.fetch(key)
|
||||||
elsif default != UNDEFINED
|
elsif default != UNDEFINED
|
||||||
@static.fetch(key, default, &block)
|
if block_given?
|
||||||
|
@static.fetch(key, &block)
|
||||||
|
else
|
||||||
|
@static.fetch(key, default)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
@static.fetch(key, &block)
|
@static.fetch(key, &block)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,25 +7,26 @@ module Liquid
|
|||||||
|
|
||||||
def add_global_filter(filter)
|
def add_global_filter(filter)
|
||||||
strainer_class_cache.clear
|
strainer_class_cache.clear
|
||||||
global_filters << filter
|
GlobalCache.add_filter(filter)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(context, filters = [])
|
def create(context, filters = [])
|
||||||
strainer_from_cache(filters).new(context)
|
strainer_from_cache(filters).new(context)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
GlobalCache = Class.new(StrainerTemplate)
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def global_filters
|
|
||||||
@global_filters ||= []
|
|
||||||
end
|
|
||||||
|
|
||||||
def strainer_from_cache(filters)
|
def strainer_from_cache(filters)
|
||||||
strainer_class_cache[filters] ||= begin
|
if filters.empty?
|
||||||
klass = Class.new(StrainerTemplate)
|
GlobalCache
|
||||||
global_filters.each { |f| klass.add_filter(f) }
|
else
|
||||||
filters.each { |f| klass.add_filter(f) }
|
strainer_class_cache[filters] ||= begin
|
||||||
klass
|
klass = Class.new(GlobalCache)
|
||||||
|
filters.each { |f| klass.add_filter(f) }
|
||||||
|
klass
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ module Liquid
|
|||||||
filter_methods.include?(method.to_s)
|
filter_methods.include?(method.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def inherited(subclass)
|
||||||
|
super
|
||||||
|
subclass.instance_variable_set(:@filter_methods, @filter_methods.dup)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def filter_methods
|
def filter_methods
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# @public_docs
|
||||||
module Liquid
|
module Liquid
|
||||||
# Assign sets a variable in your template.
|
# @public_docs
|
||||||
|
# @title Assign
|
||||||
|
# @syntax The syntax
|
||||||
|
# @summary The summary
|
||||||
|
# @type tag
|
||||||
|
# @description
|
||||||
|
# Assign sets a variable in your template.
|
||||||
#
|
#
|
||||||
# {% assign foo = 'monkey' %}
|
# {% assign foo = 'monkey' %}
|
||||||
#
|
#
|
||||||
# You can then use the variable later in the page.
|
# You can then use the variable later in the page.
|
||||||
#
|
|
||||||
# {{ foo }}
|
|
||||||
#
|
#
|
||||||
|
# {{ foo }}
|
||||||
class Assign < Tag
|
class Assign < Tag
|
||||||
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
|
Syntax = /(#{VariableSignature}+)\s*=\s*(.*)\s*/om
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ module Liquid
|
|||||||
|
|
||||||
# After the first condition unless works just like if
|
# After the first condition unless works just like if
|
||||||
@blocks[1..-1].each do |block|
|
@blocks[1..-1].each do |block|
|
||||||
result = block.evaluate(context)
|
result = Liquid::Utils.to_liquid_value(
|
||||||
result = result.to_liquid_value if result.is_a?(Liquid::Drop)
|
block.evaluate(context)
|
||||||
|
)
|
||||||
|
|
||||||
if result
|
if result
|
||||||
return block.attachment.render_to_output_buffer(context, output)
|
return block.attachment.render_to_output_buffer(context, output)
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Liquid
|
module Liquid
|
||||||
VERSION = "5.0.2.alpha"
|
VERSION = "5.3.0.alpha"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,11 +3,19 @@
|
|||||||
require 'yaml'
|
require 'yaml'
|
||||||
|
|
||||||
module Database
|
module Database
|
||||||
|
DATABASE_FILE_PATH = "#{__dir__}/vision.database.yml"
|
||||||
|
|
||||||
# Load the standard vision toolkit database and re-arrage it to be simply exportable
|
# Load the standard vision toolkit database and re-arrage it to be simply exportable
|
||||||
# to liquid as assigns. All this is based on Shopify
|
# to liquid as assigns. All this is based on Shopify
|
||||||
def self.tables
|
def self.tables
|
||||||
@tables ||= begin
|
@tables ||= begin
|
||||||
db = YAML.load_file("#{__dir__}/vision.database.yml")
|
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
|
||||||
|
|
||||||
# From vision source
|
# From vision source
|
||||||
db['products'].each do |product|
|
db['products'].each do |product|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class ContextSensitiveDrop < Liquid::Drop
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Category < Liquid::Drop
|
class Category
|
||||||
attr_accessor :name
|
attr_accessor :name
|
||||||
|
|
||||||
def initialize(name)
|
def initialize(name)
|
||||||
@@ -36,8 +36,9 @@ class Category < Liquid::Drop
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class CategoryDrop
|
class CategoryDrop < Liquid::Drop
|
||||||
attr_accessor :category, :context
|
attr_accessor :category, :context
|
||||||
|
|
||||||
def initialize(category)
|
def initialize(category)
|
||||||
@category = category
|
@category = category
|
||||||
end
|
end
|
||||||
@@ -405,45 +406,42 @@ class ContextTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_lambda_is_called_once
|
def test_lambda_is_called_once
|
||||||
|
@global = 0
|
||||||
|
|
||||||
@context['callcount'] = proc {
|
@context['callcount'] = proc {
|
||||||
@global ||= 0
|
@global += 1
|
||||||
@global += 1
|
|
||||||
@global.to_s
|
@global.to_s
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_equal('1', @context['callcount'])
|
assert_equal('1', @context['callcount'])
|
||||||
assert_equal('1', @context['callcount'])
|
assert_equal('1', @context['callcount'])
|
||||||
assert_equal('1', @context['callcount'])
|
assert_equal('1', @context['callcount'])
|
||||||
|
|
||||||
@global = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_nested_lambda_is_called_once
|
def test_nested_lambda_is_called_once
|
||||||
|
@global = 0
|
||||||
|
|
||||||
@context['callcount'] = { "lambda" => proc {
|
@context['callcount'] = { "lambda" => proc {
|
||||||
@global ||= 0
|
@global += 1
|
||||||
@global += 1
|
|
||||||
@global.to_s
|
@global.to_s
|
||||||
} }
|
} }
|
||||||
|
|
||||||
assert_equal('1', @context['callcount.lambda'])
|
assert_equal('1', @context['callcount.lambda'])
|
||||||
assert_equal('1', @context['callcount.lambda'])
|
assert_equal('1', @context['callcount.lambda'])
|
||||||
assert_equal('1', @context['callcount.lambda'])
|
assert_equal('1', @context['callcount.lambda'])
|
||||||
|
|
||||||
@global = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_lambda_in_array_is_called_once
|
def test_lambda_in_array_is_called_once
|
||||||
|
@global = 0
|
||||||
|
|
||||||
@context['callcount'] = [1, 2, proc {
|
@context['callcount'] = [1, 2, proc {
|
||||||
@global ||= 0
|
@global += 1
|
||||||
@global += 1
|
|
||||||
@global.to_s
|
@global.to_s
|
||||||
}, 4, 5]
|
}, 4, 5]
|
||||||
|
|
||||||
assert_equal('1', @context['callcount[2]'])
|
assert_equal('1', @context['callcount[2]'])
|
||||||
assert_equal('1', @context['callcount[2]'])
|
assert_equal('1', @context['callcount[2]'])
|
||||||
assert_equal('1', @context['callcount[2]'])
|
assert_equal('1', @context['callcount[2]'])
|
||||||
|
|
||||||
@global = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_access_to_context_from_proc
|
def test_access_to_context_from_proc
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# 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,6 +3,27 @@
|
|||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
class ProfilerTest < Minitest::Test
|
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
|
include Liquid
|
||||||
|
|
||||||
class ProfilingFileSystem
|
class ProfilingFileSystem
|
||||||
@@ -198,16 +219,22 @@ class ProfilerTest < Minitest::Test
|
|||||||
|
|
||||||
def test_profiling_supports_self_time
|
def test_profiling_supports_self_time
|
||||||
t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true)
|
t = Template.parse("{% for item in collection %} {{ item }} {% endfor %}", profile: true)
|
||||||
t.render!("collection" => ["one", "two"])
|
collection = [
|
||||||
leaf = t.profiler[0].children[0]
|
TestDrop.new("one"),
|
||||||
|
TestDrop.new("two"),
|
||||||
|
]
|
||||||
|
output = t.render!("collection" => collection)
|
||||||
|
assert_equal(" one two ", output)
|
||||||
|
|
||||||
assert_operator(leaf.self_time, :>, 0)
|
leaf = t.profiler[0].children[0]
|
||||||
|
assert_operator(leaf.self_time, :>, 0.0)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_profiling_supports_total_time
|
def test_profiling_supports_total_time
|
||||||
t = Template.parse("{% if true %} {% increment test %} {{ test }} {% endif %}", profile: true)
|
t = Template.parse("{% if true %} {{ test }} {% endif %}", profile: true)
|
||||||
t.render!
|
output = t.render!("test" => TestDrop.new("one"))
|
||||||
|
assert_equal(" one ", output)
|
||||||
|
|
||||||
assert_operator(t.profiler[0].total_time, :>, 0)
|
assert_operator(t.profiler[0].total_time, :>, 0.0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,10 +3,6 @@
|
|||||||
|
|
||||||
require 'test_helper'
|
require 'test_helper'
|
||||||
|
|
||||||
class Filters
|
|
||||||
include Liquid::StandardFilters
|
|
||||||
end
|
|
||||||
|
|
||||||
class TestThing
|
class TestThing
|
||||||
attr_reader :foo
|
attr_reader :foo
|
||||||
|
|
||||||
@@ -29,8 +25,24 @@ class TestThing
|
|||||||
end
|
end
|
||||||
|
|
||||||
class TestDrop < Liquid::Drop
|
class TestDrop < Liquid::Drop
|
||||||
def test
|
def initialize(value:)
|
||||||
"testfoo"
|
@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)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -53,10 +65,13 @@ class NumberLikeThing < Liquid::Drop
|
|||||||
end
|
end
|
||||||
|
|
||||||
class StandardFiltersTest < Minitest::Test
|
class StandardFiltersTest < Minitest::Test
|
||||||
|
Filters = Class.new(Liquid::StrainerTemplate)
|
||||||
|
Filters.add_filter(Liquid::StandardFilters)
|
||||||
|
|
||||||
include Liquid
|
include Liquid
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
@filters = Filters.new
|
@filters = Filters.new(Context.new)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_size
|
def test_size
|
||||||
@@ -259,8 +274,8 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
{ "price" => 1, "handle" => "gamma" },
|
{ "price" => 1, "handle" => "gamma" },
|
||||||
{ "price" => 2, "handle" => "epsilon" },
|
{ "price" => 2, "handle" => "epsilon" },
|
||||||
{ "price" => 4, "handle" => "alpha" },
|
{ "price" => 4, "handle" => "alpha" },
|
||||||
{ "handle" => "delta" },
|
|
||||||
{ "handle" => "beta" },
|
{ "handle" => "beta" },
|
||||||
|
{ "handle" => "delta" },
|
||||||
]
|
]
|
||||||
assert_equal(expectation, @filters.sort(input, "price"))
|
assert_equal(expectation, @filters.sort(input, "price"))
|
||||||
end
|
end
|
||||||
@@ -363,8 +378,9 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal(["foo"], @filters.uniq("foo"))
|
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([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"))
|
assert_equal([{ "a" => 1 }, { "a" => 3 }, { "a" => 2 }], @filters.uniq([{ "a" => 1 }, { "a" => 3 }, { "a" => 1 }, { "a" => 2 }], "a"))
|
||||||
testdrop = TestDrop.new
|
test_drop = TestDrop.new(value: "test")
|
||||||
assert_equal([testdrop], @filters.uniq([testdrop, TestDrop.new], 'test'))
|
test_drop_alternate = TestDrop.new(value: "test")
|
||||||
|
assert_equal([test_drop], @filters.uniq([test_drop, test_drop_alternate], 'value'))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_uniq_empty_array
|
def test_uniq_empty_array
|
||||||
@@ -423,6 +439,16 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_template_result("woot: 1", '{{ foo | map: "whatever" }}', "foo" => [t])
|
assert_template_result("woot: 1", '{{ foo | map: "whatever" }}', "foo" => [t])
|
||||||
end
|
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
|
def test_map_on_hashes
|
||||||
assert_template_result("4217", '{{ thing | map: "foo" | map: "bar" }}',
|
assert_template_result("4217", '{{ thing | map: "foo" | map: "bar" }}',
|
||||||
"thing" => { "foo" => [{ "bar" => 42 }, { "bar" => 17 }] })
|
"thing" => { "foo" => [{ "bar" => 42 }, { "bar" => 17 }] })
|
||||||
@@ -441,9 +467,9 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_map_over_proc
|
def test_map_over_proc
|
||||||
drop = TestDrop.new
|
drop = TestDrop.new(value: "testfoo")
|
||||||
p = proc { drop }
|
p = proc { drop }
|
||||||
templ = '{{ procs | map: "test" }}'
|
templ = '{{ procs | map: "value" }}'
|
||||||
assert_template_result("testfoo", templ, "procs" => [p])
|
assert_template_result("testfoo", templ, "procs" => [p])
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -539,19 +565,31 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_replace
|
def test_replace
|
||||||
assert_equal('2 2 2 2', @filters.replace('1 1 1 1', '1', 2))
|
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('2 1 1 1', @filters.replace_first('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_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
|
end
|
||||||
|
|
||||||
def test_remove
|
def test_remove
|
||||||
assert_equal(' ', @filters.remove("a a a a", 'a'))
|
assert_equal(' ', @filters.remove("a a a a", 'a'))
|
||||||
assert_equal(' ', @filters.remove("1 1 1 1", 1))
|
assert_template_result(' ', "{{ '1 1 1 1' | remove: 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_equal('b a a', @filters.remove_first("a b a a", 'a '))
|
||||||
assert_template_result('a a a', "{{ 'a a a a' | remove_first: '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 }}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_pipes_in_string_arguments
|
def test_pipes_in_string_arguments
|
||||||
@@ -728,6 +766,8 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal("bar", @filters.default([], "bar"))
|
assert_equal("bar", @filters.default([], "bar"))
|
||||||
assert_equal("bar", @filters.default({}, "bar"))
|
assert_equal("bar", @filters.default({}, "bar"))
|
||||||
assert_template_result('bar', "{{ false | 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
|
end
|
||||||
|
|
||||||
def test_default_handle_false
|
def test_default_handle_false
|
||||||
@@ -738,6 +778,8 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal("bar", @filters.default([], "bar", "allow_false" => true))
|
assert_equal("bar", @filters.default([], "bar", "allow_false" => true))
|
||||||
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('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
|
end
|
||||||
|
|
||||||
def test_cannot_access_private_methods
|
def test_cannot_access_private_methods
|
||||||
@@ -766,6 +808,18 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
assert_equal(expectation, @filters.where(input, "ok"))
|
assert_equal(expectation, @filters.where(input, "ok"))
|
||||||
end
|
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
|
def test_where_no_key_set
|
||||||
input = [
|
input = [
|
||||||
{ "handle" => "alpha", "ok" => true },
|
{ "handle" => "alpha", "ok" => true },
|
||||||
@@ -811,7 +865,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_all_filters_never_raise_non_liquid_exception
|
def test_all_filters_never_raise_non_liquid_exception
|
||||||
test_drop = TestDrop.new
|
test_drop = TestDrop.new(value: "test")
|
||||||
test_drop.context = Context.new
|
test_drop.context = Context.new
|
||||||
test_enum = TestEnumerable.new
|
test_enum = TestEnumerable.new
|
||||||
test_enum.context = Context.new
|
test_enum.context = Context.new
|
||||||
@@ -836,19 +890,14 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
{ 1 => "bar" },
|
{ 1 => "bar" },
|
||||||
["foo", 123, nil, true, false, Drop, ["foo"], { foo: "bar" }],
|
["foo", 123, nil, true, false, Drop, ["foo"], { foo: "bar" }],
|
||||||
]
|
]
|
||||||
test_types.each do |first|
|
StandardFilters.public_instance_methods(false).each do |method|
|
||||||
test_types.each do |other|
|
arg_count = @filters.method(method).arity
|
||||||
(@filters.methods - Object.methods).each do |method|
|
arg_count *= -1 if arg_count < 0
|
||||||
arg_count = @filters.method(method).arity
|
|
||||||
arg_count *= -1 if arg_count < 0
|
test_types.repeated_permutation(arg_count) do |args|
|
||||||
inputs = [first]
|
@filters.send(method, *args)
|
||||||
inputs << ([other] * (arg_count - 1)) if arg_count > 1
|
rescue Liquid::Error
|
||||||
begin
|
nil
|
||||||
@filters.send(method, *inputs)
|
|
||||||
rescue Liquid::ArgumentError, Liquid::ZeroDivisionError
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+10
-10
@@ -72,21 +72,21 @@ module Minitest
|
|||||||
end
|
end
|
||||||
|
|
||||||
def with_global_filter(*globals)
|
def with_global_filter(*globals)
|
||||||
original_global_filters = Liquid::StrainerFactory.instance_variable_get(:@global_filters)
|
original_global_cache = Liquid::StrainerFactory::GlobalCache
|
||||||
Liquid::StrainerFactory.instance_variable_set(:@global_filters, [])
|
Liquid::StrainerFactory.send(:remove_const, :GlobalCache)
|
||||||
globals.each do |global|
|
Liquid::StrainerFactory.const_set(:GlobalCache, Class.new(Liquid::StrainerTemplate))
|
||||||
Liquid::StrainerFactory.add_global_filter(global)
|
|
||||||
end
|
|
||||||
|
|
||||||
Liquid::StrainerFactory.send(:strainer_class_cache).clear
|
|
||||||
|
|
||||||
globals.each do |global|
|
globals.each do |global|
|
||||||
Liquid::Template.register_filter(global)
|
Liquid::Template.register_filter(global)
|
||||||
end
|
end
|
||||||
yield
|
|
||||||
ensure
|
|
||||||
Liquid::StrainerFactory.send(:strainer_class_cache).clear
|
Liquid::StrainerFactory.send(:strainer_class_cache).clear
|
||||||
Liquid::StrainerFactory.instance_variable_set(:@global_filters, original_global_filters)
|
begin
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
Liquid::StrainerFactory.send(:remove_const, :GlobalCache)
|
||||||
|
Liquid::StrainerFactory.const_set(:GlobalCache, original_global_cache)
|
||||||
|
Liquid::StrainerFactory.send(:strainer_class_cache).clear
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_error_mode(mode)
|
def with_error_mode(mode)
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_basic_condition
|
def test_basic_condition
|
||||||
assert_equal(false, Condition.new(1, '==', 2).evaluate)
|
assert_equal(false, Condition.new(1, '==', 2).evaluate(Context.new))
|
||||||
assert_equal(true, Condition.new(1, '==', 1).evaluate)
|
assert_equal(true, Condition.new(1, '==', 1).evaluate(Context.new))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_default_operators_evalute_true
|
def test_default_operators_evalute_true
|
||||||
@@ -67,11 +67,11 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_hash_compare_backwards_compatibility
|
def test_hash_compare_backwards_compatibility
|
||||||
assert_nil(Condition.new({}, '>', 2).evaluate)
|
assert_nil(Condition.new({}, '>', 2).evaluate(Context.new))
|
||||||
assert_nil(Condition.new(2, '>', {}).evaluate)
|
assert_nil(Condition.new(2, '>', {}).evaluate(Context.new))
|
||||||
assert_equal(false, Condition.new({}, '==', 2).evaluate)
|
assert_equal(false, Condition.new({}, '==', 2).evaluate(Context.new))
|
||||||
assert_equal(true, Condition.new({ 'a' => 1 }, '==', 'a' => 1).evaluate)
|
assert_equal(true, Condition.new({ 'a' => 1 }, '==', 'a' => 1).evaluate(Context.new))
|
||||||
assert_equal(true, Condition.new({ 'a' => 2 }, 'contains', 'a').evaluate)
|
assert_equal(true, Condition.new({ 'a' => 2 }, 'contains', 'a').evaluate(Context.new))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_contains_works_on_arrays
|
def test_contains_works_on_arrays
|
||||||
@@ -106,30 +106,29 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
|
|
||||||
def test_or_condition
|
def test_or_condition
|
||||||
condition = Condition.new(1, '==', 2)
|
condition = Condition.new(1, '==', 2)
|
||||||
|
assert_equal(false, condition.evaluate(Context.new))
|
||||||
assert_equal(false, condition.evaluate)
|
|
||||||
|
|
||||||
condition.or(Condition.new(2, '==', 1))
|
condition.or(Condition.new(2, '==', 1))
|
||||||
|
|
||||||
assert_equal(false, condition.evaluate)
|
assert_equal(false, condition.evaluate(Context.new))
|
||||||
|
|
||||||
condition.or(Condition.new(1, '==', 1))
|
condition.or(Condition.new(1, '==', 1))
|
||||||
|
|
||||||
assert_equal(true, condition.evaluate)
|
assert_equal(true, condition.evaluate(Context.new))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_and_condition
|
def test_and_condition
|
||||||
condition = Condition.new(1, '==', 1)
|
condition = Condition.new(1, '==', 1)
|
||||||
|
|
||||||
assert_equal(true, condition.evaluate)
|
assert_equal(true, condition.evaluate(Context.new))
|
||||||
|
|
||||||
condition.and(Condition.new(2, '==', 2))
|
condition.and(Condition.new(2, '==', 2))
|
||||||
|
|
||||||
assert_equal(true, condition.evaluate)
|
assert_equal(true, condition.evaluate(Context.new))
|
||||||
|
|
||||||
condition.and(Condition.new(2, '==', 1))
|
condition.and(Condition.new(2, '==', 1))
|
||||||
|
|
||||||
assert_equal(false, condition.evaluate)
|
assert_equal(false, condition.evaluate(Context.new))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_should_allow_custom_proc_operator
|
def test_should_allow_custom_proc_operator
|
||||||
@@ -148,6 +147,20 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
assert_evaluates_true(VariableLookup.new("one"), '==', VariableLookup.new("another"))
|
assert_evaluates_true(VariableLookup.new("one"), '==', VariableLookup.new("another"))
|
||||||
end
|
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
|
private
|
||||||
|
|
||||||
def assert_evaluates_true(left, op, right)
|
def assert_evaluates_true(left, op, right)
|
||||||
|
|||||||
@@ -159,6 +159,13 @@ class ParseTreeVisitorTest < Minitest::Test
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_for_range
|
||||||
|
assert_equal(
|
||||||
|
["test"],
|
||||||
|
visit(%({% for x in (1..test) %}{% endfor %}))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def test_tablerow_in
|
def test_tablerow_in
|
||||||
assert_equal(
|
assert_equal(
|
||||||
["test"],
|
["test"],
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ class StrainerFactoryUnitTest < Minitest::Test
|
|||||||
/\ALiquid error: wrong number of arguments \((1 for 0|given 1, expected 0)\)\z/,
|
/\ALiquid error: wrong number of arguments \((1 for 0|given 1, expected 0)\)\z/,
|
||||||
exception.message
|
exception.message
|
||||||
)
|
)
|
||||||
assert_equal(exception.backtrace[0].split(':')[0], __FILE__)
|
source = AccessScopeFilters.instance_method(:public_filter).source_location
|
||||||
|
assert_equal(source.map(&:to_s), exception.backtrace[0].split(':')[0..1])
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_strainer_only_invokes_public_filter_methods
|
def test_strainer_only_invokes_public_filter_methods
|
||||||
|
|||||||
@@ -57,8 +57,8 @@ class StrainerTemplateUnitTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_add_filter_does_not_raise_when_module_overrides_previously_registered_method
|
def test_add_filter_does_not_raise_when_module_overrides_previously_registered_method
|
||||||
strainer = Context.new.strainer
|
|
||||||
with_global_filter do
|
with_global_filter do
|
||||||
|
strainer = Context.new.strainer
|
||||||
strainer.class.add_filter(PublicMethodOverrideFilter)
|
strainer.class.add_filter(PublicMethodOverrideFilter)
|
||||||
assert(strainer.class.send(:filter_methods).include?('public_filter'))
|
assert(strainer.class.send(:filter_methods).include?('public_filter'))
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user