Compare commits

..

1 Commits

Author SHA1 Message Date
Simon Génier
a61903da54 Add support for iterating ranges with float boundaries.
The meaning I gave is to start at the lower bound and step by one. This has the
advantage of having (n..m) and ((n.to_f)..(m.to_f)) behave the same. Another
thing we could do that have this same property is to cast bounds to integer.
This would do the right thing if there is a floating point rounding error, but I
feel it is more surprising for cases such as (1.5..8).

Note that it is not possible to create ranges with floating point boundaries
directly in Liquid at the moment. However, since there is no distinction between
Ruby and Liquid ranges, a drop can introduce such a value by returning it.
2017-08-09 12:13:00 -04:00
11 changed files with 44 additions and 60 deletions

View File

@@ -7,6 +7,8 @@ AllCops:
Metrics/BlockNesting:
Max: 3
Exclude:
- 'lib/liquid/block_body.rb'
Metrics/ModuleLength:
Enabled: false

View File

@@ -15,35 +15,38 @@ module Liquid
def parse(tokenizer, parse_context)
parse_context.line_number = tokenizer.line_number
while token = tokenizer.shift
next if token.empty?
case
when token.start_with?(TAGSTART)
whitespace_handler(token, parse_context)
unless token =~ FullToken
raise_missing_tag_terminator(token, parse_context)
unless token.empty?
case
when token.start_with?(TAGSTART)
whitespace_handler(token, parse_context)
if token =~ FullToken
tag_name = $1
markup = $2
# fetch the tag from registered blocks
if tag = registered_tags[tag_name]
new_tag = tag.parse(tag_name, markup, tokenizer, parse_context)
@blank &&= new_tag.blank?
@nodelist << new_tag
else
# end parsing if we reach an unknown tag and let the caller decide
# determine how to proceed
return yield tag_name, markup
end
else
raise_missing_tag_terminator(token, parse_context)
end
when token.start_with?(VARSTART)
whitespace_handler(token, parse_context)
@nodelist << create_variable(token, parse_context)
@blank = false
else
if parse_context.trim_whitespace
token.lstrip!
end
parse_context.trim_whitespace = false
@nodelist << token
@blank &&= !!(token =~ /\A\s*\z/)
end
tag_name = $1
markup = $2
# fetch the tag from registered blocks
unless tag = registered_tags[tag_name]
# end parsing if we reach an unknown tag and let the caller decide
# determine how to proceed
return yield tag_name, markup
end
new_tag = tag.parse(tag_name, markup, tokenizer, parse_context)
@blank &&= new_tag.blank?
@nodelist << new_tag
when token.start_with?(VARSTART)
whitespace_handler(token, parse_context)
@nodelist << create_variable(token, parse_context)
@blank = false
else
if parse_context.trim_whitespace
token.lstrip!
end
parse_context.trim_whitespace = false
@nodelist << token
@blank &&= !!(token =~ /\A\s*\z/)
end
parse_context.line_number = tokenizer.line_number
end

View File

@@ -171,9 +171,7 @@ module Liquid
if scope.nil?
@environments.each do |e|
variable = lookup_and_evaluate(e, key, raise_on_not_found: raise_on_not_found)
# When lookup returned a value OR there is no value but the lookup also did not raise
# then it is the value we are looking for.
if !variable.nil? || @strict_variables && raise_on_not_found
unless variable.nil?
scope = e
break
end

View File

@@ -7,12 +7,6 @@ class String # :nodoc:
end
end
class Symbol # :nodoc:
def to_liquid
to_s
end
end
class Array # :nodoc:
def to_liquid
self

View File

@@ -201,14 +201,12 @@ module Liquid
# Replace occurrences of a string with another
def replace(input, string, replacement = ''.freeze)
replacement = replacement.to_s
input.to_s.gsub(string.to_s) { replacement }
input.to_s.gsub(string.to_s, replacement.to_s)
end
# Replace the first occurrences of a string with another
def replace_first(input, string, replacement = ''.freeze)
replacement = replacement.to_s
input.to_s.sub(string.to_s) { replacement }
input.to_s.sub(string.to_s, replacement.to_s)
end
# remove a substring

View File

@@ -30,11 +30,11 @@ module Liquid
end
def render(context)
context.registers[:cycle] ||= {}
context.registers[:cycle] ||= Hash.new(0)
context.stack do
key = context.evaluate(@name)
iteration = context.registers[:cycle][key].to_i
iteration = context.registers[:cycle][key]
result = context.evaluate(@variables[iteration])
iteration += 1
iteration = 0 if iteration >= @variables.size

View File

@@ -120,7 +120,7 @@ module Liquid
private
def collection_segment(context)
offsets = context.registers[:for] ||= {}
offsets = context.registers[:for] ||= Hash.new(0)
from = if @from == :continue
offsets[@name].to_i
@@ -129,7 +129,7 @@ module Liquid
end
collection = context.evaluate(@collection_name)
collection = collection.to_a if collection.is_a?(Range)
collection = collection.step(1).to_a if collection.is_a?(Range)
limit = context.evaluate(@limit)
to = limit ? limit.to_i + from : nil

View File

@@ -362,10 +362,8 @@ class StandardFiltersTest < Minitest::Test
def test_replace
assert_equal '2 2 2 2', @filters.replace('1 1 1 1', '1', 2)
assert_equal '2 2 2 2', @filters.replace('1 1 1 1', 1, 2)
assert_equal "\\& \\& \\& \\&", @filters.replace('1 1 1 1', '1', "\\&")
assert_equal '2 1 1 1', @filters.replace_first('1 1 1 1', '1', 2)
assert_equal '2 1 1 1', @filters.replace_first('1 1 1 1', 1, 2)
assert_equal '\\& 1 1 1', @filters.replace_first("1 1 1 1", '1', "\\&")
assert_template_result '2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}"
end

View File

@@ -48,6 +48,10 @@ HERE
def test_for_with_variable_range
assert_template_result(' 1 2 3 ', '{%for item in (1..foobar) %} {{item}} {%endfor%}', "foobar" => 3)
assert_template_result(' 1.0 2.0 3.0 ', '{%for item in foobar %} {{item}} {%endfor%}', "foobar" => (1..3.0))
assert_template_result(' 1.0 2.0 3.0 ', '{%for item in foobar %} {{item}} {%endfor%}', "foobar" => (1.0..3))
assert_template_result(' 1.0 2.0 3.0 ', '{%for item in foobar %} {{item}} {%endfor%}', "foobar" => (1.0..3.0))
assert_template_result(' 1.5 2.5 ', '{%for item in foobar %} {{item}} {%endfor%}', "foobar" => (1.5..3))
end
def test_for_with_hash_value_range

View File

@@ -261,15 +261,6 @@ class TemplateTest < Minitest::Test
assert_equal 'Liquid error: undefined variable d', t.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)
assert_equal 0, t.errors.count
assert_equal 'something', result
end
def test_undefined_variables_raise
t = Template.parse("{{x}} {{y}} {{z.a}} {{z.b}} {{z.c.d}}")

View File

@@ -89,8 +89,4 @@ class VariableTest < Minitest::Test
def test_multiline_variable
assert_equal 'worked', Template.parse("{{\ntest\n}}").render!('test' => 'worked')
end
def test_render_symbol
assert_template_result 'bar', '{{ foo }}', 'foo' => :bar
end
end