public
Rubygem
Description: Liquid markup language. Save, customer facing template language for flexible web apps.
Homepage: http://www.liquidmarkup.org
Clone URL: git://github.com/tobi/liquid.git
Click here to lend your support to: liquid and make a donation at www.pledgie.com !
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
Tobias Lütke (author)
Thu May 08 09:30:48 -0700 2008
commit  4c0cfae0b725cd6945fcdd495b0c877b08b8a90b
tree    4a48a8f05ce379202b478d7a1a4965bdad9df5f7
parent  7f58cbf82d9a064c191a1e5ca957f1850bbed5fb
...
90
91
92
93
94
 
95
96
97
...
90
91
92
 
 
93
94
95
96
0
@@ -90,8 +90,7 @@ module Liquid
0
           token.respond_to?(:render) ? token.render(context) : token
0
         rescue Exception => e
0
           context.handle_error(e)
0
- end
0
-
0
+ end
0
       end
0
     end
0
   end
...
60
61
62
 
63
64
65
...
60
61
62
63
64
65
66
0
@@ -60,6 +60,7 @@ module Liquid
0
 
0
     # push new local scope on the stack. use <tt>Context#stack</tt> instead
0
     def push
0
+ raise StackLevelError, "Nesting too deep" if @scopes.length > 100
0
       @scopes.unshift({})
0
     end
0
     
...
7
8
9
 
10
11
...
7
8
9
10
11
12
0
@@ -7,4 +7,5 @@ module Liquid
0
   class FileSystemError < Error; end
0
   class StandardError < Error; end
0
   class SyntaxError < Error; end
0
+ class StackLevelError < Error; end
0
 end
0
\ No newline at end of file
...
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
 
 
 
 
 
 
86
87
 
 
 
 
 
 
 
 
 
88
89
90
91
92
93
 
 
94
95
 
 
 
 
 
96
97
98
...
103
104
105
106
 
107
108
109
110
111
112
113
114
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
116
117
...
64
65
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
68
69
70
71
72
73
 
74
75
76
77
78
79
80
81
82
83
 
 
 
 
 
84
85
86
 
87
88
89
90
91
92
93
94
...
99
100
101
 
102
103
104
105
 
 
 
 
 
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
0
@@ -64,35 +64,31 @@ module Liquid
0
       collection = context[@collection_name]
0
       collection = collection.to_a if collection.is_a?(Range)
0
     
0
- return '' if collection.nil? or collection.empty?
0
-
0
- range = (0..collection.length)
0
-
0
- if @attributes['limit'] or @attributes['offset']
0
- offset = 0
0
- if @attributes['offset'] == 'continue'
0
- offset = context.registers[:for][@name]
0
- else
0
- offset = context[@attributes['offset']] || 0
0
- end
0
- limit = context[@attributes['limit']]
0
-
0
- range_end = limit ? offset + limit : collection.length
0
- range = (offset..range_end-1)
0
-
0
- # Save the range end in the registers so that future calls to
0
- # offset:continue have something to pick up
0
- context.registers[:for][@name] = range_end
0
+ return '' unless collection.respond_to?(:each)
0
+
0
+ from = if @attributes['offset'] == 'continue'
0
+ context.registers[:for][@name].to_i
0
+ else
0
+ context[@attributes['offset']].to_i
0
       end
0
-
0
+
0
+ limit = context[@attributes['limit']]
0
+ to = limit ? limit.to_i + from : nil
0
+
0
+
0
+ segment = slice_collection_using_each(collection, from, to)
0
+
0
+ return '' if segment.empty?
0
+
0
       result = []
0
- segment = collection[range]
0
- return '' if segment.nil?
0
-
0
- context.stack do
0
- length = segment.length
0
+
0
+ length = segment.length
0
       
0
- segment.each_with_index do |item, index|
0
+ # Store our progress through the collection for the continue flag
0
+ context.registers[:for][@name] = from + segment.length
0
+
0
+ context.stack do
0
+ segment.each_with_index do |item, index|
0
           context[@variable_name] = item
0
           context['forloop'] = {
0
             'name' => @name,
0
@@ -103,15 +99,32 @@ module Liquid
0
             'rindex0' => length - index -1,
0
             'first' => (index == 0),
0
             'last' => (index == length - 1) }
0
-
0
+
0
           result << render_all(@nodelist, context)
0
         end
0
       end
0
-
0
- # Store position of last element we rendered. This allows us to do
0
-
0
- result
0
- end
0
+ result
0
+ end
0
+
0
+ def slice_collection_using_each(collection, from, to)
0
+ segments = []
0
+ index = 0
0
+ yielded = 0
0
+ collection.each do |item|
0
+
0
+ if to && to <= index
0
+ break
0
+ end
0
+
0
+ if from <= index
0
+ segments << item
0
+ end
0
+
0
+ index += 1
0
+ end
0
+
0
+ segments
0
+ end
0
   end
0
 
0
   Template.register_tag('for', For)
...
83
84
85
86
 
87
88
89
...
107
108
109
110
 
 
111
112
113
114
115
116
117
118
119
 
