Remove mutable render state from Liquid::Template

Use Liquid::Context to access this mutable state after rendering or to have
state that is persisted between mutations
This commit is contained in:
Dylan Thacker-Smith
2020-07-22 12:13:35 -04:00
parent a1d982ca76
commit 219168e89f
6 changed files with 169 additions and 212 deletions

View File

@@ -80,22 +80,24 @@ It is also recommended that you use it in the template editors of existing apps
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.
When one of these options is set to true, all errors about undefined variables and undefined filters will be stored in an `errors` array on the `Liquid::Context` instance used for rendering.
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 })
context = Liquid::Context.new({ 'x' => 1, 'z' => { 'a' => 2 } })
template.render(context, { strict_variables: true })
#=> '1 2 ' # when a variable is undefined, it's rendered as nil
template.errors
context.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 })
context = Liquid::Context.new({ 'x' => 'foo' })
template.render(context, { strict_filters: true })
#=> '' # when at least one filter in the filter chain is undefined, a whole expression is rendered as nil
template.errors
context.errors
#=> [#<Liquid::UndefinedFilter: Liquid error: undefined filter filter1>]
```
@@ -111,4 +113,4 @@ template.render!({ 'x' => 1}, { strict_variables: true })
To help track usages of a feature or code path in production, we have released opt-in usage tracking. To enable this, we provide an empty `Liquid:: Usage.increment` method which you can customize to your needs. The feature is well suited to https://github.com/Shopify/statsd-instrument. However, the choice of implementation is up to you.
Once you have enabled usage tracking, we recommend reporting any events through Github Issues that your system may be logging. It is highly likely this event has been added to consider deprecating or improving code specific to this event, so please raise any concerns.
Once you have enabled usage tracking, we recommend reporting any events through Github Issues that your system may be logging. It is highly likely this event has been added to consider deprecating or improving code specific to this event, so please raise any concerns.

View File

@@ -16,7 +16,7 @@ module Liquid
#
class Template
attr_accessor :root
attr_reader :resource_limits, :warnings
attr_reader :warnings
class TagRegistry
include Enumerable
@@ -106,11 +106,6 @@ module Liquid
end
end
def initialize
@rethrow_errors = false
@resource_limits = ResourceLimits.new(Template.default_resource_limits)
end
# Parse source code.
# Returns self for easy chaining
def parse(source, options = {})
@@ -123,22 +118,6 @@ module Liquid
self
end
def registers
@registers ||= {}
end
def assigns
@assigns ||= {}
end
def instance_assigns
@instance_assigns ||= {}
end
def errors
@errors ||= []
end
# Render takes a hash with local variables.
#
# if you use the same filters over and over again consider registering them globally
@@ -153,37 +132,18 @@ module Liquid
# * <tt>registers</tt> : hash with register variables. Those can be accessed from
# filters and tags and might be useful to integrate liquid more with its host application
#
def render(*args)
def render(assigns_or_context = nil, options = nil)
return '' if @root.nil?
context = case args.first
when Liquid::Context
c = args.shift
if @rethrow_errors
c.exception_renderer = ->(_e) { raise }
end
c
when Liquid::Drop
drop = args.shift
drop.context = Context.new([drop, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
when Hash
Context.new([args.shift, assigns], instance_assigns, registers, @rethrow_errors, @resource_limits)
when nil
Context.new(assigns, instance_assigns, registers, @rethrow_errors, @resource_limits)
else
raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
end
context = coerce_context(assigns_or_context)
output = nil
context_register = context.registers.is_a?(StaticRegisters) ? context.registers.static : context.registers
case args.last
case options
when Hash
options = args.pop
output = options[:output] if options[:output]
output = options[:output] if options[:output]
options[:registers]&.each do |key, register|
context_register[key] = register
@@ -191,7 +151,7 @@ module Liquid
apply_options_to_context(context, options)
when Module, Array
context.add_filters(args.pop)
context.add_filters(options)
end
Template.registers.each do |key, register|
@@ -209,14 +169,15 @@ module Liquid
end
rescue Liquid::MemoryError => e
context.handle_error(e)
ensure
@errors = context.errors
end
end
def render!(*args)
@rethrow_errors = true
render(*args)
def render!(assigns_or_context = nil, options = nil)
context = coerce_context(assigns_or_context)
# rethrow errors
context.exception_renderer = ->(_e) { raise }
render(context, options)
end
def render_to_output_buffer(context, output)
@@ -225,6 +186,22 @@ module Liquid
private
def coerce_context(assigns_or_context)
case assigns_or_context
when Liquid::Context
assigns_or_context
when Liquid::Drop
drop = assigns_or_context
drop.context = Context.build(environments: [drop])
when Hash
Context.build(environments: [assigns_or_context])
when nil
Context.build
else
raise ArgumentError, "Expected Hash or Liquid::Context as parameter"
end
end
def tokenize(source)
Tokenizer.new(source, @line_numbers)
end

View File

@@ -40,26 +40,29 @@ class ErrorHandlingTest < Minitest::Test
def test_standard_error
template = Liquid::Template.parse(' {{ errors.standard_error }} ')
assert_equal(' Liquid error: standard error ', template.render('errors' => ErrorDrop.new))
context = Liquid::Context.new('errors' => ErrorDrop.new)
assert_equal(' Liquid error: standard error ', template.render(context))
assert_equal(1, template.errors.size)
assert_equal(StandardError, template.errors.first.class)
assert_equal(1, context.errors.size)
assert_equal(StandardError, context.errors.first.class)
end
def test_syntax
template = Liquid::Template.parse(' {{ errors.syntax_error }} ')
assert_equal(' Liquid syntax error: syntax error ', template.render('errors' => ErrorDrop.new))
context = Liquid::Context.new('errors' => ErrorDrop.new)
assert_equal(' Liquid syntax error: syntax error ', template.render(context))
assert_equal(1, template.errors.size)
assert_equal(SyntaxError, template.errors.first.class)
assert_equal(1, context.errors.size)
assert_equal(SyntaxError, context.errors.first.class)
end
def test_argument
template = Liquid::Template.parse(' {{ errors.argument_error }} ')
assert_equal(' Liquid error: argument error ', template.render('errors' => ErrorDrop.new))
context = Liquid::Context.new('errors' => ErrorDrop.new)
assert_equal(' Liquid error: argument error ', template.render(context))
assert_equal(1, template.errors.size)
assert_equal(ArgumentError, template.errors.first.class)
assert_equal(1, context.errors.size)
assert_equal(ArgumentError, context.errors.first.class)
end
def test_missing_endtag_parse_time_error
@@ -78,9 +81,10 @@ class ErrorHandlingTest < Minitest::Test
def test_lax_unrecognized_operator
template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', error_mode: :lax)
assert_equal(' Liquid error: Unknown operator =! ', template.render)
assert_equal(1, template.errors.size)
assert_equal(Liquid::ArgumentError, template.errors.first.class)
context = Liquid::Context.new('errors' => ErrorDrop.new)
assert_equal(' Liquid error: Unknown operator =! ', template.render(context))
assert_equal(1, context.errors.size)
assert_equal(Liquid::ArgumentError, context.errors.first.class)
end
def test_with_line_numbers_adds_numbers_to_parser_errors
@@ -202,10 +206,11 @@ class ErrorHandlingTest < Minitest::Test
def test_default_exception_renderer_with_internal_error
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
output = template.render('errors' => ErrorDrop.new)
context = Liquid::Context.new('errors' => ErrorDrop.new)
output = template.render(context)
assert_equal('This is a runtime error: Liquid error (line 1): internal', output)
assert_equal([Liquid::InternalError], template.errors.map(&:class))
assert_equal([Liquid::InternalError], context.errors.map(&:class))
end
def test_setting_default_exception_renderer
@@ -217,10 +222,11 @@ class ErrorHandlingTest < Minitest::Test
}
template = Liquid::Template.parse('This is a runtime error: {{ errors.argument_error }}')
output = template.render('errors' => ErrorDrop.new)
context = Liquid::Context.new('errors' => ErrorDrop.new)
output = template.render(context)
assert_equal('This is a runtime error: ', output)
assert_equal([Liquid::ArgumentError], template.errors.map(&:class))
assert_equal([Liquid::ArgumentError], context.errors.map(&:class))
ensure
Liquid::Template.default_exception_renderer = old_exception_renderer if old_exception_renderer
end
@@ -233,11 +239,12 @@ class ErrorHandlingTest < Minitest::Test
e.cause
}
output = template.render({ 'errors' => ErrorDrop.new }, exception_renderer: handler)
context = Liquid::Context.new('errors' => ErrorDrop.new)
output = template.render(context, exception_renderer: handler)
assert_equal('This is a runtime error: runtime error', output)
assert_equal([Liquid::InternalError], exceptions.map(&:class))
assert_equal(exceptions, template.errors)
assert_equal(exceptions, context.errors)
assert_equal('#<RuntimeError: runtime error>', exceptions.first.cause.inspect)
end
@@ -250,15 +257,16 @@ class ErrorHandlingTest < Minitest::Test
def test_included_template_name_with_line_numbers
old_file_system = Liquid::Template.file_system
context = Liquid::Context.new('errors' => ErrorDrop.new)
begin
Liquid::Template.file_system = TestFileSystem.new
template = Liquid::Template.parse("Argument error:\n{% include 'product' %}", line_numbers: true)
page = template.render('errors' => ErrorDrop.new)
page = template.render(context)
ensure
Liquid::Template.file_system = old_file_system
end
assert_equal("Argument error:\nLiquid error (product line 1): argument error", page)
assert_equal("product", template.errors.first.template_name)
assert_equal("product", context.errors.first.template_name)
end
end

View File

@@ -217,8 +217,9 @@ class IncludeTagTest < Minitest::Test
Liquid::Template.file_system = TestFileSystem.new
a = Liquid::Template.parse(' {% include "nested_template" %}')
a.render!
assert_empty(a.errors)
context = Liquid::Context.new
a.render!(context)
assert_empty(context.errors)
end
def test_passing_options_to_included_templates
@@ -257,9 +258,10 @@ class IncludeTagTest < Minitest::Test
def test_including_with_strict_variables
template = Liquid::Template.parse("{% include 'simple' %}", error_mode: :warn)
template.render(nil, strict_variables: true)
context = Liquid::Context.new
template.render(context, strict_variables: true)
assert_equal([], template.errors)
assert_equal([], context.errors)
end
def test_break_through_include

View File

@@ -38,12 +38,6 @@ end
class TemplateTest < Minitest::Test
include Liquid
def test_instance_assigns_persist_on_same_template_object_between_parses
t = Template.new
assert_equal('from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render!)
assert_equal('from instance assigns', t.parse("{{ foo }}").render!)
end
def test_warnings_is_not_exponential_time
str = "false"
100.times do
@@ -54,12 +48,6 @@ class TemplateTest < Minitest::Test
assert_equal([], Timeout.timeout(1) { t.warnings })
end
def test_instance_assigns_persist_on_same_template_parsing_between_renders
t = Template.new.parse("{{ foo }}{% assign foo = 'foo' %}{{ foo }}")
assert_equal('foo', t.render!)
assert_equal('foofoo', t.render!)
end
def test_custom_assigns_do_not_persist_on_same_template
t = Template.new
assert_equal('from custom assigns', t.parse("{{ foo }}").render!('foo' => 'from custom assigns'))
@@ -72,25 +60,6 @@ class TemplateTest < Minitest::Test
assert_equal('from custom assigns', t.parse("{{ foo }}").render!('foo' => 'from custom assigns'))
end
def test_persistent_assigns_squash_instance_assigns
t = Template.new
assert_equal('from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render!)
t.assigns['foo'] = 'from persistent assigns'
assert_equal('from persistent assigns', t.parse("{{ foo }}").render!)
end
def test_lambda_is_called_once_from_persistent_assigns_over_multiple_parses_and_renders
t = Template.new
t.assigns['number'] = -> {
@global ||= 0
@global += 1
}
assert_equal('1', t.parse("{{number}}").render!)
assert_equal('1', t.parse("{{number}}").render!)
assert_equal('1', t.render!)
@global = nil
end
def test_lambda_is_called_once_from_custom_assigns_over_multiple_parses_and_renders
t = Template.new
assigns = { 'number' => -> {
@@ -105,112 +74,124 @@ class TemplateTest < Minitest::Test
def test_resource_limits_works_with_custom_length_method
t = Template.parse("{% assign foo = bar %}")
t.resource_limits.render_length_limit = 42
assert_equal("", t.render!("bar" => SomethingWithLength.new))
context = Liquid::Context.new("bar" => SomethingWithLength.new)
context.resource_limits.render_length_limit = 42
assert_equal("", t.render!(context))
end
def test_resource_limits_render_length
t = Template.parse("0123456789")
t.resource_limits.render_length_limit = 5
assert_equal("Liquid error: Memory limits exceeded", t.render)
assert(t.resource_limits.reached?)
context = Liquid::Context.new
context.resource_limits.render_length_limit = 5
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
assert(context.resource_limits.reached?)
t.resource_limits.render_length_limit = 10
context.resource_limits.render_length_limit = 10
assert_equal("0123456789", t.render!)
refute_nil(t.resource_limits.render_length)
refute_nil(context.resource_limits.render_length)
end
def test_resource_limits_render_score
t = Template.parse("{% for a in (1..10) %} {% for a in (1..10) %} foo {% endfor %} {% endfor %}")
t.resource_limits.render_score_limit = 50
assert_equal("Liquid error: Memory limits exceeded", t.render)
assert(t.resource_limits.reached?)
context = Liquid::Context.new
context.resource_limits.render_score_limit = 50
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
assert(context.resource_limits.reached?)
t = Template.parse("{% for a in (1..100) %} foo {% endfor %}")
t.resource_limits.render_score_limit = 50
assert_equal("Liquid error: Memory limits exceeded", t.render)
assert(t.resource_limits.reached?)
context.resource_limits.render_score_limit = 50
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
assert(context.resource_limits.reached?)
t.resource_limits.render_score_limit = 200
assert_equal((" foo " * 100), t.render!)
refute_nil(t.resource_limits.render_score)
context.resource_limits.render_score_limit = 200
assert_equal((" foo " * 100), t.render!(context))
refute_nil(context.resource_limits.render_score)
end
def test_resource_limits_assign_score
t = Template.parse("{% assign foo = 42 %}{% assign bar = 23 %}")
t.resource_limits.assign_score_limit = 1
assert_equal("Liquid error: Memory limits exceeded", t.render)
assert(t.resource_limits.reached?)
context = Liquid::Context.new
context.resource_limits.assign_score_limit = 1
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
assert(context.resource_limits.reached?)
t.resource_limits.assign_score_limit = 2
assert_equal("", t.render!)
refute_nil(t.resource_limits.assign_score)
context.resource_limits.assign_score_limit = 2
assert_equal("", t.render!(context))
refute_nil(context.resource_limits.assign_score)
end
def test_resource_limits_assign_score_counts_bytes_not_characters
t = Template.parse("{% assign foo = 'すごい' %}")
t.render
assert_equal(9, t.resource_limits.assign_score)
context = Liquid::Context.new
t.render(context)
assert_equal(9, context.resource_limits.assign_score)
t = Template.parse("{% capture foo %}すごい{% endcapture %}")
t.render
assert_equal(9, t.resource_limits.assign_score)
t.render(context)
assert_equal(9, context.resource_limits.assign_score)
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?)
context = Liquid::Context.new
context.resource_limits.assign_score_limit = 3
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
assert(context.resource_limits.reached?)
t.resource_limits.assign_score_limit = 5
assert_equal("", t.render!)
context.resource_limits.assign_score_limit = 5
assert_equal("", t.render!(context))
end
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.resource_limits.render_score_limit = 50
assert_equal("Liquid error: Memory limits exceeded", t.render)
assert(t.resource_limits.reached?)
context = Liquid::Context.new
context.resource_limits.render_score_limit = 50
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
assert(context.resource_limits.reached?)
end
def test_resource_limits_hash_in_template_gets_updated_even_if_no_limits_are_set
t = Template.parse("{% for a in (1..100) %} {% assign foo = 1 %} {% endfor %}")
t.render!
assert(t.resource_limits.assign_score > 0)
assert(t.resource_limits.render_score > 0)
assert(t.resource_limits.render_length > 0)
context = Liquid::Context.new
t.render!(context)
assert(context.resource_limits.assign_score > 0)
assert(context.resource_limits.render_score > 0)
assert(context.resource_limits.render_length > 0)
end
def test_render_length_persists_between_blocks
t = Template.parse("{% if true %}aaaa{% endif %}")
t.resource_limits.render_length_limit = 7
assert_equal("Liquid error: Memory limits exceeded", t.render)
t.resource_limits.render_length_limit = 8
assert_equal("aaaa", t.render)
context = Liquid::Context.new
context.resource_limits.render_length_limit = 7
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
context.resource_limits.render_length_limit = 8
assert_equal("aaaa", t.render(context))
t = Template.parse("{% if true %}aaaa{% endif %}{% if true %}bbb{% endif %}")
t.resource_limits.render_length_limit = 13
assert_equal("Liquid error: Memory limits exceeded", t.render)
t.resource_limits.render_length_limit = 14
assert_equal("aaaabbb", t.render)
context = Liquid::Context.new
context.resource_limits.render_length_limit = 13
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
context.resource_limits.render_length_limit = 14
assert_equal("aaaabbb", t.render(context))
t = Template.parse("{% if true %}a{% endif %}{% if true %}b{% endif %}{% if true %}a{% endif %}{% if true %}b{% endif %}{% if true %}a{% endif %}{% if true %}b{% endif %}")
t.resource_limits.render_length_limit = 5
assert_equal("Liquid error: Memory limits exceeded", t.render)
t.resource_limits.render_length_limit = 11
assert_equal("Liquid error: Memory limits exceeded", t.render)
t.resource_limits.render_length_limit = 12
assert_equal("ababab", t.render)
context = Liquid::Context.new
context.resource_limits.render_length_limit = 5
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
context.resource_limits.render_length_limit = 11
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
context.resource_limits.render_length_limit = 12
assert_equal("ababab", t.render(context))
end
def test_render_length_uses_number_of_bytes_not_characters
t = Template.parse("{% if true %}すごい{% endif %}")
t.resource_limits.render_length_limit = 10
assert_equal("Liquid error: Memory limits exceeded", t.render)
t.resource_limits.render_length_limit = 18
assert_equal("すごい", t.render)
context = Liquid::Context.new
context.resource_limits.render_length_limit = 10
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
context.resource_limits.render_length_limit = 18
assert_equal("すごい", t.render(context))
end
def test_default_resource_limits_unaffected_by_render_with_context
@@ -224,11 +205,12 @@ class TemplateTest < Minitest::Test
def test_can_use_drop_as_context
t = Template.new
t.registers['lulz'] = 'haha'
drop = TemplateContextDrop.new
assert_equal('fizzbuzz', t.parse('{{foo}}').render!(drop))
assert_equal('bar', t.parse('{{bar}}').render!(drop))
assert_equal('haha', t.parse("{{baz}}").render!(drop))
context = Liquid::Context.build(environments: drop, registers: { 'lulz' => 'haha' })
drop.context = context
assert_equal('fizzbuzz', t.parse('{{foo}}').render!(context))
assert_equal('bar', t.parse('{{bar}}').render!(context))
assert_equal('haha', t.parse("{{baz}}").render!(context))
end
def test_render_bang_force_rethrow_errors_on_passed_context
@@ -280,25 +262,27 @@ class TemplateTest < Minitest::Test
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)
t = Template.parse("{{x}} {{y}} {{z.a}} {{z.b}} {{z.c.d}}")
context = Liquid::Context.new('x' => 33, 'z' => { 'a' => 32, 'c' => { 'e' => 31 } })
result = t.render(context, 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)
assert_equal(3, context.errors.count)
assert_instance_of(Liquid::UndefinedVariable, context.errors[0])
assert_equal('Liquid error: undefined variable y', context.errors[0].message)
assert_instance_of(Liquid::UndefinedVariable, context.errors[1])
assert_equal('Liquid error: undefined variable b', context.errors[1].message)
assert_instance_of(Liquid::UndefinedVariable, context.errors[2])
assert_equal('Liquid error: undefined variable d', context.errors[2].message)
end
def test_nil_value_does_not_raise
Liquid::Template.error_mode = :strict
t = Template.parse("some{{x}}thing")
result = t.render!({ 'x' => nil }, strict_variables: true)
t = Template.parse("some{{x}}thing")
context = Liquid::Context.new('x' => nil)
result = t.render!(context, strict_variables: true)
assert_equal(0, t.errors.count)
assert_equal(0, context.errors.count)
assert_equal('something', result)
end
@@ -313,11 +297,13 @@ class TemplateTest < Minitest::Test
def test_undefined_drop_methods
d = DropWithUndefinedMethod.new
t = Template.new.parse('{{ foo }} {{ woot }}')
result = t.render(d, strict_variables: true)
context = Liquid::Context.new(d)
d.context = context
result = t.render(context, strict_variables: true)
assert_equal('foo ', result)
assert_equal(1, t.errors.count)
assert_instance_of(Liquid::UndefinedDropMethod, t.errors[0])
assert_equal(1, context.errors.count)
assert_instance_of(Liquid::UndefinedDropMethod, context.errors[0])
end
def test_undefined_drop_methods_raise
@@ -336,12 +322,13 @@ class TemplateTest < Minitest::Test
"-#{v}-"
end
end
result = t.render({ 'a' => 123, 'x' => 'foo' }, filters: [filters], strict_filters: true)
context = Liquid::Context.new('a' => 123, 'x' => 'foo')
result = t.render(context, 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)
assert_equal(1, context.errors.count)
assert_instance_of(Liquid::UndefinedFilter, context.errors[0])
assert_equal('Liquid error: undefined filter somefilter1', context.errors[0].message)
end
def test_undefined_filters_raise

View File

@@ -51,29 +51,10 @@ class VariableTest < Minitest::Test
assert_equal('cat', Template.parse("{{ nil | append: 'cat' }}").render!)
end
def test_preset_assigns
template = Template.parse(%({{ test }}))
template.assigns['test'] = 'worked'
assert_equal('worked', template.render!)
end
def test_reuse_parsed_template
template = Template.parse(%({{ greeting }} {{ name }}))
template.assigns['greeting'] = 'Goodbye'
template = Template.parse(%({{ greeting }} {{ name }}))
assert_equal('Hello Tobi', template.render!('greeting' => 'Hello', 'name' => 'Tobi'))
assert_equal('Hello ', template.render!('greeting' => 'Hello', 'unknown' => 'Tobi'))
assert_equal('Hello Brian', template.render!('greeting' => 'Hello', 'name' => 'Brian'))
assert_equal('Goodbye Brian', template.render!('name' => 'Brian'))
assert_equal({ 'greeting' => 'Goodbye' }, template.assigns)
end
def test_assigns_not_polluted_from_template
template = Template.parse(%({{ test }}{% assign test = 'bar' %}{{ test }}))
template.assigns['test'] = 'baz'
assert_equal('bazbar', template.render!)
assert_equal('bazbar', template.render!)
assert_equal('foobar', template.render!('test' => 'foo'))
assert_equal('bazbar', template.render!)
assert_equal('Goodbye Brian', template.render!('greeting' => 'Goodbye', 'name' => 'Brian'))
end
def test_hash_with_default_proc