Class: DFuzz::Generator

Inherits:
Enumerator
  • Object
show all
Includes:
Enumerable
Defined in:
lib/dfuzz/generator18.rb,
lib/dfuzz/generator.rb

Overview

Generator converts an internal iterator (i.e. an Enumerable object) to an external iterator.

Example

require 'generator'

# Generator from an Enumerable object
g = Generator.new(['A', 'B', 'C', 'Z'])

while g.next?
  puts g.next
end

# Generator from a block
g = Generator.new { |g|
  for i in 'A'..'C'
    g.yield i
  end

  g.yield 'Z'
}

# The same result as above
while g.next?
  puts g.next
end

Direct Known Subclasses

Char, EmailAddress, Fudge, Sequential, String

Instance Method Summary collapse

Constructor Details

#initialize(enum = nil, &block) ⇒ Generator

Creates a new generator either from an Enumerable object or from a block.

In the former, block is ignored even if given.

In the latter, the given block is called with the generator itself, and expected to call the yield method for each element.



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/dfuzz/generator18.rb', line 67

def initialize(enum = nil, &block)
  if enum
    @block = proc{|g| enum.each{|value| g.yield value}}
  else
    @block = block
  end
  @index = 0
  @queue = []
  @main_thread = nil
  @loop_thread.kill if defined?(@loop_thread)
  @loop_thread = Thread.new do
    Thread.stop
    begin
      @block.call(self)
    rescue
      @main_thread.raise $!
    ensure
      @main_thread.wakeup
    end
  end
  Thread.pass until @loop_thread.stop?
  self
end

Instance Method Details

#currentObject

Returns the element at the current position.

Raises:

  • (EOFError)


151
152
153
154
# File 'lib/dfuzz/generator18.rb', line 151

def current
  raise EOFError.new("no more elements available") if end?
  @queue.first
end

#eachObject

Rewinds the generator and enumerates the elements.



163
164
165
166
167
168
169
# File 'lib/dfuzz/generator18.rb', line 163

def each
  rewind
  until end?
    yield self.next
  end
  self
end

#empty?Boolean

Returns:

  • (Boolean)


14
# File 'lib/dfuzz/generator.rb', line 14

def empty?; !self.next?; end

#end?Boolean

Returns true if the generator has reached the end.

Returns:

  • (Boolean)


108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/dfuzz/generator18.rb', line 108

def end?
  if @queue.empty?
    if @main_thread
      raise "should not be called in Generator.new{|g| ... }"
    end
    Thread.critical = true
    begin
      @main_thread = Thread.current
      @loop_thread.wakeup
      Thread.stop
    rescue ThreadError
      # ignore
    ensure
      @main_thread = nil
      Thread.critical = false
    end
  end
  @queue.empty?
end

#indexObject

Returns the current index (position) counting from zero.



134
135
136
# File 'lib/dfuzz/generator18.rb', line 134

def index
  @index
end

#nextObject

Returns the element at the current position and moves forward.

Raises:

  • (EOFError)


144
145
146
147
148
# File 'lib/dfuzz/generator18.rb', line 144

def next
  raise EOFError.new("no more elements available") if end?
  @index += 1
  @queue.shift
end

#next?Boolean

Returns true if the generator has not reached the end yet.

Returns:

  • (Boolean)


129
130
131
132
133
134
135
136
# File 'lib/dfuzz/generator18.rb', line 129

def next?
  begin
    self.peek
    true
  rescue StopIteration
    false
  end
end

#posObject

Returns the current index (position) counting from zero.



139
140
141
# File 'lib/dfuzz/generator18.rb', line 139

def pos
  @index
end

#rewindObject

Rewinds the generator.



157
158
159
160
# File 'lib/dfuzz/generator18.rb', line 157

def rewind
  initialize(nil, &@block) if @index.nonzero?
  self
end

#shiftObject

def to_a; self; end



13
# File 'lib/dfuzz/generator.rb', line 13

def shift; next? ? self.next : nil; end

#yield(value) ⇒ Object

Yields an element to the generator.



92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/dfuzz/generator18.rb', line 92

def yield(value)
  if Thread.current != @loop_thread
    raise "should be called in Generator.new{|g| ... }"
  end
  Thread.critical = true
  begin
    @queue << value
    @main_thread.wakeup
    Thread.stop
  ensure
    Thread.critical = false
  end
  self
end