boss_queue 0.1.10 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -19,21 +19,25 @@ boss_queue uses an Amazon SQS queue and a Amazon DynamoDB table for each environ
19
19
  AWS.config(:access_key_id => <access_key_id>,
20
20
  :secret_access_key => <secret_access_key>)
21
21
 
22
+ queue = BossQueue.new # default queue name
23
+ # or
24
+ queue = BossQueue.new(:queue => 'emails') # customized queue name (so you can have separate job queues)
25
+
22
26
  BossQueue.environment = 'development'
23
- BossQueue.create_table
24
- BossQueue.create_queue
27
+ queue.create_table
28
+ queue.create_queue
25
29
 
26
30
  BossQueue.environment = 'staging'
27
- BossQueue.create_table
28
- BossQueue.create_queue
31
+ queue.create_table
32
+ queue.create_queue
29
33
 
30
34
  # BossQueue.create_table(read_capacity, write_capacity)
31
35
  # One read capacity unit = two eventually consistent reads per second, for items up 4 KB in size.
32
36
  # One write capacity unit = one write per second, for items up to 1 KB in size.
33
37
 
34
38
  BossQueue.environment = 'production'
35
- BossQueue.create_table(50, 10)
36
- BossQueue.create_queue
39
+ queue.create_table(50, 10)
40
+ queue.create_queue
37
41
 
38
42
 
39
43
  Alternatively, in each of the respective environments, do:
@@ -55,18 +59,19 @@ Usage
55
59
  =====
56
60
 
57
61
  myobject = MyClass.new
58
- BossQueue.failure_action = 'none' # default is 'retry' which retries up to four times
62
+ # default failure action is 'retry' which retries up to four times
63
+ queue = BossQueue.new(:failure_action => 'none', :queue => 'emails')
59
64
 
60
65
  # can enqueue instance methods (assumes that objects have an id and a #find(id) method)
61
- BossQueue.enqueue(myobject, :method_to_execute, arg1, arg2)
66
+ queue.enqueue(myobject, :method_to_execute, arg1, arg2)
62
67
  # enqueue with a delay of up to 900 seconds (15 minutes)
63
- BossQueue.enqueue_with_delay(60, myobject, :method_to_execute, arg1, arg2, arg3)
68
+ queue.enqueue_with_delay(60, myobject, :method_to_execute, arg1, arg2, arg3)
64
69
 
65
70
  # can enqueue class methods
66
- BossQueue.enqueue(MyClass, :method_to_execute)
67
- BossQueue.enqueue_with_delay(60, MyClass, :method_to_execute, arg1, arg2)
71
+ queue.enqueue(MyClass, :method_to_execute)
72
+ queue.enqueue_with_delay(60, MyClass, :method_to_execute, arg1, arg2)
68
73
 
69
- BossQueue.work
74
+ queue.work
70
75
 
71
76
  # failures are left in the DynamoDB table with the failed boolean set to true
72
77
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.10
1
+ 0.2.0
data/boss_queue.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "boss_queue"
8
- s.version = "0.1.10"
8
+ s.version = "0.2.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"]
@@ -1,50 +1,48 @@
1
1
  require 'json'
2
2
 
3
3
  class BossQueue
4
- @@environment
4
+ @@environment = nil
5
5
 
6
- def self.environment=(env)
7
- @@environment = env
6
+ def initialize(options={})
7
+ @failure_action = options[:failure_action] if options[:failure_action]
8
+ @queue_postfix = options[:queue] ? '_' + options[:queue] : ''
8
9
  end
9
10
 
10
- @@failure_action
11
-
12
- def self.failure_action
13
- @@failure_action ||= 'retry'
11
+ def self.environment=(env)
12
+ @@environment = env
14
13
  end
15
14
 
16
- def self.failure_action=(env)
17
- @@failure_action = env
15
+ def failure_action
16
+ @failure_action ||= 'retry'
18
17
  end
