micro_q 0.6.1

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.
Files changed (46) hide show
  1. data/.gitignore +13 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +16 -0
  4. data/Gemfile +2 -0
  5. data/LICENSE +22 -0
  6. data/README.md +27 -0
  7. data/Rakefile +6 -0
  8. data/lib/micro_q/config.rb +27 -0
  9. data/lib/micro_q/manager/default.rb +38 -0
  10. data/lib/micro_q/manager.rb +1 -0
  11. data/lib/micro_q/methods/active_record.rb +21 -0
  12. data/lib/micro_q/methods/class.rb +13 -0
  13. data/lib/micro_q/methods/instance.rb +13 -0
  14. data/lib/micro_q/methods.rb +25 -0
  15. data/lib/micro_q/middleware/chain.rb +102 -0
  16. data/lib/micro_q/middleware/server/retry.rb +32 -0
  17. data/lib/micro_q/middleware.rb +1 -0
  18. data/lib/micro_q/proxies/base.rb +49 -0
  19. data/lib/micro_q/proxies/class.rb +6 -0
  20. data/lib/micro_q/proxies/instance.rb +9 -0
  21. data/lib/micro_q/proxies.rb +3 -0
  22. data/lib/micro_q/queue/default.rb +90 -0
  23. data/lib/micro_q/queue.rb +1 -0
  24. data/lib/micro_q/util.rb +29 -0
  25. data/lib/micro_q/version.rb +7 -0
  26. data/lib/micro_q/worker/standard.rb +40 -0
  27. data/lib/micro_q/worker.rb +1 -0
  28. data/lib/micro_q.rb +46 -0
  29. data/micro_q.gemspec +27 -0
  30. data/spec/helpers/methods_examples.rb +45 -0
  31. data/spec/lib/config_spec.rb +47 -0
  32. data/spec/lib/manager/default_spec.rb +69 -0
  33. data/spec/lib/methods/active_record_spec.rb +67 -0
  34. data/spec/lib/methods/class_spec.rb +61 -0
  35. data/spec/lib/methods/instance_spec.rb +55 -0
  36. data/spec/lib/micro_q_spec.rb +80 -0
  37. data/spec/lib/middleware/chain_spec.rb +266 -0
  38. data/spec/lib/middleware/server/retry_spec.rb +87 -0
  39. data/spec/lib/proxies/base_spec.rb +184 -0
  40. data/spec/lib/proxies/class_spec.rb +15 -0
  41. data/spec/lib/proxies/instance_spec.rb +41 -0
  42. data/spec/lib/queue/default_spec.rb +158 -0
  43. data/spec/lib/util_spec.rb +51 -0
  44. data/spec/lib/worker/standard_spec.rb +88 -0
  45. data/spec/spec_helper.rb +59 -0
  46. metadata +219 -0
