Class: Arrow::Template::Iterator

Inherits:
Arrow::Object show all
Includes:
Enumerable
Defined in:
lib/arrow/template/iterator.rb

Overview

The Arrow::Template::Iterator class, instances of which can be used to provide an iteration context to nodes in an Arrow template.

Lots of the ideas for this class were stolen/influenced in no small way by Hal Fulton’s “super-iterator” post to the Ruby-talk ML [ruby-talk: 46337].

Examples

  ### Render the directive's bracketed nodes once for each item in the
  ### iterated content.
  def render_subnodes( attribute, template, scope )
      res = []
  
      iterator = Arrow::Template::Iterator.new( attribute )
      iterator.each {|iter,*blockArgs|
  
          # Process the nodes
          template.with_overridden_attributes( scope, 'iterator' => iter ) {|template|
              res << template.render( @subnodes, scope )
          }
      }
  
      return *res
  end

Authors

  • Michael Granger

Please see the file LICENSE in the top-level directory for licensing details.

Instance Attribute Summary

Instance Method Summary

Methods inherited from Arrow::Object

deprecate_class_method, deprecate_method, inherited

Methods included from Arrow::Loggable

#log

Constructor Details

- (Iterator) initialize(*items)

Create a new Arrow::Template::Iterator object for the given items.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/arrow/template/iterator.rb', line 48

def initialize( *items )
  if items.length == 1 && items[0].is_a?( Enumerable )
    @items = items[0]
  else
    @items = items
  end

  @iteration = nil
  @lastItem  = nil
  @item      = nil
  @nextItem  = nil
  @iterating = false
  @skipped   = false
  @marker    = nil
end

Instance Attribute Details

- (Object) items

The list of items in this iteration



70
71
72
# File 'lib/arrow/template/iterator.rb', line 70

def items
  @items
end

- (Object) iteration

The index of the current iteration



73
74
75
# File 'lib/arrow/template/iterator.rb', line 73

def iteration
  @iteration
end

- (Object) lastItem (readonly)

The item previous to the currently iterated one. If this is the first iteration, this will be nil.



77
78
79
# File 'lib/arrow/template/iterator.rb', line 77

def lastItem
  @lastItem
end

- (Object) nextItem (readonly)

The item which succeeds the currently iterated one. If this is the last iteration, this will be nil.



81
82
83
# File 'lib/arrow/template/iterator.rb', line 81

def nextItem
  @nextItem
end

Instance Method Details

- (Object) break

Cause iteration to immediately terminate, ala the ‘break’ keyword



155
156
157
158
# File 'lib/arrow/template/iterator.rb', line 155

def break
  # Jump back into the outer loop of #each 
  throw( :break ) if @iterating
end

- (Object) each

The primary iteration interface.

Raises:

  • (LocalJumpError)


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
# File 'lib/arrow/template/iterator.rb', line 85

def each
  items = @items.dup
  @items = @items.entries
  raise LocalJumpError, "no block given" unless block_given?

  #self.log.debug "Iterating over @items = %p" % [ @items ]

  # Save this point so #restart can jump back here later. This is in a
  # loop because it needs to be remade after it's used the first time.
  until @marker
    @marker = callcc {|cc| cc}
  end
  @iterating = true
  @iteration = 0

  # Mark the outer loop for #break
  catch( :break ) {
    until @iteration >= @items.length

      # Catch a skip with the number of items to skip. Unskipped
      # iterations "skip" 0 items.
      n = catch( :skip ) {
        @lastItem  = self.first? ? nil : @items[ @iteration - 1 ]
        @item    = @items[ @iteration ]
        @nextItem  = self.last? ? nil : @items[ @iteration + 1 ]

        if @item.is_a?( Array )
          yield( self, *@item )
        else
          yield( self, @item )
        end

        0
      }

      # Set the skipped flag for next iteration if we're skipping
      @skipped = n.nonzero?
      @iteration += n + 1
    end
  }

  #self.log.debug "Returning from Iterator#each"

  return @items
ensure
  @items    = items
  @iteration  = nil
  @lastItem = nil
  @item   = nil
  @nextItem = nil
  @iterating  = false
  @skipped  = false
  @marker   = nil
end

- (Boolean) even?

Return true if the current iteration is an even-numbered iteration.

Returns:

  • (Boolean)


196
197
198
# File 'lib/arrow/template/iterator.rb', line 196

def even?
  return !self.odd?
end

- (Object) even_or_odd

Returns either “even” if the iteration is in even-numbered iteration, or “odd”.



182
183
184
# File 'lib/arrow/template/iterator.rb', line 182

def even_or_odd
  return self.odd? ? "odd" : "even"
end

- (Boolean) first?

Returns true if the current iteration is the first one.

Returns:

  • (Boolean)


176
177
178
# File 'lib/arrow/template/iterator.rb', line 176

def first?
  return @iteration == 0
end

- (Boolean) last?

Returns true if the current iteration is the last one.

Returns:

  • (Boolean)


202
203
204
# File 'lib/arrow/template/iterator.rb', line 202

def last?
  return @iteration == @items.length - 1
end

- (Boolean) odd?

Returns true if the current iteration is an odd-numbered iteration.

Returns:

  • (Boolean)


189
190
191
# File 'lib/arrow/template/iterator.rb', line 189

def odd?
  return @iterating && ( @iteration % 2 ).nonzero?
end

- (Object) redo

Redo the current iteration



149
150
151
# File 'lib/arrow/template/iterator.rb', line 149

def redo
  throw( :skip, -1 ) if @iterating
end

- (Object) restart

Cause iteration to begin over again



162
163
164
165
166
# File 'lib/arrow/template/iterator.rb', line 162

def restart
  # Call back into the continuation that was saved at the beginning of
  # #each
  @marker.call if @iterating
end

- (Object) skip(n = 1)

Cause the next n items to be skipped



142
143
144
145
# File 'lib/arrow/template/iterator.rb', line 142

def skip( n=1 )
  # Jump back into #each with the number of iterations to skip
  throw( :skip, n ) if @iterating
end

- (Boolean) skipped?

Returns true if the last iteration skipped one or more items.

Returns:

  • (Boolean)


170
171
172
# File 'lib/arrow/template/iterator.rb', line 170

def skipped?
  @skipped
end