19
18
 
20
-
21
- def self.table_name
22
- "#{self.queue_prefix}boss_queue_jobs"
19
+ def table_name
20
+ "#{BossQueue.queue_prefix}boss_queue_jobs"
23
21
  end
24
22
 
25
- def self.queue_name
26
- "#{self.queue_prefix}boss_queue"
23
+ def queue_name
24
+ "#{BossQueue.queue_prefix}boss_queue#{@queue_postfix}"
27
25
  end
28
26
 
29
27
 
30
- def self.create_table(read_capacity=1, write_capacity=1, options={})
28
+ def create_table(read_capacity=1, write_capacity=1, options={})
31
29
  create_opts = {}
32
30
  create_opts[:hash_key] = { :id => :string }
33
31
 
34
- AWS::DynamoDB.new.tables.create(self.table_name, read_capacity, write_capacity, create_opts)
32
+ AWS::DynamoDB.new.tables.create(table_name, read_capacity, write_capacity, create_opts)
35
33
  end
36
34
 
37
- def self.create_queue
38
- AWS::SQS::QueueCollection.new.create(self.queue_name, :default_visibility_timeout => 5 * 60)
35
+ def create_queue
36
+ AWS::SQS::QueueCollection.new.create(queue_name, :default_visibility_timeout => 5 * 60)
39
37
  end
40
38
 
41
- def self.work
39
+ def work
42
40
  work_done = false
43
- self.sqs_queue.receive_message do |job_id|
41
+ sqs_queue.receive_message do |job_id|
44
42
  # When a block is given, each message is yielded to the block and then deleted as long as the block exits normally - http://docs.aws.amazon.com/AWSRubySDK/latest/frames.html
45
43
  begin
46
44
  job = BossQueue::Job.shard(table_name).find(job_id.body)
47
- job.queue_name = self.queue_name
45
+ job.sqs_queue = sqs_queue
48
46
  job.work
49
47
  work_done = true
50
48
  rescue AWS::Record::RecordNotFound
@@ -53,17 +51,17 @@ class BossQueue
53
51
  work_done
54
52
  end
55
53
 
56
- def self.enqueue(class_or_instance, method_name, *args)
57
- job = self.create_job(class_or_instance, method_name, *args)
54
+ def enqueue(class_or_instance, method_name, *args)
55
+ job = create_job(class_or_instance, method_name, *args)
58
56
  job.enqueue
59
57
  end
60
58
 
61
- def self.enqueue_with_delay(delay, class_or_instance, method_name, *args)
62
- job = self.create_job(class_or_instance, method_name, *args)
59
+ def enqueue_with_delay(delay, class_or_instance, method_name, *args)
60
+ job = create_job(class_or_instance, method_name, *args)
63
61
  job.enqueue_with_delay(delay)
64
62
  end
65
63
 
66
- def self.create_job(class_or_instance, method_name, *args) # :nodoc:
64
+ def create_job(class_or_instance, method_name, *args) # :nodoc:
67
65
  job = BossQueue::Job.shard(table_name).new
68
66
  if class_or_instance.is_a?(Class)
69
67
  class_name = class_or_instance.to_s
@@ -74,8 +72,8 @@ class BossQueue
74
72
  instance_id = class_or_instance.id
75
73
  job.kind = "#{class_name}##{method_name}"
76
74
  end
77
- job.queue_name = self.queue_name
78
- job.failure_action = self.failure_action
75
+ job.queue_name = queue_name
76
+ job.failure_action = failure_action
79
77
  job.model_class_name = class_name
80
78
  job.model_id = instance_id unless instance_id.nil?
81
79
  job.job_method = method_name.to_s
@@ -84,8 +82,8 @@ class BossQueue
84
82
  job
85
83
  end
86
84
 
