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:
Tobias Lütke
2008-05-08 12:30:48 -04:00
parent 7f58cbf82d
commit 4c0cfae0b7
9 changed files with 94 additions and 47 deletions

View File

@@ -90,8 +90,7 @@ module Liquid
token.respond_to?(:render) ? token.render(context) : token
rescue Exception => e
context.handle_error(e)
end
end
end
end
end

View File

@@ -60,6 +60,7 @@ module Liquid
# push new local scope on the stack. use <tt>Context#stack</tt> instead
def push
raise StackLevelError, "Nesting too deep" if @scopes.length > 100
@scopes.unshift({})
end

View File

@@ -7,4 +7,5 @@ module Liquid
class FileSystemError < Error; end
class StandardError < Error; end
class SyntaxError < Error; end
class StackLevelError < Error; end
end

View File

@@ -64,35 +64,31 @@ module Liquid
collection = context[@collection_name]
collection = collection.to_a if collection.is_a?(Range)
return '' if collection.nil? or collection.empty?
range = (0..collection.length)
if @attributes['limit'] or @attributes['offset']
offset = 0
if @attributes['offset'] == 'continue'
offset = context.registers[:for][@name]
else
offset = context[@attributes['offset']] || 0
end
limit = context[@attributes['limit']]
range_end = limit ? offset + limit : collection.length
range = (offset..range_end-1)
# Save the range end in the registers so that future calls to
# offset:continue have something to pick up
context.registers[:for][@name] = range_end
return '' unless collection.respond_to?(:each)
from = if @attributes['offset'] == 'continue'
context.registers[:for][@name].to_i
else
context[@attributes['offset']].to_i
end
result = []
segment = collection[range]
return '' if segment.nil?
context.stack do
length = segment.length
limit = context[@attributes['limit']]
to = limit ? limit.to_i + from : nil
segment = slice_collection_using_each(collection, from, to)
segment.each_with_index do |item, index|
return '' if segment.empty?
result = []
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,
@@ -103,15 +99,32 @@ module Liquid
'rindex0' => length - index -1,
'first' => (index == 0),
'last' => (index == length - 1) }
result << render_all(@nodelist, context)
end
end
# Store position of last element we rendered. This allows us to do
result
end
result
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
Template.register_tag('for', For)

View File

@@ -83,7 +83,7 @@ module Liquid
# filters and tags and might be useful to integrate liquid more with its host application
#
def render(*args)
return '' if @root.nil?
return '' if @root.nil?
context = case args.first
when Liquid::Context
@@ -107,17 +107,17 @@ module Liquid
if options[:filters]
context.add_filters(options[:filters])
end
end
when Module
context.add_filters(args.pop)
when Array
context.add_filters(args.pop)
end
# render the nodelist.
# for performance reasons we get a array back here. to_s will make a string out of it
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
ensure
@errors = context.errors