pigeon 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009 Scott Tadman
1
+ Copyright (c) 2009-2011 Scott Tadman, The Working Group Inc.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.2
1
+ 0.6.0
data/lib/pigeon.rb CHANGED
@@ -18,3 +18,5 @@ end
18
18
  # NOTE: This file needs to be directly loaded due to some kind of peculiar
19
19
  # issue where requiring it later causes a run-time Interrupt exception.
20
20
  require 'pigeon/logger'
21
+
22
+ require 'fastthread' rescue nil
data/lib/pigeon/engine.rb CHANGED
@@ -132,8 +132,12 @@ class Pigeon::Engine
132
132
  end
133
133
 
134
134
  def self.start(options = nil)
135
- pid = Pigeon::Support.daemonize do
136
- launch(options)
135
+ logger = self.engine_logger
136
+
137
+ pid = Pigeon::Support.daemonize(logger) do
138
+ launch({
139
+ :logger => logger
140
+ }.merge(options || { }))
137
141
  end
138
142
 
139
143
  pid_file.create!(pid)
@@ -3,23 +3,23 @@ require 'optparse'
3
3
  class Pigeon::Launcher
4
4
  # == Class Methods ========================================================
5
5
 
6
- def self.launch(engine, options)
6
+ def self.launch(engine = Pigeon::Engine, *arguments)
7
+ arguments = %w[ start ] if (arguments.empty?)
8
+
9
+ new(engine).handle_args(*arguments)
7
10
  end
8
11
 
9
12
  # == Instance Methods =====================================================
10
13
 
11
14
  def initialize(with_engine = Pigeon::Engine)
12
15
  @engine = with_engine
13
- @options = { }
16
+
17
+ yield(self) if (block_given?)
14
18
  end
15
19
 
16
20
  def handle_args(*args)
17
21
  op = OptionParser.new
18
22
 
19
- op.on("-s", "--supervise") do
20
- @options[:supervise] = true
21
- end
22
-
23
23
  command = op.parse(*args.flatten).first
24
24
 
25
25
  begin
@@ -20,7 +20,7 @@ class Pigeon::Processor
20
20
  def initialize(queue = nil, context = nil, &filter)
21
21
  @id = Pigeon::Support.unique_id
22
22
  @lock = Mutex.new
23
- @filter = filter || lambda { |task| true }
23
+ @filter = filter
24
24
  @context = context
25
25
 
26
26
  if (queue)
@@ -40,7 +40,7 @@ class Pigeon::Processor
40
40
  if (@queue = queue)
41
41
  @claim = lambda do |task|
42
42
  @lock.synchronize do
43
- if (!@task and @filter.call(task))
43
+ if (!@task and (!@filter or @filter.call(task)))
44
44
  @task = queue.claim(task)
45
45
 
46
46
  before_task(@task)
@@ -59,7 +59,7 @@ class Pigeon::Processor
59
59
  # Returns true if the given task would be accepted by the filter defined
60
60
  # for this processor.
61
61
  def accept?(task)
62
- @filter.call(task)
62
+ !@filter or @filter.call(task)
63
63
  end
64
64
 
65
65
  # Returns true if a task is currently being processed, false otherwise.
data/lib/pigeon/queue.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'monitor'
2
+
1
3
  class Pigeon::Queue
2
4
  # == Constants ============================================================
3
5
 
@@ -44,15 +46,13 @@ class Pigeon::Queue
44
46
  # and order them, so it should take two arguments and return the relative
45
47
  # difference (-1, 0, 1) like Array#sort would work.
46
48
  def initialize(&block)
47
- @filter_lock = Mutex.new
48
- @observer_lock = Mutex.new
49
-
50
- @claimable_task = { }
51
49
  @filters = self.class.filters.dup
50
+ @filters.extend(MonitorMixin)
52
51
  @observers = { }
52
+ @observers.extend(MonitorMixin)
53
+ @claimable_task = { }
53
54
  @processors = [ ]
54
55
  @next_task = { }
55
- @insert_backlog = [ ]
56
56
 
57
57
  if (block_given?)
58
58
  @sort_by = block
@@ -69,7 +69,7 @@ class Pigeon::Queue
69
69
  raise BlockRequired unless (block_given?)
70
70
 
71
71
  @sort_by = block
72
- @filter_lock.synchronize do
72
+ @filters.synchronize do
73
73
  @tasks = Pigeon::SortedArray.new(&@sort_by) + @tasks
74
74
 
75
75
  @next_task = { }
@@ -82,10 +82,10 @@ class Pigeon::Queue
82
82
  def observe(filter_name = nil, &block)
