mirror of
https://github.com/kemko/liquid.git
synced 2026-01-02 16:25:42 +03:00
Compare commits
27 Commits
fix-hash-c
...
stack-leve
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1707980a48 | ||
|
|
1370a102c9 | ||
|
|
c9bac9befe | ||
|
|
210a0616f3 | ||
|
|
5149cde5c3 | ||
|
|
22f2cec5de | ||
|
|
4318240ae0 | ||
|
|
aa79c33dda | ||
|
|
b1ef28566e | ||
|
|
41bcc48222 | ||
|
|
27d5106dc9 | ||
|
|
7334073be2 | ||
|
|
5dcefd7d77 | ||
|
|
25c7b05916 | ||
|
|
d17f86ba4d | ||
|
|
384e4313ff | ||
|
|
23f2af8ff5 | ||
|
|
a93eac0268 | ||
|
|
2cc7493cb0 | ||
|
|
85463e1753 | ||
|
|
52ff9b0e84 | ||
|
|
0c58328a40 | ||
|
|
2bb3552033 | ||
|
|
8b751ddf46 | ||
|
|
e5cbdb2b27 | ||
|
|
6ed6e7e12f | ||
|
|
f41ed78378 |
@@ -19,6 +19,10 @@ matrix:
|
|||||||
allow_failures:
|
allow_failures:
|
||||||
- rvm: jruby-head
|
- rvm: jruby-head
|
||||||
|
|
||||||
|
install:
|
||||||
|
- gem install rainbow -v 2.2.1
|
||||||
|
- bundle install
|
||||||
|
|
||||||
script: "bundle exec rake"
|
script: "bundle exec rake"
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
|
|||||||
5
Gemfile
5
Gemfile
@@ -3,9 +3,12 @@ source 'https://rubygems.org'
|
|||||||
gemspec
|
gemspec
|
||||||
gem 'stackprof', platforms: :mri_21
|
gem 'stackprof', platforms: :mri_21
|
||||||
|
|
||||||
|
group :benchmark, :test do
|
||||||
|
gem 'benchmark-ips'
|
||||||
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
gem 'spy', '0.4.1'
|
gem 'spy', '0.4.1'
|
||||||
gem 'benchmark-ips'
|
|
||||||
gem 'rubocop', '0.34.2'
|
gem 'rubocop', '0.34.2'
|
||||||
|
|
||||||
platform :mri do
|
platform :mri do
|
||||||
|
|||||||
@@ -20,10 +20,13 @@
|
|||||||
* Add concat filter to concatenate arrays (#429) [Diogo Beato]
|
* Add concat filter to concatenate arrays (#429) [Diogo Beato]
|
||||||
* Ruby 1.9 support dropped (#491) [Justin Li]
|
* Ruby 1.9 support dropped (#491) [Justin Li]
|
||||||
* Liquid::Template.file_system's read_template_file method is no longer passed the context. (#441) [James Reid-Smith]
|
* Liquid::Template.file_system's read_template_file method is no longer passed the context. (#441) [James Reid-Smith]
|
||||||
* Remove support for `liquid_methods`
|
* Remove `liquid_methods` (See https://github.com/Shopify/liquid/pull/568 for replacement)
|
||||||
* Liquid::Template.register_filter raises when the module overrides registered public methods as private or protected (#705) [Gaurav Chande]
|
* Liquid::Template.register_filter raises when the module overrides registered public methods as private or protected (#705) [Gaurav Chande]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
* Fix variable names being detected as an operator when starting with contains (#788) [Michael Angell]
|
||||||
|
* Fix include tag used with strict_variables (#828) [QuickPay]
|
||||||
* Fix map filter when value is a Proc (#672) [Guillaume Malette]
|
* Fix map filter when value is a Proc (#672) [Guillaume Malette]
|
||||||
* Fix truncate filter when value is not a string (#672) [Guillaume Malette]
|
* Fix truncate filter when value is not a string (#672) [Guillaume Malette]
|
||||||
* Fix behaviour of escape filter when input is nil (#665) [Tanel Jakobsoo]
|
* Fix behaviour of escape filter when input is nil (#665) [Tanel Jakobsoo]
|
||||||
|
|||||||
@@ -93,10 +93,11 @@ module Liquid
|
|||||||
rescue MemoryError => e
|
rescue MemoryError => e
|
||||||
raise e
|
raise e
|
||||||
rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e
|
rescue UndefinedVariable, UndefinedDropMethod, UndefinedFilter => e
|
||||||
context.handle_error(e, token.line_number, token.raw)
|
context.handle_error(e, token.line_number)
|
||||||
output << nil
|
output << nil
|
||||||
rescue ::StandardError => e
|
rescue ::StandardError => e
|
||||||
output << context.handle_error(e, token.line_number, token.raw)
|
line_number = token.is_a?(String) ? nil : token.line_number
|
||||||
|
output << context.handle_error(e, line_number)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -106,7 +107,7 @@ module Liquid
|
|||||||
private
|
private
|
||||||
|
|
||||||
def render_node(node, context)
|
def render_node(node, context)
|
||||||
node_output = (node.respond_to?(:render) ? node.render(context) : node)
|
node_output = node.is_a?(String) ? node : node.render(context)
|
||||||
node_output = node_output.is_a?(Array) ? node_output.join : node_output.to_s
|
node_output = node_output.is_a?(Array) ? node_output.join : node_output.to_s
|
||||||
|
|
||||||
context.resource_limits.render_length += node_output.length
|
context.resource_limits.render_length += node_output.length
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ module Liquid
|
|||||||
# c.evaluate #=> true
|
# c.evaluate #=> true
|
||||||
#
|
#
|
||||||
class Condition #:nodoc:
|
class Condition #:nodoc:
|
||||||
|
@@depth = 0
|
||||||
@@operators = {
|
@@operators = {
|
||||||
'=='.freeze => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
|
'=='.freeze => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
|
||||||
'!='.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
|
'!='.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
|
||||||
@@ -47,6 +48,11 @@ module Liquid
|
|||||||
when :or
|
when :or
|
||||||
result || @child_condition.evaluate(context)
|
result || @child_condition.evaluate(context)
|
||||||
when :and
|
when :and
|
||||||
|
@@depth += 1
|
||||||
|
if @@depth >= 500
|
||||||
|
@@depth = 0
|
||||||
|
raise StackLevelError, "Nesting too deep".freeze
|
||||||
|
end
|
||||||
result && @child_condition.evaluate(context)
|
result && @child_condition.evaluate(context)
|
||||||
else
|
else
|
||||||
result
|
result
|
||||||
@@ -110,7 +116,7 @@ module Liquid
|
|||||||
|
|
||||||
if operation.respond_to?(:call)
|
if operation.respond_to?(:call)
|
||||||
operation.call(self, left, right)
|
operation.call(self, left, right)
|
||||||
elsif left.respond_to?(operation) && right.respond_to?(operation)
|
elsif left.respond_to?(operation) && right.respond_to?(operation) && !left.is_a?(Hash) && !right.is_a?(Hash)
|
||||||
begin
|
begin
|
||||||
left.send(operation, right)
|
left.send(operation, right)
|
||||||
rescue ::ArgumentError => e
|
rescue ::ArgumentError => e
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ module Liquid
|
|||||||
@interrupts.pop
|
@interrupts.pop
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_error(e, line_number = nil, raw_token = nil)
|
def handle_error(e, line_number = nil)
|
||||||
e = internal_error unless e.is_a?(Liquid::Error)
|
e = internal_error unless e.is_a?(Liquid::Error)
|
||||||
e.template_name ||= template_name
|
e.template_name ||= template_name
|
||||||
e.line_number ||= line_number
|
e.line_number ||= line_number
|
||||||
@@ -160,7 +160,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Fetches an object starting at the local scope and then moving up the hierachy
|
# Fetches an object starting at the local scope and then moving up the hierachy
|
||||||
def find_variable(key)
|
def find_variable(key, raise_on_not_found: true)
|
||||||
# This was changed from find() to find_index() because this is a very hot
|
# This was changed from find() to find_index() because this is a very hot
|
||||||
# path and find_index() is optimized in MRI to reduce object allocation
|
# path and find_index() is optimized in MRI to reduce object allocation
|
||||||
index = @scopes.find_index { |s| s.key?(key) }
|
index = @scopes.find_index { |s| s.key?(key) }
|
||||||
@@ -170,7 +170,7 @@ module Liquid
|
|||||||
|
|
||||||
if scope.nil?
|
if scope.nil?
|
||||||
@environments.each do |e|
|
@environments.each do |e|
|
||||||
variable = lookup_and_evaluate(e, key)
|
variable = lookup_and_evaluate(e, key, raise_on_not_found: raise_on_not_found)
|
||||||
unless variable.nil?
|
unless variable.nil?
|
||||||
scope = e
|
scope = e
|
||||||
break
|
break
|
||||||
@@ -179,7 +179,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
scope ||= @environments.last || @scopes.last
|
scope ||= @environments.last || @scopes.last
|
||||||
variable ||= lookup_and_evaluate(scope, key)
|
variable ||= lookup_and_evaluate(scope, key, raise_on_not_found: raise_on_not_found)
|
||||||
|
|
||||||
variable = variable.to_liquid
|
variable = variable.to_liquid
|
||||||
variable.context = self if variable.respond_to?(:context=)
|
variable.context = self if variable.respond_to?(:context=)
|
||||||
@@ -187,8 +187,8 @@ module Liquid
|
|||||||
variable
|
variable
|
||||||
end
|
end
|
||||||
|
|
||||||
def lookup_and_evaluate(obj, key)
|
def lookup_and_evaluate(obj, key, raise_on_not_found: true)
|
||||||
if @strict_variables && obj.respond_to?(:key?) && !obj.key?(key)
|
if @strict_variables && raise_on_not_found && obj.respond_to?(:key?) && !obj.key?(key)
|
||||||
raise Liquid::UndefinedVariable, "undefined variable #{key}"
|
raise Liquid::UndefinedVariable, "undefined variable #{key}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ module Liquid
|
|||||||
DOUBLE_STRING_LITERAL = /"[^\"]*"/
|
DOUBLE_STRING_LITERAL = /"[^\"]*"/
|
||||||
NUMBER_LITERAL = /-?\d+(\.\d+)?/
|
NUMBER_LITERAL = /-?\d+(\.\d+)?/
|
||||||
DOTDOT = /\.\./
|
DOTDOT = /\.\./
|
||||||
COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains/
|
COMPARISON_OPERATOR = /==|!=|<>|<=?|>=?|contains(?=\s)/
|
||||||
|
|
||||||
def initialize(input)
|
def initialize(input)
|
||||||
@ss = StringScanner.new(input.rstrip)
|
@ss = StringScanner.new(input)
|
||||||
end
|
end
|
||||||
|
|
||||||
def tokenize
|
def tokenize
|
||||||
@@ -29,6 +29,7 @@ module Liquid
|
|||||||
|
|
||||||
until @ss.eos?
|
until @ss.eos?
|
||||||
@ss.skip(/\s*/)
|
@ss.skip(/\s*/)
|
||||||
|
break if @ss.eos?
|
||||||
tok = case
|
tok = case
|
||||||
when t = @ss.scan(COMPARISON_OPERATOR) then [:comparison, t]
|
when t = @ss.scan(COMPARISON_OPERATOR) then [:comparison, t]
|
||||||
when t = @ss.scan(SINGLE_STRING_LITERAL) then [:string, t]
|
when t = @ss.scan(SINGLE_STRING_LITERAL) then [:string, t]
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ module Liquid
|
|||||||
end
|
end
|
||||||
|
|
||||||
def join(glue)
|
def join(glue)
|
||||||
to_a.join(glue)
|
to_a.join(glue.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
def concat(args)
|
def concat(args)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ module Liquid
|
|||||||
|
|
||||||
def self.add_filter(filter)
|
def self.add_filter(filter)
|
||||||
raise ArgumentError, "Expected module but got: #{filter.class}" unless filter.is_a?(Module)
|
raise ArgumentError, "Expected module but got: #{filter.class}" unless filter.is_a?(Module)
|
||||||
unless self.class.include?(filter)
|
unless self.include?(filter)
|
||||||
invokable_non_public_methods = (filter.private_instance_methods + filter.protected_instance_methods).select { |m| invokable?(m) }
|
invokable_non_public_methods = (filter.private_instance_methods + filter.protected_instance_methods).select { |m| invokable?(m) }
|
||||||
if invokable_non_public_methods.any?
|
if invokable_non_public_methods.any?
|
||||||
raise MethodOverrideError, "Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')}"
|
raise MethodOverrideError, "Filter overrides registered public methods as non public: #{invokable_non_public_methods.join(', ')}"
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ module Liquid
|
|||||||
# {{ item.name }}
|
# {{ item.name }}
|
||||||
# {% end %}
|
# {% end %}
|
||||||
#
|
#
|
||||||
# To reverse the for loop simply use {% for item in collection reversed %}
|
# To reverse the for loop simply use {% for item in collection reversed %} (note that the flag's spelling is different to the filter `reverse`)
|
||||||
#
|
#
|
||||||
# == Available variables:
|
# == Available variables:
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ module Liquid
|
|||||||
variable = if @variable_name_expr
|
variable = if @variable_name_expr
|
||||||
context.evaluate(@variable_name_expr)
|
context.evaluate(@variable_name_expr)
|
||||||
else
|
else
|
||||||
context.find_variable(template_name)
|
context.find_variable(template_name, raise_on_not_found: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
old_template_name = context.template_name
|
old_template_name = context.template_name
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first
|
|||||||
profiler = ThemeRunner.new
|
profiler = ThemeRunner.new
|
||||||
|
|
||||||
Benchmark.ips do |x|
|
Benchmark.ips do |x|
|
||||||
x.time = 60
|
x.time = 10
|
||||||
x.warmup = 5
|
x.warmup = 5
|
||||||
|
|
||||||
puts
|
puts
|
||||||
@@ -13,5 +13,6 @@ Benchmark.ips do |x|
|
|||||||
puts
|
puts
|
||||||
|
|
||||||
x.report("parse:") { profiler.compile }
|
x.report("parse:") { profiler.compile }
|
||||||
x.report("parse & run:") { profiler.run }
|
x.report("render:") { profiler.render }
|
||||||
|
x.report("parse & render:") { profiler.run }
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -21,53 +21,100 @@ class ThemeRunner
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Load all templates into memory, do this now so that
|
# Initialize a new liquid ThemeRunner instance
|
||||||
# we don't profile IO.
|
# Will load all templates into memory, do this now so that we don't profile IO.
|
||||||
def initialize
|
def initialize
|
||||||
@tests = Dir[__dir__ + '/tests/**/*.liquid'].collect do |test|
|
@tests = Dir[__dir__ + '/tests/**/*.liquid'].collect do |test|
|
||||||
next if File.basename(test) == 'theme.liquid'
|
next if File.basename(test) == 'theme.liquid'
|
||||||
|
|
||||||
theme_path = File.dirname(test) + '/theme.liquid'
|
theme_path = File.dirname(test) + '/theme.liquid'
|
||||||
|
{
|
||||||
[File.read(test), (File.file?(theme_path) ? File.read(theme_path) : nil), test]
|
liquid: File.read(test),
|
||||||
|
layout: (File.file?(theme_path) ? File.read(theme_path) : nil),
|
||||||
|
template_name: test
|
||||||
|
}
|
||||||
end.compact
|
end.compact
|
||||||
|
|
||||||
|
compile_all_tests
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# `compile` will test just the compilation portion of liquid without any templates
|
||||||
def compile
|
def compile
|
||||||
# Dup assigns because will make some changes to them
|
@tests.each do |test_hash|
|
||||||
|
Liquid::Template.new.parse(test_hash[:liquid])
|
||||||
@tests.each do |liquid, layout, template_name|
|
Liquid::Template.new.parse(test_hash[:layout])
|
||||||
tmpl = Liquid::Template.new
|
|
||||||
tmpl.parse(liquid)
|
|
||||||
tmpl = Liquid::Template.new
|
|
||||||
tmpl.parse(layout)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# `run` is called to benchmark rendering and compiling at the same time
|
||||||
def run
|
def run
|
||||||
# Dup assigns because will make some changes to them
|
each_test do |liquid, layout, assigns, page_template, template_name|
|
||||||
assigns = Database.tables.dup
|
|
||||||
|
|
||||||
@tests.each do |liquid, layout, template_name|
|
|
||||||
# Compute page_tempalte outside of profiler run, uninteresting to profiler
|
|
||||||
page_template = File.basename(template_name, File.extname(template_name))
|
|
||||||
compile_and_render(liquid, layout, assigns, page_template, template_name)
|
compile_and_render(liquid, layout, assigns, page_template, template_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# `render` is called to benchmark just the render portion of liquid
|
||||||
|
def render
|
||||||
|
@compiled_tests.each do |test|
|
||||||
|
tmpl = test[:tmpl]
|
||||||
|
assigns = test[:assigns]
|
||||||
|
layout = test[:layout]
|
||||||
|
|
||||||
|
if layout
|
||||||
|
assigns['content_for_layout'] = tmpl.render!(assigns)
|
||||||
|
layout.render!(assigns)
|
||||||
|
else
|
||||||
|
tmpl.render!(assigns)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
def compile_and_render(template, layout, assigns, page_template, template_file)
|
def compile_and_render(template, layout, assigns, page_template, template_file)
|
||||||
|
compiled_test = compile_test(template, layout, assigns, page_template, template_file)
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
|
||||||
|
if layout
|
||||||
|
parsed_layout = tmpl.parse(layout)
|
||||||
|
{ tmpl: parsed_template, assigns: assigns, layout: parsed_layout }
|
||||||
|
else
|
||||||
|
{ tmpl: parsed_template, assigns: assigns }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# utility method with similar functionality needed in `compile_all_tests` and `run`
|
||||||
|
def each_test
|
||||||
|
# Dup assigns because will make some changes to them
|
||||||
|
assigns = Database.tables.dup
|
||||||
|
|
||||||
|
@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])
|
||||||
|
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 = Liquid::Template.new
|
||||||
tmpl.assigns['page_title'] = 'Page title'
|
tmpl.assigns['page_title'] = 'Page title'
|
||||||
tmpl.assigns['template'] = page_template
|
tmpl.assigns['template'] = page_template
|
||||||
tmpl.registers[:file_system] = ThemeRunner::FileSystem.new(File.dirname(template_file))
|
tmpl.registers[:file_system] = ThemeRunner::FileSystem.new(File.dirname(template_file))
|
||||||
|
tmpl
|
||||||
content_for_layout = tmpl.parse(template).render!(assigns)
|
|
||||||
|
|
||||||
if layout
|
|
||||||
assigns['content_for_layout'] = content_for_layout
|
|
||||||
tmpl.parse(layout).render!(assigns)
|
|
||||||
else
|
|
||||||
content_for_layout
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -115,4 +115,8 @@ class ParsingQuirksTest < Minitest::Test
|
|||||||
assert_template_result('12345', "{% for i in (1...5) %}{{ i }}{% endfor %}")
|
assert_template_result('12345', "{% for i in (1...5) %}{{ i }}{% endfor %}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_contains_in_id
|
||||||
|
assert_template_result(' YES ', '{% if containsallshipments == true %} YES {% endif %}', 'containsallshipments' => true)
|
||||||
|
end
|
||||||
end # ParsingQuirksTest
|
end # ParsingQuirksTest
|
||||||
|
|||||||
@@ -170,6 +170,7 @@ class StandardFiltersTest < Minitest::Test
|
|||||||
def test_join
|
def test_join
|
||||||
assert_equal '1 2 3 4', @filters.join([1, 2, 3, 4])
|
assert_equal '1 2 3 4', @filters.join([1, 2, 3, 4])
|
||||||
assert_equal '1 - 2 - 3 - 4', @filters.join([1, 2, 3, 4], ' - ')
|
assert_equal '1 - 2 - 3 - 4', @filters.join([1, 2, 3, 4], ' - ')
|
||||||
|
assert_equal '1121314', @filters.join([1, 2, 3, 4], 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_sort
|
def test_sort
|
||||||
|
|||||||
@@ -235,4 +235,11 @@ class IncludeTagTest < Minitest::Test
|
|||||||
|
|
||||||
assert_template_result "Product: Draft 151cm ", "{% assign page = 'product' %}{% include page for foo %}", "foo" => { 'title' => 'Draft 151cm' }
|
assert_template_result "Product: Draft 151cm ", "{% assign page = 'product' %}{% include page for foo %}", "foo" => { 'title' => 'Draft 151cm' }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_including_with_strict_variables
|
||||||
|
template = Liquid::Template.parse("{% include 'simple' %}", error_mode: :warn)
|
||||||
|
template.render(nil, strict_variables: true)
|
||||||
|
|
||||||
|
assert_equal [], template.errors
|
||||||
|
end
|
||||||
end # IncludeTagTest
|
end # IncludeTagTest
|
||||||
|
|||||||
@@ -64,6 +64,14 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
assert_evaluates_argument_error '1', '<=', 0
|
assert_evaluates_argument_error '1', '<=', 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_hash_compare_backwards_compatibility
|
||||||
|
assert_equal nil, Condition.new({}, '>', 2).evaluate
|
||||||
|
assert_equal nil, Condition.new(2, '>', {}).evaluate
|
||||||
|
assert_equal false, Condition.new({}, '==', 2).evaluate
|
||||||
|
assert_equal true, Condition.new({ 'a' => 1 }, '==', { 'a' => 1 }).evaluate
|
||||||
|
assert_equal true, Condition.new({ 'a' => 2 }, 'contains', 'a').evaluate
|
||||||
|
end
|
||||||
|
|
||||||
def test_contains_works_on_arrays
|
def test_contains_works_on_arrays
|
||||||
@context = Liquid::Context.new
|
@context = Liquid::Context.new
|
||||||
@context['array'] = [1, 2, 3, 4, 5]
|
@context['array'] = [1, 2, 3, 4, 5]
|
||||||
@@ -122,6 +130,17 @@ class ConditionUnitTest < Minitest::Test
|
|||||||
assert_equal false, condition.evaluate
|
assert_equal false, condition.evaluate
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_maximum_recursion_depth
|
||||||
|
condition = Condition.new(1, '==', 1)
|
||||||
|
|
||||||
|
assert_raises(Liquid::StackLevelError) do
|
||||||
|
(1..510).each do
|
||||||
|
condition.evaluate
|
||||||
|
condition.and Condition.new(2, '==', 2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_should_allow_custom_proc_operator
|
def test_should_allow_custom_proc_operator
|
||||||
Condition.operators['starts_with'] = proc { |cond, left, right| left =~ %r{^#{right}} }
|
Condition.operators['starts_with'] = proc { |cond, left, right| left =~ %r{^#{right}} }
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class LexerUnitTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_comparison
|
def test_comparison
|
||||||
tokens = Lexer.new('== <> contains').tokenize
|
tokens = Lexer.new('== <> contains ').tokenize
|
||||||
assert_equal [[:comparison, '=='], [:comparison, '<>'], [:comparison, 'contains'], [:end_of_string]], tokens
|
assert_equal [[:comparison, '=='], [:comparison, '<>'], [:comparison, 'contains'], [:end_of_string]], tokens
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -145,4 +145,20 @@ class StrainerUnitTest < Minitest::Test
|
|||||||
Strainer.global_filter(LateAddedFilter)
|
Strainer.global_filter(LateAddedFilter)
|
||||||
assert_equal 'filtered', Strainer.create(nil).invoke('late_added_filter', 'input')
|
assert_equal 'filtered', Strainer.create(nil).invoke('late_added_filter', 'input')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_add_filter_does_not_include_already_included_module
|
||||||
|
mod = Module.new do
|
||||||
|
class << self
|
||||||
|
attr_accessor :include_count
|
||||||
|
def included(mod)
|
||||||
|
self.include_count += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.include_count = 0
|
||||||
|
end
|
||||||
|
strainer = Context.new.strainer
|
||||||
|
strainer.class.add_filter(mod)
|
||||||
|
strainer.class.add_filter(mod)
|
||||||
|
assert_equal 1, mod.include_count
|
||||||
|
end
|
||||||
end # StrainerTest
|
end # StrainerTest
|
||||||
|
|||||||
Reference in New Issue
Block a user