Class: Liquid::For

Inherits:
Block show all
Defined in:
lib/liquid/tags/for.rb

Overview

“For” iterates over an array or collection. Several useful variables are available to you within the loop.

Basic usage:

{% for item in collection %}
  {{ forloop.index }}: {{ item.name }}
{% endfor %}

Advanced usage:

{% for item in collection %}
  <div {% if forloop.first %}class="first"{% endif %}>
    Item {{ forloop.index }}: {{ item.name }}
  </div>
{% else %}
  There is nothing in the collection.
{% endfor %}

You can also define a limit and offset much like SQL. Remember that offset starts at 0 for the first item.

{% for item in collection limit:5 offset:10 %}
  {{ item.name }}
{% end %}

You can also specify an order for the collection items

{% for item in collection order:ascending %}
  {{ item.name }}
{% end %}

You can also specify which attribute to sort by.

{% for item in collection sort_by:name order:descending %}
  {{ item.name }}
{% end %}

To reverse the for loop simply use for item in collection reversed %

Available variables:

forloop.name

‘item-collection’

forloop.length

Length of the loop

forloop.index

The current item’s position in the collection; forloop.index starts at 1. This is helpful for non-programmers who start believe the first item in an array is 1, not 0.

forloop.index0

The current item’s position in the collection where the first item is 0

forloop.rindex

Number of items remaining in the loop (length - index) where 1 is the last item.

forloop.rindex0

Number of items remaining in the loop where 0 is the last item.

forloop.first

Returns true if the item is the first item.

forloop.last

Returns true if the item is the last item.

Constant Summary collapse

Syntax =
/(\w+)\s+in\s+(#{QuotedFragment}+)\s*(reversed)?/

Constants inherited from Block

Block::ContentOfVariable, Block::FullToken, Block::IsTag, Block::IsVariable

Instance Attribute Summary

Attributes inherited from Tag

#nodelist

Instance Method Summary collapse

Methods inherited from Block

#block_delimiter, #block_name, #create_variable, #end_tag, #parse

Methods inherited from Tag

#name, #parse

Constructor Details

#initialize(tag_name, markup, tokens) ⇒ For

Returns a new instance of For.



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/liquid/tags/for.rb', line 61

def initialize(tag_name, markup, tokens)
  if markup =~ Syntax
    @variable_name = $1
    @collection_name = $2
    @name = "#{$1}-#{$2}"           
    @reversed = $3             
    @attributes = {}
    markup.scan(TagAttributes) do |key, value|
      @attributes[key] = value
    end
    @reversed = 'reversed' if @attributes['order'] == 'descending'
  else
    raise SyntaxError.new("Syntax Error in 'for loop' - Valid syntax: for [item] in [collection]")
  end

  @nodelist = @for_block = []
  super
end

Instance Method Details

#render(context) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/liquid/tags/for.rb', line 85

def render(context)        
  context.registers[:for] ||= Hash.new(0)

  # Dup so reverse! doesn't hit the real object
  collection = context[@collection_name].dup
  collection = collection.to_a if collection.is_a?(Range)

  # Maintains Ruby 1.8.7 String#each behaviour on 1.9
  return render_else(context) unless iterable?(collection)
                                             
  sort_property = @attributes['sort_by']
  order_property = @attributes['order']
  if sort_property || order_property
    collection = if sort_property.nil? && (@attributes['order'] == 'ascending' || @attributes['order'] == 'descending')
      collection.sort
    elsif collection.first.respond_to?('[]') and !collection.first[sort_property].nil?
      collection.sort {|a,b| a[sort_property] <=> b[sort_property] }
    elsif collection.first.respond_to?(sort_property)
      collection.sort {|a,b| a.send(sort_property) <=> b.send(sort_property) }
    elsif collection.first.respond_to?(:to_liquid) and collection.first.to_liquid.respond_to?('[]') and !collection.first.to_liquid[sort_property].nil?
      collection.sort {|a,b| "#{a.to_liquid[sort_property]}" <=> "#{b.to_liquid[sort_property]}" };
    else
      collection
    end
  end

  collection.reverse! if @reversed

  from = if @attributes['offset'] == 'continue'
    context.registers[:for][@name].to_i
  else
    context[@attributes['offset']].to_i
  end
    
  limit = context[@attributes['limit']]
  to    = limit ? limit.to_i + from : nil  
      
                   
  segment = slice_collection_using_each(collection, from, to)      
  
  return render_else(context) 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,
        'length'  => length,
        'index'   => index + 1, 
        'index0'  => index, 
        'rindex'  => length - index,
        'rindex0' => length - index -1,
        'first'   => (index == 0),
        'last'    => (index == length - 1) }

      result << render_all(@for_block, context)
    end
  end
  result     
end

#slice_collection_using_each(collection, from, to) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/liquid/tags/for.rb', line 153

def slice_collection_using_each(collection, from, to)       
  segments = []      
  index = 0      
  yielded = 0

  # Maintains Ruby 1.8.7 String#each behaviour on 1.9
  return [collection] if non_blank_string?(collection)

  collection.each do |item|         
            
    if to && to <= index
      break
    end
    
    if from <= index                               
      segments << item
    end                    
            
    index += 1
  end    

  segments
end

#unknown_tag(tag, markup, tokens) ⇒ Object



80
81
82
83
# File 'lib/liquid/tags/for.rb', line 80

def unknown_tag(tag, markup, tokens)
  return super unless tag == 'else'
  @nodelist = @else_block = []
end