mirror of
https://github.com/kemko/liquid.git
synced 2026-01-03 16:55:40 +03:00
Compare commits
3 Commits
inline-com
...
no-templat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5c31861f9 | ||
|
|
895e63e40a | ||
|
|
219168e89f |
12
README.md
12
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.
|
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.
|
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:
|
Here are some examples:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
template = Liquid::Template.parse("{{x}} {{y}} {{z.a}} {{z.b}}")
|
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
|
#=> '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>]
|
#=> [#<Liquid::UndefinedVariable: Liquid error: undefined variable y>, #<Liquid::UndefinedVariable: Liquid error: undefined variable b>]
|
||||||
```
|
```
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
template = Liquid::Template.parse("{{x | filter1 | upcase}}")
|
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
|
#=> '' # 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>]
|
#=> [#<Liquid::UndefinedFilter: Liquid error: undefined filter filter1>]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ module Liquid
|
|||||||
@strict_variables = false
|
@strict_variables = false
|
||||||
@resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits)
|
@resource_limits = resource_limits || ResourceLimits.new(Template.default_resource_limits)
|
||||||
@base_scope_depth = 0
|
@base_scope_depth = 0
|
||||||
squash_instance_assigns_with_environments
|
|
||||||
|
|
||||||
self.exception_renderer = Template.default_exception_renderer
|
self.exception_renderer = Template.default_exception_renderer
|
||||||
if rethrow_errors
|
if rethrow_errors
|
||||||
@@ -246,16 +245,5 @@ module Liquid
|
|||||||
rescue Liquid::InternalError => exc
|
rescue Liquid::InternalError => exc
|
||||||
exc
|
exc
|
||||||
end
|
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 # Context
|
||||||
end # Liquid
|
end # Liquid
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ module Liquid
|
|||||||
#
|
#
|
||||||
class Template
|
class Template
|
||||||
attr_accessor :root
|
attr_accessor :root
|
||||||
attr_reader :resource_limits, :warnings
|
attr_reader :warnings
|
||||||
|
|
||||||
class TagRegistry
|
class TagRegistry
|
||||||
include Enumerable
|
include Enumerable
|
||||||
@@ -106,11 +106,6 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize
|
|
||||||
@rethrow_errors = false
|
|
||||||
@resource_limits = ResourceLimits.new(Template.default_resource_limits)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Parse source code.
|
# Parse source code.
|
||||||
# Returns self for easy chaining
|
# Returns self for easy chaining
|
||||||
def parse(source, options = {})
|
def parse(source, options = {})
|
||||||
@@ -123,22 +118,6 @@ module Liquid
|
|||||||
self
|
self
|
||||||
end
|
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.
|
# Render takes a hash with local variables.
|
||||||
#
|
#
|
||||||
# if you use the same filters over and over again consider registering them globally
|
# 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
|
# * <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
|
# 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?
|
return '' if @root.nil?
|
||||||
|
|
||||||
context = case args.first
|
context = coerce_context(assigns_or_context)
|
||||||
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
|
|
||||||
|
|
||||||
output = nil
|
output = nil
|
||||||
|
|
||||||
context_register = context.registers.is_a?(StaticRegisters) ? context.registers.static : context.registers
|
context_register = context.registers.is_a?(StaticRegisters) ? context.registers.static : context.registers
|
||||||
|
|
||||||
case args.last
|
case options
|
||||||
when Hash
|
when Hash
|
||||||
options = args.pop
|
output = options[:output] if options[:output]
|
||||||
output = options[:output] if options[:output]
|
|
||||||
|
|
||||||
options[:registers]&.each do |key, register|
|
options[:registers]&.each do |key, register|
|
||||||
context_register[key] = register
|
context_register[key] = register
|
||||||
@@ -191,7 +151,7 @@ module Liquid
|
|||||||
|
|
||||||
apply_options_to_context(context, options)
|
apply_options_to_context(context, options)
|
||||||
when Module, Array
|
when Module, Array
|
||||||
context.add_filters(args.pop)
|
context.add_filters(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
Template.registers.each do |key, register|
|
Template.registers.each do |key, register|
|
||||||
@@ -209,14 +169,15 @@ module Liquid
|
|||||||
end
|
end
|
||||||
rescue Liquid::MemoryError => e
|
rescue Liquid::MemoryError => e
|
||||||
context.handle_error(e)
|
context.handle_error(e)
|
||||||
ensure
|
|
||||||
@errors = context.errors
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render!(*args)
|
def render!(assigns_or_context = nil, options = nil)
|
||||||
@rethrow_errors = true
|
context = coerce_context(assigns_or_context)
|
||||||
render(*args)
|
# rethrow errors
|
||||||
|
context.exception_renderer = ->(_e) { raise }
|
||||||
|
|
||||||
|
render(context, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_to_output_buffer(context, output)
|
def render_to_output_buffer(context, output)
|
||||||
@@ -225,6 +186,22 @@ module Liquid
|
|||||||
|
|
||||||
private
|
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)
|
def tokenize(source)
|
||||||
Tokenizer.new(source, @line_numbers)
|
Tokenizer.new(source, @line_numbers)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -73,26 +73,29 @@ class ThemeRunner
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def compile_and_render(template, layout, assigns, page_template, template_file)
|
def compile_and_render(template, layout, assigns, page_template)
|
||||||
compiled_test = compile_test(template, layout, assigns, page_template, template_file)
|
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)
|
assigns['content_for_layout'] = compiled_test[:tmpl].render!(assigns)
|
||||||
compiled_test[:layout].render!(assigns) if layout
|
compiled_test[:layout].render!(assigns) if layout
|
||||||
end
|
end
|
||||||
|
|
||||||
def compile_all_tests
|
def compile_all_tests
|
||||||
@compiled_tests = []
|
@compiled_tests = []
|
||||||
each_test do |liquid, layout, assigns, page_template, template_name|
|
each_test do |liquid, layout, assigns, _page_template, _template_name|
|
||||||
@compiled_tests << compile_test(liquid, layout, assigns, page_template, template_name)
|
@compiled_tests << compile_test(liquid, layout, assigns)
|
||||||
end
|
end
|
||||||
@compiled_tests
|
@compiled_tests
|
||||||
end
|
end
|
||||||
|
|
||||||
def compile_test(template, layout, assigns, page_template, template_file)
|
def compile_test(template, layout, assigns)
|
||||||
tmpl = init_template(page_template, template_file)
|
parsed_template = Liquid::Template.parse(template).dup
|
||||||
parsed_template = tmpl.parse(template).dup
|
|
||||||
|
|
||||||
if layout
|
if layout
|
||||||
parsed_layout = tmpl.parse(layout)
|
parsed_layout = Liquid::Template.parse(layout)
|
||||||
{ tmpl: parsed_template, assigns: assigns, layout: parsed_layout }
|
{ tmpl: parsed_template, assigns: assigns, layout: parsed_layout }
|
||||||
else
|
else
|
||||||
{ tmpl: parsed_template, assigns: assigns }
|
{ tmpl: parsed_template, assigns: assigns }
|
||||||
@@ -107,16 +110,7 @@ class ThemeRunner
|
|||||||
@tests.each do |test_hash|
|
@tests.each do |test_hash|
|
||||||
# Compute page_template outside of profiler run, uninteresting to profiler
|
# Compute page_template outside of profiler run, uninteresting to profiler
|
||||||
page_template = File.basename(test_hash[:template_name], File.extname(test_hash[:template_name]))
|
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
|
||||||
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
|
end
|
||||||
|
|||||||
@@ -40,26 +40,29 @@ class ErrorHandlingTest < Minitest::Test
|
|||||||
|
|
||||||
def test_standard_error
|
def test_standard_error
|
||||||
template = Liquid::Template.parse(' {{ errors.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(1, context.errors.size)
|
||||||
assert_equal(StandardError, template.errors.first.class)
|
assert_equal(StandardError, context.errors.first.class)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_syntax
|
def test_syntax
|
||||||
template = Liquid::Template.parse(' {{ errors.syntax_error }} ')
|
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(1, context.errors.size)
|
||||||
assert_equal(SyntaxError, template.errors.first.class)
|
assert_equal(SyntaxError, context.errors.first.class)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_argument
|
def test_argument
|
||||||
template = Liquid::Template.parse(' {{ errors.argument_error }} ')
|
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(1, context.errors.size)
|
||||||
assert_equal(ArgumentError, template.errors.first.class)
|
assert_equal(ArgumentError, context.errors.first.class)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_missing_endtag_parse_time_error
|
def test_missing_endtag_parse_time_error
|
||||||
@@ -78,9 +81,10 @@ class ErrorHandlingTest < Minitest::Test
|
|||||||
|
|
||||||
def test_lax_unrecognized_operator
|
def test_lax_unrecognized_operator
|
||||||
template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', error_mode: :lax)
|
template = Liquid::Template.parse(' {% if 1 =! 2 %}ok{% endif %} ', error_mode: :lax)
|
||||||
assert_equal(' Liquid error: Unknown operator =! ', template.render)
|
context = Liquid::Context.new('errors' => ErrorDrop.new)
|
||||||
assert_equal(1, template.errors.size)
|
assert_equal(' Liquid error: Unknown operator =! ', template.render(context))
|
||||||
assert_equal(Liquid::ArgumentError, template.errors.first.class)
|
assert_equal(1, context.errors.size)
|
||||||
|
assert_equal(Liquid::ArgumentError, context.errors.first.class)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_with_line_numbers_adds_numbers_to_parser_errors
|
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
|
def test_default_exception_renderer_with_internal_error
|
||||||
template = Liquid::Template.parse('This is a runtime error: {{ errors.runtime_error }}', line_numbers: true)
|
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('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
|
end
|
||||||
|
|
||||||
def test_setting_default_exception_renderer
|
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 }}')
|
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('This is a runtime error: ', output)
|
||||||
assert_equal([Liquid::ArgumentError], template.errors.map(&:class))
|
assert_equal([Liquid::ArgumentError], context.errors.map(&:class))
|
||||||
ensure
|
ensure
|
||||||
Liquid::Template.default_exception_renderer = old_exception_renderer if old_exception_renderer
|
Liquid::Template.default_exception_renderer = old_exception_renderer if old_exception_renderer
|
||||||
end
|
end
|
||||||
@@ -233,11 +239,12 @@ class ErrorHandlingTest < Minitest::Test
|
|||||||
e.cause
|
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('This is a runtime error: runtime error', output)
|
||||||
assert_equal([Liquid::InternalError], exceptions.map(&:class))
|
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)
|
assert_equal('#<RuntimeError: runtime error>', exceptions.first.cause.inspect)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -250,15 +257,16 @@ class ErrorHandlingTest < Minitest::Test
|
|||||||
def test_included_template_name_with_line_numbers
|
def test_included_template_name_with_line_numbers
|
||||||
old_file_system = Liquid::Template.file_system
|
old_file_system = Liquid::Template.file_system
|
||||||
|
|
||||||
|
context = Liquid::Context.new('errors' => ErrorDrop.new)
|
||||||
begin
|
begin
|
||||||
Liquid::Template.file_system = TestFileSystem.new
|
Liquid::Template.file_system = TestFileSystem.new
|
||||||
|
|
||||||
template = Liquid::Template.parse("Argument error:\n{% include 'product' %}", line_numbers: true)
|
template = Liquid::Template.parse("Argument error:\n{% include 'product' %}", line_numbers: true)
|
||||||
page = template.render('errors' => ErrorDrop.new)
|
page = template.render(context)
|
||||||
ensure
|
ensure
|
||||||
Liquid::Template.file_system = old_file_system
|
Liquid::Template.file_system = old_file_system
|
||||||
end
|
end
|
||||||
assert_equal("Argument error:\nLiquid error (product line 1): argument error", page)
|
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
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -217,8 +217,9 @@ class IncludeTagTest < Minitest::Test
|
|||||||
Liquid::Template.file_system = TestFileSystem.new
|
Liquid::Template.file_system = TestFileSystem.new
|
||||||
|
|
||||||
a = Liquid::Template.parse(' {% include "nested_template" %}')
|
a = Liquid::Template.parse(' {% include "nested_template" %}')
|
||||||
a.render!
|
context = Liquid::Context.new
|
||||||
assert_empty(a.errors)
|
a.render!(context)
|
||||||
|
assert_empty(context.errors)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_passing_options_to_included_templates
|
def test_passing_options_to_included_templates
|
||||||
@@ -257,9 +258,10 @@ class IncludeTagTest < Minitest::Test
|
|||||||
|
|
||||||
def test_including_with_strict_variables
|
def test_including_with_strict_variables
|
||||||
template = Liquid::Template.parse("{% include 'simple' %}", error_mode: :warn)
|
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
|
end
|
||||||
|
|
||||||
def test_break_through_include
|
def test_break_through_include
|
||||||
|
|||||||
@@ -38,12 +38,6 @@ end
|
|||||||
class TemplateTest < Minitest::Test
|
class TemplateTest < Minitest::Test
|
||||||
include Liquid
|
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
|
def test_warnings_is_not_exponential_time
|
||||||
str = "false"
|
str = "false"
|
||||||
100.times do
|
100.times do
|
||||||
@@ -54,43 +48,15 @@ class TemplateTest < Minitest::Test
|
|||||||
assert_equal([], Timeout.timeout(1) { t.warnings })
|
assert_equal([], Timeout.timeout(1) { t.warnings })
|
||||||
end
|
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
|
def test_custom_assigns_do_not_persist_on_same_template
|
||||||
t = Template.new
|
t = Template.new
|
||||||
assert_equal('from custom assigns', t.parse("{{ foo }}").render!('foo' => 'from custom assigns'))
|
assert_equal('from custom assigns', t.parse("{{ foo }}").render!('foo' => 'from custom assigns'))
|
||||||
assert_equal('', t.parse("{{ foo }}").render!)
|
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 instance assigns', t.parse("{% assign foo = 'from instance assigns' %}{{ foo }}").render!)
|
||||||
assert_equal('from custom assigns', t.parse("{{ foo }}").render!('foo' => 'from custom assigns'))
|
assert_equal('from custom assigns', t.parse("{{ foo }}").render!('foo' => 'from custom assigns'))
|
||||||
end
|
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
|
def test_lambda_is_called_once_from_custom_assigns_over_multiple_parses_and_renders
|
||||||
t = Template.new
|
t = Template.new
|
||||||
assigns = { 'number' => -> {
|
assigns = { 'number' => -> {
|
||||||
@@ -105,112 +71,124 @@ class TemplateTest < Minitest::Test
|
|||||||
|
|
||||||
def test_resource_limits_works_with_custom_length_method
|
def test_resource_limits_works_with_custom_length_method
|
||||||
t = Template.parse("{% assign foo = bar %}")
|
t = Template.parse("{% assign foo = bar %}")
|
||||||
t.resource_limits.render_length_limit = 42
|
context = Liquid::Context.new("bar" => SomethingWithLength.new)
|
||||||
assert_equal("", t.render!("bar" => SomethingWithLength.new))
|
context.resource_limits.render_length_limit = 42
|
||||||
|
assert_equal("", t.render!(context))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_resource_limits_render_length
|
def test_resource_limits_render_length
|
||||||
t = Template.parse("0123456789")
|
t = Template.parse("0123456789")
|
||||||
t.resource_limits.render_length_limit = 5
|
context = Liquid::Context.new
|
||||||
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
context.resource_limits.render_length_limit = 5
|
||||||
assert(t.resource_limits.reached?)
|
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!)
|
assert_equal("0123456789", t.render!)
|
||||||
refute_nil(t.resource_limits.render_length)
|
refute_nil(context.resource_limits.render_length)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_resource_limits_render_score
|
def test_resource_limits_render_score
|
||||||
t = Template.parse("{% for a in (1..10) %} {% for a in (1..10) %} foo {% endfor %} {% endfor %}")
|
t = Template.parse("{% for a in (1..10) %} {% for a in (1..10) %} foo {% endfor %} {% endfor %}")
|
||||||
t.resource_limits.render_score_limit = 50
|
context = Liquid::Context.new
|
||||||
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
context.resource_limits.render_score_limit = 50
|
||||||
assert(t.resource_limits.reached?)
|
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 = Template.parse("{% for a in (1..100) %} foo {% endfor %}")
|
||||||
t.resource_limits.render_score_limit = 50
|
context.resource_limits.render_score_limit = 50
|
||||||
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
|
||||||
assert(t.resource_limits.reached?)
|
assert(context.resource_limits.reached?)
|
||||||
|
|
||||||
t.resource_limits.render_score_limit = 200
|
context.resource_limits.render_score_limit = 200
|
||||||
assert_equal((" foo " * 100), t.render!)
|
assert_equal((" foo " * 100), t.render!(context))
|
||||||
refute_nil(t.resource_limits.render_score)
|
refute_nil(context.resource_limits.render_score)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_resource_limits_assign_score
|
def test_resource_limits_assign_score
|
||||||
t = Template.parse("{% assign foo = 42 %}{% assign bar = 23 %}")
|
t = Template.parse("{% assign foo = 42 %}{% assign bar = 23 %}")
|
||||||
t.resource_limits.assign_score_limit = 1
|
context = Liquid::Context.new
|
||||||
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
context.resource_limits.assign_score_limit = 1
|
||||||
assert(t.resource_limits.reached?)
|
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
|
||||||
|
assert(context.resource_limits.reached?)
|
||||||
|
|
||||||
t.resource_limits.assign_score_limit = 2
|
context.resource_limits.assign_score_limit = 2
|
||||||
assert_equal("", t.render!)
|
assert_equal("", t.render!(context))
|
||||||
refute_nil(t.resource_limits.assign_score)
|
refute_nil(context.resource_limits.assign_score)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_resource_limits_assign_score_counts_bytes_not_characters
|
def test_resource_limits_assign_score_counts_bytes_not_characters
|
||||||
t = Template.parse("{% assign foo = 'すごい' %}")
|
t = Template.parse("{% assign foo = 'すごい' %}")
|
||||||
t.render
|
context = Liquid::Context.new
|
||||||
assert_equal(9, t.resource_limits.assign_score)
|
t.render(context)
|
||||||
|
assert_equal(9, context.resource_limits.assign_score)
|
||||||
|
|
||||||
t = Template.parse("{% capture foo %}すごい{% endcapture %}")
|
t = Template.parse("{% capture foo %}すごい{% endcapture %}")
|
||||||
t.render
|
t.render(context)
|
||||||
assert_equal(9, t.resource_limits.assign_score)
|
assert_equal(9, context.resource_limits.assign_score)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_resource_limits_assign_score_nested
|
def test_resource_limits_assign_score_nested
|
||||||
t = Template.parse("{% assign foo = 'aaaa' | reverse %}")
|
t = Template.parse("{% assign foo = 'aaaa' | reverse %}")
|
||||||
|
|
||||||
t.resource_limits.assign_score_limit = 3
|
context = Liquid::Context.new
|
||||||
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
context.resource_limits.assign_score_limit = 3
|
||||||
assert(t.resource_limits.reached?)
|
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
|
||||||
|
assert(context.resource_limits.reached?)
|
||||||
|
|
||||||
t.resource_limits.assign_score_limit = 5
|
context.resource_limits.assign_score_limit = 5
|
||||||
assert_equal("", t.render!)
|
assert_equal("", t.render!(context))
|
||||||
end
|
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
|
context = Liquid::Context.new
|
||||||
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
context.resource_limits.render_score_limit = 50
|
||||||
assert(t.resource_limits.reached?)
|
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
|
||||||
|
assert(context.resource_limits.reached?)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_resource_limits_hash_in_template_gets_updated_even_if_no_limits_are_set
|
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 = Template.parse("{% for a in (1..100) %} {% assign foo = 1 %} {% endfor %}")
|
||||||
t.render!
|
context = Liquid::Context.new
|
||||||
assert(t.resource_limits.assign_score > 0)
|
t.render!(context)
|
||||||
assert(t.resource_limits.render_score > 0)
|
assert(context.resource_limits.assign_score > 0)
|
||||||
assert(t.resource_limits.render_length > 0)
|
assert(context.resource_limits.render_score > 0)
|
||||||
|
assert(context.resource_limits.render_length > 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_length_persists_between_blocks
|
def test_render_length_persists_between_blocks
|
||||||
t = Template.parse("{% if true %}aaaa{% endif %}")
|
t = Template.parse("{% if true %}aaaa{% endif %}")
|
||||||
t.resource_limits.render_length_limit = 7
|
context = Liquid::Context.new
|
||||||
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
context.resource_limits.render_length_limit = 7
|
||||||
t.resource_limits.render_length_limit = 8
|
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
|
||||||
assert_equal("aaaa", t.render)
|
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 = Template.parse("{% if true %}aaaa{% endif %}{% if true %}bbb{% endif %}")
|
||||||
t.resource_limits.render_length_limit = 13
|
context = Liquid::Context.new
|
||||||
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
context.resource_limits.render_length_limit = 13
|
||||||
t.resource_limits.render_length_limit = 14
|
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
|
||||||
assert_equal("aaaabbb", t.render)
|
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 = 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
|
context = Liquid::Context.new
|
||||||
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
context.resource_limits.render_length_limit = 5
|
||||||
t.resource_limits.render_length_limit = 11
|
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
|
||||||
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
context.resource_limits.render_length_limit = 11
|
||||||
t.resource_limits.render_length_limit = 12
|
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
|
||||||
assert_equal("ababab", t.render)
|
context.resource_limits.render_length_limit = 12
|
||||||
|
assert_equal("ababab", t.render(context))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_render_length_uses_number_of_bytes_not_characters
|
def test_render_length_uses_number_of_bytes_not_characters
|
||||||
t = Template.parse("{% if true %}すごい{% endif %}")
|
t = Template.parse("{% if true %}すごい{% endif %}")
|
||||||
t.resource_limits.render_length_limit = 10
|
context = Liquid::Context.new
|
||||||
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
context.resource_limits.render_length_limit = 10
|
||||||
t.resource_limits.render_length_limit = 18
|
assert_equal("Liquid error: Memory limits exceeded", t.render(context))
|
||||||
assert_equal("すごい", t.render)
|
context.resource_limits.render_length_limit = 18
|
||||||
|
assert_equal("すごい", t.render(context))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_default_resource_limits_unaffected_by_render_with_context
|
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
|
def test_can_use_drop_as_context
|
||||||
t = Template.new
|
t = Template.new
|
||||||
t.registers['lulz'] = 'haha'
|
|
||||||
drop = TemplateContextDrop.new
|
drop = TemplateContextDrop.new
|
||||||
assert_equal('fizzbuzz', t.parse('{{foo}}').render!(drop))
|
context = Liquid::Context.build(environments: drop, registers: { 'lulz' => 'haha' })
|
||||||
assert_equal('bar', t.parse('{{bar}}').render!(drop))
|
drop.context = context
|
||||||
assert_equal('haha', t.parse("{{baz}}").render!(drop))
|
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
|
end
|
||||||
|
|
||||||
def test_render_bang_force_rethrow_errors_on_passed_context
|
def test_render_bang_force_rethrow_errors_on_passed_context
|
||||||
@@ -280,25 +259,27 @@ class TemplateTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_undefined_variables
|
def test_undefined_variables
|
||||||
t = Template.parse("{{x}} {{y}} {{z.a}} {{z.b}} {{z.c.d}}")
|
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)
|
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('33 32 ', result)
|
||||||
assert_equal(3, t.errors.count)
|
assert_equal(3, context.errors.count)
|
||||||
assert_instance_of(Liquid::UndefinedVariable, t.errors[0])
|
assert_instance_of(Liquid::UndefinedVariable, context.errors[0])
|
||||||
assert_equal('Liquid error: undefined variable y', t.errors[0].message)
|
assert_equal('Liquid error: undefined variable y', context.errors[0].message)
|
||||||
assert_instance_of(Liquid::UndefinedVariable, t.errors[1])
|
assert_instance_of(Liquid::UndefinedVariable, context.errors[1])
|
||||||
assert_equal('Liquid error: undefined variable b', t.errors[1].message)
|
assert_equal('Liquid error: undefined variable b', context.errors[1].message)
|
||||||
assert_instance_of(Liquid::UndefinedVariable, t.errors[2])
|
assert_instance_of(Liquid::UndefinedVariable, context.errors[2])
|
||||||
assert_equal('Liquid error: undefined variable d', t.errors[2].message)
|
assert_equal('Liquid error: undefined variable d', context.errors[2].message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_nil_value_does_not_raise
|
def test_nil_value_does_not_raise
|
||||||
Liquid::Template.error_mode = :strict
|
Liquid::Template.error_mode = :strict
|
||||||
t = Template.parse("some{{x}}thing")
|
t = Template.parse("some{{x}}thing")
|
||||||
result = t.render!({ 'x' => nil }, strict_variables: true)
|
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)
|
assert_equal('something', result)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -313,11 +294,13 @@ class TemplateTest < Minitest::Test
|
|||||||
def test_undefined_drop_methods
|
def test_undefined_drop_methods
|
||||||
d = DropWithUndefinedMethod.new
|
d = DropWithUndefinedMethod.new
|
||||||
t = Template.new.parse('{{ foo }} {{ woot }}')
|
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('foo ', result)
|
||||||
assert_equal(1, t.errors.count)
|
assert_equal(1, context.errors.count)
|
||||||
assert_instance_of(Liquid::UndefinedDropMethod, t.errors[0])
|
assert_instance_of(Liquid::UndefinedDropMethod, context.errors[0])
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_undefined_drop_methods_raise
|
def test_undefined_drop_methods_raise
|
||||||
@@ -336,12 +319,13 @@ class TemplateTest < Minitest::Test
|
|||||||
"-#{v}-"
|
"-#{v}-"
|
||||||
end
|
end
|
||||||
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('123 ', result)
|
||||||
assert_equal(1, t.errors.count)
|
assert_equal(1, context.errors.count)
|
||||||
assert_instance_of(Liquid::UndefinedFilter, t.errors[0])
|
assert_instance_of(Liquid::UndefinedFilter, context.errors[0])
|
||||||
assert_equal('Liquid error: undefined filter somefilter1', t.errors[0].message)
|
assert_equal('Liquid error: undefined filter somefilter1', context.errors[0].message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_undefined_filters_raise
|
def test_undefined_filters_raise
|
||||||
|
|||||||
@@ -51,29 +51,10 @@ class VariableTest < Minitest::Test
|
|||||||
assert_equal('cat', Template.parse("{{ nil | append: 'cat' }}").render!)
|
assert_equal('cat', Template.parse("{{ nil | append: 'cat' }}").render!)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_preset_assigns
|
|
||||||
template = Template.parse(%({{ test }}))
|
|
||||||
template.assigns['test'] = 'worked'
|
|
||||||
assert_equal('worked', template.render!)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_reuse_parsed_template
|
def test_reuse_parsed_template
|
||||||
template = Template.parse(%({{ greeting }} {{ name }}))
|
template = Template.parse(%({{ greeting }} {{ name }}))
|
||||||
template.assigns['greeting'] = 'Goodbye'
|
|
||||||
assert_equal('Hello Tobi', template.render!('greeting' => 'Hello', 'name' => 'Tobi'))
|
assert_equal('Hello Tobi', template.render!('greeting' => 'Hello', 'name' => 'Tobi'))
|
||||||
assert_equal('Hello ', template.render!('greeting' => 'Hello', 'unknown' => 'Tobi'))
|
assert_equal('Goodbye Brian', template.render!('greeting' => 'Goodbye', 'name' => 'Brian'))
|
||||||
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!)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_hash_with_default_proc
|
def test_hash_with_default_proc
|
||||||
|
|||||||
Reference in New Issue
Block a user