diff --git a/lib/liquid/tags/for.rb b/lib/liquid/tags/for.rb index 8d2b27b..47e67d1 100644 --- a/lib/liquid/tags/for.rb +++ b/lib/liquid/tags/for.rb @@ -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 diff --git a/test/liquid/tags/for_tag_test.rb b/test/liquid/tags/for_tag_test.rb index edfdc88..6a0e55d 100644 --- a/test/liquid/tags/for_tag_test.rb +++ b/test/liquid/tags/for_tag_test.rb @@ -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