boss_queue 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -67,6 +67,9 @@ Usage
67
67
  myobject = MyClass.new
68
68
  # default failure action is 'retry' which retries up to four times
69
69
  queue = BossQueue.new(:failure_action => 'none', :queue => 'emails')
70
+ # or we can set up a callback method to be called on the enqueued class / object
71
+ # when there is a failure
72
+ queue = BossQueue.new(:failure_action => 'callback', :failure_callback => :method_to_execute_on_failure, :queue => 'emails')
70
73
 
71
74
  # can enqueue instance methods (assumes that objects have an id and a #find(id) method)
72
75
  queue.enqueue(myobject, :method_to_execute, arg1, arg2)
@@ -82,7 +85,7 @@ Usage
82
85
 
83
86
  # failures are left in the DynamoDB table with the failed boolean set to true
84
87
 
85
- BossQueue does not at present have a daemon component such as Sidekiq or Resque.
88
+ BossQueue does not at present have a daemon component like Sidekiq and Resque do.
86
89
 
87
90
 
88
91
  Future Work
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.4
1
+ 0.3.0
data/boss_queue.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "boss_queue"
8
- s.version = "0.2.4"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Daniel Nelson"]
12
- s.date = "2013-09-27"
12
+ s.date = "2013-10-03"
13
13
  s.description = "A fault tolerant job queue built around Amazon SQS & DynamoDB"
14
14
  s.email = "daniel@populr.me"
