mirror of
https://github.com/kemko/liquid.git
synced 2026-01-01 15:55:40 +03:00
Compare commits
3 Commits
parse-cont
...
no-templat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5c31861f9 | ||
|
|
895e63e40a | ||
|
|
219168e89f |
14
README.md
14
README.md
@@ -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.
|
||||
|
||||
@@ -34,7 +34,6 @@ module Liquid
|
||||
@strict_variables = false
|
||||
@resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits)
|
||||
@base_scope_depth = 0
|
||||
squash_instance_assigns_with_environments
|
||||
|
||||
self.exception_renderer = Template.default_exception_renderer
|
||||
if rethrow_errors
|
||||
@@ -246,16 +245,5 @@ module Liquid
|
||||
rescue Liquid::InternalError => exc
|
||||
exc
|
||||
end
|
||||
|
||||
def squash_instance_assigns_with_environments
|
||||
@scopes.last.each_key do |k|
|
||||
@environments.each do |env|
|
||||
if env.key?(k)
|
||||
scopes.last[k] = lookup_and_evaluate(env, k)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end # squash_instance_assigns_with_environments
|
||||
end # Context
|
||||
end # Liquid
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -73,26 +73,29 @@ class ThemeRunner
|
||||
|
||||
private
|
||||
|
||||
def compile_and_render(template, layout, assigns, page_template, template_file)
|
||||
compiled_test = compile_test(template, layout, assigns, page_template, template_file)
|
||||
def compile_and_render(template, layout, assigns, page_template)
|
||||
assigns = assigns.merge(
|
||||
'page_title' => 'Page title',
|
||||
'template' => page_template,
|
||||
)
|
||||
compiled_test = compile_test(template, layout, assigns)
|
||||
assigns['content_for_layout'] = compiled_test[:tmpl].render!(assigns)
|
||||
compiled_test[:layout].render!(assigns) if layout
|
||||
end
|
||||
|
||||
def compile_all_tests
|
||||
@compiled_tests = []
|
||||
each_test do |liquid, layout, assigns, page_template, template_name|
|
||||
@compiled_tests << compile_test(liquid, layout, assigns, page_template, template_name)
|
||||
each_test do |liquid, layout, assigns, _page_template, _template_name|
|
||||
@compiled_tests << compile_test(liquid, layout, assigns)
|
||||
end
|
||||
@compiled_tests
|
||||
end
|
||||
|
||||
def compile_test(template, layout, assigns, page_template, template_file)
|
||||
tmpl = init_template(page_template, template_file)
|
||||
parsed_template = tmpl.parse(template).dup
|
||||
def compile_test(template, layout, assigns)
|
||||
parsed_template = Liquid::Template.parse(template).dup
|
||||
|
||||
if layout
|
||||
parsed_layout = tmpl.parse(layout)
|
||||
parsed_layout = Liquid::Template.parse(layout)
|
||||
{ tmpl: parsed_template, assigns: assigns, layout: parsed_layout }
|
||||
else
|
||||
{ tmpl: parsed_template, assigns: assigns }
|
||||
@@ -107,16 +110,7 @@ class ThemeRunner
|
||||
@tests.each do |test_hash|
|
||||
# Compute page_template outside of profiler run, uninteresting to profiler
|
||||
page_template = File.basename(test_hash[:template_name], File.extname(test_hash[:template_name]))
|
||||
yield(test_hash[:liquid], test_hash[:layout], assigns, page_template, test_hash[:template_name])
|
||||
yield(test_hash[:liquid], test_hash[:layout], assigns, page_template)
|
||||
end
|
||||
end
|
||||
|
||||
# set up a new Liquid::Template object for use in `compile_and_render` and `compile_test`
|
||||
def init_template(page_template, template_file)
|
||||
tmpl = Liquid::Template.new
|
||||
tmpl.assigns['page_title'] = 'Page title'
|
||||
tmpl.assigns['template'] = page_template
|
||||
tmpl.registers[:file_system] = ThemeRunner::FileSystem.new(File.dirname(template_file))
|
||||
tmpl
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,43 +48,15 @@ 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'))
|
||||
assert_equal('', t.parse("{{ foo }}").render!)
|
||||
end
|
||||
|
||||
def test_custom_assigns_squash_instance_assigns
|
||||
t = Template.new
|
||||
assert_equal('from instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render!)
|
||||
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 +71,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 +202,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 +259,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 +294,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 +319,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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user