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