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