class Liquid::For

“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 %}

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.

forloop.parentloop

Provides access to the parent loop, if present.

Constants

Syntax

Public Class Methods

new(tag_name, markup, options) click to toggle source
Calls superclass method Liquid::Block::new
# File lib/liquid/tags/for.rb, line 49
def initialize(tag_name, markup, options)
  super
  @from = @limit = nil
  parse_with_selected_parser(markup)
  @for_block = BlockBody.new
  @else_block = nil
end

Public Instance Methods

nodelist() click to toggle source
# File lib/liquid/tags/for.rb, line 62
def nodelist
  @else_block ? [@for_block, @else_block] : [@for_block]
end
parse(tokens) click to toggle source
# File lib/liquid/tags/for.rb, line 57
def parse(tokens)
  return unless parse_body(@for_block, tokens)
  parse_body(@else_block, tokens)
end
render(context) click to toggle source
# File lib/liquid/tags/for.rb, line 71
def render(context)
  segment = collection_segment(context)

  if segment.empty?
    render_else(context)
  else
    render_segment(context, segment)
  end
end
unknown_tag(tag, markup, tokens) click to toggle source
Calls superclass method Liquid::Block#unknown_tag
# File lib/liquid/tags/for.rb, line 66
def unknown_tag(tag, markup, tokens)
  return super unless tag == 'else'.freeze
  @else_block = BlockBody.new
end

Protected Instance Methods

lax_parse(markup) click to toggle source
# File lib/liquid/tags/for.rb, line 83
def lax_parse(markup)
  if markup =~ Syntax
    @variable_name = $1
    collection_name = $2
    @reversed = !!$3
    @name = "#{@variable_name}-#{collection_name}"
    @collection_name = Expression.parse(collection_name)
    markup.scan(TagAttributes) do |key, value|
      set_attribute(key, value)
    end
  else
    raise SyntaxError.new(options[:locale].t("errors.syntax.for".freeze))
  end
end
strict_parse(markup) click to toggle source
# File lib/liquid/tags/for.rb, line 98
def strict_parse(markup)
  p = Parser.new(markup)
  @variable_name = p.consume(:id)
  raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_in".freeze)) unless p.id?('in'.freeze)
  collection_name = p.expression
  @name = "#{@variable_name}-#{collection_name}"
  @collection_name = Expression.parse(collection_name)
  @reversed = p.id?('reversed'.freeze)

  while p.look(:id) && p.look(:colon, 1)
    unless attribute = p.id?('limit'.freeze) || p.id?('offset'.freeze)
      raise SyntaxError.new(options[:locale].t("errors.syntax.for_invalid_attribute".freeze))
    end
    p.consume
    set_attribute(attribute, p.expression)
  end
  p.consume(:end_of_string)
end

Private Instance Methods

collection_segment(context) click to toggle source
# File lib/liquid/tags/for.rb, line 119
def collection_segment(context)
  offsets = context.registers[:for] ||= Hash.new(0)

  from = if @from == :continue
    offsets[@name].to_i
  else
    context.evaluate(@from).to_i
  end

  collection = context.evaluate(@collection_name)
  collection = collection.to_a if collection.is_a?(Range)

  limit = context.evaluate(@limit)
  to = limit ? limit.to_i + from : nil

  segment = Utils.slice_collection(collection, from, to)
  segment.reverse! if @reversed

  offsets[@name] = from + segment.length

  segment
end
render_else(context) click to toggle source
# File lib/liquid/tags/for.rb, line 189
def render_else(context)
  @else_block ? @else_block.render(context) : ''.freeze
end
render_segment(context, segment) click to toggle source
# File lib/liquid/tags/for.rb, line 142
def render_segment(context, segment)
  for_stack = context.registers[:for_stack] ||= []
  length = segment.length

  result = ''

  context.stack do
    loop_vars = Liquid::ForloopDrop.new(@name, length, for_stack[-1])

    for_stack.push(loop_vars)

    begin
      context['forloop'.freeze] = loop_vars

      segment.each_with_index do |item, index|
        context[@variable_name] = item
        result << @for_block.render(context)
        loop_vars.send(:increment!)

        # Handle any interrupts if they exist.
        if context.interrupt?
          interrupt = context.pop_interrupt
          break if interrupt.is_a? BreakInterrupt
          next if interrupt.is_a? ContinueInterrupt
        end
      end
    ensure
      for_stack.pop
    end
  end

  result
end
set_attribute(key, expr) click to toggle source
# File lib/liquid/tags/for.rb, line 176
def set_attribute(key, expr)
  case key
  when 'offset'.freeze
    @from = if expr == 'continue'.freeze
      :continue
    else
      Expression.parse(expr)
    end
  when 'limit'.freeze
    @limit = Expression.parse(expr)
  end
end