micro_q 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +16 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +27 -0
- data/Rakefile +6 -0
- data/lib/micro_q/config.rb +27 -0
- data/lib/micro_q/manager/default.rb +38 -0
- data/lib/micro_q/manager.rb +1 -0
- data/lib/micro_q/methods/active_record.rb +21 -0
- data/lib/micro_q/methods/class.rb +13 -0
- data/lib/micro_q/methods/instance.rb +13 -0
- data/lib/micro_q/methods.rb +25 -0
- data/lib/micro_q/middleware/chain.rb +102 -0
- data/lib/micro_q/middleware/server/retry.rb +32 -0
- data/lib/micro_q/middleware.rb +1 -0
- data/lib/micro_q/proxies/base.rb +49 -0
- data/lib/micro_q/proxies/class.rb +6 -0
- data/lib/micro_q/proxies/instance.rb +9 -0
- data/lib/micro_q/proxies.rb +3 -0
- data/lib/micro_q/queue/default.rb +90 -0
- data/lib/micro_q/queue.rb +1 -0
- data/lib/micro_q/util.rb +29 -0
- data/lib/micro_q/version.rb +7 -0
- data/lib/micro_q/worker/standard.rb +40 -0
- data/lib/micro_q/worker.rb +1 -0
- data/lib/micro_q.rb +46 -0
- data/micro_q.gemspec +27 -0
- data/spec/helpers/methods_examples.rb +45 -0
- data/spec/lib/config_spec.rb +47 -0
- data/spec/lib/manager/default_spec.rb +69 -0
- data/spec/lib/methods/active_record_spec.rb +67 -0
- data/spec/lib/methods/class_spec.rb +61 -0
- data/spec/lib/methods/instance_spec.rb +55 -0
- data/spec/lib/micro_q_spec.rb +80 -0
- data/spec/lib/middleware/chain_spec.rb +266 -0
- data/spec/lib/middleware/server/retry_spec.rb +87 -0
- data/spec/lib/proxies/base_spec.rb +184 -0
- data/spec/lib/proxies/class_spec.rb +15 -0
- data/spec/lib/proxies/instance_spec.rb +41 -0
- data/spec/lib/queue/default_spec.rb +158 -0
- data/spec/lib/util_spec.rb +51 -0
- data/spec/lib/worker/standard_spec.rb +88 -0
- data/spec/spec_helper.rb +59 -0
- metadata +219 -0
data/micro_q.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'micro_q/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "micro_q"
|
8
|
+
gem.version = MicroQ::VERSION
|
9
|
+
gem.authors = ["Brian Norton"]
|
10
|
+
gem.email = ["brian.nort@gmail.com"]
|
11
|
+
gem.description = ""
|
12
|
+
gem.summary = ""
|
13
|
+
gem.homepage = "http://github.com/bnorton/micro-q"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^spec/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency "celluloid"
|
21
|
+
gem.add_development_dependency "rake"
|
22
|
+
gem.add_development_dependency "rspec"
|
23
|
+
gem.add_development_dependency "timecop"
|
24
|
+
gem.add_development_dependency "psych"
|
25
|
+
gem.add_development_dependency "activerecord", "> 3.2.0"
|
26
|
+
gem.add_development_dependency "sqlite3-ruby"
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
shared_examples_for "a_worker" do |method|
|
2
|
+
describe "additions (#{method})" do
|
3
|
+
it 'should add an async proxy' do
|
4
|
+
subject.respond_to?(:async).should == true
|
5
|
+
end
|
6
|
+
|
7
|
+
it 'should proxy to the object' do
|
8
|
+
subject.async.respond_to?(method).should == true
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'before first calling the _async method' do
|
12
|
+
it 'should not respond to the _async method' do
|
13
|
+
subject.respond_to?(method).should == true
|
14
|
+
subject.respond_to?("#{method}_async").should == false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'after first call' do
|
19
|
+
before do
|
20
|
+
subject.send("#{method}_async")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should add a _async method" do
|
24
|
+
subject.respond_to?(method).should == true
|
25
|
+
subject.respond_to?("#{method}_async").should == true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'errors' do
|
31
|
+
describe 'when calling a missing _async method' do
|
32
|
+
it 'should fail' do
|
33
|
+
expect {
|
34
|
+
subject.send("some_method_async")
|
35
|
+
}.to raise_error
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should raise an undefined method error (without the _async)' do
|
39
|
+
ex = (begin; subject.send("some_method_async"); rescue => e; e end)
|
40
|
+
|
41
|
+
ex.message.should match(/undefined method \`some_method_async\'/)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MicroQ::Config do
|
4
|
+
[[:a_key, 'abc123'], [:key, 'the-value']].each do |(method, value)|
|
5
|
+
describe "##{method}" do
|
6
|
+
before do
|
7
|
+
subject.send("#{method}=", value)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should access the given value (:sym)' do
|
11
|
+
subject[method].should == value
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should access the given value (str)' do
|
15
|
+
subject[method.to_s].should == value
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should have the value at ##{method}" do
|
19
|
+
subject.send(method).should == value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'defaults' do
|
25
|
+
subject { MicroQ.config }
|
26
|
+
|
27
|
+
it 'should have 3 workers' do
|
28
|
+
subject.workers.should == 3
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should have a 5 second interval' do
|
32
|
+
subject.interval.should == 5
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should have a 120 second timeout' do
|
36
|
+
subject.timeout.should == 120
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should have middleware chain' do
|
40
|
+
subject.middleware.class.should == MicroQ::Middleware::Chain
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should not have a logfile' do
|
44
|
+
subject.logfile.should == nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MicroQ::Manager::Default do
|
4
|
+
let(:create) { -> { subject } }
|
5
|
+
|
6
|
+
describe '.new' do
|
7
|
+
before do
|
8
|
+
MicroQ.config.workers = 4
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should create a default queue' do
|
12
|
+
MicroQ::Queue::Default.should_receive(:new)
|
13
|
+
|
14
|
+
create.call
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should create a worker pool' do
|
18
|
+
MicroQ::Worker::Standard.should_receive(:pool)
|
19
|
+
|
20
|
+
create.call
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should be a config.workers size pool' do
|
24
|
+
MicroQ::Worker::Standard.should_receive(:pool).with(hash_including(:size => 4))
|
25
|
+
|
26
|
+
create.call
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#queue' do
|
31
|
+
it 'should be the queue' do
|
32
|
+
subject.queue.wrapped_object.class.should == MicroQ::Queue::Default
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#workers' do
|
37
|
+
it 'should be the workers' do
|
38
|
+
subject.workers.class.should == Celluloid::PoolManager
|
39
|
+
subject.workers.inspect.should match(/MicroQ::Worker::Standard/)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#start' do
|
44
|
+
it 'should not be performing' do
|
45
|
+
subject.workers.should_not_receive(:perform!)
|
46
|
+
|
47
|
+
subject.start
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'when the queue has dequeue-able items' do
|
51
|
+
before do
|
52
|
+
@item, @other_item = mock(Hash), mock(Hash)
|
53
|
+
@queue = mock(MicroQ::Queue::Default, :dequeue => [@item, @other_item])
|
54
|
+
MicroQ::Queue::Default.stub(:new).and_return(@queue)
|
55
|
+
|
56
|
+
@pool = mock(Celluloid::PoolManager)
|
57
|
+
MicroQ::Worker::Standard.stub(:pool).and_return(@pool)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should perform the items' do
|
61
|
+
@pool.should_receive(:perform!).with(@item) do
|
62
|
+
@pool.should_receive(:perform!).with(@other_item)
|
63
|
+
end
|
64
|
+
|
65
|
+
subject.start
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_record'
|
3
|
+
require 'micro_q/methods/active_record'
|
4
|
+
|
5
|
+
describe MicroQ::Methods::ActiveRecord, :active_record => true do
|
6
|
+
class Repository < ActiveRecord::Base
|
7
|
+
def id
|
8
|
+
456
|
9
|
+
end
|
10
|
+
|
11
|
+
def process
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
shared_examples_for 'an async AR instance' do
|
16
|
+
before do
|
17
|
+
@proxy = mock(MicroQ::Proxy::Instance, :process => nil)
|
18
|
+
MicroQ::Proxy::Instance.stub(:new).and_return(@proxy)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should create a proxy' do
|
22
|
+
MicroQ::Proxy::Instance.should_receive(:new).and_return(@proxy)
|
23
|
+
|
24
|
+
method.call
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should have the class' do
|
28
|
+
MicroQ::Proxy::Instance.should_receive(:new).with(hash_including(:class => subject.class)).and_return(@proxy)
|
29
|
+
|
30
|
+
method.call
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should have the activerecord find with the record id' do
|
34
|
+
MicroQ::Proxy::Instance.should_receive(:new).with(hash_including(:loader => {:method => 'find', :args => [456]})).and_return(@proxy)
|
35
|
+
|
36
|
+
method.call
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should call the method' do
|
40
|
+
@proxy.should_receive(:process)
|
41
|
+
|
42
|
+
method.call
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should pass arguments' do
|
46
|
+
@proxy.should_receive(:process).with(1, 2, 3)
|
47
|
+
|
48
|
+
method.call(1, 2, 3)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
subject { Repository.new }
|
53
|
+
|
54
|
+
it_behaves_like 'a_worker', 'process'
|
55
|
+
|
56
|
+
describe 'when an _async method is called' do
|
57
|
+
let(:method) { lambda {|*args| subject.process_async(*args) } }
|
58
|
+
|
59
|
+
it_behaves_like 'an async AR instance'
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'when calling to async.method proxy' do
|
63
|
+
let(:method) { lambda {|*args| subject.async.process(*args) } }
|
64
|
+
|
65
|
+
it_behaves_like 'an async AR instance'
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MicroQ::Methods::Class do
|
4
|
+
class MyWorker
|
5
|
+
def self.seed
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
shared_examples_for 'an async class' do
|
10
|
+
before do
|
11
|
+
@proxy = mock(MicroQ::Proxy::Class, :seed => nil)
|
12
|
+
MicroQ::Proxy::Class.stub(:new).and_return(@proxy)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should create a proxy' do
|
16
|
+
MicroQ::Proxy::Class.should_receive(:new).and_return(@proxy)
|
17
|
+
|
18
|
+
method.call
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should have the class' do
|
22
|
+
MicroQ::Proxy::Class.should_receive(:new).with(hash_including(:class => subject)).and_return(@proxy)
|
23
|
+
|
24
|
+
method.call
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should have a loader without a method' do
|
28
|
+
MicroQ::Proxy::Class.should_receive(:new).with(hash_including(:loader => {})).and_return(@proxy)
|
29
|
+
|
30
|
+
method.call
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should call the method' do
|
34
|
+
@proxy.should_receive(:seed)
|
35
|
+
|
36
|
+
method.call
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should pass arguments' do
|
40
|
+
@proxy.should_receive(:seed).with(1, 2, 3)
|
41
|
+
|
42
|
+
method.call(1, 2, 3)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
subject { MyWorker }
|
47
|
+
|
48
|
+
it_behaves_like 'a_worker', 'seed'
|
49
|
+
|
50
|
+
describe 'when an _async method is called' do
|
51
|
+
let(:method) { lambda {|*args| subject.seed_async(*args) } }
|
52
|
+
|
53
|
+
it_behaves_like 'an async class'
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'when calling to async.method proxy' do
|
57
|
+
let(:method) { lambda {|*args| subject.async.seed(*args) } }
|
58
|
+
|
59
|
+
it_behaves_like 'an async class'
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MicroQ::Methods::Instance do
|
4
|
+
class MyWorker
|
5
|
+
def process
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
shared_examples_for 'an async instance' do
|
10
|
+
before do
|
11
|
+
@proxy = mock(MicroQ::Proxy::Instance, :process => nil)
|
12
|
+
MicroQ::Proxy::Instance.stub(:new).and_return(@proxy)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should create a proxy' do
|
16
|
+
MicroQ::Proxy::Instance.should_receive(:new).and_return(@proxy)
|
17
|
+
|
18
|
+
method.call
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should have the class' do
|
22
|
+
MicroQ::Proxy::Instance.should_receive(:new).with(hash_including(:class => subject.class)).and_return(@proxy)
|
23
|
+
|
24
|
+
method.call
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should call the method' do
|
28
|
+
@proxy.should_receive(:process)
|
29
|
+
|
30
|
+
method.call
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should pass arguments' do
|
34
|
+
@proxy.should_receive(:process).with(1, 2, 3)
|
35
|
+
|
36
|
+
method.call(1, 2, 3)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
subject { MyWorker.new }
|
41
|
+
|
42
|
+
it_behaves_like 'a_worker', 'process'
|
43
|
+
|
44
|
+
describe 'when an _async method is called' do
|
45
|
+
let(:method) { lambda {|*args| subject.process_async(*args) } }
|
46
|
+
|
47
|
+
it_behaves_like 'an async instance'
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'when calling to async.method proxy' do
|
51
|
+
let(:method) { lambda {|*args| subject.async.process(*args) } }
|
52
|
+
|
53
|
+
it_behaves_like 'an async instance'
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MicroQ do
|
4
|
+
describe '.configure' do
|
5
|
+
it 'should be the config' do
|
6
|
+
MicroQ.configure {|config| config.class.should == MicroQ::Config }
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should cache the value" do
|
10
|
+
configs = []
|
11
|
+
|
12
|
+
2.times { MicroQ.configure {|c| configs << c } }
|
13
|
+
|
14
|
+
configs[0].should == configs[1]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '.config' do
|
19
|
+
it 'should be the config' do
|
20
|
+
MicroQ.config.class.should == MicroQ::Config
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '.middleware' do
|
25
|
+
it 'should alias the middleware on the config' do
|
26
|
+
MicroQ.middleware.should == MicroQ.config.middleware
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '.start' do
|
31
|
+
def start
|
32
|
+
MicroQ.start
|
33
|
+
end
|
34
|
+
|
35
|
+
before do
|
36
|
+
@manager = mock(MicroQ::Manager::Default, :start! => nil)
|
37
|
+
MicroQ::Manager::Default.stub(:new).and_return(@manager)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should create a queue' do
|
41
|
+
MicroQ::Manager::Default.should_receive(:new).and_return(@manager)
|
42
|
+
|
43
|
+
start
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should cache the queue' do
|
47
|
+
MicroQ::Manager::Default.should_receive(:new).once.and_return(@manager)
|
48
|
+
|
49
|
+
3.times { start }
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should asynchronously start the manager (once)' do
|
53
|
+
@manager.should_receive(:start!).once
|
54
|
+
|
55
|
+
start
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '.push' do
|
60
|
+
let(:args) { [{ :class => 'WorkerClass' }, { :option => 'value' }] }
|
61
|
+
|
62
|
+
def push
|
63
|
+
MicroQ.push(*args)
|
64
|
+
end
|
65
|
+
|
66
|
+
before do
|
67
|
+
@async = mock(Celluloid::AsyncProxy)
|
68
|
+
@manager = mock(MicroQ::Manager::Default, :start! => nil, :queue => mock("Queue", :async => @async))
|
69
|
+
MicroQ::Manager::Default.stub(:new).and_return(@manager)
|
70
|
+
|
71
|
+
MicroQ.start
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should delegate to the manager\'s queue' do
|
75
|
+
@async.should_receive(:push).with(*args)
|
76
|
+
|
77
|
+
push
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|