public
Fork of technoweenie/mephisto
Description: A mirror of the mephisto code-base
Homepage: http://mephistoblog.com/
Clone URL: git://github.com/danielw/mephisto.git
updated Liquid to the latest revision
danielw (author)
Mon Apr 28 13:16:45 -0700 2008
commit  ba541ed01348c6359bfb6a5c2402606490282ff5
tree    7b91b4478a4b7cbf0bde19ff721904bf3d7c0c9f
parent  9072b487bf45c5e41e33c66b32d94aea84732d1b
...
1
2
 
 
3
4
5
...
1
2
3
4
5
6
7
0
@@ -1,5 +1,7 @@
0
 Changelog
0
 
0
+Added If with or / and expressions
0
+
0
 Implemented .to_liquid for all objects which can be passed to liquid like Strings Arrays Hashes Numerics and Booleans.
0
 To export new objects to liquid just implement .to_liquid on them and return objects which themselves have .to_liquid methods.
0
 
...
13
14
15
16
17
 
 
18
19
20
21
...
13
14
15
 
 
16
17
18
19
20
21
0
@@ -13,8 +13,8 @@ included in all copies or substantial portions of the Software.
0
 
0
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
0
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
0
-NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
0
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
0
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
0
 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
0
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
0
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0
\ No newline at end of file
...
13
14
15
16
17
 
 
18
19
20
...
32
33
34
 
35
36
37
38
 
 
39
40
41
...
52
53
54
 
55
56
57
...
13
14
15
 
 
16
17
18
19
20
...
32
33
34
35
36
37
 
 
38
39
40
41
42
...
53
54
55
56
57
58
59
0
@@ -13,8 +13,8 @@
0
 #
0
 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
0
 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
0
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
0
-# NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
0
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
0
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
0
 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
0
 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
0
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
0
@@ -32,10 +32,11 @@ module Liquid
0
   VariableSegment = /[\w\-]\??/
0
   VariableStart = /\{\{/
0
   VariableEnd = /\}\}/
0
+ VariableIncompleteEnd = /\}\}?/
0
   QuotedFragment = /"[^"]+"|'[^']+'|[^\s,|]+/