83
83
  raise BlockRequired unless (block_given?)
84
84
 
85
- @observer_lock.synchronize do
86
- @observers[filter_name] ||= [ ]
85
+ @observers.synchronize do
86
+ set = @observers[filter_name] ||= [ ]
87
87
 
88
- @observers[filter_name] << block
88
+ set << block
89
89
  end
90
90
 
91
91
  task = assign_next_task(filter_name)
@@ -95,7 +95,7 @@ class Pigeon::Queue
95
95
  # Proc must be passed in, as a block with an identical function will not
96
96
  # be considered equivalent.
97
97
  def remove_observer(filter_name = nil, &block)
98
- @observer_lock.synchronize do
98
+ @observers.synchronize do
99
99
  set = @observers[filter_name]
100
100
 
101
101
  set and set.delete(block)
@@ -104,7 +104,7 @@ class Pigeon::Queue
104
104
 
105
105
  # Adds a processor to the queue and adds an observer claim method.
106
106
  def add_processor(processor, &claim)
107
- @observer_lock.synchronize do
107
+ @observers.synchronize do
108
108
  @processors << processor
109
109
  end
110
110
 
@@ -113,7 +113,7 @@ class Pigeon::Queue
113
113
 
114
114
  # Removes a processor from the queue and removes an observer claim method.
115
115
  def remove_processor(processor, &claim)
116
- @observer_lock.synchronize do
116
+ @observers.synchronize do
117
117
  @processors.delete(processor)
118
118
  end
119
119
 
@@ -125,7 +125,7 @@ class Pigeon::Queue
125
125
  def filter(filter_name, &block)
126
126
  raise BlockRequired unless (block_given?)
127
127
 
128
- @filter_lock.synchronize do
128
+ @filters.synchronize do
129
129
  @filters[filter_name] = block
130
130
  end
131
131
 
@@ -141,69 +141,64 @@ class Pigeon::Queue
141
141
  # If there is an insert operation already in progress, put this task in
142
142
  # the backlog for subsequent processing.
143
143
 
144
- if (@observer_lock.locked?)
145
- @insert_backlog << task
146
- return task
144
+ Pigeon::Engine.execute_in_main_thread do
145
+ self.execute_add_task!(task)
147
146
  end
148
147
 
149
- active_task = task
150
-
151
- while (active_task) do
152
- # Set the claimable task flag for this task since it is not yet in the
153
- # actual task queue.
154
- @claimable_task[active_task] = true
155
-
156
- unless (@observers.empty?)
157
- @observer_lock.synchronize do
158
- @observers.each do |filter_name, list|
159
- # Check if this task matches the filter restrictions, and if it
160
- # does then call the observer chain in order.
161
- if (@filters[filter_name].call(active_task))
162
- @observers[filter_name].each do |proc|
163
- case (proc.arity)
164
- when 2
165
- proc.call(self, active_task)
166
- else
167
- proc.call(active_task)
168
- end
148
+ task
149
+ end
169
150
 
170
- # An observer callback has the opportunity to claim a task,
171
- # and if it does, the claimable task flag will be false. Loop
172
- # only while the task is claimable.
173
- break unless (@claimable_task[active_task])
151
+ def execute_add_task!(task)
152
+ # Set the claimable task flag for this task since it is not yet in the
153
+ # actual task queue.
154
+ @claimable_task[task] = true
155
+
156
+ unless (@observers.empty?)
157
+ @observers.synchronize do
158
+ @observers.each do |filter_name, list|
159
+ # Check if this task matches the filter restrictions, and if it
160
+ # does then call the observer chain in order.
161
+ if (@filters[filter_name].call(task))
162
+ @observers[filter_name].each do |proc|
163
+ case (proc.arity)
164
+ when 2
165
+ proc.call(self, task)
166
+ else
167
+ proc.call(task)
174
168
  end
169
+
170
+ # An observer callback has the opportunity to claim a task,
171
+ # and if it does, the claimable task flag will be false. Loop
172
+ # only while the task is claimable.
173
+ break unless (@claimable_task[task])
175
174
  end
176
175
  end
177
176
  end
178
177
  end
178
+ end
179
179
 
180
- # If this task wasn't claimed by an observer then insert it in the
181
- # main task queue.
182
- if (@claimable_task.delete(active_task))
183
- @filter_lock.synchronize do
184
- @tasks << active_task
180
+ # If this task wasn't claimed by an observer then insert it in the
181
+ # main task queue.
182
+ if (@claimable_task.delete(task))
183
+ @filters.synchronize do
184
+ @tasks << task
185
+
186
+ # Update the next task slots for all of the unassigned filters and
187
+ # trigger observer callbacks as required.
188
+ @next_task.each do |filter_name, next_task|
189
+ next if (next_task)
185
190
 
