pigeon 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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