micro_q 0.6.1

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