120
 
 
121
122
123
...
83
84
85
 
86
87
88
89
...
107
108
109
 
110
111
112
113
114
115
116
 
 
 
 
117
118
119
120
121
122
123
0
@@ -83,7 +83,7 @@ module Liquid
0
     # filters and tags and might be useful to integrate liquid more with its host application
0
     #
0
     def render(*args)
0
- return '' if @root.nil?
0
+ return '' if @root.nil?
0
 
0
       context = case args.first
0
       when Liquid::Context
0
@@ -107,17 +107,17 @@ module Liquid
0
 
0
         if options[:filters]
0
           context.add_filters(options[:filters])
0
- end
0
+ end
0
+
0
       when Module
0
         context.add_filters(args.pop)
0
       when Array
0
         context.add_filters(args.pop)
0
       end
0
-
0
-
0
- # render the nodelist.
0
- # for performance reasons we get a array back here. to_s will make a string out of it
0
+
0
       begin
0
+ # render the nodelist.
0
+ # for performance reasons we get a array back here. join will make a string out of it
0
         @root.render(context).join
0
       ensure
0
         @errors = context.errors
...
59
60
61
 
 
 
 
 
 
 
 
 
 
62
63
64
...
132
133
134
 
 
 
 
135
136
137
...
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
...
142
143
144
145
146
147
148
149
150
151
0
@@ -59,6 +59,16 @@ class ProductDrop < Liquid::Drop
0
     def callmenot
0
       "protected"
0
     end
0
+end
0
+
0
+class EnumerableDrop < Liquid::Drop
0
+ include Enumerable
0
+
0
+ def each
0
+ yield 1
0
+ yield 2
0
+ yield 3
0
+ end
0
 end
0
 
0
 
0
@@ -132,6 +142,10 @@ class DropsTest < Test::Unit::TestCase
0
   
0
   def test_access_context_from_drop
0
     assert_equal '123', Liquid::Template.parse( '{%for a in dummy%}{{ context.loop_pos }}{% endfor %}' ).render('context' => ContextDrop.new, 'dummy' => [1,2,3])
0
+ end
0
+
0
+ def test_enumerable_drop
0
+ assert_equal '123', Liquid::Template.parse( '{% for c in collection %}{{c}}{% endfor %}').render('collection' => EnumerableDrop.new)
0
   end
0
   
0
   
...
57
58
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
61
 
62
63
64
...
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
0
@@ -57,8 +57,23 @@ class ErrorHandlingTest < Test::Unit::TestCase
0
       
0
     end
0
     
0
+ end
0
+
0
+ def test_missing_endtag
0
+
0
+ assert_nothing_raised do
0
+
0
+ template = Liquid::Template.parse(' {% for a in b %} ... ')
0
+ assert_equal ' Liquid error: Unknown operator =! ', template.render
0
+
0
+ assert_equal 1, template.errors.size
0
+ assert_equal Liquid::SyntaxError, template.errors.first.class
0
+
0
+ end
0
+
0
   end
0
   
0
+
0
   def test_unrecognized_operator
0
     
0
     assert_nothing_raised do
...
96
97
98
99
100
101
 
 
 
 
102
103
104
...
96
97
98
 
 
 
99
100
101
102
103
104
105
0
@@ -96,9 +96,10 @@ class IncludeTagTest < Test::Unit::TestCase
0
     end
0
     
0
     Liquid::Template.file_system = infinite_file_system.new
0
-
0
- assert_match /-{552}Liquid error: stack level too deep$/,
0
- Template.parse("{% include 'loop' %}").render
0
+
0
+ assert_raise(Liquid::StackLevelError) do
0
+ Template.parse("{% include 'loop' %}").render!
0
+ end
0
     
0
   end
0
             
...
104
105
106
107
108
 
 
 
 
 
109
110
111
 
112
113
114
...
104
105
106
 
 
107
108
109
110
111
112
113
 
114
115
116
117
0
@@ -104,11 +104,14 @@ HERE
0
     assert_template_result('12','{%for i in array limit:2 %}{{ i }}{%endfor%}',assigns)
0
     assert_template_result('1234','{%for i in array limit:4 %}{{ i }}{%endfor%}',assigns)
0
     assert_template_result('3456','{%for i in array limit:4 offset:2 %}{{ i }}{%endfor%}',assigns)
0
- assert_template_result('3456','{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}',assigns)
0
-
0
+ assert_template_result('3456','{%for i in array limit: 4 offset: 2 %}{{ i }}{%endfor%}',assigns)
0
+ end
0
+
0
+ def test_dynamic_variable_limiting
0
+ assigns = {'array' => [1,2,3,4,5,6,7,8,9,0]}
0
     assigns['limit'] = 2
0
     assigns['offset'] = 2
0
- assert_template_result('34','{%for i in array limit: limit offset: offset %}{{ i }}{%endfor%}',assigns)
0
+ assert_template_result('34','{%for i in array limit: limit offset: offset %}{{ i }}{%endfor%}',assigns)
0
   end
0
   
0
   def test_nested_for

Comments

    No one has commented yet.