87
- def self.sqs_queue # :nodoc:
88
- AWS::SQS.new.queues[AWS::SQS.new.queues.url_for(self.queue_name)]
85
+ def sqs_queue # :nodoc:
86
+ @sqs_queue ||= AWS::SQS.new.queues[AWS::SQS.new.queues.url_for(queue_name)]
89
87
  end
90
88
 
91
89
  def self.environment # :nodoc:
@@ -66,10 +66,14 @@ class BossQueue
66
66
  60 * 2**(failed_attempts - 1)
67
67
  end
68
68
 
69
+ def sqs_queue=(queue_obj) # :nodoc:
70
+ @sqs_queue = queue_obj
71
+ end
72
+
69
73
  private
70
74
 
71
75
  def sqs_queue
72
- AWS::SQS.new.queues[AWS::SQS.new.queues.url_for(queue_name)]
76
+ @sqs_queue ||= AWS::SQS.new.queues[AWS::SQS.new.queues.url_for(queue_name)]
73
77
  end
74
78
 
75
79
  # from ActiveSupport source: http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-constantize
@@ -1,6 +1,19 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe "BossQueue module" do
4
+ before(:each) do
5
+ BossQueue.environment = 'test'
6
+
7
+ BossQueue::Job.any_instance.stub(:kind=)
8
+ BossQueue::Job.any_instance.stub(:queue_name=)
9
+ BossQueue::Job.any_instance.stub(:failure_action=)
10
+ BossQueue::Job.any_instance.stub(:model_class_name=)
11
+ BossQueue::Job.any_instance.stub(:model_id=)
12
+ BossQueue::Job.any_instance.stub(:job_method=)
13
+ BossQueue::Job.any_instance.stub(:job_arguments=)
14
+ BossQueue::Job.any_instance.stub(:save!)
15
+ BossQueue::Job.any_instance.stub(:enqueue)
16
+ end
4
17
 
5
18
  it "should respond to environment" do
6
19
  BossQueue.should respond_to(:environment)
@@ -10,17 +23,13 @@ describe "BossQueue module" do
10
23
  BossQueue.should respond_to(:environment=)
11
24
  end
12
25
 
13
- it "should respond to failure_action" do
14
- BossQueue.should respond_to(:failure_action)
15
- end
16
-
17
- it "should respond to failure_action=" do
18
- BossQueue.should respond_to(:failure_action=)
19
- end
20
-
21
26
  describe "#failure_action" do
22
27
  it "should default to 'retry'" do
23
- BossQueue.failure_action.should == 'retry'
28
+ BossQueue.new.failure_action.should == 'retry'
29
+ end
30
+
31
+ it "should be overridable by a :failure_action option on initialize" do
32
+ BossQueue.new(:failure_action => 'none').failure_action.should == 'none'
24
33
  end
25
34
  end
26
35
 
@@ -32,35 +41,35 @@ describe "BossQueue module" do
32
41
  context "when @@environment is 'development'" do
33
42
  it "should be 'dev_boss_queue_jobs'" do
34
43
  BossQueue.environment = 'development'
35
- BossQueue.table_name.should == 'dev_boss_queue_jobs'
44
+ BossQueue.new.table_name.should == 'dev_boss_queue_jobs'
36
45
  end
37
46
  end
38
47
 
39
48
  context "when @@environment is 'production'" do
40
49
  it "should be 'boss_queue_jobs'" do
41
50
  BossQueue.environment = 'production'
42
- BossQueue.table_name.should == 'boss_queue_jobs'
51
+ BossQueue.new.table_name.should == 'boss_queue_jobs'
43
52
  end
44
53
  end
45
54
 
46
55
  context "when @@environment is 'staging'" do
47
56
  it "should be 'staging_boss_queue_jobs'" do
48
57
  BossQueue.environment = 'staging'
49
- BossQueue.table_name.should == 'staging_boss_queue_jobs'
58
+ BossQueue.new.table_name.should == 'staging_boss_queue_jobs'
50
59
  end
51
60
  end