0
   TagAttributes = /(\w+)\s*\:\s*(#{QuotedFragment})/
0
- TemplateParser = /(#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableEnd})/
0
- VariableParser = /(?=\[)#{VariableSegment}+(?=\])|#{VariableSegment}+/
0
+ TemplateParser = /(#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd})/
0
+ VariableParser = /\[[^\]]+\]|#{VariableSegment}+/
0
 end
0
 
0
 require 'liquid/drop'
0
@@ -52,6 +53,7 @@ require 'liquid/template'
0
 require 'liquid/htmltags'
0
 require 'liquid/standardfilters'
0
 require 'liquid/condition'
0
+require 'liquid/module_ex'
0
 
0
 # Load all the tags of the standard library
0
 #
...
87
88
89
90
91
92
93
94
 
95
96
97
...
87
88
89
 
 
 
 
 
90
91
92
93
0
@@ -87,11 +87,7 @@ module Liquid
0
     def render_all(list, context)
0
       list.collect do |token|
0
         begin
0
- if token.respond_to?(:render)
0
- token.render(context)
0
- else
0
- token.to_s
0
- end
0
+ token.respond_to?(:render) ? token.render(context) : token
0
         rescue Exception => e
0
           context.handle_error(e)
0
         end
...
1
2
 
3
4
5
6
7
8
9
 
10
11
12
...
14
15
16
17
 
 
18
19
20
...
26
27
28
 
 
29
30
31
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
34
35
...
38
39
40
 
 
 
 
41
42
43
...
70
71
72
 
73
74
75
76
77
78
 
79
80
81
82
83
84
 
 
85
86
87
...
1
 
2
3
4
5
6
7
8
 
9
10
11
12
...
14
15
16
 
17
18
19
20
21
...
27
28
29
30
31
32
33
34
 
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
...
58
59
60
61
62
63
64
65
66
67
...
94
95
96
97
98
99
100
101
102
 
103
104
105
106
107
108
 
109
110
111
112
113
0
@@ -1,12 +1,12 @@
0
 module Liquid
0
- # Container for liquid nodes which conveniently wrapps decision making logic
0
+ # Container for liquid nodes which conveniently wraps decision making logic
0
   #
0
   # Example:
0
   #
0
   # c = Condition.new('1', '==', '1')
0
   # c.evaluate #=> true
0
   #
0
- class Condition
0
+ class Condition #:nodoc:
0
     @@operators = {
0
       '==' => lambda { |cond, left, right| cond.send(:equal_variables, left, right) },
0
       '!=' => lambda { |cond, left, right| !cond.send(:equal_variables, left, right) },
0
@@ -14,7 +14,8 @@ module Liquid
0
       '<' => :<,
0
       '>' => :>,
0
       '>=' => :>=,
0
- '<=' => :<=
0
+ '<=' => :<=,
0
+ 'contains' => lambda { |cond, left, right| left.include?(right) },
0
     }
0
     
0
     def self.operators
0
@@ -26,10 +27,29 @@ module Liquid
0
   
0
     def initialize(left = nil, operator = nil, right = nil)
0
       @left, @operator, @right = left, operator, right
0
+ @child_relation = nil
0
+ @child_condition = nil
0
     end
0
     
0
     def evaluate(context = Context.new)
0
- interpret_condition(left, right, operator, context)
0
+ result = interpret_condition(left, right, operator, context)
0
+
0
+ case @child_relation
0
+ when :or
0
+ result || @child_condition.evaluate(context)
0
+ when :and
0
+ result && @child_condition.evaluate(context)
0
+ else
0
+ result
0
+ end
0
+ end
0
+
0
+ def or(condition)
0
+ @child_relation, @child_condition = :or, condition
0
+ end
0
+
0
+ def and(condition)
0
+ @child_relation, @child_condition = :and, condition
0
     end
0
   
0
     def attach(attachment)
0
@@ -38,6 +58,10 @@ module Liquid
0
   
0
     def else?
0
       false
0
+ end
0
+
0
+ def inspect
0
+ "#<Condition #{[@left, @operator, @right].compact.join(' ')}>"
0
     end
0
     
0
     private
0
@@ -70,18 +94,20 @@ module Liquid
0
       return context[left] if op == nil
0
 
0
       left, right = context[left], context[right]
0
+
0
 
0
       operation = self.class.operators[op] || raise(ArgumentError.new("Error in tag '#{name}' - Unknown operator #{op}"))
0
 
0
       if operation.respond_to?(:call)
0
         operation.call(self, left, right)
0
- elsif left.respond_to?(operation) and right.respond_to?(operation)
0
+ elsif left.respond_to?(operation) and right.respond_to?(operation)
0
         left.send(operation, right)
0
       else
0
         nil
0
       end
0
     end
0
- end
0
+ end
0
+
0
 
0
   class ElseCondition < Condition
0
       
...
128
129
130
131
 
 
 
132
133
134
...
175
176
177
178
 
 
179
180
 
 
 
 
 
 
181
182
183
184
185
 
 
 
 
 
 
 
 
186
187
188
 
 
189
190
191
192
193
 
 
 
 
194
195
196
197
 
 
 
198
199
200
 
 
201
202
203
204
 
 
 
205
206
 
207
208
209
210
211
 
 
 
 
 
212
213
214
...
128
129
130
 
131
132
133
134
135
136
...
177
178
179
 
180
181
182
 
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
 
 
203
204
205
 
 
 
 
206
207
208
209
210
 
 
 
211
212
213
214
 
 
215
216
217
 
 
 
218
219
220
221
 
222
223
 
 
 
 
224
225
226
227
228
229
230
231
0
@@ -128,7 +128,9 @@ module Liquid
0
       when 'true'
0
         true
0
       when 'false'
0
- false
0
+ false
0
+ when 'blank'
0
+ :blank?
0
       when 'empty'
0
         :empty?
0
       # Single quoted strings
0
@@ -175,40 +177,55 @@ module Liquid
0
     # assert_equal 'tobi', @context['hash[name]']
0
     #
0
     def variable(markup)
0
- parts = markup.scan(VariableParser)
0
+ parts = markup.scan(VariableParser)
0
+ square_bracketed = /^\[(.*)\]$/
0
       
0
- if object = find_variable(parts.shift)
0
+ first_part = parts.shift
0
+ if first_part =~ square_bracketed
0
+ first_part = resolve($1)
0
+ end
0
+
0
+ if object = find_variable(first_part)
0
             
0
         parts.each do |part|
0
 
0
           # If object is a hash we look for the presence of the key and if its available
0
           # we return it
0
+
0
+ if part =~ square_bracketed
0
+ part = resolve($1)
0
+
0
+ object[pos] = object[part].call(self) if object[part].is_a?(Proc) and object.respond_to?(:[]=)
0
+ object = object[part].to_liquid
0
+
0
+ else
0
 
0
- # Hash
0
- if object.respond_to?(:has_key?) and object.has_key?(part)
0
+ # Hash
0
+ if object.respond_to?(:has_key?) and object.has_key?(part)
0
           
0
- # if its a proc we will replace the entry in the hash table with the proc
0
- res = object[part]
0
- res = object[part] = res.call(self) if res.is_a?(Proc) and object.respond_to?(:[]=)
0
- object = res.to_liquid
0
+ # if its a proc we will replace the entry in the hash table with the proc
0
+ res = object[part]
0
+ res = object[part] = res.call(self) if res.is_a?(Proc) and object.respond_to?(:[]=)
0
+ object = res.to_liquid
0
 
0
- # Array
0
- elsif object.respond_to?(:fetch) and part =~ /^\d+$/
0
- pos = part.to_i
0
+ # Array
0
+ elsif object.respond_to?(:fetch) and part =~ /^\d+$/
0
+ pos = part.to_i
0
 
0
- object[pos] = object[pos].call(self) if object[pos].is_a?(Proc) and object.respond_to?(:[]=)
0
- object = object[pos].to_liquid
0
+ object[pos] = object[pos].call(self) if object[pos].is_a?(Proc) and object.respond_to?(:[]=)
0
+ object = object[pos].to_liquid
0
           
0
- # Some special cases. If no key with the same name was found we interpret following calls
0
- # as commands and call them on the current object
0
- elsif object.respond_to?(part) and ['size', 'first', 'last'].include?(part)
0
+ # Some special cases. If no key with the same name was found we interpret following calls
0
+ # as commands and call them on the current object
0
+ elsif object.respond_to?(part) and ['size', 'first', 'last'].include?(part)
0
           
0
- object = object.send(part.intern).to_liquid
0
+ object = object.send(part.intern).to_liquid
0
         
0
- # No key was present with the desired value and it wasn't one of the directly supported
0
- # keywords either. The only thing we got left is to return nil
0
- else
0
- return nil
0
+ # No key was present with the desired value and it wasn't one of the directly supported
0
+ # keywords either. The only thing we got left is to return nil
0
+ else
0
+ return nil
0
+ end
0
           end
0
                 
0
           # If we are dealing with a drop here we have to
...
42
43
44
 
 
 
45
46
47
48
 
 
 
 
 
 
 
49
50
 
51
52
 
53
54
 
 
55
56
57
...
42
43
44
45
46
47
48
49
50
 
51
52
53
54
55
56
57
58
 
59
60
 
61
62
 
63
64
65
66
67
0
@@ -42,16 +42,26 @@ module Liquid
0
             'length' => length,
0
             'index' => index + 1,
0
             'index0' => index,
0
+ 'col' => col + 1,
0
+ 'col0' => col,
0
+ 'index0' => index,
0
             'rindex' => length - index,
0
             'rindex0' => length - index -1,
0
             'first' => (index == 0),
0
- 'last' => (index == length - 1) }
0
+ 'last' => (index == length - 1),
0
+ 'col_first' => (col == 0),
0
+ 'col_last' => (col == cols - 1)
0
+ }
0
+
0
+
0
+ col += 1
0
                                 
0
- result << ["<td class=\"col#{col += 1}\">"] + render_all(@nodelist, context) + ['</td>']
0
+ result << ["<td class=\"col#{col}\">"] + render_all(@nodelist, context) + ['</td>']
0
 
0
- if col == cols and not (index == length - 1)
0
+ if col == cols and not (index == length - 1)
0
             col = 0
0
- result << ["</tr>\n<tr class=\"row#{row += 1}\">"]
0
+ row += 1
0
+ result << ["</tr>\n<tr class=\"row#{row}\">"]
0
           end
0
           
0
         end
...
 
 
1
2
3
...
22
23
24
 
 
 
 
 
 
25
26
27
...
40
41
42
43
 
 
 
 
 
 
44
45
 
46
47
48
...
51
52
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
55
56
...
1
2
3
4
5
...
24
25
26
27
28
29
30
31
32
33
34
35
...
48
49
50
 
51
52
53
54
55
56
57
58
59
60
61
62
...
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
0
@@ -1,3 +1,5 @@
0
+require 'cgi'
0
+
0
 module Liquid
0
   
0
   module StandardFilters
0
@@ -22,6 +24,12 @@ module Liquid
0
     def capitalize(input)
0
       input.to_s.capitalize
0
     end
0
+
0
+ def escape(input)
0
+ CGI.escapeHTML(input) rescue input
0
+ end
0
+
0
+ alias_method :h, :escape
0
     
0
     # Truncate a string down to x characters
0
     def truncate(input, length = 50, truncate_string = "...")
0
@@ -40,9 +48,15 @@ module Liquid
0
     end
0
     
0
     def strip_html(input)
0
- input.to_s.gsub(/<.*?>/, '')
0
+ input.to_s.gsub(/<.*?>/m, '')
0
+ end
0
+
0
+ # Remove all newlines from the string
0
+ def strip_newlines(input)
0
+ input.to_s.gsub(/\n/, '')
0
     end
0
     
0
+
0
     # Join elements of the array with certain character between them
0
     def join(input, glue = ' ')
0
       [input].flatten.join(glue)
0
@@ -51,6 +65,31 @@ module Liquid
0
     # Sort elements of the array
0
     def sort(input)
0
       [input].flatten.sort
0
+ end
0
+
0
+ # Replace occurrences of a string with another
0
+ def replace(input, string, replacement = '')
0
+ input.to_s.gsub(string, replacement)
0
+ end
0
+
0
+ # Replace the first occurrences of a string with another
0
+ def replace_first(input, string, replacement = '')
0
+ input.to_s.sub(string, replacement)
0
+ end
0
+
0
+ # remove a substring
0
+ def remove(input, string)
0
+ input.to_s.gsub(string, '')
0
+ end
0
+
0
+ # remove the first occurrences of a substring
0
+ def remove_first(input, string)
0
+ input.to_s.sub(string, '')
0
+ end
0
+
0
+ # Add <br /> tags in front of all newlines in input string
0
+ def newline_to_br(input)
0
+ input.to_s.gsub(/\n/, "<br />\n")
0
     end
0
     
0
     # Reformat a date
...
 
 
1
 
 
 
 
 
 
 
2
3
4
5
6
7
8
9
 
 
 
10
11
 
12
13
14
15
16
17
18
19
 
 
20
21
22
23
24
 
25
26
27
28
29
30
 
31
32
33
...
35
36
37
38
39
 
 
40
41
42
43
44
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
 
 
16
17
18
19
 
20
21
22
23
24
25
26
 
 
27
28
29
30
31
32
 
33
34
35
36
37
38
 
39
40
41
42
...
44
45
46
 
 
47
48
49
50
51
 
52
53
0
@@ -1,33 +1,42 @@
0
+require 'set'
0
+
0
 module Liquid
0
+
0
+
0
+ parent_object = if defined? BlankObject
0
+ BlankObject
0
+ else
0
+ Object
0
+ end
0
 
0
   # Strainer is the parent class for the filters system.
0
   # New filters are mixed into the strainer class which is then instanciated for each liquid template render run.
0
   #
0
   # One of the strainer's responsibilities is to keep malicious method calls out
0
- class Strainer
0
-
0
- @@required_methods = ["__send__", "__id__", "respond_to?", "extend", "methods"]
0
+ class Strainer < parent_object #:nodoc:
0
+ INTERNAL_METHOD = /^__/
0
+ @@required_methods = Set.new([:__send__, :__id__, :respond_to?, :extend, :methods, :class])
0
     
0
- @@filters = []
0
+ @@filters = {}
0
     
0
     def initialize(context)
0
       @context = context
0
     end
0
               
0
     def self.global_filter(filter)
0
- raise StandardError, "Passed filter is not a module" unless filter.is_a?(Module)
0
- @@filters << filter
0
+ raise StandardError, "Passed filter is not a module" unless filter.is_a?(Module)
0
+ @@filters[filter.name] = filter
0
     end
0
     
0
     def self.create(context)
0
       strainer = Strainer.new(context)
0
- @@filters.each { |m| strainer.extend(m) }
0
+ @@filters.each { |k,m| strainer.extend(m) }
0
       strainer
0
     end
0
     
0
     def respond_to?(method)
0
       method_name = method.to_s
0
- return false if method_name =~ /^__/
0
+ return false if method_name =~ INTERNAL_METHOD
0
       return false if @@required_methods.include?(method_name)
0
       super
0
     end
0
@@ -35,9 +44,9 @@ module Liquid
0
     # remove all standard methods from the bucket so circumvent security
0
     # problems
0
     instance_methods.each do |m|
0
- unless @@required_methods.include?(m)
0
- undef_method m
0
+ unless @@required_methods.include?(m.to_sym)
0
+ undef_method m
0
       end
0
     end
0
   end
0
-end
0
\ No newline at end of file
0
+end
...
1
 
 
 
 
 
 
 
 
 
2
3
4
...
1
2
3
4
5
6
7
8
9
10
11
12
13
0
@@ -1,4 +1,13 @@
0
 module Liquid
0
+
0
+ # Assign sets a variable in your template.
0
+ #
0
+ # {% assign foo = 'monkey' %}
0
+ #
0
+ # You can then use the variable later in the page.
0
+ #
0
+ # {{ monkey }}
0
+ #
0
   class Assign < Tag
0
     Syntax = /(#{VariableSignature}+)\s*=\s*(#{QuotedFragment}+)/
0
   
...
1
 
 
 
 
 
 
 
 
 
 
 
 
2
3
4
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0
@@ -1,4 +1,16 @@
0
 module Liquid
0
+
0
+ # Capture stores the result of a block into a variable without rendering it inplace.
0
+ #
0
+ # {% capture heading %}
0
+ # Monkeys!
0
+ # {% endcapture %}
0
+ # ...
0
+ # <h1>{{ monkeys }}</h1>
0
+ #
0
+ # Capture is useful for saving content for use later in your template, such as
0
+ # in a sidebar or footer.
0
+ #
0
   class Capture < Block
0
     Syntax = /(\w+)/
0
 
...
1
2
3
4
 
5
6
7
...
51
52
53
54
55
56
57
 
 
 
 
 
58
59
60
61
 
 
 
 
 
 
62
63
64
...
76
77
78
79
80
 
...
1
2
3
 
4
5
6
7
...
51
52
53
 
 
 
 
54
55
56
57
58
59
 
 
 
60
61
62
63
64
65
66
67
68
...
80
81
82
 
83
84
0
@@ -1,7 +1,7 @@
0
 module Liquid
0
   class Case < Block
0
     Syntax = /(#{QuotedFragment})/
0
- WhenSyntax = /(#{QuotedFragment})/
0
+ WhenSyntax = /(#{QuotedFragment})(?:(?:\s+or\s+|\s*\,\s*)(#{QuotedFragment}.*))?/
0
 
0
     def initialize(tag_name, markup, tokens)
0
       @blocks = []
0
@@ -51,14 +51,18 @@ module Liquid
0
     private
0
     
0
     def record_when_condition(markup)
0
- # Create a new nodelist and assign it to the new block
0
- if not markup =~ WhenSyntax
0
- raise SyntaxError.new("Syntax Error in tag 'case' - Valid when condition: {% when [condition] %} ")
0
- end
0
+ while markup
0
+ # Create a new nodelist and assign it to the new block
0
+ if not markup =~ WhenSyntax
0
+ raise SyntaxError.new("Syntax Error in tag 'case' - Valid when condition: {% when [condition] [or condition2...] %} ")
0
+ end
0
 
0
- block = Condition.new(@left, '==', $1)
0
- block.attach(@nodelist)
0
- @blocks.push(block)
0
+ markup = $2
0
+
0
+ block = Condition.new(@left, '==', $1)
0
+ block.attach(@nodelist)
0
+ @blocks.push(block)
0
+ end
0
     end
0
 
0
     def record_else_condition(markup)
0
@@ -76,4 +80,4 @@ module Liquid
0
   end
0
   
0
   Template.register_tag('case', Case)
0
-end
0
\ No newline at end of file
0
+end
...
1
 
 
 
 
 
 
 
 
 
 
 
 
 
2
3
4
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
0
@@ -1,4 +1,17 @@
0
 module Liquid
0
+
0
+ # Cycle is usually used within a loop to alternate between values, like colors or DOM classes.
0
+ #
0
+ # {% for item in items %}
0
+ # <div class="{% cycle 'red', 'green', 'blue' %}"> {{ item }} </div>
0
+ # {% end %}
0
+ #
0
+ # <div class="red"> Item one </div>
0
+ # <div class="green"> Item two </div>
0
+ # <div class="blue"> Item three </div>
0
+ # <div class="red"> Item four </div>
0
+ # <div class="green"> Item five</div>
0
+ #
0
   class Cycle < Tag
0
     SimpleSyntax = /#{QuotedFragment}/
0
     NamedSyntax = /(#{QuotedFragment})\s*\:\s*(.*)/
...
1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
3
4
...
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
...
77
78
79
80
 
81
82
83
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
...
69
70
71
 
 
72
73
74
75
76
77
 
78
79
80
 
81
82
83
...
113
114
115
 
116
117
118
119
0
@@ -1,4 +1,44 @@
0
 module Liquid
0
+
0
+ # "For" iterates over an array or collection.
0
+ # Several useful variables are available to you within the loop.
0
+ #
0
+ # == Basic usage:
0
+ # {% for item in collection %}
0
+ # {{ forloop.index }}: {{ item.name }}
0
+ # {% endfor %}
0
+ #
0
+ # == Advanced usage:
0
+ # {% for item in collection %}
0
+ # <div {% if forloop.first %}class="first"{% endif %}>
0
+ # Item {{ forloop.index }}: {{ item.name }}
0
+ # </div>
0
+ # {% endfor %}
0
+ #
0
+ # You can also define a limit and offset much like SQL. Remember
0
+ # that offset starts at 0 for the first item.
0
+ #
0
+ # {% for item in collection limit:5 offset:10 %}
0
+ # {{ item.name }}
0
+ # {% end %}
0
+ #
0
+ # == Available variables:
0
+ #
0
+ # forloop.name:: 'item-collection'
0
+ # forloop.length:: Length of the loop
0
+ # forloop.index:: The current item's position in the collection;
0
+ # forloop.index starts at 1.
0
+ # This is helpful for non-programmers who start believe
0
+ # the first item in an array is 1, not 0.
0
+ # forloop.index0:: The current item's position in the collection
0
+ # where the first item is 0
0
+ # forloop.rindex:: Number of items remaining in the loop
0
+ # (length - index) where 1 is the last item.
0
+ # forloop.rindex0:: Number of items remaining in the loop
0
+ # where 0 is the last item.
0
+ # forloop.first:: Returns true if the item is the first item.
0
+ # forloop.last:: Returns true if the item is the last item.
0
+ #
0
   class For < Block
0
     Syntax = /(\w+)\s+in\s+(#{VariableSignature}+)/
0
   
0
@@ -29,19 +69,15 @@ module Liquid
0
       range = (0..collection.length)
0
     
0
       if @attributes['limit'] or @attributes['offset']
0
-
0
-
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
-
0
         limit = context[@attributes['limit']]
0
 
0
         range_end = limit ? offset + limit : collection.length
0
-
0
         range = (offset..range_end-1)
0
       
0
         # Save the range end in the registers so that future calls to
0
@@ -77,6 +113,6 @@ module Liquid
0
       result
0
     end
0
   end
0
-
0
+
0
   Template.register_tag('for', For)
0
 end
0
\ No newline at end of file
0