186
- # Update the next task slots for all of the unassigned filters and
187
- # trigger observer callbacks as required.
188
- @next_task.each do |filter_name, next_task|
189
- next if (next_task)
190
-
191
- if (@filters[filter_name].call(active_task))
192
- @next_task[filter_name] = active_task
193
- end
191
+ if (@filters[filter_name].call(task))
192
+ @next_task[filter_name] = task
194
193
  end
195
194
  end
196
195
  end
197
-
198
- active_task = @insert_backlog.shift
199
196
  end
200
-
201
- task
202
197
  end
203
198
 
204
199
  # Iterates over each of the tasks in the queue.
205
200
  def each
206
- @filter_lock.synchronize do
201
+ @filters.synchronize do
207
202
  tasks = @tasks.dup
208
203
  end
209
204
 
@@ -217,17 +212,21 @@ class Pigeon::Queue
217
212
  # can also be used to further restrict the qualifying tasks.
218
213
  def peek(filter_name = nil, &block)
219
214
  if (block_given?)
220
- @filter_lock.synchronize do
215
+ @filters.synchronize do
221
216
  @tasks.find(&block)
222
217
  end
223
- else
218
+ elsif (filter_name)
224
219
  @next_task[filter_name] ||= begin
225
- @filter_lock.synchronize do
220
+ @filters.synchronize do
226
221
  filter_proc = @filters[filter_name]
227
222
 
228
223
  filter_proc and @tasks.find(&filter_proc)
229
224
  end
230
225
  end
226
+ else
227
+ @filters.synchronize do
228
+ @tasks.first
229
+ end
231
230
  end
232
231
  end
233
232
 
@@ -235,13 +234,13 @@ class Pigeon::Queue
235
234
  # only remove tasks matching that filter's conditions. An optional block
236
235
  # can also be used to further restrict the qualifying tasks.
237
236
  def pull(filter_name = nil, &block)
238
- unless (block_given?)
237
+ if (!block_given? and filter_name)
239
238
  block = @filters[filter_name]
240
239
  end
241
240
 
242
- @filter_lock.synchronize do
243
- tasks = @tasks.select(&block)
244
-
241
+ @filters.synchronize do
242
+ tasks = block ? @tasks.select(&block) : @tasks
243
+
245
244
  @tasks -= tasks
246
245
 
247
246
  @next_task.each do |filter_name, next_task|
@@ -260,16 +259,18 @@ class Pigeon::Queue
260
259
  # be removed from the queue and must be re-inserted if it is to be scheduled
261
260
  # again.
262
261
  def pop(filter_name = nil, &block)
263
- @filter_lock.synchronize do
262
+ @filters.synchronize do
264
263
  task =
265
264
  if (block_given?)
266
265
  @tasks.find(&block)
267
- else
266
+ elsif (filter_name)
268
267
  @next_task[filter_name] || begin
269
268
  filter_proc = @filters[filter_name]
270
269
 
271
270
  filter_proc and @tasks.find(&filter_proc)
272
271
  end
272
+ else
273
+ @tasks.first
273
274
  end
274
275
 
275
276
  if (task)
@@ -289,7 +290,7 @@ class Pigeon::Queue
289
290
  # Claims a task. This is used to indicate that the task will be processed
290
291
  # without having to be inserted into the queue.
291
292
  def claim(task)
292
- @filter_lock.synchronize do
293
+ @filters.synchronize do
293
294
  if (@claimable_task[task])
294
295
  @claimable_task[task] = false
295
296
  elsif (@tasks.delete(task))
@@ -307,9 +308,9 @@ class Pigeon::Queue
307
308
  end
308
309
 
309
310
  # Returns true if the task is queued, false otherwise.
310
- def exist?(task)
311
- @filter_lock.synchronize do
312
- @tasks.exist?(task)
311
+ def include?(task)
312
+ @filters.synchronize do
313
+ @tasks.include?(task)
313
314
  end
314
315
  end
315
316
 
@@ -318,7 +319,7 @@ class Pigeon::Queue
318
319
  # otherwise. An optional block can further restrict qualifying tasks.
319
320
  def empty?(filter_name = nil, &block)
320
321
  if (block_given?)
321
- @filter_lock.synchronize do
322
+ @filters.synchronize do
322
323
  !@tasks.find(&block)
323
324
  end
324
325
  else
@@ -332,7 +333,7 @@ class Pigeon::Queue
332
333
  def length(filter_name = nil, &block)