52
61
 
53
62
  context "when @@environment is 'staging'" do
54
63
  it "should be 'staging_boss_queue_jobs'" do
55
64
  BossQueue.environment = 'staging'
56
- BossQueue.table_name.should == 'staging_boss_queue_jobs'
65
+ BossQueue.new.table_name.should == 'staging_boss_queue_jobs'
57
66
  end
58
67
  end
59
68
 
60
69
  context "when @@environment is nil" do
61
70
  it "should raise an exception" do
62
71
  lambda {
63
- BossQueue.table_name
72
+ BossQueue.new.table_name
64
73
  }.should raise_error
65
74
  end
66
75
  end
@@ -75,38 +84,45 @@ describe "BossQueue module" do
75
84
  context "when @@environment is 'development'" do
76
85
  it "should be 'dev_boss_queue'" do
77
86
  BossQueue.environment = 'development'
78
- BossQueue.queue_name.should == 'dev_boss_queue'
87
+ BossQueue.new.queue_name.should == 'dev_boss_queue'
79
88
  end
80
89
  end
81
90
 
82
91
  context "when @@environment is 'production'" do
83
92
  it "should be 'boss_queue'" do
84
93
  BossQueue.environment = 'production'
85
- BossQueue.queue_name.should == 'boss_queue'
94
+ BossQueue.new.queue_name.should == 'boss_queue'
86
95
  end
87
96
  end
88
97
 
89
98
  context "when @@environment is 'staging'" do
90
99
  it "should be 'staging_boss_queue'" do
91
100
  BossQueue.environment = 'staging'
92
- BossQueue.queue_name.should == 'staging_boss_queue'
101
+ BossQueue.new.queue_name.should == 'staging_boss_queue'
93
102
  end
94
103
  end
95
104
 
96
105
  context "when @@environment is 'staging'" do
97
106
  it "should be 'staging_boss_queue'" do
98
107
  BossQueue.environment = 'staging'
99
- BossQueue.queue_name.should == 'staging_boss_queue'
108
+ BossQueue.new.queue_name.should == 'staging_boss_queue'
100
109
  end
101
110
  end
102
111
 
103
112
  context "when @@environment is nil" do
104
113
  it "should raise an exception" do
105
114
  lambda {
106
- BossQueue.queue_name
115
+ BossQueue.new.queue_name
107
116
  }.should raise_error
108
117
  end
109
118
  end
119
+
120
+ context "when a queue option is included in the initializer" do
121
+ it "should append that to the queue name" do
122
+ BossQueue.environment = 'production'
123
+ BossQueue.new(:queue => 'emails').queue_name.should == 'boss_queue_emails'
124
+ end
125
+ end
110
126
  end
111
127
 
112
128
  describe "#enqueue" do
@@ -129,8 +145,7 @@ describe "BossQueue module" do
129
145
 
130
146
  context "when a class" do
131
147
  it "should initialize a new BossQueue::Job object, save and call enqueue on it" do
132
- BossQueue.environment = 'test'
133
- BossQueue.failure_action = 'retry'
148
+ queue = BossQueue.new
134
149
  BossQueue::Job.any_instance.should_receive(:kind=).with('TestClass@test_class_method')
135
150
  BossQueue::Job.any_instance.should_receive(:queue_name=).with('test_boss_queue')
136
151
  BossQueue::Job.any_instance.should_receive(:failure_action=).with('retry')
@@ -140,14 +155,13 @@ describe "BossQueue module" do
140
155
  BossQueue::Job.any_instance.should_receive(:job_arguments=).with(@argument_json)
141
156
  BossQueue::Job.any_instance.should_receive(:save!)
142
157
  BossQueue::Job.any_instance.should_receive(:enqueue)
143
- BossQueue.enqueue(TestClass, :test_class_method, 'a', 'b', { 'c' => 2, 'd' => 1 })
158
+ queue.enqueue(TestClass, :test_class_method, 'a', 'b', { 'c' => 2, 'd' => 1 })
144
159
  end
