Class: Arrow::Template::Iterator

Inherits:
Object
  • 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

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Object

deprecate_class_method, deprecate_method, inherited

Constructor Details

#initialize(*items) ⇒ Iterator

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

#itemsObject

The list of items in this iteration



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

def items
  @items
end

#iterationObject

The index of the current iteration



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

def iteration
  @iteration
end

#lastItemObject (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

#nextItemObject (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

#breakObject

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

#eachObject

The primary iteration interface.



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

#even?Boolean

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

#even_or_oddObject

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

#first?Boolean

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

#last?Boolean

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

#odd?Boolean

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

#redoObject

Redo the current iteration



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

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

#restartObject

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

#skip(n = 1) ⇒ Object

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

#skipped?Boolean

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