333
334
  filter_proc = @filters[filter_name]
334
335
 
335
- @filter_lock.synchronize do
336
+ @filters.synchronize do
336
337
  filter_proc ? @tasks.count(&filter_proc) : nil
337
338
  end
338
339
  end
@@ -341,7 +342,7 @@ class Pigeon::Queue
341
342
 
342
343
  # Copies the list of queued tasks to a new Array.
343
344
  def to_a
344
- @filter_lock.synchronize do
345
+ @filters.synchronize do
345
346
  @tasks.dup
346
347
  end
347
348
  end
@@ -356,7 +357,7 @@ protected
356
357
  return task
357
358
  end
358
359
 
359
- @filter_lock.synchronize do
360
+ @filters.synchronize do
360
361
  @next_task[filter_name] ||= @tasks.find(&filter)
361
362
  end
362
363
  end
@@ -4,22 +4,70 @@ module Pigeon::Support
4
4
  # Uses the double-fork method to create a fully detached background
5
5
  # process. Returns the process ID of the created process. May throw an
6
6
  # exception if these processes could not be created.
7
- def daemonize
7
+ def daemonize(logger = nil)
8
+ delay = 10
8
9
  rfd, wfd = IO.pipe
9
10
 
10
11
  forked_pid = fork do
11
- daemon_pid = fork do
12
- yield
12
+ rfd.close
13
+
14
+ supervisor_pid = fork do
15
+ relaunch = true
16
+
17
+ while (relaunch)
18
+ daemon_pid = fork do
19
+ begin
20
+ yield
21
+ rescue Object => e
22
+ if (logger)
23
+ logger.error("Terminated with Exception: [#{e.class}] #{e}")
24
+ logger.error(e.backtrace.join("\n"))
25
+
26
+ Thread.list.each do |thread|
27
+ logger.error("Stack trace of current threads")
28
+ logger.error(thread.inspect)
29
+
30
+ if (thread.backtrace)
31
+ logger.error("\t" + thread.backtrace.join("\n\t"))
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ begin
39
+ Process.wait(daemon_pid)
40
+
41
+ # A non-zero exit status indicates some sort of error, so the
42
+ # process will be relaunched after a short delay.
43
+ relaunch = ($? != 0)
44
+
45
+ rescue Interrupt
46
+ relaunch = false
47
+ end
48
+
49
+ if (relaunch)
50
+ logger.info("Will relaunch in %d seconds" % delay)
51
+
52
+ sleep(delay)
53
+ else
54
+ logger.info("Terminated normally")
55
+ end
56
+ end
13
57
  end
58
+
59
+ wfd.puts(supervisor_pid)
14
60
 
15
- wfd.puts daemon_pid
16
61
  wfd.flush
17
62
  wfd.close
18
63
  end
19
64
 
65
+ wfd.close
66
+
20
67
  Process.wait(forked_pid)
21
68
 
22
69
  daemon_pid = rfd.readline
70
+ rfd.close
23
71
 
24
72
  daemon_pid.to_i
25
73
  end