145
160
  end
146
161
 
147
162
  context "when a class instance" do
148
163
  it "should initialize a new BossQueue::Job object, save and call enqueue on it" do
149
- BossQueue.environment = 'test'
150
- BossQueue.failure_action = 'retry'
164
+ queue = BossQueue.new
151
165
  BossQueue::Job.any_instance.should_receive(:kind=).with('TestClass#test_instance_method')
152
166
  BossQueue::Job.any_instance.should_receive(:queue_name=).with('test_boss_queue')
153
167
  BossQueue::Job.any_instance.should_receive(:failure_action=).with('retry')
@@ -157,7 +171,7 @@ describe "BossQueue module" do
157
171
  BossQueue::Job.any_instance.should_receive(:job_arguments=).with(@argument_json)
158
172
  BossQueue::Job.any_instance.should_receive(:save!)
159
173
  BossQueue::Job.any_instance.should_receive(:enqueue)
160
- BossQueue.enqueue(TestClass.new, :test_instance_method, 'a', 'b', { 'c' => 2, 'd' => 1 })
174
+ queue.enqueue(TestClass.new, :test_instance_method, 'a', 'b', { 'c' => 2, 'd' => 1 })
161
175
  end
162
176
  end
163
177
 
@@ -171,8 +185,7 @@ describe "BossQueue module" do
171
185
 
172
186
  context "when a class" do
173
187
  it "should initialize a new BossQueue::Job object, save and call enqueue on it" do
174
- BossQueue.environment = 'test'
175
- BossQueue.failure_action = 'retry'
188
+ queue = BossQueue.new
176
189
  BossQueue::Job.any_instance.should_receive(:kind=).with('TestClass@test_class_method')
177
190
  BossQueue::Job.any_instance.should_receive(:queue_name=).with('test_boss_queue')
178
191
  BossQueue::Job.any_instance.should_receive(:failure_action=).with('retry')
@@ -182,14 +195,13 @@ describe "BossQueue module" do
182
195
  BossQueue::Job.any_instance.should_receive(:job_arguments=).with(@argument_json)
183
196
  BossQueue::Job.any_instance.should_receive(:save!)
184
197
  BossQueue::Job.any_instance.should_receive(:enqueue_with_delay).with(60)
185
- BossQueue.enqueue_with_delay(60, TestClass, :test_class_method, 'a', 'b', { 'c' => 2, 'd' => 1 })
198
+ queue.enqueue_with_delay(60, TestClass, :test_class_method, 'a', 'b', { 'c' => 2, 'd' => 1 })
186
199
  end
187
200
  end
188
201
 
189
202
  context "when a class instance" do
190
203
  it "should initialize a new BossQueue::Job object, save and call enqueue on it" do
191
- BossQueue.environment = 'test'
192
- BossQueue.failure_action = 'retry'
204
+ queue = BossQueue.new
193
205
  BossQueue::Job.any_instance.should_receive(:kind=).with('TestClass#test_instance_method')
194
206
  BossQueue::Job.any_instance.should_receive(:queue_name=).with('test_boss_queue')
195
207
  BossQueue::Job.any_instance.should_receive(:failure_action=).with('retry')
@@ -199,7 +211,7 @@ describe "BossQueue module" do
199
211
  BossQueue::Job.any_instance.should_receive(:job_arguments=).with(@argument_json)
200
212
  BossQueue::Job.any_instance.should_receive(:save!)
201
213
  BossQueue::Job.any_instance.should_receive(:enqueue_with_delay).with(60)
202
- BossQueue.enqueue_with_delay(60, TestClass.new, :test_instance_method, 'a', 'b', { 'c' => 2, 'd' => 1 })
214
+ queue.enqueue_with_delay(60, TestClass.new, :test_instance_method, 'a', 'b', { 'c' => 2, 'd' => 1 })
203
215
  end
