rejects variables like a/b in for loops, closes #150

This commit is contained in:
Peter Schröder 2013-06-11 17:20:35 -04:00
parent 94ff457744
commit a2df5a421d
2 changed files with 32 additions and 26 deletions

View File

@ -1,6 +1,6 @@
module Liquid
# "For" iterates over an array or collection.
# "For" iterates over an array or collection.
# Several useful variables are available to you within the loop.
#
# == Basic usage:
@ -22,7 +22,7 @@ module Liquid
#
# {% for item in collection limit:5 offset:10 %}
# {{ item.name }}
# {% end %}
# {% end %}
#
# To reverse the for loop simply use {% for item in collection reversed %}
#
@ -31,7 +31,7 @@ module Liquid
# forloop.name:: 'item-collection'
# forloop.length:: Length of the loop
# forloop.index:: The current item's position in the collection;
# forloop.index starts at 1.
# forloop.index starts at 1.
# This is helpful for non-programmers who start believe
# the first item in an array is 1, not 0.
# forloop.index0:: The current item's position in the collection
@ -43,19 +43,19 @@ module Liquid
# forloop.first:: Returns true if the item is the first item.
# forloop.last:: Returns true if the item is the last item.
#
class For < Block
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
class For < Block
Syntax = /\A(\w+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
def initialize(tag_name, markup, tokens)
if markup =~ Syntax
@variable_name = $1
@collection_name = $2
@name = "#{$1}-#{$2}"
@reversed = $3
@name = "#{$1}-#{$2}"
@reversed = $3
@attributes = {}
markup.scan(TagAttributes) do |key, value|
@attributes[key] = value
end
end
else
raise SyntaxError.new("Syntax Error in 'for loop' - Valid syntax: for [item] in [collection]")
end
@ -68,51 +68,51 @@ module Liquid
return super unless tag == 'else'
@nodelist = @else_block = []
end
def render(context)
def render(context)
context.registers[:for] ||= Hash.new(0)
collection = context[@collection_name]
collection = collection.to_a if collection.is_a?(Range)
# Maintains Ruby 1.8.7 String#each behaviour on 1.9
return render_else(context) unless iterable?(collection)
from = if @attributes['offset'] == 'continue'
context.registers[:for][@name].to_i
else
context[@attributes['offset']].to_i
end
limit = context[@attributes['limit']]
to = limit ? limit.to_i + from : nil
to = limit ? limit.to_i + from : nil
segment = Utils.slice_collection_using_each(collection, from, to)
return render_else(context) if segment.empty?
segment.reverse! if @reversed
result = ''
length = segment.length
length = segment.length
# Store our progress through the collection for the continue flag
context.registers[:for][@name] = from + segment.length
context.stack do
segment.each_with_index do |item, index|
context[@variable_name] = item
context['forloop'] = {
'name' => @name,
'length' => length,
'index' => index + 1,
'index0' => index,
'index' => index + 1,
'index0' => index,
'rindex' => length - index,
'rindex0' => length - index - 1,
'first' => (index == 0),
'last' => (index == length - 1) }
'last' => (index == length - 1) }
result << render_all(@for_block, context)
@ -124,8 +124,8 @@ module Liquid
end
end
end
result
end
result
end
private

View File

@ -281,4 +281,10 @@ HERE
def test_blank_string_not_iterable
assert_template_result('', "{% for char in characters %}I WILL NOT BE OUTPUT{% endfor %}", 'characters' => '')
end
def test_bad_variable_naming_in_for_loop
assert_raise(Liquid::SyntaxError) do
Liquid::Template.parse('{% for a/b in x %}{% endfor %}')
end
end
end