data/pigeon.gemspec CHANGED
@@ -5,15 +5,14 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{pigeon}
8
- s.version = "0.5.2"
8
+ s.version = "0.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["tadman"]
12
- s.date = %q{2011-04-07}
13
- s.default_executable = %q{launcher.example}
11
+ s.authors = [%q{tadman}]
12
+ s.date = %q{2011-05-12}
14
13
  s.description = %q{Pigeon is a simple way to get started building an EventMachine engine that's intended to run as a background job.}
15
14
  s.email = %q{github@tadman.ca}
16
- s.executables = ["launcher.example"]
15
+ s.executables = [%q{launcher.example}]
17
16
  s.extra_rdoc_files = [
18
17
  "LICENSE",
19
18
  "README.rdoc"
@@ -53,8 +52,8 @@ Gem::Specification.new do |s|
53
52
  "test/unit/pigeon_test.rb"
54
53
  ]
55
54
  s.homepage = %q{http://github.com/twg/pigeon}
56
- s.require_paths = ["lib"]
57
- s.rubygems_version = %q{1.6.0}
55
+ s.require_paths = [%q{lib}]
56
+ s.rubygems_version = %q{1.8.1}
58
57
  s.summary = %q{Simple daemonized EventMachine engine framework with plug-in support}
59
58
  s.test_files = [
60
59
  "test/helper.rb",
data/test/helper.rb CHANGED
@@ -34,4 +34,42 @@ class Test::Unit::TestCase
34
34
  end
35
35
  end
36
36
  end
37
+
38
+ def engine
39
+ exception = nil
40
+
41
+ @engine_thread = Thread.new do
42
+ Thread.abort_on_exception = true
43
+
44
+ # Create a thread for the engine to run on
45
+ begin
46
+ Pigeon::Engine.launch do |new_engine|
47
+ @engine = new_engine
48
+
49
+ Thread.new do
50
+ # Execute the test code in a separate thread to avoid blocking
51
+ # the EventMachine loop.
52
+ begin
53
+ yield
54
+ rescue Object => exception
55
+ ensure
56
+ begin
57
+ @engine.terminate
58
+ rescue Object
59
+ # Shutting down may trigger an exception from time to time
60
+ # if the engine itself has failed.
61
+ end
62
+ end
63
+ end
64
+ end
65
+ rescue Object => exception
66
+ end
67
+ end
68
+
69
+ @engine_thread.join
70
+
71
+ if (exception)
72
+ raise exception
73
+ end
74
+ end
37
75
  end
@@ -4,10 +4,10 @@ class PigeonLauncherTest < Test::Unit::TestCase
4
4
  def test_default_launcher
5
5
  pid = Pigeon::Launcher.launch
6
6
 
7
- assert pid
7
+ assert pid, "PID should be returned from launcher call"
8
8
  assert Pigeon::Engine.running?
9
9
 
10
- Pigeon::Launcher.stop
10
+ Pigeon::Engine.stop
11
11
 
12
12
  assert !Pigeon::Engine.running?
13
13
  end
@@ -77,72 +77,78 @@ class PigeonProcessorTest < Test::Unit::TestCase
77
77
  end
78
78
 
79
79
  def test_reassigning_queues
80
- queue_a = Pigeon::Queue.new
81
- queue_b = Pigeon::Queue.new
80
+ engine do
81
+ queue_a = Pigeon::Queue.new
82
+ queue_b = Pigeon::Queue.new
82
83
 
83
- processor = Pigeon::Processor.new(queue_a)
84
+ processor = Pigeon::Processor.new(queue_a)
84
85
 
85
- task_a = TaggedTask.new(0)
86
- assert !task_a.finished?
86
+ task_a = TaggedTask.new(0)
87
+ assert !task_a.finished?
87
88
 
88
- queue_a << task_a
89
+ queue_a << task_a
89
90
 
90
- assert_eventually(1) do
91
- task_a.finished?
92
- end
91
+ assert_eventually(1) do
92
+ task_a.finished?
93
+ end
93
94
 
94
- processor.queue = queue_b
95
+ processor.queue = queue_b
95
96
 
96
- task_b = TaggedTask.new(1)
97
- assert !task_b.finished?
97
+ task_b = TaggedTask.new(1)
98
+ assert !task_b.finished?
98
99
 
99
- queue_b << task_b
100
+ queue_b << task_b
100
101
 
101
- assert_eventually(1) do
102
- task_b.finished?
103
- end
102
+ assert_eventually(1) do
103
+ task_b.finished?
104
+ end
104
105
 
105
- task_c = TaggedTask.new(2)
106
+ task_c = TaggedTask.new(2)
106
107
 
107
- queue_a << task_c
108
+ queue_a << task_c
108
109
 
109
- sleep(1)
110
+ sleep(1)
110
111
 
111
- assert_equal false, task_c.finished?
112
+ assert_equal false, task_c.finished?
113
+ end
112
114
  end
113
115
 
114
116
  def test_can_unassign_queue_from_processor
115
- queue = Pigeon::Queue.new
116
- processor = Pigeon::Processor.new(queue)
117
+ engine do
118
+ queue = Pigeon::Queue.new
119
+ processor = Pigeon::Processor.new(queue)
117
120
 
118
- assert_equal queue, processor.queue
119
- assert_equal [ processor ], queue.processors
121
+ assert_equal queue, processor.queue
122
+ assert_equal [ processor ], queue.processors
120
123
 
121
- processor.queue = nil
124
+ processor.queue = nil
122
125
 
123
- assert_equal nil, processor.queue
124
- assert_equal [ ], queue.processors
126
+ assert_equal nil, processor.queue
127
+ assert_equal [ ], queue.processors
128
+ end
125
129
  end
126
130
 
127
131
  def test_multiple_processors
128
- queue = Pigeon::Queue.new
129
- count = 10000
132
+ engine do
133
+ queue = Pigeon::Queue.new
134
+ count = 10000
130
135
 
131
- count.times do |n|
132
- queue << TaggedTask.new(n)
133
- end
136
+ count.times do |n|
137
+ queue << TaggedTask.new(n)
138
+ end
134
139
 
135
- assert_equal count, queue.length
140
+ assert_equal count, queue.length
136
141
 
137
- processors = (0..9).to_a.collect do
138
- Pigeon::Processor.new(queue)
139
- end
142
+ processors = (0..9).to_a.collect do
143
+ Pigeon::Processor.new(queue)
144
+ end
140
145
 
141
- assert_eventually(10) do
142
- queue.empty?
143
- end
146
+ assert_eventually(10) do
147
+ queue.empty?
148
+ end
144
149
 
145
- assert_equal 0, processors.select(&:task?).length
146
- assert_equal 0, queue.length
150
+ assert_equal 0, processors.select(&:task?).length
151
+ assert_equal 0, queue.length
152
+ end
147
153
  end
148
154
  end
@@ -13,16 +13,6 @@ class PigeonQueueTest < Test::Unit::TestCase
13
13
  "<#{@tag}>"
14
14
  end
15
15
  end
16
-
17
- def setup
18
- @engine = Pigeon::Engine.new
19
-
20
- Pigeon::Engine.register_engine(@engine)
21
- end
22
-
23
- def teardown
24
- Pigeon::Engine.unregister_engine(@engine)
25
- end
26
16
 
27
17
  def test_empty_state
28
18
  queue = Pigeon::Queue.new
@@ -36,170 +26,222 @@ class PigeonQueueTest < Test::Unit::TestCase
36
26
  end
37
27
 
38
28
  def test_cycling
39
- queue = Pigeon::Queue.new
29
+ engine do
30
+ queue = Pigeon::Queue.new
40
31
 
41
- task = Pigeon::Task.new
32
+ task = Pigeon::Task.new
42
33
 
43
- assert_equal task, queue << task
34
+ assert_equal task, queue << task
35
+
36
+ assert_eventually do
37
+ queue.peek == task
38
+ end
44
39
 
45
- assert queue.peek
46
- assert_equal 1, queue.length
47
- assert !queue.empty?
40
+ assert queue.peek
41
+ assert_equal 1, queue.length
42
+ assert !queue.empty?
48
43
 
49
- found_task = queue.pop
44
+ found_task = queue.pop
50
45
 
51
- assert_equal task, found_task
46
+ assert_equal task, found_task
52
47
 
53
- assert_equal 0, queue.length
54
- assert queue.empty?
48
+ assert_equal 0, queue.length
49
+ assert queue.empty?
50
+ end
55
51
  end
56
52
 
57
53
  def test_filtering
58
- queue = Pigeon::Queue.new
54
+ engine do
55
+ queue = Pigeon::Queue.new
59
56
 
60
- tasks = (0..9).to_a.collect do |n|
61
- queue << TaggedTask.new(n)
62
- end
57
+ tasks = (0..9).to_a.collect do |n|
58
+ queue << TaggedTask.new(n)
59
+ end
60
+
61
+ assert_eventually do
62
+ queue.length == 10
63
+ end
63
64
 
64
- assert_equal (0..9).to_a, tasks.to_a.collect(&:tag)
65
+ assert_equal (0..9).to_a, tasks.to_a.collect(&:tag)
65
66
 
66
- assert_equal tasks[0], queue.peek
67
+ assert_equal tasks[0], queue.peek
67
68
 
68
- selected_task = queue.peek do |task|
69
- task.tag > 0
70
- end
69
+ selected_task = queue.peek do |task|
70
+ task.tag > 0
71
+ end
71
72
 
72
- assert_equal tasks[1], selected_task
73
+ assert_equal tasks[1], selected_task
73
74
 
74
- queue.filter(:over_7) do |task|
75
- task.tag > 7
76
- end
75
+ queue.filter(:over_7) do |task|
76
+ task.tag > 7
77
+ end
77
78
 
78
- assert_equal tasks[8], queue.peek(:over_7)
79
- assert_equal 2, queue.length(:over_7)
79
+ assert_equal tasks[8], queue.peek(:over_7)
80
+ assert_equal 2, queue.length(:over_7)
80
81
 
81
- pulled_task = queue.pop(:over_7)
82
+ pulled_task = queue.pop(:over_7)
82
83
 
83
- assert_equal 9, queue.length
84
+ assert_equal 9, queue.length
84
85
 
85
- assert_equal tasks[9], queue.peek(:over_7)
86
- assert_equal 1, queue.length(:over_7)
86
+ assert_equal tasks[9], queue.peek(:over_7)
87
+ assert_equal 1, queue.length(:over_7)
87
88
 
88
- queue.pop(:over_7)
89
+ queue.pop(:over_7)
89
90
 
90
- assert_equal nil, queue.peek(:over_7)
91
- assert_equal 0, queue.length(:over_7)
92
- assert_equal true, queue.empty?(:over_7)
91
+ assert_equal nil, queue.peek(:over_7)
92
+ assert_equal 0, queue.length(:over_7)
93
+ assert_equal true, queue.empty?(:over_7)
93
94
 
94
- new_task = queue << TaggedTask.new(10)
95
+ new_task = queue << TaggedTask.new(10)
95
96
 
96
- assert_equal new_task, queue.peek(:over_7)
97
- assert_equal 1, queue.length(:over_7)
98
- assert_equal false, queue.empty?(:over_7)
97
+ assert_eventually do
98
+ queue.include?(new_task)
99
+ end
100
+
101
+ assert_equal new_task, queue.peek(:over_7)
102
+ assert_equal 1, queue.length(:over_7)
103
+ assert_equal false, queue.empty?(:over_7)
99
104
 
100
- queue.claim(new_task)
105
+ queue.claim(new_task)
101
106
 
102
- assert_equal nil, queue.peek(:over_7)
103
- assert_equal 0, queue.length(:over_7)
104
- assert_equal true, queue.empty?(:over_7)
107
+ assert_equal nil, queue.peek(:over_7)
108
+ assert_equal 0, queue.length(:over_7)
109
+ assert_equal true, queue.empty?(:over_7)
110
+ end
105
111
  end
106
112
 
107
113
  def test_observe
108
- queue = Pigeon::Queue.new
114
+ engine do
115
+ queue = Pigeon::Queue.new
109
116
 
110
- tasks = (0..9).to_a.collect do |n|
111
- queue << TaggedTask.new(n)
112
- end
117
+ tasks = (0..9).to_a.collect do |n|
118
+ queue << TaggedTask.new(n)
119
+ end
113
120
 
114
- queue.filter(:odd) do |task|
115
- task.tag % 2 == 1
116
- end
121
+ queue.filter(:odd) do |task|
122
+ task.tag % 2 == 1
123
+ end
124
+
125
+ assert_eventually(2) do
126
+ queue.length == 10
127
+ end
117
128
 
118
- assert_equal tasks[1], queue.peek(:odd)
119
- assert_equal 5, queue.length(:odd)
129
+ assert_equal tasks[1], queue.peek(:odd)
130
+ assert_equal 5, queue.length(:odd)
120
131
 
121
- assert_equal [ tasks[1], tasks[3], tasks[5], tasks[7], tasks[9] ], queue.pull(:odd)
132
+ assert_equal [ tasks[1], tasks[3], tasks[5], tasks[7], tasks[9] ], queue.pull(:odd)
122
133
 
123
- assert_equal 5, queue.length
124
- assert_equal 0, queue.length(:odd)
134
+ assert_equal 5, queue.length
135
+ assert_equal 0, queue.length(:odd)
125
136
 
126
- added_odd = nil
137
+ added_odd = nil
127
138
 
128
- queue.observe(:odd) do |task|
129
- added_odd = task
130
- end
139
+ queue.observe(:odd) do |task|
140
+ added_odd = task
141
+ end
131
142
 
132
- queue << TaggedTask.new(10)
143
+ new_task = queue << TaggedTask.new(10)
144
+
145
+ assert_eventually(2) do
146
+ queue.include?(new_task)
147
+ end
133
148
 
134
- assert_equal nil, added_odd
149
+ assert_equal nil, added_odd
135
150
 
136
- odd_1 = queue << TaggedTask.new(11)
151
+ odd_1 = queue << TaggedTask.new(11)
152
+
153
+ assert_eventually(2) do
154
+ queue.include?(odd_1)
155
+ end
137
156
 
138
- assert_equal odd_1, added_odd
157
+ assert_equal odd_1, added_odd
139
158
 
140
- claimed_task = nil
141
- has_run = false
159
+ claimed_task = nil
160
+ has_run = false
142
161
 
143
- queue.observe(:odd) do |task|
144
- claimed_task = queue.claim(task)
145
- has_run = true
146
- end
162
+ queue.observe(:odd) do |task|
163
+ claimed_task = queue.claim(task)
164
+ has_run = true
165
+ end
147
166
 
148
- # Observer callbacks are not triggered on existing data, only on new
149
- # insertions.
150
- assert_equal false, has_run
151
- assert_equal nil, claimed_task
152
- assert_equal 7, queue.length
153
- assert_equal 1, queue.length(:odd)
154
-
155
- queue << TaggedTask.new(12)
167
+ # Observer callbacks are not triggered on existing data, only on new
168
+ # insertions.
169
+ assert_equal false, has_run
170
+ assert_equal nil, claimed_task
171
+ assert_equal 7, queue.length
172
+ assert_equal 1, queue.length(:odd)
173
+
174
+ new_task = queue << TaggedTask.new(12)
175
+
176
+ assert_eventually(2) do
177
+ queue.include?(new_task)
178
+ end
156
179
 
157
- assert_equal nil, claimed_task
158
- assert_equal 8, queue.length
159
- assert_equal 1, queue.length(:odd)
180
+ assert_equal nil, claimed_task
181
+ assert_equal 8, queue.length
182
+ assert_equal 1, queue.length(:odd)
160
183
 
161
- odd_2 = queue << TaggedTask.new(13)
184
+ odd_2 = queue << TaggedTask.new(13)
185
+
186
+ assert_eventually(2) do
187
+ claimed_task == odd_2
188
+ end
162
189
 
163
- # Adding a task that matches the filter triggers the callback.
164
- assert_equal odd_2, claimed_task
165
- assert_equal true, has_run
190
+ # Adding a task that matches the filter triggers the callback.
191
+ assert_equal odd_2, claimed_task
192
+ assert_equal true, has_run
166
193
 
167
- # Clear out all of the odd entries.
168
- queue.pull(:odd)
194
+ # Clear out all of the odd entries.
195
+ queue.pull(:odd)
169
196
 
170
- claimed_task = nil
171
- has_run = false
197
+ claimed_task = nil
198
+ has_run = false
172
199
 
173
- queue << TaggedTask.new(14)
200
+ new_task = queue << TaggedTask.new(14)
201
+
202
+ assert_eventually(2) do
203
+ queue.include?(new_task)
204
+ end
174
205
 
175
- assert_equal nil, claimed_task
176
- assert_equal false, has_run
206
+ assert_equal nil, claimed_task
207
+ assert_equal false, has_run
177
208
 
178
- odd_2 = queue << TaggedTask.new(15)
209
+ odd_2 = queue << TaggedTask.new(15)
210
+
211
+ assert_eventually(2) do
212
+ odd_2 == claimed_task
213
+ end
179
214
 
180
- assert_equal odd_2, claimed_task
181
- assert_equal true, has_run
215
+ assert_equal odd_2, claimed_task
216
+ assert_equal true, has_run
182
217
 
183
- assert_equal 8, queue.length
184
- assert_equal 0, queue.length(:odd)
218
+ assert_equal 8, queue.length
219
+ assert_equal 0, queue.length(:odd)
220
+ end
185
221
  end
186
222
 
187
223
  def test_can_add_during_observe
188
- queue = Pigeon::Queue.new
224
+ engine do
225
+ queue = Pigeon::Queue.new
189
226
 
190
- queue.observe do |task|
191
- if (task.tag < 10)
192
- queue.claim(task)
227
+ queue.observe do |task|
228
+ if (task.tag < 10)
229
+ queue.claim(task)
193
230
 
194
- queue << TaggedTask.new(task.tag + 1)
231
+ queue << TaggedTask.new(task.tag + 1)
232
+ end
195
233
  end
196
- end
197
234
 
198
- queue << TaggedTask.new(0)
235
+ new_task = queue << TaggedTask.new(0)
236
+
237
+ assert_eventually(2) do
238
+ queue.peek
239
+ end
199
240
 
200
- assert queue.peek
201
- assert_equal 10, queue.peek.tag
202
- assert_equal 1, queue.length
203
- assert queue.peek
241
+ assert queue.peek
242
+ assert_equal 10, queue.peek.tag
243
+ assert_equal 1, queue.length
244
+ assert queue.peek
245
+ end
204
246
  end
205
247
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: pigeon
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.5.2
5
+ version: 0.6.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - tadman
@@ -10,8 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-04-07 00:00:00 -04:00
14
- default_executable: launcher.example
13
+ date: 2011-05-12 00:00:00 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: eventmachine
@@ -66,7 +65,6 @@ files:
66
65
  - test/unit/pigeon_sorted_array_test.rb
67
66
  - test/unit/pigeon_task_test.rb
68
67
  - test/unit/pigeon_test.rb
69
- has_rdoc: true
70
68
  homepage: http://github.com/twg/pigeon
71
69
  licenses: []
72
70
 
@@ -90,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
88
  requirements: []
91
89
 
92
90
  rubyforge_project:
93
- rubygems_version: 1.6.0
91
+ rubygems_version: 1.8.1
94
92
  signing_key:
95
93
  specification_version: 3
96
94
  summary: Simple daemonized EventMachine engine framework with plug-in support