204
216
  end
205
217
 
@@ -207,59 +219,64 @@ describe "BossQueue module" do
207
219
 
208
220
  describe "#work" do
209
221
  before(:each) do
210
- @queue = double('queue')
222
+ @sqs_queue = double('queue')
211
223
  AWS::SQS.stub_chain(:new, :queues, :url_for).and_return('queue_url')
212
- AWS::SQS.stub_chain(:new, :queues, :[]).and_return(@queue)
224
+ AWS::SQS.stub_chain(:new, :queues, :[]).and_return(@sqs_queue)
213
225
 
214
226
  @sqs_message = double('message')
215
227
  @sqs_message.stub(:body).and_return('ijk')
216
- @queue.stub(:receive_message)
228
+ @sqs_queue.stub(:receive_message)
217
229
 
218
230
  @job = double('job')
219
231
  @job.stub(:work)
220
- @job.stub(:queue_name=)
232
+ @job.stub(:sqs_queue=)
221
233
  BossQueue::Job.stub_chain(:shard, :find).and_return(@job)
222
234
  end
223
235
 
224
- it "should dequeue from SQS" do
225
- @queue.should_receive(:receive_message).and_yield(@sqs_message)
226
- BossQueue.work
236
+ it "should dequeue from SQS using the value of sqs_queue_url" do
237
+ @sqs_queue.should_receive(:receive_message).and_yield(@sqs_message)
238
+ BossQueue.new.work
227
239
  end
228
240
 
229
241
  context "when something is dequeued from SQS" do
230
242
  before(:each) do
231
- @queue.should_receive(:receive_message).and_yield(@sqs_message)
243
+ @sqs_queue.should_receive(:receive_message).and_yield(@sqs_message)
232
244
  end
233
245
 
234
246
  it "should use the dequeued id to retrieve a BossQueue::Job object" do
235
247
  shard = double('shard')
236
- BossQueue::Job.should_receive(:shard).with(BossQueue.table_name).and_return(shard)
248
+ BossQueue::Job.should_receive(:shard).with(BossQueue.new.table_name).and_return(shard)
237
249
  shard.should_receive(:find).with('ijk').and_return(@job)
238
- BossQueue.work
250
+ BossQueue.new.work
239
251
  end
240
252
 
241
253
  context "when the dequeued id does not match a BossQueue::Job object" do
242
254
  it "should not raise an exception" do
243
255
  BossQueue::Job.stub_chain(:shard, :find).and_raise(AWS::Record::RecordNotFound.new)
244
256
  lambda {
245
- BossQueue.work
257
+ BossQueue.new.work
246
258
  }.should_not raise_error
247
259
  end
248
260
  end
249
261
 
262
+ it "should set the sqs_queue of the job since we already did the work of retrieving it's url" do
263
+ @job.should_receive(:sqs_queue=).with(@sqs_queue)
264
+ BossQueue.new.work
265
+ end
266
+
250
267
  it "should call work on the BossQueue::Job object" do
251
268
  @job.should_receive(:work)
252
- BossQueue.work
269
+ BossQueue.new.work
253
270
  end
254
271
 
255
272
  it "should return true" do
256
- BossQueue.work.should be_true
273
+ BossQueue.new.work.should be_true
257
274
  end
258
275
  end
259
276
 
260
277
  context "when nothing is dequeued from SQS" do
261
278
  it "should return false" do
262
- BossQueue.work.should be_false
279
+ BossQueue.new.work.should be_false
263
280
  end
264
281
  end
265
282
  end
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.1.10
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -160,7 +160,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
160
160
  version: '0'
161
161
  segments:
162
162
  - 0
163
- hash: -2976249042738102520
163
+ hash: 539574413423499853
164
164
  required_rubygems_version: !ruby/object:Gem::Requirement
165
165
  none: false
166
166
  requirements: