class Liquid::For

@liquid_public_docs @liquid_type tag @liquid_category iteration @liquid_name for @liquid_summary

Renders an expression for every item in an array.

@liquid_description

You can do a maximum of 50 iterations with a `for` loop. If you need to iterate over more than 50 items, then use the
[`paginate` tag](/docs/api/liquid/tags/paginate) to split the items over multiple pages.

> Tip:
> Every `for` loop has an associated [`forloop` object](/docs/api/liquid/objects/forloop) with information about the loop.

@liquid_syntax

{% for variable in array %}
  expression
{% endfor %}

@liquid_syntax_keyword variable The current item in the array. @liquid_syntax_keyword array The array to iterate over. @liquid_syntax_keyword expression The expression to render for each iteration. @liquid_optional_param limit [number] The number of iterations to perform. @liquid_optional_param offset [number] The 1-based index to start iterating at. @liquid_optional_param range [untyped] A custom numeric range to iterate over. @liquid_optional_param reversed [untyped] Iterate in reverse order.

Constants

Syntax

Attributes

collection_name[R]
from[R]
limit[R]
variable_name[R]

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 32
def initialize(tag_name, markup, options)
  super
  @from = @limit = nil
  parse_with_selected_parser(markup)
  @for_block = new_body
  @else_block = nil
end

Public Instance Methods

nodelist() click to toggle source
# File lib/liquid/tags/for.rb, line 52
def nodelist
  @else_block ? [@for_block, @else_block] : [@for_block]
end
parse(tokens) click to toggle source
# File lib/liquid/tags/for.rb, line 40
def parse(tokens)
  if parse_body(@for_block, tokens)
    parse_body(@else_block, tokens)
  end
  if blank?
    @else_block&.remove_blank_strings
    @for_block.remove_blank_strings
  end
  @else_block&.freeze
  @for_block.freeze
end
render_to_output_buffer(context, output) click to toggle source
# File lib/liquid/tags/for.rb, line 61
def render_to_output_buffer(context, output)
  segment = collection_segment(context)

  if segment.empty?
    render_else(context, output)
  else
    render_segment(context, output, segment)
  end

  output
end
unknown_tag(tag, markup, tokens) click to toggle source
Calls superclass method Liquid::Block#unknown_tag
# File lib/liquid/tags/for.rb, line 56
def unknown_tag(tag, markup, tokens)
  return super unless tag == 'else'
  @else_block = new_body
end

Protected Instance Methods

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

  collection_name  = p.expression
  @collection_name = parse_expression(collection_name)

  @name     = "#{@variable_name}-#{collection_name}"
  @reversed = p.id?('reversed')

  while p.look(:comma) || p.look(:id)
    p.consume?(:comma)
    unless (attribute = p.id?('limit') || p.id?('offset'))
      raise SyntaxError, options[:locale].t("errors.syntax.for_invalid_attribute")
    end
    p.consume(:colon)
    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 114
def collection_segment(context)
  offsets = context.registers[:for] ||= {}

  from = if @from == :continue
    offsets[@name].to_i
  else
    from_value = context.evaluate(@from)
    if from_value.nil?
      0
    else
      Utils.to_integer(from_value)
    end
  end

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

  limit_value = context.evaluate(@limit)
  to = if limit_value.nil?
    nil
  else
    Utils.to_integer(limit_value) + from
  end

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

  offsets[@name] = from + segment.length

  segment
end
render_else(context, output) click to toggle source
# File lib/liquid/tags/for.rb, line 190
def render_else(context, output)
  if @else_block
    @else_block.render_to_output_buffer(context, output)
  else
    output
  end
end
render_segment(context, output, segment) click to toggle source
# File lib/liquid/tags/for.rb, line 146
def render_segment(context, output, segment)
  for_stack = context.registers[:for_stack] ||= []
  length    = segment.length

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

    for_stack.push(loop_vars)

    begin
      context['forloop'] = loop_vars

      segment.each do |item|
        context[@variable_name] = item
        @for_block.render_to_output_buffer(context, output)
        loop_vars.send(:increment!)

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

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