@@ -0,0 +1,266 @@
1
+ require 'spec_helper'
2
+
3
+ describe MicroQ::Middleware::Chain do
4
+ describe MicroQ::Middleware::Chain::Base do
5
+ subject { MicroQ::Middleware::Chain::Base.new }
6
+
7
+ class MyMiddleware; end
8
+ class OtherMiddleware; end
9
+
10
+ describe '#add' do
11
+ before do
12
+ subject.add MyMiddleware
13
+ end
14
+
15
+ it 'should add the item' do
16
+ subject.entries.should include(MyMiddleware)
17
+ end
18
+
19
+ it 'should be appended' do
20
+ subject.entries.last.should == MyMiddleware
21
+ end
22
+
23
+ it 'should only add unique items' do
24
+ subject.add MyMiddleware
25
+ subject.add MyMiddleware
26
+
27
+ subject.entries.uniq.should == subject.entries
28
+ end
29
+ end
30
+
31
+ describe '#remove' do
32
+ before do
33
+ subject.add MyMiddleware
34
+
35
+ subject.entries.should include(MyMiddleware)
36
+ subject.remove MyMiddleware
37
+ end
38
+
39
+ it 'should remove the item' do
40
+ subject.entries.should_not include(MyMiddleware)
41
+ end
42
+ end
43
+
44
+ describe '#clear' do
45
+ before do
46
+ subject.add MyMiddleware
47
+ end
48
+
49
+ it 'should remove all entries' do
50
+ subject.entries.should == [MyMiddleware]
51
+ subject.clear
52
+
53
+ subject.entries.should == []
54
+ end
55
+ end
56
+
57
+ describe '#add_before' do
58
+ class MiddleMiddleware; end
59
+
60
+ def add_before
61
+ subject.add_before(OtherMiddleware, MiddleMiddleware)
62
+ end
63
+
64
+ before do
65
+ subject.clear
66
+ subject.add MyMiddleware
67
+ subject.add OtherMiddleware
68
+ end
69
+
70
+ it 'should not add nils' do
71
+ subject.add_before MyMiddleware
72
+ subject.entries.should have(2).items
73
+ end
74
+
75
+ it 'should add the middleware in the specified place' do
76
+ add_before
77
+
78
+ subject.entries.should == [
79
+ MyMiddleware,
80
+ MiddleMiddleware,
81
+ OtherMiddleware
82
+ ]
83
+ end
84
+
85
+ describe 'when relocating middleware' do
86
+ before do
87
+ subject.add MiddleMiddleware
88
+ end
89
+
90
+ it 'should move the middleware to the specified place' do
91
+ subject.entries.should == [
92
+ MyMiddleware,
93
+ OtherMiddleware,
94
+ MiddleMiddleware
95
+ ]
96
+
97
+ add_before
98
+
99
+ subject.entries.should == [
100
+ MyMiddleware,
101
+ MiddleMiddleware,
102
+ OtherMiddleware
103
+ ]
104
+ end
105
+ end
106
+
107
+ describe 'when adding multiple' do
108
+ class SecondMiddleMiddleware; end
109
+
110
+ def add_before
111
+ subject.add_before(OtherMiddleware, MiddleMiddleware, SecondMiddleMiddleware)
112
+ end
113
+
114
+ it 'should add the middlewares in the specified place' do
115
+ add_before
116
+
117
+ subject.entries.should == [
118
+ MyMiddleware,
119
+ MiddleMiddleware,
120
+ SecondMiddleMiddleware,
121
+ OtherMiddleware
122
+ ]
123
+ end
124
+ end
125
+ end
126
+
127
+ describe '#add_after' do
128
+ class MiddleMiddleware; end
129
+
130
+ def add_after
131
+ subject.add_after(OtherMiddleware, MiddleMiddleware)
132
+ end
133
+
134
+ before do
135
+ subject.clear
136
+ subject.add MyMiddleware
137
+ subject.add OtherMiddleware
138
+ end
139
+
140
+ it 'should not add nils' do
141
+ subject.add_after MyMiddleware
142
+ subject.entries.should have(2).items
143
+ end
144
+
145
+ it 'should add the middleware in the specified place' do
146
+ add_after
147
+
148
+ subject.entries.should == [
149
+ MyMiddleware,
150
+ OtherMiddleware,
151
+ MiddleMiddleware
152
+ ]
153
+ end
154
+
155
+ describe 'when relocating middleware' do
156
+ before do
157
+ subject.add_before MyMiddleware, MiddleMiddleware
158
+ end
159
+
160
+ it 'should move the middleware to the specified place' do
161
+ subject.entries.should == [
162
+ MiddleMiddleware,
163
+ MyMiddleware,
164
+ OtherMiddleware
165
+ ]
166
+
167
+ add_after
168
+
169
+ subject.entries.should == [
170
+ MyMiddleware,
171
+ OtherMiddleware,
172
+ MiddleMiddleware
173
+ ]
174
+ end
175
+ end
176
+
177
+ describe 'when adding multiple' do
178
+ class SecondMiddleMiddleware; end
179
+
180
+ def add_after
181
+ subject.add_after(OtherMiddleware, MiddleMiddleware, SecondMiddleMiddleware)
182
+ end
183
+
184
+ it 'should add the middlewares in the specified place' do
185
+ add_after
186
+
187
+ subject.entries.should == [
188
+ MyMiddleware,
189
+ OtherMiddleware,
190
+ MiddleMiddleware,
191
+ SecondMiddleMiddleware
192
+ ]
193
+ end
194
+ end
195
+ end
196
+ end
197
+
198
+ describe '.server' do
199
+ it 'should expose the server middleware' do
200
+ subject.server.class.should == MicroQ::Middleware::Chain::Server
201
+ end
202
+
203
+ it 'should cache the object' do
204
+ subject.server.object_id.should == subject.server.object_id
205
+ end
206
+
207
+ describe 'defaults' do
208
+ [MicroQ::Middleware::Server::Retry].each do |klass|
209
+ it "should include #{klass}" do
210
+ subject.server.entries.should include(klass)
211
+ end
212
+ end
213
+
214
+ it 'should be 1 item long' do
215
+ subject.server.entries.should have(1).items
216
+ end
217
+ end
218
+ end
219
+
220
+ describe '.client' do
221
+ it 'should expose the server middleware' do
222
+ subject.client.class.should == MicroQ::Middleware::Chain::Client
223
+ end
224
+
225
+ it 'should cache the object' do
226
+ subject.client.object_id.should == subject.client.object_id
227
+ end
228
+
229
+ it "should be empty" do
230
+ subject.client.entries.should == []
231
+ end
232
+ end
233
+
234
+ describe '.call' do
235
+ let(:worker) { MyWorker.new }
236
+ let(:payload) { {'class' => 'MyWorker'} }
237
+
238
+ class MyWorker
239
+ end
240
+
241
+ before do
242
+ @retry = mock(MicroQ::Middleware::Server::Retry)
243
+ MicroQ::Middleware::Server::Retry.stub(:new).and_return(@retry)
244
+ end
245
+
246
+ describe 'server' do
247
+ def call
248
+ subject.server.call(worker, payload)
249
+ end
250
+
251
+ it 'should make a new middleware chain' do
252
+ subject.server.entries.each do |entry|
253
+ entry.should_receive(:new).and_return(mock('entry', :call => nil))
254
+ end
255
+
256
+ call
257
+ end
258
+
259
+ it 'should call each item' do
260
+ @retry.should_receive(:call).with(worker, payload)
261
+
262
+ call
263
+ end
264
+ end
265
+ end
266
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ describe MicroQ::Middleware::Server::Retry, :middleware => true do
4
+ describe '#call' do
5
+ let(:foo) { mock("Foo", :bar => nil) }
6
+ let(:block) { lambda { foo.bar } }
7
+
8
+ def call
9
+ subject.call @worker, @payload, &block
10
+ end
11
+
12
+ it 'should execute the block' do
13
+ foo.should_receive(:bar)
14
+
15
+ call
16
+ end
17
+
18
+ describe 'when the block raises an Exception' do
19
+ let(:exception) { Exception.new }
20
+ let(:block) { lambda { raise exception } }
21
+
22
+ describe 'when retry is disabled' do
23
+ before do
24
+ @payload['retry'] = false
25
+ end
26
+
27
+ it 'should re-raise the error' do
28
+ expect {
29
+ call
30
+ }.to raise_error(exception)
31
+ end
32
+ end
33
+
34
+ describe 'when retry is enabled' do
35
+ before do
36
+ @payload['retry'] = true
37
+ end
38
+
39
+ it 'should re-raise the error' do
40
+ expect {
41
+ call
42
+ }.to raise_error(exception)
43
+ end
44
+
45
+ it 'should increment the number of retries' do
46
+ @payload['retried'].should be_nil
47
+
48
+ safe(:call); @payload['retried']['count'].should == 1
49
+ safe(:call); @payload['retried']['count'].should == 2
50
+ end
51
+
52
+ it 'should update the retry when time' do
53
+ Timecop.freeze(DateTime.now) do
54
+ safe(:call); @payload['retried']['when'].to_i.should == (Time.now + 15).to_i
55
+
56
+ Timecop.travel(100)
57
+ safe(:call); @payload['retried']['when'].to_i.should == (Time.now + 15).to_i
58
+ end
59
+ end
60
+
61
+ it 'should set the last retry time' do
62
+ Timecop.freeze do
63
+ safe(:call)
64
+
65
+ @payload['retried']['at'].should == Time.now
66
+ end
67
+ end
68
+
69
+ it 'should push the message back onto the queue' do
70
+ MicroQ.should_receive(:push) do |payload, *|
71
+ payload.should == @payload
72
+ end
73
+
74
+ safe(:call)
75
+ end
76
+
77
+ it 'should enqueue for the next retry time' do
78
+ Timecop.freeze do
79
+ MicroQ.should_receive(:push).with(anything, hash_including('when' => (Time.now + 15).to_f))
80
+
81
+ safe(:call)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,184 @@
1
+ require 'spec_helper'
2
+
3
+ describe MicroQ::Proxy::Base do
4
+ class MyModel
5
+ def self.seed
6
+ end
7
+
8
+ def process
9
+ end
10
+ end
11
+
12
+ let(:options) { { :class => MyModel } }
13
+
14
+ subject { MicroQ::Proxy::Base.new(options) }
15
+
16
+ describe '.new' do
17
+ describe 'when given the :at key' do
18
+ before do
19
+ Timecop.freeze
20
+
21
+ options[:at] = (Time.now + 60)
22
+ end
23
+
24
+ it 'should store the value' do
25
+ subject.at.should == (Time.now + 60).to_i
26
+ end
27
+ end
28
+
29
+ describe 'when given the :after key' do
30
+ before do
31
+ Timecop.freeze
32
+
33
+ options[:after] = 60
34
+ end
35
+
36
+ it 'should store the at time' do
37
+ subject.at.should == (Time.now + 60).to_i
38
+ end
39
+ end
40
+ end
41
+
42
+ describe 'valid?' do
43
+ it { should be_valid }
44
+ it { subject.klass.should == MyModel }
45
+
46
+ describe 'class' do
47
+ it 'should require a class' do
48
+ options[:class] = nil
49
+
50
+ should_not be_valid
51
+ end
52
+
53
+ it 'should require a constant' do
54
+ options[:class] = 'InvalidClass'
55
+
56
+ should_not be_valid
57
+ end
58
+
59
+ describe '#errors' do
60
+ [nil, 'InvalidClass'].each do |type|
61
+ it "should have an error for #{type.inspect}" do
62
+ options[:class] = type
63
+
64
+ subject.errors.should include("Proxies require a valid class")
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ describe "#respond_to?" do
72
+ it 'should be false' do
73
+ subject.respond_to?(:not_a_method).should == false
74
+ end
75
+
76
+ describe 'for a method the class responds to' do
77
+ it 'should be true' do
78
+ subject.respond_to?(:seed).should == true
79
+ end
80
+ end
81
+
82
+ describe 'for a method the proxy responds to and the class doesn\'t' do
83
+ before do
84
+ @method = (subject.methods - options[:class].methods).first
85
+ end
86
+
87
+ it 'should be true' do
88
+ options[:class].respond_to?(@method).should == false
89
+ subject.klass.respond_to?(@method).should == false
90
+
91
+ subject.respond_to?(@method).should == true
92
+ end
93
+ end
94
+ end
95
+
96
+ describe '#method' do
97
+ it 'should store the method' do
98
+ subject.some_method
99
+
100
+ subject.method.should == 'some_method'
101
+ end
102
+ end
103
+
104
+ describe '#args' do
105
+ let(:args) { [1 ,2, 'value'] }
106
+
107
+ it 'should store given arguments' do
108
+ subject.some_method(*args)
109
+
110
+ subject.args.should == [1 ,2, 'value']
111
+ end
112
+ end
113
+
114
+ describe 'method invocations' do
115
+ let(:method) { -> { subject.some_method(1, 2) } }
116
+
117
+ it 'should push the message' do
118
+ MicroQ.should_receive(:push)
119
+
120
+ method.call
121
+ end
122
+
123
+ it 'should have the class' do
124
+ MicroQ.should_receive(:push).with(hash_including(:class => MyModel))
125
+
126
+ method.call
127
+ end
128
+
129
+ it 'should have the method' do
130
+ MicroQ.should_receive(:push).with(hash_including(:method => 'some_method'))
131
+
132
+ method.call
133
+ end
134
+
135
+ it 'should have the args' do
136
+ MicroQ.should_receive(:push).with(hash_including(:args => [1, 2]))
137
+
138
+ method.call
139
+ end
140
+
141
+ describe 'when given options' do
142
+ before do
143
+ options[:loader] = {:method => 'find', :args => [456]}
144
+ options[:foo] = 'bar'
145
+ end
146
+
147
+ it 'should have the random key' do
148
+ MicroQ.should_receive(:push).with(hash_including(:foo => 'bar'))
149
+
150
+ method.call
151
+ end
152
+
153
+ it 'should have the loader' do
154
+ MicroQ.should_receive(:push).with(hash_including(:loader => {:method => 'find', :args => [456]}))
155
+
156
+ method.call
157
+ end
158
+ end
159
+
160
+ describe 'when performing at a specific time' do
161
+ before do
162
+ options[:at] = Time.now + 60
163
+ end
164
+
165
+ it 'should push with the right \'when\' key' do
166
+ MicroQ.should_receive(:push).with(anything, :when => (Time.now + 60).to_i)
167
+
168
+ method.call
169
+ end
170
+ end
171
+
172
+ describe 'when performing after a specific time' do
173
+ before do
174
+ options[:after] = 120
175
+ end
176
+
177
+ it 'should push with the right \'when\' key' do
178
+ MicroQ.should_receive(:push).with(anything, :when => (Time.now + 120).to_i)
179
+
180
+ method.call
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe MicroQ::Proxy::Class do
4
+ class MyModel
5
+ def self.configure
6
+ end
7
+ end
8
+ let(:options) { { :class => MyModel } }
9
+
10
+ subject { MicroQ::Proxy::Class.new(options) }
11
+
12
+ it 'should be a base proxy' do
13
+ subject.class.ancestors.should include(MicroQ::Proxy::Base)
14
+ end
15
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe MicroQ::Proxy::Instance do
4
+ class MyModel
5
+ def process
6
+ end
7
+ end
8
+
9
+ let(:options) { { :class => MyModel } }
10
+
11
+ subject { MicroQ::Proxy::Instance.new(options) }
12
+
13
+ it 'should be a base proxy' do
14
+ subject.class.ancestors.should include(MicroQ::Proxy::Base)
15
+ end
16
+
17
+ describe "#respond_to?" do
18
+ it 'should be false' do
19
+ subject.respond_to?(:not_a_method).should == false
20
+ end
21
+
22
+ describe 'for a method the instance responds to' do
23
+ it 'should be true' do
24
+ subject.respond_to?(:process).should == true
25
+ end
26
+ end
27
+
28
+ describe 'for a method the proxy responds to and a class instance doesn\'t' do
29
+ before do
30
+ @method = (subject.methods - options[:class].new.methods).first
31
+ end
32
+
33
+ it 'should be true' do
34
+ options[:class].new.respond_to?(@method).should == false
35
+ subject.klass.new.respond_to?(@method).should == false
36
+
37
+ subject.respond_to?(@method).should == true
38
+ end
39
+ end
40
+ end
41
+ end