15
15
  s.extra_rdoc_files = [
@@ -5,6 +5,7 @@ class BossQueue
5
5
 
6
6
  def initialize(options={})
7
7
  @failure_action = options[:failure_action] if options[:failure_action]
8
+ @failure_callback = options[:failure_callback] if options[:failure_callback]
8
9
  @queue_postfix = options[:queue] ? '_' + options[:queue] : ''
9
10
  end
10
11
 
@@ -16,6 +17,10 @@ class BossQueue
16
17
  @failure_action ||= 'retry'
17
18
  end
18
19
 
20
+ def failure_callback
21
+ @failure_callback
22
+ end
23
+
19
24
  def table_name
20
25
  "#{BossQueue.queue_prefix}boss_queue_jobs"
21
26
  end
@@ -57,33 +62,32 @@ class BossQueue
57
62
  job_dequeued
58
63
  end
59
64
 
60
- def enqueue(class_or_instance, method_name, *args)
61
- job = create_job(class_or_instance, method_name, *args)
65
+ def enqueue(class_or_instance, callback_method, *args)
66
+ job = create_job(class_or_instance, callback_method, *args)
62
67
  job.enqueue
63
68
  end
64
69
 
65
- def enqueue_with_delay(delay, class_or_instance, method_name, *args)
66
- job = create_job(class_or_instance, method_name, *args)
70
+ def enqueue_with_delay(delay, class_or_instance, callback_method, *args)
71
+ job = create_job(class_or_instance, callback_method, *args)
67
72
  job.enqueue_with_delay(delay)
68
73
  end
69
74
 
70
- def create_job(class_or_instance, method_name, *args) # :nodoc:
75
+ def create_job(class_or_instance, callback_method, *args) # :nodoc:
71
76
  job = BossQueue::Job.shard(table_name).new
72
77
  if class_or_instance.is_a?(Class)
73
78
  class_name = class_or_instance.to_s
74
79
  instance_id = nil
75
- job.kind = "#{class_name}@#{method_name}"
76
80
  else
77
81
  class_name = class_or_instance.class.to_s
78
82
  instance_id = class_or_instance.id
79
- job.kind = "#{class_name}##{method_name}"
80
83
  end
81
84
  job.queue_name = queue_name
82
85
  job.failure_action = failure_action
86
+ job.failure_callback = failure_callback.to_s if failure_action == 'callback' && failure_callback
83
87
  job.model_class_name = class_name
84
88
  job.model_id = instance_id unless instance_id.nil?
85
- job.job_method = method_name.to_s
86
- job.job_arguments = JSON.generate(args)
89
+ job.callback = callback_method.to_s
90
+ job.args = JSON.generate(args)
87
91
  job.save!
88
92
  job
89
93
  end
@@ -5,16 +5,16 @@ class BossQueue
5
5
  class Job < AWS::Record::HashModel
6
6
  attr_accessor :queue_name
7
7
 
8
- string_attr :kind # an index based model_class_name, job_method
9
8
  boolean_attr :failed
10
9
 
11
10
  string_attr :model_class_name
12
11
  string_attr :model_id
13
- string_attr :job_method
14
- string_attr :job_arguments
12
+ string_attr :callback
13
+ string_attr :args
15
14
 
16
15
  integer_attr :failed_attempts
17
16
  string_attr :failure_action
17
+ string_attr :failure_callback
18
18
  string_attr :exception_name
19
19
  string_attr :exception_message
20
20
  string_attr :stacktrace
@@ -59,14 +59,7 @@ class BossQueue
59
59
 
60
60
  def work
61
61
  begin
62
- klass = constantize(model_class_name)
63
- if model_id
64
- target = klass.find(model_id)
65
- else
66
- target = klass
67
- end
68
- args = JSON.parse(job_arguments)
69
- target.send(job_method, *args)
62
+ target.send(callback, *arguments)
70
63
  destroy
71
64
  rescue StandardError => err
72
65
  fail(err)
@@ -82,11 +75,17 @@ class BossQueue
82
75
 
83
76
  if failure_action == 'retry' && retry_delay
84
77
  enqueue_with_delay(retry_delay)
78
+ self.save!
79
+
80
+ elsif failure_action == 'callback' &&
81
+ failure_callback &&
82
+ target.send(failure_callback, *arguments)
83
+ destroy
84
+
85
85
  else
86
86
  self.failed = true
87
+ self.save!
87
88
  end
88
-
89
- self.save!
90
89
  end
91
90
 
92
91
  def retry_delay
@@ -100,6 +99,19 @@ class BossQueue
100
99
 
101
100
  private
102
101
 
102
+ def arguments
103
+ JSON.parse(args)
104
+ end
105
+
106
+ def target
107
+ klass = constantize(model_class_name)
108
+ if model_id
109
+ klass.find(model_id)
110
+ else
111
+ klass
112
+ end
113
+ end
114
+
103
115
  def sqs_queue
104
116
  @sqs_queue ||= AWS::SQS.new.queues[AWS::SQS.new.queues.url_for(queue_name)]
105
117
  end
@@ -4,15 +4,16 @@ describe "BossQueue module" do
4
4
  before(:each) do
5
5
  BossQueue.environment = 'test'
6
6
 
7
- BossQueue::Job.any_instance.stub(:kind=)
8
7
  BossQueue::Job.any_instance.stub(:queue_name=)
9
8
  BossQueue::Job.any_instance.stub(:failure_action=)
9
+ BossQueue::Job.any_instance.stub(:failure_callback=)
10
10
  BossQueue::Job.any_instance.stub(:model_class_name=)
11
11
  BossQueue::Job.any_instance.stub(:model_id=)
12
- BossQueue::Job.any_instance.stub(:job_method=)
13
- BossQueue::Job.any_instance.stub(:job_arguments=)
12
+ BossQueue::Job.any_instance.stub(:callback=)
13
+ BossQueue::Job.any_instance.stub(:args=)
14
14
  BossQueue::Job.any_instance.stub(:save!)
15
15
  BossQueue::Job.any_instance.stub(:enqueue)
16
+ BossQueue::Job.any_instance.stub(:enqueue_with_delay)
16
17
  end
17
18
 
18
19
  it "should respond to environment" do
@@ -146,13 +147,12 @@ describe "BossQueue module" do
146
147
  context "when a class" do
147
148
  it "should initialize a new BossQueue::Job object, save and call enqueue on it" do
148
149
  queue = BossQueue.new
149
- BossQueue::Job.any_instance.should_receive(:kind=).with('TestClass@test_class_method')
150
150
  BossQueue::Job.any_instance.should_receive(:queue_name=).with('test_boss_queue')
151
151
  BossQueue::Job.any_instance.should_receive(:failure_action=).with('retry')
152
152
  BossQueue::Job.any_instance.should_receive(:model_class_name=).with('TestClass')
153
153
  BossQueue::Job.any_instance.should_not_receive(:model_id=)
154
- BossQueue::Job.any_instance.should_receive(:job_method=).with('test_class_method')
155
- BossQueue::Job.any_instance.should_receive(:job_arguments=).with(@argument_json)
154
+ BossQueue::Job.any_instance.should_receive(:callback=).with('test_class_method')
155
+ BossQueue::Job.any_instance.should_receive(:args=).with(@argument_json)
156
156
  BossQueue::Job.any_instance.should_receive(:save!)
157
157
  BossQueue::Job.any_instance.should_receive(:enqueue)
158
158
  queue.enqueue(TestClass, :test_class_method, 'a', 'b', { 'c' => 2, 'd' => 1 })
@@ -162,19 +162,26 @@ describe "BossQueue module" do
162
162
  context "when a class instance" do
163
163
  it "should initialize a new BossQueue::Job object, save and call enqueue on it" do
164
164
  queue = BossQueue.new
165
- BossQueue::Job.any_instance.should_receive(:kind=).with('TestClass#test_instance_method')
166
165
  BossQueue::Job.any_instance.should_receive(:queue_name=).with('test_boss_queue')
167
166
  BossQueue::Job.any_instance.should_receive(:failure_action=).with('retry')
168
167
  BossQueue::Job.any_instance.should_receive(:model_class_name=).with('TestClass')
169
168
  BossQueue::Job.any_instance.should_receive(:model_id=).with('xyz')
170
- BossQueue::Job.any_instance.should_receive(:job_method=).with('test_instance_method')
171
- BossQueue::Job.any_instance.should_receive(:job_arguments=).with(@argument_json)
169
+ BossQueue::Job.any_instance.should_receive(:callback=).with('test_instance_method')
170
+ BossQueue::Job.any_instance.should_receive(:args=).with(@argument_json)
172
171
  BossQueue::Job.any_instance.should_receive(:save!)
173
172
  BossQueue::Job.any_instance.should_receive(:enqueue)
174
173
  queue.enqueue(TestClass.new, :test_instance_method, 'a', 'b', { 'c' => 2, 'd' => 1 })
175
174
  end
176
175
  end
177
176
 
177
+ context "when failure_action is 'callback'" do
178
+ it "should set the job failure_callback to the failure_callback option" do
179
+ queue = BossQueue.new(:failure_action => 'callback', :failure_callback => :call_if_failed)
180
+ BossQueue::Job.any_instance.should_receive(:failure_callback=).with('call_if_failed')
181
+ queue.enqueue(TestClass, :test_class_method, 'a', 'b', { 'c' => 2, 'd' => 1 })
182
+ end
183
+ end
184
+
178
185
  end
179
186
 
180
187
  describe "#enqueue_with_delay" do
@@ -186,13 +193,12 @@ describe "BossQueue module" do
186
193
  context "when a class" do
187
194
  it "should initialize a new BossQueue::Job object, save and call enqueue on it" do
188
195
  queue = BossQueue.new
189
- BossQueue::Job.any_instance.should_receive(:kind=).with('TestClass@test_class_method')
190
196
  BossQueue::Job.any_instance.should_receive(:queue_name=).with('test_boss_queue')
191
197
  BossQueue::Job.any_instance.should_receive(:failure_action=).with('retry')
192
198
  BossQueue::Job.any_instance.should_receive(:model_class_name=).with('TestClass')
193
199
  BossQueue::Job.any_instance.should_not_receive(:model_id=)
194
- BossQueue::Job.any_instance.should_receive(:job_method=).with('test_class_method')
195
- BossQueue::Job.any_instance.should_receive(:job_arguments=).with(@argument_json)
200
+ BossQueue::Job.any_instance.should_receive(:callback=).with('test_class_method')
201
+ BossQueue::Job.any_instance.should_receive(:args=).with(@argument_json)
196
202
  BossQueue::Job.any_instance.should_receive(:save!)
197
203
  BossQueue::Job.any_instance.should_receive(:enqueue_with_delay).with(60)
198
204
  queue.enqueue_with_delay(60, TestClass, :test_class_method, 'a', 'b', { 'c' => 2, 'd' => 1 })
@@ -202,19 +208,26 @@ describe "BossQueue module" do
202
208
  context "when a class instance" do
203
209
  it "should initialize a new BossQueue::Job object, save and call enqueue on it" do
204
210
  queue = BossQueue.new
205
- BossQueue::Job.any_instance.should_receive(:kind=).with('TestClass#test_instance_method')
206
211
  BossQueue::Job.any_instance.should_receive(:queue_name=).with('test_boss_queue')
207
212
  BossQueue::Job.any_instance.should_receive(:failure_action=).with('retry')
208
213
  BossQueue::Job.any_instance.should_receive(:model_class_name=).with('TestClass')
209
214
  BossQueue::Job.any_instance.should_receive(:model_id=).with('xyz')
210
- BossQueue::Job.any_instance.should_receive(:job_method=).with('test_instance_method')
211
- BossQueue::Job.any_instance.should_receive(:job_arguments=).with(@argument_json)
215
+ BossQueue::Job.any_instance.should_receive(:callback=).with('test_instance_method')
216
+ BossQueue::Job.any_instance.should_receive(:args=).with(@argument_json)
212
217
  BossQueue::Job.any_instance.should_receive(:save!)
213
218
  BossQueue::Job.any_instance.should_receive(:enqueue_with_delay).with(60)
214
219
  queue.enqueue_with_delay(60, TestClass.new, :test_instance_method, 'a', 'b', { 'c' => 2, 'd' => 1 })
215
220
  end
216
221
  end
217
222
 
223
+ context "when failure_action is 'callback'" do
224
+ it "should set the job failure_callback to the failure_callback option" do
225
+ queue = BossQueue.new(:failure_action => 'callback', :failure_callback => :call_if_failed)
226
+ BossQueue::Job.any_instance.should_receive(:failure_callback=).with('call_if_failed')
227
+ queue.enqueue_with_delay(60, TestClass, :test_class_method, 'a', 'b', { 'c' => 2, 'd' => 1 })
228
+ end
229
+ end
230
+
218
231
  end
219
232
 
220
233
  describe "#work" do
data/spec/job_spec.rb CHANGED
@@ -96,21 +96,21 @@ describe "BossQueue::Job" do
96
96
  end
97
97
 
98
98
 
99
- it "should respond to job_method" do
100
- BossQueue::Job.new.should respond_to(:job_method)
99
+ it "should respond to callback" do
100
+ BossQueue::Job.new.should respond_to(:callback)
101
101
  end
102
102
 
103
- it "should respond to job_method=" do
104
- BossQueue::Job.new.should respond_to(:job_method=)
103
+ it "should respond to callback=" do
104
+ BossQueue::Job.new.should respond_to(:callback=)
105
105
  end
106
106
 
107
107
 
108
- it "should respond to job_arguments" do
109
- BossQueue::Job.new.should respond_to(:job_arguments)
108
+ it "should respond to args" do
109
+ BossQueue::Job.new.should respond_to(:args)
110
110
  end
111
111
 
112
- it "should respond to job_arguments=" do
113
- BossQueue::Job.new.should respond_to(:job_arguments=)
112
+ it "should respond to args=" do
113
+ BossQueue::Job.new.should respond_to(:args=)
114
114
  end
115
115
 
116
116
 
@@ -120,10 +120,10 @@ describe "BossQueue::Job" do
120
120
  @job.stub(:destroy)
121
121
  @job.model_class_name = 'TestClass'
122
122
  @job.model_id = 'xyz'
123
- @job.job_method = 'test_instance_method'
123
+ @job.callback = 'test_instance_method'
124
124
  @arguments = ['a', 'b', { 'c' => 2, 'd' => 1 }]
125
125
  @argument_json = JSON.generate(@arguments)
126
- @job.job_arguments = @argument_json
126
+ @job.args = @argument_json
127
127
  @instance_to_work_on = double('instance_to_work_on')
128
128
  @instance_to_work_on.stub(:test_instance_method)
129
129
  TestClass.stub(:find).and_return(@instance_to_work_on)
@@ -147,10 +147,10 @@ describe "BossQueue::Job" do
147
147
  @job = BossQueue::Job.new
148
148
  @job.stub(:destroy)
149
149
  @job.model_class_name = 'TestClass'
150
- @job.job_method = 'test_class_method'
150
+ @job.callback = 'test_class_method'
151
151
  @arguments = ['a', 'b', { 'c' => 2, 'd' => 1 }]
152
152
  @argument_json = JSON.generate(@arguments)
153
- @job.job_arguments = @argument_json
153
+ @job.args = @argument_json
154
154
  end
155
155
 
156
156
  it "should pass the job arguments to the job method on the class" do
@@ -316,6 +316,47 @@ describe "BossQueue::Job" do
316
316
  @job.fail(@err)
317
317
  end
318
318
 
319
+ context "when failure_action is 'callback'" do
320
+ before(:each) do
321
+ @job.failure_action = 'callback'
322
+ @job.failure_callback = 'failure'
323
+
324
+ @job.stub(:destroy)
325
+ @job.model_class_name = 'TestClass'
326
+ @job.model_id = 'xyz'
327
+ @job.callback = 'test_instance_method'
328
+ @arguments = ['a', 'b', { 'c' => 2, 'd' => 1 }]
329
+ @argument_json = JSON.generate(@arguments)
330
+ @job.args = @argument_json
331
+ @instance_to_work_on = double('instance_to_work_on')
332
+ @instance_to_work_on.stub(:test_instance_method)
333
+ @instance_to_work_on.stub(:failure).and_return(false)
334
+ TestClass.stub(:find).and_return(@instance_to_work_on)
335
+ end
336
+
337
+ it "should call the failure_callback method with the arguments" do
338
+ @instance_to_work_on.should_receive(:failure).with('a', 'b', { 'c' => 2, 'd' => 1 })
339
+ @job.fail(@err)
340
+ end
341
+
342
+ context "when the failure_callback method returns truthy" do
343
+ it "should delete the job" do
344
+ @job.should_receive(:destroy)
345
+ @instance_to_work_on.stub(:failure).and_return(true)
346
+ @job.fail(@err)
347
+ end
348
+ end
349
+
350
+ context "when the failure_callback method returns falsey" do
351
+ it "should mark the job as failed" do
352
+ @job.should_not_receive(:destroy)
353
+ @instance_to_work_on.stub(:failure).and_return(false)
354
+ @job.fail(@err)
355
+ @job.failed.should be_true
356
+ end
357
+ end
358
+ end
359
+
319
360
  context "when failure_action is 'retry'" do
320
361
  before(:each) do
321
362
  @job.failure_action = 'retry'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boss_queue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-09-27 00:00:00.000000000 Z
12
+ date: 2013-10-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk
@@ -160,7 +160,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
160
160
  version: '0'
161
161
  segments:
162
162
  - 0
163
- hash: 2592417147040776871
163
+ hash: 2478012361978662154
164
164
  required_rubygems_version: !ruby/object:Gem::Requirement
165
165
  none: false
166
166
  requirements: