perfectqueue 0.8.44.1 → 0.8.45
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.
- checksums.yaml +4 -4
- data/ChangeLog +5 -2
- data/lib/perfectqueue/application/decider.rb +2 -2
- data/lib/perfectqueue/application/router.rb +1 -1
- data/lib/perfectqueue/backend/rdb_compat.rb +40 -58
- data/lib/perfectqueue/engine.rb +2 -5
- data/lib/perfectqueue/multiprocess/thread_processor.rb +1 -1
- data/lib/perfectqueue/task.rb +2 -1
- data/lib/perfectqueue/task_metadata.rb +2 -4
- data/lib/perfectqueue/version.rb +1 -1
- data/perfectqueue.gemspec +1 -2
- data/spec/application/base_spec.rb +81 -0
- data/spec/application/decider_spec.rb +56 -0
- data/spec/application/dispatch_spec.rb +22 -0
- data/spec/application/router_spec.rb +48 -0
- data/spec/backend_spec.rb +9 -0
- data/spec/blocking_flag_spec.rb +103 -0
- data/spec/client_spec.rb +28 -0
- data/spec/daemons_logger_spec.rb +35 -0
- data/spec/engine_spec.rb +159 -0
- data/spec/multiprocess/child_process_monitor_spec.rb +300 -0
- data/spec/multiprocess/child_process_spec.rb +160 -0
- data/spec/multiprocess/fork_processor_spec.rb +170 -0
- data/spec/multiprocess/thread_processor_spec.rb +52 -0
- data/spec/queue_spec.rb +73 -68
- data/spec/rdb_compat_backend_spec.rb +481 -19
- data/spec/runner_spec.rb +32 -0
- data/spec/signal_thread_spec.rb +43 -0
- data/spec/stress.rb +1 -1
- data/spec/supervisor_spec.rb +188 -33
- data/spec/task_metadata_spec.rb +69 -0
- data/spec/task_monitor_spec.rb +42 -0
- data/spec/task_spec.rb +70 -0
- metadata +40 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca67d530351a8a7e0ff2f950d10248bc23eb5e2e
|
4
|
+
data.tar.gz: 5b66bc3219182f721af358e0bce7836ffdca356d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 282b4e05105a5b6fe92cfb3c406b415221784350cb1449e87fe42b481fa7adc0b5d5318c67cc5922e9deafbd244939b2481e76c10ced8b15e981605eacddde35
|
7
|
+
data.tar.gz: 7f4a15e96b072fa04144d04f6f92396a730b76de07c0cb45c6f63d9c30d84bbfff88d7cab526a7a895d6265ed74cc897f9d0b8293d3a7c8271d67cba65c4acee
|
data/ChangeLog
CHANGED
@@ -19,7 +19,7 @@
|
|
19
19
|
module PerfectQueue
|
20
20
|
module Application
|
21
21
|
|
22
|
-
class UndefinedDecisionError
|
22
|
+
class UndefinedDecisionError < StandardError
|
23
23
|
end
|
24
24
|
|
25
25
|
class Decider
|
@@ -39,7 +39,7 @@ module PerfectQueue
|
|
39
39
|
begin
|
40
40
|
m = method(type)
|
41
41
|
rescue NameError
|
42
|
-
raise UndefinedDecisionError, "Undefined decision #{type} options=#{
|
42
|
+
raise UndefinedDecisionError, "Undefined decision #{type} options=#{opts.inspect}"
|
43
43
|
end
|
44
44
|
m.call(opts)
|
45
45
|
end
|
@@ -58,7 +58,7 @@ module PerfectQueue
|
|
58
58
|
when String, Symbol
|
59
59
|
pattern = /\A#{Regexp.escape(pattern)}\z/
|
60
60
|
else
|
61
|
-
raise
|
61
|
+
raise ArgumentError, "pattern should be String or Regexp but got #{pattern.class}: #{pattern.inspect}"
|
62
62
|
end
|
63
63
|
|
64
64
|
@patterns << [pattern, sym]
|
@@ -21,9 +21,6 @@ module PerfectQueue
|
|
21
21
|
class RDBCompatBackend
|
22
22
|
include BackendHelper
|
23
23
|
|
24
|
-
# backport from v0.8.46
|
25
|
-
EVENT_HORIZON = 13_0000_0000 # 2011-03-13 07:06:40 UTC
|
26
|
-
|
27
24
|
class Token < Struct.new(:key)
|
28
25
|
end
|
29
26
|
|
@@ -40,10 +37,7 @@ module PerfectQueue
|
|
40
37
|
#password = config[:password]
|
41
38
|
#user = config[:user]
|
42
39
|
|
43
|
-
|
44
|
-
when /sqlite/i
|
45
|
-
@db = Sequel.connect(url, :max_connections=>1)
|
46
|
-
when /mysql/i
|
40
|
+
if /\Amysql/i =~ url
|
47
41
|
require 'uri'
|
48
42
|
|
49
43
|
uri = URI.parse(url)
|
@@ -63,8 +57,20 @@ module PerfectQueue
|
|
63
57
|
else
|
64
58
|
@use_connection_pooling = !!config[:sslca]
|
65
59
|
end
|
60
|
+
@table_lock = lambda {
|
61
|
+
locked = nil
|
62
|
+
loop do
|
63
|
+
@db.fetch("SELECT GET_LOCK('#{@table}', #{LOCK_WAIT_TIMEOUT}) locked") do |row|
|
64
|
+
locked = true if row[:locked] == 1
|
65
|
+
end
|
66
|
+
break if locked
|
67
|
+
end
|
68
|
+
}
|
69
|
+
@table_unlock = lambda {
|
70
|
+
@db.run("SELECT RELEASE_LOCK('#{@table}')")
|
71
|
+
}
|
66
72
|
else
|
67
|
-
raise ConfigError, "
|
73
|
+
raise ConfigError, "only 'mysql' is supported"
|
68
74
|
end
|
69
75
|
|
70
76
|
@last_time = Time.now.to_i
|
@@ -79,7 +85,6 @@ module PerfectQueue
|
|
79
85
|
SELECT id, timeout, data, created_at, resource
|
80
86
|
FROM `#{@table}`
|
81
87
|
WHERE timeout <= ? AND timeout <= ? AND created_at IS NOT NULL
|
82
|
-
AND #{EVENT_HORIZON} < timeout
|
83
88
|
ORDER BY timeout ASC
|
84
89
|
LIMIT ?
|
85
90
|
SQL
|
@@ -94,37 +99,11 @@ LEFT JOIN (
|
|
94
99
|
GROUP BY resource
|
95
100
|
) AS R ON resource = res
|
96
101
|
WHERE timeout <= ? AND created_at IS NOT NULL AND (max_running-running IS NULL OR max_running-running > 0)
|
97
|
-
AND #{EVENT_HORIZON} < timeout
|
98
102
|
ORDER BY weight DESC, timeout ASC
|
99
103
|
LIMIT ?
|
100
104
|
SQL
|
101
105
|
end
|
102
106
|
|
103
|
-
case url.split('//',2)[0].to_s
|
104
|
-
when /sqlite/i
|
105
|
-
# sqlite always locks tables on BEGIN
|
106
|
-
@table_lock = nil
|
107
|
-
@table_unlock = nil
|
108
|
-
when /mysql/i
|
109
|
-
@table_lock = lambda {
|
110
|
-
locked = nil
|
111
|
-
loop do
|
112
|
-
@db.fetch("SELECT GET_LOCK('#{@table}', #{LOCK_WAIT_TIMEOUT}) locked") do |row|
|
113
|
-
locked = true if row[:locked] == 1
|
114
|
-
end
|
115
|
-
break if locked
|
116
|
-
end
|
117
|
-
}
|
118
|
-
@table_unlock = lambda {
|
119
|
-
@db.run("SELECT RELEASE_LOCK('#{@table}')")
|
120
|
-
}
|
121
|
-
else
|
122
|
-
@table_lock = lambda {
|
123
|
-
@db.run("LOCK TABLE `#{@table}`")
|
124
|
-
}
|
125
|
-
@table_unlock = nil
|
126
|
-
end
|
127
|
-
|
128
107
|
@prefetch_break_types = config[:prefetch_break_types] || []
|
129
108
|
|
130
109
|
@cleanup_interval = config[:cleanup_interval] || DEFAULT_DELETE_INTERVAL
|
@@ -190,6 +169,24 @@ SQL
|
|
190
169
|
}
|
191
170
|
end
|
192
171
|
|
172
|
+
def compress_data(data, compression)
|
173
|
+
if compression == 'gzip'
|
174
|
+
require 'zlib'
|
175
|
+
require 'stringio'
|
176
|
+
io = StringIO.new
|
177
|
+
io.set_encoding(Encoding::ASCII_8BIT)
|
178
|
+
gz = Zlib::GzipWriter.new(io)
|
179
|
+
begin
|
180
|
+
gz.write(data)
|
181
|
+
ensure
|
182
|
+
gz.close
|
183
|
+
end
|
184
|
+
data = io.string
|
185
|
+
data = Sequel::SQL::Blob.new(data)
|
186
|
+
end
|
187
|
+
data
|
188
|
+
end
|
189
|
+
|
193
190
|
# => Task
|
194
191
|
def submit(key, type, data, options)
|
195
192
|
now = (options[:now] || Time.now).to_i
|
@@ -200,23 +197,7 @@ SQL
|
|
200
197
|
max_running = options[:max_running]
|
201
198
|
data = data ? data.dup : {}
|
202
199
|
data['type'] = type
|
203
|
-
|
204
|
-
d = data.to_json
|
205
|
-
|
206
|
-
if options[:compression] == 'gzip'
|
207
|
-
require 'zlib'
|
208
|
-
require 'stringio'
|
209
|
-
io = StringIO.new
|
210
|
-
gz = Zlib::GzipWriter.new(io)
|
211
|
-
begin
|
212
|
-
gz.write(d)
|
213
|
-
ensure
|
214
|
-
gz.close
|
215
|
-
end
|
216
|
-
d = io.string
|
217
|
-
d.force_encoding('ASCII-8BIT') if d.respond_to?(:force_encoding)
|
218
|
-
d = Sequel::SQL::Blob.new(d)
|
219
|
-
end
|
200
|
+
d = compress_data(data.to_json, options[:compression])
|
220
201
|
|
221
202
|
connect {
|
222
203
|
begin
|
@@ -240,7 +221,7 @@ SQL
|
|
240
221
|
|
241
222
|
if @cleanup_interval_count <= 0
|
242
223
|
connect_locked {
|
243
|
-
@db["DELETE FROM `#{@table}` WHERE
|
224
|
+
@db["DELETE FROM `#{@table}` WHERE timeout <= ? AND created_at IS NULL", now].delete
|
244
225
|
@cleanup_interval_count = @cleanup_interval
|
245
226
|
}
|
246
227
|
end
|
@@ -281,8 +262,6 @@ SQL
|
|
281
262
|
|
282
263
|
# => nil
|
283
264
|
def cancel_request(key, options)
|
284
|
-
now = (options[:now] || Time.now).to_i
|
285
|
-
|
286
265
|
# created_at=0 means cancel_requested
|
287
266
|
connect {
|
288
267
|
n = @db["UPDATE `#{@table}` SET created_at=0 WHERE id=? AND created_at IS NOT NULL", key].update
|
@@ -323,7 +302,7 @@ SQL
|
|
323
302
|
params = [sql, next_timeout]
|
324
303
|
if data
|
325
304
|
sql << ", data=?"
|
326
|
-
params << data.to_json
|
305
|
+
params << compress_data(data.to_json, options[:compression])
|
327
306
|
end
|
328
307
|
sql << " WHERE id=? AND created_at IS NOT NULL"
|
329
308
|
params << key
|
@@ -336,8 +315,6 @@ SQL
|
|
336
315
|
raise PreemptedError, "task key=#{key} does not exist or preempted."
|
337
316
|
elsif row[:created_at] == nil
|
338
317
|
raise PreemptedError, "task key=#{key} preempted."
|
339
|
-
elsif row[:created_at] <= 0
|
340
|
-
raise CancelRequestedError, "task key=#{key} is cancel requested."
|
341
318
|
else # row[:timeout] == next_timeout
|
342
319
|
# ok
|
343
320
|
end
|
@@ -395,6 +372,8 @@ SQL
|
|
395
372
|
else
|
396
373
|
STDERR.puts err + "\n abort."
|
397
374
|
end
|
375
|
+
else
|
376
|
+
err = $!
|
398
377
|
end
|
399
378
|
|
400
379
|
STDERR.puts "disconnects current connection: #{err}"
|
@@ -411,6 +390,7 @@ SQL
|
|
411
390
|
GZIP_MAGIC_BYTES = [0x1f, 0x8b].pack('CC')
|
412
391
|
|
413
392
|
def create_attributes(now, row)
|
393
|
+
compression = nil
|
414
394
|
if row[:created_at] === nil
|
415
395
|
created_at = nil # unknown creation time
|
416
396
|
status = TaskStatus::FINISHED
|
@@ -434,6 +414,7 @@ SQL
|
|
434
414
|
if d[0, 2] == GZIP_MAGIC_BYTES
|
435
415
|
require 'zlib'
|
436
416
|
require 'stringio'
|
417
|
+
compression = 'gzip'
|
437
418
|
gz = Zlib::GzipReader.new(StringIO.new(d))
|
438
419
|
begin
|
439
420
|
d = gz.read
|
@@ -464,6 +445,7 @@ SQL
|
|
464
445
|
:max_running => row[:max_running],
|
465
446
|
:message => nil, # not supported
|
466
447
|
:node => nil, # not supported
|
448
|
+
:compression => compression,
|
467
449
|
}
|
468
450
|
end
|
469
451
|
|
data/lib/perfectqueue/engine.rb
CHANGED
@@ -56,7 +56,7 @@ module PerfectQueue
|
|
56
56
|
@processors << @processor_class.new(@runner, @processors.size+1, config)
|
57
57
|
end
|
58
58
|
elsif extra < 0
|
59
|
-
-extra.times do
|
59
|
+
(-extra).times do
|
60
60
|
c = @processors.shift
|
61
61
|
c.stop(immediate)
|
62
62
|
c.join
|
@@ -106,10 +106,7 @@ module PerfectQueue
|
|
106
106
|
def replace(immediate, command=[$0]+ARGV)
|
107
107
|
return if @replaced_pid
|
108
108
|
stop(immediate)
|
109
|
-
@replaced_pid = Process.
|
110
|
-
exec(*command)
|
111
|
-
exit!(127)
|
112
|
-
end
|
109
|
+
@replaced_pid = Process.spawn(*command)
|
113
110
|
self
|
114
111
|
end
|
115
112
|
|
@@ -121,7 +121,7 @@ module PerfectQueue
|
|
121
121
|
end
|
122
122
|
|
123
123
|
def process(task)
|
124
|
-
@log.info "acquired task task=#{task.key} id=#{@processor_id}
|
124
|
+
@log.info "acquired task task=#{task.key} id=#{@processor_id}"
|
125
125
|
begin
|
126
126
|
r = @runner.new(task)
|
127
127
|
@tm.set_task(task, r)
|
data/lib/perfectqueue/task.rb
CHANGED
@@ -58,6 +58,7 @@ module PerfectQueue
|
|
58
58
|
class TaskWithMetadata < Task
|
59
59
|
def initialize(client, key, attributes)
|
60
60
|
super(client, key)
|
61
|
+
@compression = attributes.delete(:compression)
|
61
62
|
@attributes = attributes
|
62
63
|
end
|
63
64
|
|
@@ -93,7 +94,7 @@ module PerfectQueue
|
|
93
94
|
def update_data!(hash)
|
94
95
|
data = @attributes[:data] || {}
|
95
96
|
merged = data.merge(hash)
|
96
|
-
heartbeat!(:
|
97
|
+
heartbeat!(data: merged, compression: compression)
|
97
98
|
@attributes[:data] = merged
|
98
99
|
end
|
99
100
|
|
@@ -19,6 +19,7 @@
|
|
19
19
|
module PerfectQueue
|
20
20
|
module TaskMetadataAccessors
|
21
21
|
attr_reader :attributes
|
22
|
+
attr_reader :compression
|
22
23
|
|
23
24
|
def type
|
24
25
|
@attributes[:type]
|
@@ -60,10 +61,6 @@ module PerfectQueue
|
|
60
61
|
status == TaskStatus::FINISHED
|
61
62
|
end
|
62
63
|
|
63
|
-
def running?
|
64
|
-
status == TaskStatus::RUNNING
|
65
|
-
end
|
66
|
-
|
67
64
|
def waiting?
|
68
65
|
status == TaskStatus::WAITING
|
69
66
|
end
|
@@ -83,6 +80,7 @@ module PerfectQueue
|
|
83
80
|
def initialize(client, key, attributes)
|
84
81
|
super(client)
|
85
82
|
@key = key
|
83
|
+
@compression = attributes.delete(:compression)
|
86
84
|
@attributes = attributes
|
87
85
|
end
|
88
86
|
|
data/lib/perfectqueue/version.rb
CHANGED
data/perfectqueue.gemspec
CHANGED
@@ -19,8 +19,7 @@ Gem::Specification.new do |gem|
|
|
19
19
|
|
20
20
|
gem.add_dependency "sequel", "~> 3.48.0"
|
21
21
|
gem.add_development_dependency "rake", "~> 0.9.2"
|
22
|
-
gem.add_development_dependency "rspec", "~>
|
22
|
+
gem.add_development_dependency "rspec", "~> 3.3.0"
|
23
23
|
gem.add_development_dependency "simplecov", "~> 0.10.0"
|
24
|
-
gem.add_development_dependency "sqlite3", "~> 1.3.3"
|
25
24
|
gem.add_development_dependency "mysql2", "~> 0.3.20"
|
26
25
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PerfectQueue::Application::Base do
|
4
|
+
describe '.decider=' do
|
5
|
+
it 'defines .decider which returns the decider' do
|
6
|
+
decider_klass = double('decider_klass')
|
7
|
+
klass = PerfectQueue::Application::Base
|
8
|
+
allow(klass).to receive(:decider).and_call_original
|
9
|
+
allow(klass).to receive(:decider=).with(decider_klass).and_call_original
|
10
|
+
expect(klass.decider = decider_klass).to eq(decider_klass)
|
11
|
+
expect(klass.decider).to eq(decider_klass)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '.decider' do
|
16
|
+
it 'returns DefaultDecider' do
|
17
|
+
expect(PerfectQueue::Application::Base.decider).to eq(PerfectQueue::Application::DefaultDecider)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#new' do
|
22
|
+
let (:task){ double('task') }
|
23
|
+
let (:base) { PerfectQueue::Application::Base.new(task) }
|
24
|
+
it 'calls super and set decider'do
|
25
|
+
expect(base).to be_an_instance_of(PerfectQueue::Application::Base)
|
26
|
+
expect(base.instance_variable_get(:@task)).to eq(task)
|
27
|
+
expect(base.instance_variable_get(:@decider)).to be_an_instance_of(Application::DefaultDecider)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#run' do
|
32
|
+
let (:base) { PerfectQueue::Application::Base.new(double('task')) }
|
33
|
+
it 'returns nil if before_perform returns false' do
|
34
|
+
allow(base).to receive(:before_perform).and_return(false)
|
35
|
+
expect(base.run).to be_nil
|
36
|
+
end
|
37
|
+
it 'returns nil' do
|
38
|
+
expect(base).to receive(:before_perform).exactly(:once).and_call_original
|
39
|
+
expect(base).to receive(:perform).exactly(:once).and_return(nil)
|
40
|
+
expect(base).to receive(:after_perform).exactly(:once).and_call_original
|
41
|
+
expect(base.run).to be_nil
|
42
|
+
end
|
43
|
+
it 'calls unexpected_error_raised on error' do
|
44
|
+
allow(base).to receive(:before_perform).exactly(:once).and_call_original
|
45
|
+
allow(base).to receive(:perform).exactly(:once) { raise }
|
46
|
+
allow(base).to receive(:decide!).with(:unexpected_error_raised, error: kind_of(Exception)).exactly(:once)
|
47
|
+
expect(base.run).to be_nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#before_perform' do
|
52
|
+
let (:base) { PerfectQueue::Application::Base.new(double('task')) }
|
53
|
+
it 'returns true' do
|
54
|
+
expect(base.before_perform).to be true
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#after_perform' do
|
59
|
+
let (:base) { PerfectQueue::Application::Base.new(double('task')) }
|
60
|
+
it 'returns nil' do
|
61
|
+
expect(base.after_perform).to be_nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#decide!' do
|
66
|
+
let (:base) do
|
67
|
+
decider = double('decider')
|
68
|
+
expect(decider).to receive(:decide!).with(:type, :option).exactly(:once)
|
69
|
+
decider_klass = double('decider_klass')
|
70
|
+
allow(decider_klass).to receive(:new).with(kind_of(PerfectQueue::Application::Base)).and_return(decider)
|
71
|
+
klass = PerfectQueue::Application::Base
|
72
|
+
allow(klass).to receive(:decider).and_call_original
|
73
|
+
allow(klass).to receive(:decider=).with(decider_klass).and_call_original
|
74
|
+
klass.decider = decider_klass
|
75
|
+
klass.new(double('task'))
|
76
|
+
end
|
77
|
+
it 'calls decider.decide' do
|
78
|
+
expect(base.decide!(:type, :option)).to be_nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PerfectQueue::Application::UndefinedDecisionError do
|
4
|
+
it { is_expected.to be_an_instance_of(PerfectQueue::Application::UndefinedDecisionError) }
|
5
|
+
it { is_expected.to be_a(Exception) }
|
6
|
+
end
|
7
|
+
|
8
|
+
describe PerfectQueue::Application::Decider do
|
9
|
+
describe '#new' do
|
10
|
+
let (:decider) { PerfectQueue::Application::Decider.new(nil) }
|
11
|
+
it do
|
12
|
+
expect(decider).to be_an_instance_of(PerfectQueue::Application::Decider)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#queue' do
|
17
|
+
let (:queue){ double('queue') }
|
18
|
+
let (:decider) do
|
19
|
+
base = double('base')
|
20
|
+
allow(base).to receive(:queue).exactly(:once).and_return(queue)
|
21
|
+
PerfectQueue::Application::Decider.new(base)
|
22
|
+
end
|
23
|
+
it 'calls @base.queue' do
|
24
|
+
expect(decider.queue).to eq(queue)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#task' do
|
29
|
+
let (:task){ double('task') }
|
30
|
+
let (:decider) do
|
31
|
+
base = double('base')
|
32
|
+
allow(base).to receive(:task).exactly(:once).and_return(task)
|
33
|
+
PerfectQueue::Application::Decider.new(base)
|
34
|
+
end
|
35
|
+
it 'calls @base.task' do
|
36
|
+
expect(decider.task).to eq(task)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#decide!' do
|
41
|
+
let (:decider) { PerfectQueue::Application::Decider.new(nil) }
|
42
|
+
it 'calls the specified method' do
|
43
|
+
allow(decider).to receive(:foo).exactly(:once).with(72).and_return(42)
|
44
|
+
expect(decider.decide!(:foo, 72)).to eq(42)
|
45
|
+
end
|
46
|
+
it 'raises UndefinedDecisionError on unknown method' do
|
47
|
+
expect{ decider.decide!(:foo, 72) }.to raise_error(PerfectQueue::Application::UndefinedDecisionError)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe PerfectQueue::Application::DefaultDecider do
|
53
|
+
subject { PerfectQueue::Application::DefaultDecider.new(nil) }
|
54
|
+
it { is_expected.to be_a(PerfectQueue::Application::Decider) }
|
55
|
+
it { is_expected.to be_an_instance_of(PerfectQueue::Application::DefaultDecider) }
|
56
|
+
end
|