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.
- 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
|