mirror of
https://github.com/kemko/liquid.git
synced 2026-01-06 18:25:41 +03:00
Compare commits
20 Commits
force-to_l
...
latest-bun
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
622b8011db | ||
|
|
021bafd260 | ||
|
|
04c393ab07 | ||
|
|
9a7778e52c | ||
|
|
dde00253f9 | ||
|
|
18d1644980 | ||
|
|
c424d47274 | ||
|
|
8e6b9d503d | ||
|
|
8be38d1795 | ||
|
|
3146d5c3f2 | ||
|
|
0cc8b68a97 | ||
|
|
5a50c12953 | ||
|
|
a6fa4c5c38 | ||
|
|
dadd9b4dd2 | ||
|
|
6434b8d2bb | ||
|
|
2d891ddd8f | ||
|
|
60b508b151 | ||
|
|
3891f14a1a | ||
|
|
198f0aa366 | ||
|
|
f2e6adf566 |
@@ -19,6 +19,10 @@ matrix:
|
|||||||
allow_failures:
|
allow_failures:
|
||||||
- rvm: jruby-head
|
- rvm: jruby-head
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- gem update --system
|
||||||
|
- gem install bundler --pre
|
||||||
|
|
||||||
script: "bundle exec rake"
|
script: "bundle exec rake"
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
|
|||||||
@@ -4,23 +4,22 @@
|
|||||||
|
|
||||||
* Bugfixes
|
* Bugfixes
|
||||||
* Performance improvements
|
* Performance improvements
|
||||||
* Features which are likely to be useful to the majority of Liquid users
|
* Features that are likely to be useful to the majority of Liquid users
|
||||||
|
|
||||||
## Things we won't merge
|
## Things we won't merge
|
||||||
|
|
||||||
* Code which introduces considerable performance degrations
|
* Code that introduces considerable performance degrations
|
||||||
* Code which touches performance critical parts of Liquid and comes without benchmarks
|
* Code that touches performance-critical parts of Liquid and comes without benchmarks
|
||||||
* Features which are not important for most people (we want to keep the core Liquid code small and tidy)
|
* Features that are not important for most people (we want to keep the core Liquid code small and tidy)
|
||||||
* Features which can easily be implemented on top of Liquid (for example as a custom filter or custom filesystem)
|
* Features that can easily be implemented on top of Liquid (for example as a custom filter or custom filesystem)
|
||||||
* Code which comes without tests
|
* Code that does not include tests
|
||||||
* Code which breaks existing tests
|
* Code that breaks existing tests
|
||||||
|
|
||||||
## Workflow
|
## Workflow
|
||||||
|
|
||||||
* Fork the Liquid repository
|
* Fork the Liquid repository
|
||||||
* Create a new branch in your fork
|
* Create a new branch in your fork
|
||||||
* If it makes sense, add tests for your code and run a performance benchmark
|
* If it makes sense, add tests for your code and/or run a performance benchmark
|
||||||
* Make sure all tests pass
|
* Make sure all tests pass (`bundle exec rake`)
|
||||||
* Create a pull request
|
* Create a pull request
|
||||||
* In the description, ping one of [@boourns](https://github.com/boourns), [@fw42](https://github.com/fw42), [@camilo](https://github.com/camilo), [@dylanahsmith](https://github.com/dylanahsmith), or [@arthurnn](https://github.com/arthurnn) and ask for a code review.
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
## 4.0.0 / not yet released / branch "master"
|
## 4.0.0 / not yet released / branch "master"
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
* Add strict_variables and strict_filters options to detect undefined references (#691)
|
||||||
* Improve loop performance (#681) [Florian Weingarten]
|
* Improve loop performance (#681) [Florian Weingarten]
|
||||||
* Rename Drop method `before_method` to `liquid_method_missing` (#661) [Thierry Joyal]
|
* Rename Drop method `before_method` to `liquid_method_missing` (#661) [Thierry Joyal]
|
||||||
* Add url_decode filter to invert url_encode (#645) [Larry Archer]
|
* Add url_decode filter to invert url_encode (#645) [Larry Archer]
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
* Ruby 1.9 support dropped (#491) [Justin Li]
|
* Ruby 1.9 support dropped (#491) [Justin Li]
|
||||||
* Liquid::Template.file_system's read_template_file method is no longer passed the context. (#441) [James Reid-Smith]
|
* Liquid::Template.file_system's read_template_file method is no longer passed the context. (#441) [James Reid-Smith]
|
||||||
* Remove support for `liquid_methods`
|
* Remove support for `liquid_methods`
|
||||||
|
* Liquid::Template.register_filter raises when the module overrides registered public methods as private or protected (#705) [Gaurav Chande]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
* Fix map filter when value is a Proc (#672) [Guillaume Malette]
|
* Fix map filter when value is a Proc (#672) [Guillaume Malette]
|
||||||
|
|||||||
31
README.md
31
README.md
@@ -73,3 +73,34 @@ This is useful for doing things like enabling strict mode only in the theme edit
|
|||||||
|
|
||||||
It is recommended that you enable `:strict` or `:warn` mode on new apps to stop invalid templates from being created.
|
It is recommended that you enable `:strict` or `:warn` mode on new apps to stop invalid templates from being created.
|
||||||
It is also recommended that you use it in the template editors of existing apps to give editors better error messages.
|
It is also recommended that you use it in the template editors of existing apps to give editors better error messages.
|
||||||
|
|
||||||
|
### Undefined variables and filters
|
||||||
|
|
||||||
|
By default, the renderer doesn't raise or in any other way notify you if some variables or filters are missing, i.e. not passed to the `render` method.
|
||||||
|
You can improve this situation by passing `strict_variables: true` and/or `strict_filters: true` options to the `render` method.
|
||||||
|
When one of these options is set to true, all errors about undefined variables and undefined filters will be stored in `errors` array of a `Liquid::Template` instance.
|
||||||
|
Here are some examples:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
template = Liquid::Template.parse("{{x}} {{y}} {{z.a}} {{z.b}}")
|
||||||
|
template.render({ 'x' => 1, 'z' => { 'a' => 2 } }, { strict_variables: true })
|
||||||
|
#=> '1 2 ' # when a variable is undefined, it's rendered as nil
|
||||||
|
template.errors
|
||||||
|
#=> [#<Liquid::UndefinedVariable: Liquid error: undefined variable y>, #<Liquid::UndefinedVariable: Liquid error: undefined variable b>]
|
||||||
|
```
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
template = Liquid::Template.parse("{{x | filter1 | upcase}}")
|
||||||
|
template.render({ 'x' => 'foo' }, { strict_filters: true })
|
||||||
|
#=> '' # when at least one filter in the filter chain is undefined, a whole expression is rendered as nil
|
||||||
|
template.errors
|
||||||
|
#=> [#<Liquid::UndefinedFilter: Liquid error: undefined filter filter1>]
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to raise on a first exception instead of pushing all of them in `errors`, you can use `render!` method:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
template = Liquid::Template.parse("{{x}} {{y}}")
|
||||||
|
template.render!({ 'x' => 1}, { strict_variables: true })
|
||||||
|
#=> Liquid::UndefinedVariable: Liquid error: undefined variable y
|
||||||
|
```
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ module Liquid
|
|||||||
end
|
end
|
||||||
rescue MemoryError => e
|
rescue MemoryError => e
|
||||||
raise e
|
raise e
|
||||||
|
rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e
|
||||||
|
context.handle_error(e, token.line_number)
|
||||||
|
output << nil
|
||||||
rescue ::StandardError => e
|
rescue ::StandardError => e
|
||||||
output << context.handle_error(e, token.line_number)
|
output << context.handle_error(e, token.line_number)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ module Liquid
|
|||||||
# context['bob'] #=> nil class Context
|
# context['bob'] #=> nil class Context
|
||||||
class Context
|
class Context
|
||||||
attr_reader :scopes, :errors, :registers, :environments, :resource_limits
|
attr_reader :scopes, :errors, :registers, :environments, :resource_limits
|
||||||
attr_accessor :exception_handler, :template_name, :partial, :global_filter
|
attr_accessor :exception_handler, :template_name, :partial, :global_filter, :strict_variables, :strict_filters
|
||||||
|
|
||||||
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil)
|
def initialize(environments = {}, outer_scope = {}, registers = {}, rethrow_errors = false, resource_limits = nil)
|
||||||
@environments = [environments].flatten
|
@environments = [environments].flatten
|
||||||
@@ -205,7 +205,13 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def lookup_and_evaluate(obj, key)
|
def lookup_and_evaluate(obj, key)
|
||||||
if (value = obj[key]).is_a?(Proc) && obj.respond_to?(:[]=)
|
if @strict_variables && obj.respond_to?(:key?) && !obj.key?(key)
|
||||||
|
raise Liquid::UndefinedVariable, "undefined variable #{key}"
|
||||||
|
end
|
||||||
|
|
||||||
|
value = obj[key]
|
||||||
|
|
||||||
|
if value.is_a?(Proc) && obj.respond_to?(:[]=)
|
||||||
obj[key] = (value.arity == 0) ? value.call : value.call(self)
|
obj[key] = (value.arity == 0) ? value.call : value.call(self)
|
||||||
else
|
else
|
||||||
value
|
value
|
||||||
|
|||||||
@@ -24,8 +24,9 @@ module Liquid
|
|||||||
attr_writer :context
|
attr_writer :context
|
||||||
|
|
||||||
# Catch all for the method
|
# Catch all for the method
|
||||||
def liquid_method_missing(_method)
|
def liquid_method_missing(method)
|
||||||
nil
|
return nil unless @context && @context.strict_variables
|
||||||
|
raise Liquid::UndefinedDropMethod, "undefined method #{method}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# called by liquid to invoke a drop
|
# called by liquid to invoke a drop
|
||||||
|
|||||||
@@ -56,4 +56,8 @@ module Liquid
|
|||||||
MemoryError = Class.new(Error)
|
MemoryError = Class.new(Error)
|
||||||
ZeroDivisionError = Class.new(Error)
|
ZeroDivisionError = Class.new(Error)
|
||||||
FloatDomainError = Class.new(Error)
|
FloatDomainError = Class.new(Error)
|
||||||
|
UndefinedVariable = Class.new(Error)
|
||||||
|
UndefinedDropMethod = Class.new(Error)
|
||||||
|
UndefinedFilter = Class.new(Error)
|
||||||
|
MethodOverrideError = Class.new(Error)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ module Liquid
|
|||||||
File.join(root, @pattern % template_path)
|
File.join(root, @pattern % template_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
raise FileSystemError, "Illegal template path '#{File.expand_path(full_path)}'" unless File.expand_path(full_path) =~ /\A#{File.expand_path(root)}/
|
raise FileSystemError, "Illegal template path '#{File.expand_path(full_path)}'" unless File.expand_path(full_path).start_with?(File.expand_path(root))
|
||||||
|
|
||||||
full_path
|
full_path
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -125,8 +125,6 @@ module Liquid
|
|||||||
[]
|
[]
|
||||||
elsif ary.first.respond_to?(:[]) && !ary.first[property].nil?
|
elsif ary.first.respond_to?(:[]) && !ary.first[property].nil?
|
||||||
ary.sort { |a, b| a[property] <=> b[property] }
|
ary.sort { |a, b| a[property] <=> b[property] }
|
||||||
elsif ary.first.respond_to?(property)
|
|
||||||
ary.sort { |a, b| a.send(property) <=> b.send(property) }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -141,8 +139,6 @@ module Liquid
|
|||||||
[]
|
[]
|
||||||
elsif ary.first.respond_to?(:[]) && !ary.first[property].nil?
|
elsif ary.first.respond_to?(:[]) && !ary.first[property].nil?
|
||||||
ary.sort { |a, b| a[property].casecmp(b[property]) }
|
ary.sort { |a, b| a[property].casecmp(b[property]) }
|
||||||
elsif ary.first.respond_to?(property)
|
|
||||||
ary.sort { |a, b| a.send(property).casecmp(b.send(property)) }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -191,8 +187,6 @@ module Liquid
|
|||||||
[]
|
[]
|
||||||
elsif ary.first.respond_to?(:[])
|
elsif ary.first.respond_to?(:[])
|
||||||
ary.reject{ |a| a[property].nil? }
|
ary.reject{ |a| a[property].nil? }
|
||||||
elsif ary.first.respond_to?(property)
|
|
||||||
ary.reject { |a| a.send(property).nil? }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -26,10 +26,15 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.add_filter(filter)
|
def self.add_filter(filter)
|
||||||
raise ArgumentError, "Expected module but got: #{f.class}" unless filter.is_a?(Module)
|
raise ArgumentError, "Expected module but got: #{filter.class}" unless filter.is_a?(Module)
|
||||||
unless self.class.include?(filter)
|
unless self.class.include?(filter)
|
||||||
send(:include, filter)
|
invokable_non_public_methods = (filter.private_instance_methods + filter.protected_instance_methods).select { |m| invokable?(m) }
|
||||||
@filter_methods.merge(filter.public_instance_methods.map(&:to_s))
|
if invokable_non_public_methods.any?
|
||||||
|
raise MethodOverrideError, "Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')}"
|
||||||
|
else
|
||||||
|
send(:include, filter)
|
||||||
|
@filter_methods.merge(filter.public_instance_methods.map(&:to_s))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -48,6 +53,8 @@ module Liquid
|
|||||||
def invoke(method, *args)
|
def invoke(method, *args)
|
||||||
if self.class.invokable?(method)
|
if self.class.invokable?(method)
|
||||||
send(method, *args)
|
send(method, *args)
|
||||||
|
elsif @context && @context.strict_filters
|
||||||
|
raise Liquid::UndefinedFilter, "undefined filter #{method}"
|
||||||
else
|
else
|
||||||
args.first
|
args.first
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -23,16 +23,28 @@ module Liquid
|
|||||||
def render(context)
|
def render(context)
|
||||||
val = @from.render(context)
|
val = @from.render(context)
|
||||||
context.scopes.last[@to] = val
|
context.scopes.last[@to] = val
|
||||||
|
context.resource_limits.assign_score += assign_score_of(val)
|
||||||
inc = val.instance_of?(String) || val.instance_of?(Array) || val.instance_of?(Hash) ? val.length : 1
|
|
||||||
context.resource_limits.assign_score += inc
|
|
||||||
|
|
||||||
''.freeze
|
''.freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def blank?
|
def blank?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def assign_score_of(val)
|
||||||
|
if val.instance_of?(String)
|
||||||
|
val.length
|
||||||
|
elsif val.instance_of?(Array) || val.instance_of?(Hash)
|
||||||
|
sum = 1
|
||||||
|
# Uses #each to avoid extra allocations.
|
||||||
|
val.each { |child| sum += assign_score_of(child) }
|
||||||
|
sum
|
||||||
|
else
|
||||||
|
1
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Template.register_tag('assign'.freeze, Assign)
|
Template.register_tag('assign'.freeze, Assign)
|
||||||
|
|||||||
@@ -181,12 +181,7 @@ module Liquid
|
|||||||
|
|
||||||
registers.merge!(options[:registers]) if options[:registers].is_a?(Hash)
|
registers.merge!(options[:registers]) if options[:registers].is_a?(Hash)
|
||||||
|
|
||||||
context.add_filters(options[:filters]) if options[:filters]
|
apply_options_to_context(context, options)
|
||||||
|
|
||||||
context.global_filter = options[:global_filter] if options[:global_filter]
|
|
||||||
|
|
||||||
context.exception_handler = options[:exception_handler] if options[:exception_handler]
|
|
||||||
|
|
||||||
when Module, Array
|
when Module, Array
|
||||||
context.add_filters(args.pop)
|
context.add_filters(args.pop)
|
||||||
end
|
end
|
||||||
@@ -235,5 +230,13 @@ module Liquid
|
|||||||
yield
|
yield
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def apply_options_to_context(context, options)
|
||||||
|
context.add_filters(options[:filters]) if options[:filters]
|
||||||
|
context.global_filter = options[:global_filter] if options[:global_filter]
|
||||||
|
context.exception_handler = options[:exception_handler] if options[:exception_handler]
|
||||||
|
context.strict_variables = options[:strict_variables] if options[:strict_variables]
|
||||||
|
context.strict_filters = options[:strict_filters] if options[:strict_filters]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -55,9 +55,11 @@ module Liquid
|
|||||||
object = object.send(key).to_liquid
|
object = object.send(key).to_liquid
|
||||||
|
|
||||||
# No key was present with the desired value and it wasn't one of the directly supported
|
# No key was present with the desired value and it wasn't one of the directly supported
|
||||||
# keywords either. The only thing we got left is to return nil
|
# keywords either. The only thing we got left is to return nil or
|
||||||
|
# raise an exception if `strict_variables` option is set to true
|
||||||
else
|
else
|
||||||
return nil
|
return nil unless context.strict_variables
|
||||||
|
raise Liquid::UndefinedVariable, "undefined variable #{key}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# If we are dealing with a drop here we have to
|
# If we are dealing with a drop here we have to
|
||||||
|
|||||||
@@ -27,6 +27,12 @@ class ErroneousDrop < Liquid::Drop
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class DropWithUndefinedMethod < Liquid::Drop
|
||||||
|
def foo
|
||||||
|
'foo'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class TemplateTest < Minitest::Test
|
class TemplateTest < Minitest::Test
|
||||||
include Liquid
|
include Liquid
|
||||||
|
|
||||||
@@ -133,6 +139,17 @@ class TemplateTest < Minitest::Test
|
|||||||
refute_nil t.resource_limits.assign_score
|
refute_nil t.resource_limits.assign_score
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_resource_limits_assign_score_nested
|
||||||
|
t = Template.parse("{% assign foo = 'aaaa' | reverse %}")
|
||||||
|
|
||||||
|
t.resource_limits.assign_score_limit = 3
|
||||||
|
assert_equal "Liquid error: Memory limits exceeded", t.render
|
||||||
|
assert t.resource_limits.reached?
|
||||||
|
|
||||||
|
t.resource_limits.assign_score_limit = 5
|
||||||
|
assert_equal "", t.render!
|
||||||
|
end
|
||||||
|
|
||||||
def test_resource_limits_aborts_rendering_after_first_error
|
def test_resource_limits_aborts_rendering_after_first_error
|
||||||
t = Template.parse("{% for a in (1..100) %} foo1 {% endfor %} bar {% for a in (1..100) %} foo2 {% endfor %}")
|
t = Template.parse("{% for a in (1..100) %} foo1 {% endfor %} bar {% for a in (1..100) %} foo2 {% endfor %}")
|
||||||
t.resource_limits.render_score_limit = 50
|
t.resource_limits.render_score_limit = 50
|
||||||
@@ -225,4 +242,68 @@ class TemplateTest < Minitest::Test
|
|||||||
|
|
||||||
assert_equal 'BOB filtered', rendered_template
|
assert_equal 'BOB filtered', rendered_template
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_undefined_variables
|
||||||
|
t = Template.parse("{{x}} {{y}} {{z.a}} {{z.b}} {{z.c.d}}")
|
||||||
|
result = t.render({ 'x' => 33, 'z' => { 'a' => 32, 'c' => { 'e' => 31 } } }, { strict_variables: true })
|
||||||
|
|
||||||
|
assert_equal '33 32 ', result
|
||||||
|
assert_equal 3, t.errors.count
|
||||||
|
assert_instance_of Liquid::UndefinedVariable, t.errors[0]
|
||||||
|
assert_equal 'Liquid error: undefined variable y', t.errors[0].message
|
||||||
|
assert_instance_of Liquid::UndefinedVariable, t.errors[1]
|
||||||
|
assert_equal 'Liquid error: undefined variable b', t.errors[1].message
|
||||||
|
assert_instance_of Liquid::UndefinedVariable, t.errors[2]
|
||||||
|
assert_equal 'Liquid error: undefined variable d', t.errors[2].message
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_undefined_variables_raise
|
||||||
|
t = Template.parse("{{x}} {{y}} {{z.a}} {{z.b}} {{z.c.d}}")
|
||||||
|
|
||||||
|
assert_raises UndefinedVariable do
|
||||||
|
t.render!({ 'x' => 33, 'z' => { 'a' => 32, 'c' => { 'e' => 31 } } }, { strict_variables: true })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_undefined_drop_methods
|
||||||
|
d = DropWithUndefinedMethod.new
|
||||||
|
t = Template.new.parse('{{ foo }} {{ woot }}')
|
||||||
|
result = t.render(d, { strict_variables: true })
|
||||||
|
|
||||||
|
assert_equal 'foo ', result
|
||||||
|
assert_equal 1, t.errors.count
|
||||||
|
assert_instance_of Liquid::UndefinedDropMethod, t.errors[0]
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_undefined_drop_methods_raise
|
||||||
|
d = DropWithUndefinedMethod.new
|
||||||
|
t = Template.new.parse('{{ foo }} {{ woot }}')
|
||||||
|
|
||||||
|
assert_raises UndefinedDropMethod do
|
||||||
|
t.render!(d, { strict_variables: true })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_undefined_filters
|
||||||
|
t = Template.parse("{{a}} {{x | upcase | somefilter1 | somefilter2 | somefilter3}}")
|
||||||
|
filters = Module.new do
|
||||||
|
def somefilter3(v)
|
||||||
|
"-#{v}-"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
result = t.render({ 'a' => 123, 'x' => 'foo' }, { filters: [filters], strict_filters: true })
|
||||||
|
|
||||||
|
assert_equal '123 ', result
|
||||||
|
assert_equal 1, t.errors.count
|
||||||
|
assert_instance_of Liquid::UndefinedFilter, t.errors[0]
|
||||||
|
assert_equal 'Liquid error: undefined filter somefilter1', t.errors[0].message
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_undefined_filters_raise
|
||||||
|
t = Template.parse("{{x | somefilter1 | upcase | somefilter2}}")
|
||||||
|
|
||||||
|
assert_raises UndefinedFilter do
|
||||||
|
t.render!({ 'x' => 'foo' }, { strict_filters: true })
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -77,4 +77,60 @@ class StrainerUnitTest < Minitest::Test
|
|||||||
assert_kind_of b, strainer
|
assert_kind_of b, strainer
|
||||||
assert_kind_of Liquid::StandardFilters, strainer
|
assert_kind_of Liquid::StandardFilters, strainer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_add_filter_when_wrong_filter_class
|
||||||
|
c = Context.new
|
||||||
|
s = c.strainer
|
||||||
|
wrong_filter = ->(v) { v.reverse }
|
||||||
|
|
||||||
|
assert_raises ArgumentError do
|
||||||
|
s.class.add_filter(wrong_filter)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module PrivateMethodOverrideFilter
|
||||||
|
private
|
||||||
|
|
||||||
|
def public_filter
|
||||||
|
"overriden as private"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_add_filter_raises_when_module_privately_overrides_registered_public_methods
|
||||||
|
strainer = Context.new.strainer
|
||||||
|
|
||||||
|
error = assert_raises(Liquid::MethodOverrideError) do
|
||||||
|
strainer.class.add_filter(PrivateMethodOverrideFilter)
|
||||||
|
end
|
||||||
|
assert_equal 'Liquid error: Filter overrides registered public methods as non public: public_filter', error.message
|
||||||
|
end
|
||||||
|
|
||||||
|
module ProtectedMethodOverrideFilter
|
||||||
|
protected
|
||||||
|
|
||||||
|
def public_filter
|
||||||
|
"overriden as protected"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_add_filter_raises_when_module_overrides_registered_public_method_as_protected
|
||||||
|
strainer = Context.new.strainer
|
||||||
|
|
||||||
|
error = assert_raises(Liquid::MethodOverrideError) do
|
||||||
|
strainer.class.add_filter(ProtectedMethodOverrideFilter)
|
||||||
|
end
|
||||||
|
assert_equal 'Liquid error: Filter overrides registered public methods as non public: public_filter', error.message
|
||||||
|
end
|
||||||
|
|
||||||
|
module PublicMethodOverrideFilter
|
||||||
|
def public_filter
|
||||||
|
"public"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_add_filter_does_not_raise_when_module_overrides_previously_registered_method
|
||||||
|
strainer = Context.new.strainer
|
||||||
|
strainer.class.add_filter(PublicMethodOverrideFilter)
|
||||||
|
assert strainer.class.filter_methods.include?('public_filter')
|
||||||
|
end
|
||||||
end # StrainerTest
|
end # StrainerTest
|
||||||
|
|||||||
Reference in New Issue
Block a user