mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-12 23:40:45 -07:00
Changed implementation of For in such a way that it only depends on the existence of a each method. This allows drops to simply implement each for enumeration
This commit is contained in:
@@ -91,7 +91,6 @@ module Liquid
|
|||||||
rescue Exception => e
|
rescue Exception => e
|
||||||
context.handle_error(e)
|
context.handle_error(e)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ module Liquid
|
|||||||
|
|
||||||
# push new local scope on the stack. use <tt>Context#stack</tt> instead
|
# push new local scope on the stack. use <tt>Context#stack</tt> instead
|
||||||
def push
|
def push
|
||||||
|
raise StackLevelError, "Nesting too deep" if @scopes.length > 100
|
||||||
@scopes.unshift({})
|
@scopes.unshift({})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ module Liquid
|
|||||||
class FileSystemError < Error; end
|
class FileSystemError < Error; end
|
||||||
class StandardError < Error; end
|
class StandardError < Error; end
|
||||||
class SyntaxError < Error; end
|
class SyntaxError < Error; end
|
||||||
|
class StackLevelError < Error; end
|
||||||
end
|
end
|
||||||
+33
-20
@@ -64,34 +64,30 @@ module Liquid
|
|||||||
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)
|
||||||
|
|
||||||
return '' if collection.nil? or collection.empty?
|
return '' unless collection.respond_to?(:each)
|
||||||
|
|
||||||
range = (0..collection.length)
|
from = if @attributes['offset'] == 'continue'
|
||||||
|
context.registers[:for][@name].to_i
|
||||||
if @attributes['limit'] or @attributes['offset']
|
|
||||||
offset = 0
|
|
||||||
if @attributes['offset'] == 'continue'
|
|
||||||
offset = context.registers[:for][@name]
|
|
||||||
else
|
else
|
||||||
offset = context[@attributes['offset']] || 0
|
context[@attributes['offset']].to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
limit = context[@attributes['limit']]
|
limit = context[@attributes['limit']]
|
||||||
|
to = limit ? limit.to_i + from : nil
|
||||||
|
|
||||||
range_end = limit ? offset + limit : collection.length
|
|
||||||
range = (offset..range_end-1)
|
|
||||||
|
|
||||||
# Save the range end in the registers so that future calls to
|
segment = slice_collection_using_each(collection, from, to)
|
||||||
# offset:continue have something to pick up
|
|
||||||
context.registers[:for][@name] = range_end
|
return '' if segment.empty?
|
||||||
end
|
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
segment = collection[range]
|
|
||||||
return '' if segment.nil?
|
|
||||||
|
|
||||||
context.stack do
|
|
||||||
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|
|
segment.each_with_index do |item, index|
|
||||||
context[@variable_name] = item
|
context[@variable_name] = item
|
||||||
context['forloop'] = {
|
context['forloop'] = {
|
||||||
@@ -107,11 +103,28 @@ module Liquid
|
|||||||
result << render_all(@nodelist, context)
|
result << render_all(@nodelist, context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Store position of last element we rendered. This allows us to do
|
|
||||||
|
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def slice_collection_using_each(collection, from, to)
|
||||||
|
segments = []
|
||||||
|
index = 0
|
||||||
|
yielded = 0
|
||||||
|
collection.each do |item|
|
||||||
|
|
||||||
|
if to && to <= index
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if from <= index
|
||||||
|
segments << item
|
||||||
|
end
|
||||||
|
|
||||||
|
index += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
segments
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Template.register_tag('for', For)
|
Template.register_tag('for', For)
|
||||||
|
|||||||
@@ -108,16 +108,16 @@ module Liquid
|
|||||||
if options[:filters]
|
if options[:filters]
|
||||||
context.add_filters(options[:filters])
|
context.add_filters(options[:filters])
|
||||||
end
|
end
|
||||||
|
|
||||||
when Module
|
when Module
|
||||||
context.add_filters(args.pop)
|
context.add_filters(args.pop)
|
||||||
when Array
|
when Array
|
||||||
context.add_filters(args.pop)
|
context.add_filters(args.pop)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# render the nodelist.
|
|
||||||
# for performance reasons we get a array back here. to_s will make a string out of it
|
|
||||||
begin
|
begin
|
||||||
|
# render the nodelist.
|
||||||
|
# for performance reasons we get a array back here. join will make a string out of it
|
||||||
@root.render(context).join
|
@root.render(context).join
|
||||||
ensure
|
ensure
|
||||||
@errors = context.errors
|
@errors = context.errors
|
||||||
|
|||||||
@@ -61,6 +61,16 @@ class ProductDrop < Liquid::Drop
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class EnumerableDrop < Liquid::Drop
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
def each
|
||||||
|
yield 1
|
||||||
|
yield 2
|
||||||
|
yield 3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
class DropsTest < Test::Unit::TestCase
|
class DropsTest < Test::Unit::TestCase
|
||||||
include Liquid
|
include Liquid
|
||||||
@@ -134,6 +144,10 @@ class DropsTest < Test::Unit::TestCase
|
|||||||
assert_equal '123', Liquid::Template.parse( '{%for a in dummy%}{{ context.loop_pos }}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1,2,3])
|
assert_equal '123', Liquid::Template.parse( '{%for a in dummy%}{{ context.loop_pos }}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1,2,3])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_enumerable_drop
|
||||||
|
assert_equal '123', Liquid::Template.parse( '{% for c in collection %}{{c}}{% endfor %}').render('collection' => EnumerableDrop.new)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -59,6 +59,21 @@ class ErrorHandlingTest < Test::Unit::TestCase
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_missing_endtag
|
||||||
|
|
||||||
|
assert_nothing_raised do
|
||||||
|
|
||||||
|
template = Liquid::Template.parse(' {% for a in b %} ... ')
|
||||||
|
assert_equal ' Liquid error: Unknown operator =! ', template.render
|
||||||
|
|
||||||
|
assert_equal 1, template.errors.size
|
||||||
|
assert_equal Liquid::SyntaxError, template.errors.first.class
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
def test_unrecognized_operator
|
def test_unrecognized_operator
|
||||||
|
|
||||||
assert_nothing_raised do
|
assert_nothing_raised do
|
||||||
|
|||||||
@@ -97,8 +97,9 @@ class IncludeTagTest < Test::Unit::TestCase
|
|||||||
|
|
||||||
Liquid::Template.file_system = infinite_file_system.new
|
Liquid::Template.file_system = infinite_file_system.new
|
||||||
|
|
||||||
assert_match /-{552}Liquid error: stack level too deep$/,
|
assert_raise(Liquid::StackLevelError) do
|
||||||
Template.parse("{% include 'loop' %}").render
|
Template.parse("{% include 'loop' %}").render!
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,10 @@ HERE
|
|||||||
assert_template_result('1234','{%for i in array limit:4 %}{{ i }}{%endfor%}',assigns)
|
assert_template_result('1234','{%for i in array limit:4 %}{{ i }}{%endfor%}',assigns)
|
||||||
assert_template_result('3456','{%for i in array limit:4 offset:2 %}{{ i }}{%endfor%}',assigns)
|
assert_template_result('3456','{%for i in array limit:4 offset:2 %}{{ i }}{%endfor%}',assigns)
|
||||||
assert_template_result('3456','{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}',assigns)
|
assert_template_result('3456','{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}',assigns)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_dynamic_variable_limiting
|
||||||
|
assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]}
|
||||||
assigns['limit'] = 2
|
assigns['limit'] = 2
|
||||||
assigns['offset'] = 2
|
assigns['offset'] = 2
|
||||||
assert_template_result('34','{%for i in array limit: limit offset: offset %}{{ i }}{%endfor%}',assigns)
|
assert_template_result('34','{%for i in array limit: limit offset: offset %}{{ i }}{%endfor%}',assigns)
|
||||||
|
|||||||
Reference in New Issue
Block a user