mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-12 23:40:45 -07:00
rejects variables like a/b in for loops, closes #150
This commit is contained in:
+26
-26
@@ -1,6 +1,6 @@
|
|||||||
module Liquid
|
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.
|
# Several useful variables are available to you within the loop.
|
||||||
#
|
#
|
||||||
# == Basic usage:
|
# == Basic usage:
|
||||||
@@ -22,7 +22,7 @@ module Liquid
|
|||||||
#
|
#
|
||||||
# {% for item in collection limit:5 offset:10 %}
|
# {% for item in collection limit:5 offset:10 %}
|
||||||
# {{ 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 %}
|
||||||
#
|
#
|
||||||
@@ -31,7 +31,7 @@ module Liquid
|
|||||||
# forloop.name:: 'item-collection'
|
# forloop.name:: 'item-collection'
|
||||||
# forloop.length:: Length of the loop
|
# forloop.length:: Length of the loop
|
||||||
# forloop.index:: The current item's position in the collection;
|
# 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
|
# This is helpful for non-programmers who start believe
|
||||||
# the first item in an array is 1, not 0.
|
# the first item in an array is 1, not 0.
|
||||||
# forloop.index0:: The current item's position in the collection
|
# 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.first:: Returns true if the item is the first item.
|
||||||
# forloop.last:: Returns true if the item is the last item.
|
# forloop.last:: Returns true if the item is the last item.
|
||||||
#
|
#
|
||||||
class For < Block
|
class For < Block
|
||||||
Syntax = /(\w+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
|
Syntax = /\A(\w+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/o
|
||||||
|
|
||||||
def initialize(tag_name, markup, tokens)
|
def initialize(tag_name, markup, tokens)
|
||||||
if markup =~ Syntax
|
if markup =~ Syntax
|
||||||
@variable_name = $1
|
@variable_name = $1
|
||||||
@collection_name = $2
|
@collection_name = $2
|
||||||
@name = "#{$1}-#{$2}"
|
@name = "#{$1}-#{$2}"
|
||||||
@reversed = $3
|
@reversed = $3
|
||||||
@attributes = {}
|
@attributes = {}
|
||||||
markup.scan(TagAttributes) do |key, value|
|
markup.scan(TagAttributes) do |key, value|
|
||||||
@attributes[key] = value
|
@attributes[key] = value
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
raise SyntaxError.new("Syntax Error in 'for loop' - Valid syntax: for [item] in [collection]")
|
raise SyntaxError.new("Syntax Error in 'for loop' - Valid syntax: for [item] in [collection]")
|
||||||
end
|
end
|
||||||
@@ -68,51 +68,51 @@ module Liquid
|
|||||||
return super unless tag == 'else'
|
return super unless tag == 'else'
|
||||||
@nodelist = @else_block = []
|
@nodelist = @else_block = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def render(context)
|
def render(context)
|
||||||
context.registers[:for] ||= Hash.new(0)
|
context.registers[:for] ||= Hash.new(0)
|
||||||
|
|
||||||
collection = context[@collection_name]
|
collection = context[@collection_name]
|
||||||
collection = collection.to_a if collection.is_a?(Range)
|
collection = collection.to_a if collection.is_a?(Range)
|
||||||
|
|
||||||
# Maintains Ruby 1.8.7 String#each behaviour on 1.9
|
# Maintains Ruby 1.8.7 String#each behaviour on 1.9
|
||||||
return render_else(context) unless iterable?(collection)
|
return render_else(context) unless iterable?(collection)
|
||||||
|
|
||||||
from = if @attributes['offset'] == 'continue'
|
from = if @attributes['offset'] == 'continue'
|
||||||
context.registers[:for][@name].to_i
|
context.registers[:for][@name].to_i
|
||||||
else
|
else
|
||||||
context[@attributes['offset']].to_i
|
context[@attributes['offset']].to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
limit = context[@attributes['limit']]
|
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)
|
segment = Utils.slice_collection_using_each(collection, from, to)
|
||||||
|
|
||||||
return render_else(context) if segment.empty?
|
return render_else(context) if segment.empty?
|
||||||
|
|
||||||
segment.reverse! if @reversed
|
segment.reverse! if @reversed
|
||||||
|
|
||||||
result = ''
|
result = ''
|
||||||
|
|
||||||
length = segment.length
|
length = segment.length
|
||||||
|
|
||||||
# Store our progress through the collection for the continue flag
|
# Store our progress through the collection for the continue flag
|
||||||
context.registers[:for][@name] = from + segment.length
|
context.registers[:for][@name] = from + segment.length
|
||||||
|
|
||||||
context.stack do
|
context.stack do
|
||||||
segment.each_with_index do |item, index|
|
segment.each_with_index do |item, index|
|
||||||
context[@variable_name] = item
|
context[@variable_name] = item
|
||||||
context['forloop'] = {
|
context['forloop'] = {
|
||||||
'name' => @name,
|
'name' => @name,
|
||||||
'length' => length,
|
'length' => length,
|
||||||
'index' => index + 1,
|
'index' => index + 1,
|
||||||
'index0' => index,
|
'index0' => index,
|
||||||
'rindex' => length - index,
|
'rindex' => length - index,
|
||||||
'rindex0' => length - index - 1,
|
'rindex0' => length - index - 1,
|
||||||
'first' => (index == 0),
|
'first' => (index == 0),
|
||||||
'last' => (index == length - 1) }
|
'last' => (index == length - 1) }
|
||||||
|
|
||||||
result << render_all(@for_block, context)
|
result << render_all(@for_block, context)
|
||||||
|
|
||||||
@@ -124,8 +124,8 @@ module Liquid
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
|||||||
@@ -281,4 +281,10 @@ HERE
|
|||||||
def test_blank_string_not_iterable
|
def test_blank_string_not_iterable
|
||||||
assert_template_result('', "{% for char in characters %}I WILL NOT BE OUTPUT{% endfor %}", 'characters' => '')
|
assert_template_result('', "{% for char in characters %}I WILL NOT BE OUTPUT{% endfor %}", 'characters' => '')
|
||||||
end
|
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
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user