micro_q 0.6.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +33 -1
- data/lib/micro_q/config.rb +12 -1
- data/lib/micro_q/manager/default.rb +2 -2
- data/lib/micro_q/manager.rb +16 -0
- data/lib/micro_q/methods/action_mailer.rb +17 -0
- data/lib/micro_q/methods/active_record.rb +14 -0
- data/lib/micro_q/methods/class.rb +8 -0
- data/lib/micro_q/methods/instance.rb +10 -0
- data/lib/micro_q/methods.rb +5 -1
- data/lib/micro_q/middleware/chain.rb +3 -1
- data/lib/micro_q/middleware/server/connection.rb +15 -0
- data/lib/micro_q/middleware.rb +13 -0
- data/lib/micro_q/proxies/action_mailer.rb +19 -0
- data/lib/micro_q/proxies/base.rb +2 -2
- data/lib/micro_q/proxies.rb +1 -0
- data/lib/micro_q/queue/default.rb +20 -11
- data/lib/micro_q/queue.rb +23 -0
- data/lib/micro_q/util.rb +12 -1
- data/lib/micro_q/version.rb +1 -1
- data/lib/micro_q/worker.rb +16 -0
- data/lib/micro_q/wrappers/action_mailer.rb +11 -0
- data/lib/micro_q/wrappers.rb +1 -0
- data/lib/micro_q.rb +3 -2
- data/micro_q.gemspec +3 -2
- data/spec/lib/config_spec.rb +12 -0
- data/spec/lib/methods/action_mailer_spec.rb +28 -0
- data/spec/lib/methods/active_record_spec.rb +1 -1
- data/spec/lib/micro_q_spec.rb +2 -3
- data/spec/lib/middleware/chain_spec.rb +25 -14
- data/spec/lib/middleware/server/connection_spec.rb +44 -0
- data/spec/lib/middleware/server/retry_spec.rb +1 -1
- data/spec/lib/proxies/action_mailer_spec.rb +51 -0
- data/spec/lib/queue/default_spec.rb +53 -13
- data/spec/lib/util_spec.rb +25 -0
- data/spec/wrappers/action_mailer_spec.rb +58 -0
- metadata +32 -4
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# MicroQ
|
1
|
+
# MicroQ [![Build Status](https://travis-ci.org/bnorton/micro_q.png)](https://travis-ci.org/bnorton/micro_q)
|
2
2
|
|
3
3
|
MicroQ is a per-process asynchronous background queue.
|
4
4
|
|
@@ -18,6 +18,38 @@ Or install it:
|
|
18
18
|
|
19
19
|
## Usage
|
20
20
|
|
21
|
+
```ruby
|
22
|
+
# A typical worker
|
23
|
+
class MyWorker
|
24
|
+
def perform
|
25
|
+
# do some performing here
|
26
|
+
end
|
27
|
+
|
28
|
+
def update(options = {})
|
29
|
+
# do some updating
|
30
|
+
end
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
###Simple (default)
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
# Using the async proxy API
|
38
|
+
MyWorker.async.perform
|
39
|
+
|
40
|
+
MyWorker.async.update(:user_id => user.id)
|
41
|
+
|
42
|
+
# Through the raw push API
|
43
|
+
MicroQ.push(:class => 'MyWorker') # Defaults to the perform method
|
44
|
+
|
45
|
+
# With a custom method
|
46
|
+
MicroQ.push(:class => 'MyWorker', :method => 'update', :args => [{:user_id => user.id}])
|
47
|
+
```
|
48
|
+
|
49
|
+
###Advanced
|
50
|
+
|
51
|
+
###Custom Loaders
|
52
|
+
|
21
53
|
## Contributing
|
22
54
|
|
23
55
|
1. Fork it
|
data/lib/micro_q/config.rb
CHANGED
@@ -1,11 +1,22 @@
|
|
1
1
|
module MicroQ
|
2
2
|
class Config
|
3
|
+
##
|
4
|
+
# Configuration accessible via:
|
5
|
+
# 1. hash syntax (config[:k] and config[:k] = val)
|
6
|
+
# 2. method syntax (config.k and config.k = val)
|
7
|
+
#
|
8
|
+
# To change the type of worker that is used simply assign
|
9
|
+
# a new worker class (after requiring the file).
|
10
|
+
#
|
3
11
|
def initialize
|
4
12
|
@data = {
|
5
13
|
'workers' => 3,
|
6
14
|
'timeout' => 120,
|
7
15
|
'interval' => 5,
|
8
|
-
'middleware' =>
|
16
|
+
'middleware' => Middleware::Chain.new,
|
17
|
+
'manager' => Manager::Default,
|
18
|
+
'worker' => Worker::Standard,
|
19
|
+
'queue' => Queue::Default
|
9
20
|
}
|
10
21
|
end
|
11
22
|
|
@@ -20,8 +20,8 @@ module MicroQ
|
|
20
20
|
attr_reader :queue, :workers
|
21
21
|
|
22
22
|
def initialize
|
23
|
-
@queue = MicroQ
|
24
|
-
@workers = MicroQ
|
23
|
+
@queue = MicroQ.config.queue.new
|
24
|
+
@workers = MicroQ.config.worker.pool(:size => MicroQ.config.workers)
|
25
25
|
end
|
26
26
|
|
27
27
|
def start
|
data/lib/micro_q/manager.rb
CHANGED
@@ -1 +1,17 @@
|
|
1
1
|
require 'micro_q/manager/default'
|
2
|
+
|
3
|
+
##
|
4
|
+
# The Manager interface
|
5
|
+
#
|
6
|
+
# The concrete manager implementation encapsulates a queue and
|
7
|
+
# a pool of workers. The worker pool need only be a collection of
|
8
|
+
# workers that can accept and perform messages.
|
9
|
+
#
|
10
|
+
# :method: start
|
11
|
+
# - Begin the run loop for message processing.
|
12
|
+
#
|
13
|
+
# Note that the default manager is a celluloid actor that reschedules
|
14
|
+
# itself (via the after(seconds) { start }) call which is a recursive
|
15
|
+
# call that executes asynchronously. This behavior is critical and
|
16
|
+
# therefore the class must `include Celluloid`.
|
17
|
+
#
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MicroQ
|
2
|
+
module Methods
|
3
|
+
module ActionMailer
|
4
|
+
def async
|
5
|
+
MicroQ::Proxy::ActionMailer.new(
|
6
|
+
:class => MicroQ::Wrapper::ActionMailer,
|
7
|
+
:base => self
|
8
|
+
)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
MicroQ::Util.safe_require 'action_mailer'
|
15
|
+
if defined?(ActionMailer::Base)
|
16
|
+
ActionMailer::Base.send(:extend, MicroQ::Methods::ActionMailer)
|
17
|
+
end
|
@@ -1,5 +1,18 @@
|
|
1
1
|
module MicroQ
|
2
2
|
module Methods
|
3
|
+
##
|
4
|
+
# Methods that are added to AR instances
|
5
|
+
#
|
6
|
+
# When processing instance methods asynchronously, AR objects
|
7
|
+
# should not be stored. Instances that are backed by a database
|
8
|
+
# are herby serialized and re-queried from the DB at runtime.
|
9
|
+
# For AR that means simply storing the class and adding a custom 'loader'
|
10
|
+
#
|
11
|
+
# A Loader is an additional step before a method is invoked that
|
12
|
+
# generates a target object from a method invocation and arguments.
|
13
|
+
# In the case of AR, what better then 'find'. Here we simply
|
14
|
+
# store the id as the argument for find.
|
15
|
+
#
|
3
16
|
module ActiveRecord
|
4
17
|
def async
|
5
18
|
options = {
|
@@ -16,6 +29,7 @@ module MicroQ
|
|
16
29
|
end
|
17
30
|
end
|
18
31
|
|
32
|
+
MicroQ::Util.safe_require 'active_record'
|
19
33
|
if defined?(ActiveRecord::Base)
|
20
34
|
ActiveRecord::Base.send(:include, MicroQ::Methods::ActiveRecord)
|
21
35
|
end
|
@@ -1,5 +1,13 @@
|
|
1
1
|
module MicroQ
|
2
2
|
module Methods
|
3
|
+
##
|
4
|
+
# Methods that are added to all Ruby Objects (as class methods).
|
5
|
+
#
|
6
|
+
# When processing class methods asynchronously, simply store
|
7
|
+
# the calling class. The custom 'loader' describes that no
|
8
|
+
# additional methods need be called to generate the callee of the
|
9
|
+
# message invocation
|
10
|
+
#
|
3
11
|
module Class
|
4
12
|
extend MicroQ::Methods::SharedMethods
|
5
13
|
|
@@ -1,5 +1,15 @@
|
|
1
1
|
module MicroQ
|
2
2
|
module Methods
|
3
|
+
##
|
4
|
+
# Methods that are added to all Ruby Objects (as instance methods).
|
5
|
+
#
|
6
|
+
# When processing instance methods asynchronously, simply store
|
7
|
+
# the calling instances' class name. No custom 'loader' is needed but
|
8
|
+
# since the worker defines a default 'loader'. An example loader for
|
9
|
+
# a messages invoked as MyWorker.new.perform(123), is
|
10
|
+
# :loader => { :method => 'new', :args => []} which happens to be
|
11
|
+
# what the default worker does.
|
12
|
+
#
|
3
13
|
module Instance
|
4
14
|
include MicroQ::Methods::SharedMethods
|
5
15
|
|
data/lib/micro_q/methods.rb
CHANGED
@@ -4,7 +4,7 @@ module MicroQ
|
|
4
4
|
def method_missing(method, *other)
|
5
5
|
super unless /((.+)\_async$)/ === method
|
6
6
|
|
7
|
-
name = $2
|
7
|
+
name = $2
|
8
8
|
|
9
9
|
# Define the method and call through.
|
10
10
|
if name && respond_to?(name)
|
@@ -21,5 +21,9 @@ module MicroQ
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
# add Class and Instance methods first then
|
25
|
+
# override with additional extensions
|
24
26
|
require 'micro_q/methods/class'
|
25
27
|
require 'micro_q/methods/instance'
|
28
|
+
require 'micro_q/methods/active_record'
|
29
|
+
require 'micro_q/methods/action_mailer'
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'micro_q/middleware/server/retry'
|
2
|
+
require 'micro_q/middleware/server/connection'
|
2
3
|
|
3
4
|
module MicroQ
|
4
5
|
module Middleware
|
@@ -90,7 +91,8 @@ module MicroQ
|
|
90
91
|
class Server < Base
|
91
92
|
def initialize
|
92
93
|
@entries = [
|
93
|
-
MicroQ::Middleware::Server::Retry
|
94
|
+
MicroQ::Middleware::Server::Retry,
|
95
|
+
MicroQ::Middleware::Server::Connection
|
94
96
|
]
|
95
97
|
end
|
96
98
|
end
|
data/lib/micro_q/middleware.rb
CHANGED
@@ -1 +1,14 @@
|
|
1
1
|
require 'micro_q/middleware/chain'
|
2
|
+
|
3
|
+
##
|
4
|
+
# The Middleware interface
|
5
|
+
#
|
6
|
+
# Middleware is invoked around the processing of a message, either
|
7
|
+
# on the 'client' (when the message is pushed to a queue) or on the
|
8
|
+
# 'server' (when the message is being executed).
|
9
|
+
#
|
10
|
+
# :method: call
|
11
|
+
# - :args: (worker, message)
|
12
|
+
# worker is the target object in the method invocation specified by the message
|
13
|
+
# message the raw payload as de-queued by the queue
|
14
|
+
#
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module MicroQ
|
2
|
+
module Proxy
|
3
|
+
class ActionMailer < Base
|
4
|
+
def method_missing(meth, *args)
|
5
|
+
@args = [@options.delete(:base).to_s, meth.to_s, *args]
|
6
|
+
|
7
|
+
defaults = [{
|
8
|
+
:class => MicroQ::Wrapper::ActionMailer,
|
9
|
+
:method => 'perform',
|
10
|
+
:args => @args
|
11
|
+
}.merge(@options)]
|
12
|
+
|
13
|
+
defaults << { :when => at } if at
|
14
|
+
|
15
|
+
MicroQ.push(*defaults)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/micro_q/proxies/base.rb
CHANGED
data/lib/micro_q/proxies.rb
CHANGED
@@ -10,8 +10,8 @@ module MicroQ
|
|
10
10
|
# item = { 'class' => 'MyWorker', 'args' => [user.id] }
|
11
11
|
#
|
12
12
|
# queue = MicroQ::Queue::Default.new
|
13
|
-
# queue.push(item) #
|
14
|
-
# queue.
|
13
|
+
# queue.push(item) # asynchronous push (preferred)
|
14
|
+
# queue.sync_push(item) # synchronous push
|
15
15
|
#
|
16
16
|
# queue.entries
|
17
17
|
# #=> [{'class' => 'MyWorker', 'args' => [32]}]
|
@@ -32,23 +32,32 @@ module MicroQ
|
|
32
32
|
end
|
33
33
|
|
34
34
|
##
|
35
|
-
#
|
35
|
+
# Asynchronously push a message item to the queue.
|
36
|
+
#
|
37
|
+
def push(item, options={})
|
38
|
+
async.sync_push(item, options)
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Synchronously push a message item to the queue.
|
36
43
|
# Either push it to the immediate portion of the queue or store it for after when
|
37
44
|
# it should be run with the 'when' option.
|
38
45
|
#
|
39
46
|
# Options:
|
40
47
|
# when: The time/timestamp after which to run the message.
|
41
48
|
#
|
42
|
-
def
|
49
|
+
def sync_push(item, options={})
|
43
50
|
item, options = before_push(item, options)
|
44
51
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
+
MicroQ.middleware.client.call(item['class'], item, options) do
|
53
|
+
if (time = options['when'])
|
54
|
+
@later.push(
|
55
|
+
'when' => time.to_f,
|
56
|
+
'worker' => item
|
57
|
+
)
|
58
|
+
else
|
59
|
+
@entries.push(item)
|
60
|
+
end
|
52
61
|
end
|
53
62
|
end
|
54
63
|
|
data/lib/micro_q/queue.rb
CHANGED
@@ -1 +1,24 @@
|
|
1
1
|
require 'micro_q/queue/default'
|
2
|
+
|
3
|
+
##
|
4
|
+
# The Queueing interface
|
5
|
+
#
|
6
|
+
# The concrete queue implementations will all have something simple in
|
7
|
+
# common. The queue stores and returns data. The logic used for
|
8
|
+
# selecting the next items to return is critical but not relevant to the
|
9
|
+
# rest of the system nor to the interface.
|
10
|
+
#
|
11
|
+
# :method: push
|
12
|
+
# - Add items to the backing data store
|
13
|
+
# - :args: (message, options)
|
14
|
+
# message is the hash the represents an pushed item
|
15
|
+
# options are for items that dont require storing in the message itself
|
16
|
+
# but are important in queueing.
|
17
|
+
# :method: dequeue
|
18
|
+
# - Remove and return items from the data store
|
19
|
+
#
|
20
|
+
# You are otherwise able to implement this class in any suitable manner.
|
21
|
+
# If adhering to the other conventions around data structures, keys, etc,
|
22
|
+
# then it will be easier to use the other types of classes required for
|
23
|
+
# micro_q to work properly.
|
24
|
+
#
|
data/lib/micro_q/util.rb
CHANGED
@@ -13,9 +13,12 @@ module MicroQ
|
|
13
13
|
end
|
14
14
|
constant
|
15
15
|
rescue
|
16
|
-
nil
|
17
16
|
end
|
18
17
|
|
18
|
+
##
|
19
|
+
# Copy a hash and convert all keys to strings.
|
20
|
+
# Stringifies to infinite hash depth
|
21
|
+
#
|
19
22
|
def self.stringify_keys(hash)
|
20
23
|
{}.tap do |result|
|
21
24
|
hash.keys.each do |key|
|
@@ -25,5 +28,13 @@ module MicroQ
|
|
25
28
|
end
|
26
29
|
end
|
27
30
|
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Attempt to load a library but return nil if it cannot be loaded
|
34
|
+
#
|
35
|
+
def self.safe_require(lib)
|
36
|
+
require lib
|
37
|
+
rescue LoadError
|
38
|
+
end
|
28
39
|
end
|
29
40
|
end
|
data/lib/micro_q/version.rb
CHANGED
data/lib/micro_q/worker.rb
CHANGED
@@ -1 +1,17 @@
|
|
1
1
|
require 'micro_q/worker/standard'
|
2
|
+
|
3
|
+
##
|
4
|
+
# The Worker interface
|
5
|
+
#
|
6
|
+
# The concrete worker implementation is the member of the chain that
|
7
|
+
# actually invokes the middleware chain and executes/performs the given
|
8
|
+
# message.
|
9
|
+
#
|
10
|
+
# :method: perform
|
11
|
+
# - Re-hydrates and invokes the methods that the message specifies.
|
12
|
+
# - :args: (message)
|
13
|
+
# message is the hash the represents an pushed item
|
14
|
+
#
|
15
|
+
# Based on what keys are pushed to and de-queued from the queue,
|
16
|
+
# do the actions that the message specifies.
|
17
|
+
#
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'micro_q/wrappers/action_mailer'
|
data/lib/micro_q.rb
CHANGED
@@ -21,14 +21,14 @@ module MicroQ
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def self.push(*args)
|
24
|
-
manager.queue.
|
24
|
+
manager.queue.push(*args)
|
25
25
|
end
|
26
26
|
|
27
27
|
private
|
28
28
|
|
29
29
|
def self.manager
|
30
30
|
@manager ||= begin
|
31
|
-
|
31
|
+
config.manager.new.tap do |manager|
|
32
32
|
manager.start!
|
33
33
|
end
|
34
34
|
end
|
@@ -40,6 +40,7 @@ module MicroQ
|
|
40
40
|
end
|
41
41
|
|
42
42
|
require 'micro_q/middleware'
|
43
|
+
require 'micro_q/wrappers'
|
43
44
|
require 'micro_q/methods'
|
44
45
|
require 'micro_q/proxies'
|
45
46
|
require 'micro_q/worker'
|
data/micro_q.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |gem|
|
|
7
7
|
gem.name = "micro_q"
|
8
8
|
gem.version = MicroQ::VERSION
|
9
9
|
gem.authors = ["Brian Norton"]
|
10
|
-
gem.email =
|
10
|
+
gem.email = "brian.nort@gmail.com"
|
11
11
|
gem.description = ""
|
12
12
|
gem.summary = ""
|
13
13
|
gem.homepage = "http://github.com/bnorton/micro-q"
|
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.files = `git ls-files`.split($/)
|
16
16
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
17
|
gem.test_files = gem.files.grep(%r{^spec/})
|
18
|
-
gem.require_paths =
|
18
|
+
gem.require_paths = %w(lib)
|
19
19
|
|
20
20
|
gem.add_dependency "celluloid"
|
21
21
|
gem.add_development_dependency "rake"
|
@@ -23,5 +23,6 @@ Gem::Specification.new do |gem|
|
|
23
23
|
gem.add_development_dependency "timecop"
|
24
24
|
gem.add_development_dependency "psych"
|
25
25
|
gem.add_development_dependency "activerecord", "> 3.2.0"
|
26
|
+
gem.add_development_dependency "actionmailer", "> 3.2.0"
|
26
27
|
gem.add_development_dependency "sqlite3-ruby"
|
27
28
|
end
|
data/spec/lib/config_spec.rb
CHANGED
@@ -43,5 +43,17 @@ describe MicroQ::Config do
|
|
43
43
|
it 'should not have a logfile' do
|
44
44
|
subject.logfile.should == nil
|
45
45
|
end
|
46
|
+
|
47
|
+
it 'should have the default queue' do
|
48
|
+
subject.manager.should == MicroQ::Manager::Default
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should have the default queue' do
|
52
|
+
subject.queue.should == MicroQ::Queue::Default
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should have the standard worker' do
|
56
|
+
subject.worker.should == MicroQ::Worker::Standard
|
57
|
+
end
|
46
58
|
end
|
47
59
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MicroQ::Methods::ActionMailer do
|
4
|
+
class MyMailer < ActionMailer::Base
|
5
|
+
end
|
6
|
+
|
7
|
+
describe '.async' do
|
8
|
+
let(:async) { -> { MyMailer.async } }
|
9
|
+
|
10
|
+
it 'should create a mailer proxy' do
|
11
|
+
MicroQ::Proxy::ActionMailer.should_receive(:new)
|
12
|
+
|
13
|
+
async.call
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should have the class' do
|
17
|
+
MicroQ::Proxy::ActionMailer.should_receive(:new).with(hash_including(:class => MicroQ::Wrapper::ActionMailer))
|
18
|
+
|
19
|
+
async.call
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should have the base class' do
|
23
|
+
MicroQ::Proxy::ActionMailer.should_receive(:new).with(hash_including(:base => MyMailer))
|
24
|
+
|
25
|
+
async.call
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -18,7 +18,7 @@ describe MicroQ::Methods::ActiveRecord, :active_record => true do
|
|
18
18
|
MicroQ::Proxy::Instance.stub(:new).and_return(@proxy)
|
19
19
|
end
|
20
20
|
|
21
|
-
it 'should create
|
21
|
+
it 'should create an instance proxy' do
|
22
22
|
MicroQ::Proxy::Instance.should_receive(:new).and_return(@proxy)
|
23
23
|
|
24
24
|
method.call
|
data/spec/lib/micro_q_spec.rb
CHANGED
@@ -64,15 +64,14 @@ describe MicroQ do
|
|
64
64
|
end
|
65
65
|
|
66
66
|
before do
|
67
|
-
@
|
68
|
-
@manager = mock(MicroQ::Manager::Default, :start! => nil, :queue => mock("Queue", :async => @async))
|
67
|
+
@manager = mock(MicroQ::Manager::Default, :start! => nil, :queue => mock("Queue"))
|
69
68
|
MicroQ::Manager::Default.stub(:new).and_return(@manager)
|
70
69
|
|
71
70
|
MicroQ.start
|
72
71
|
end
|
73
72
|
|
74
73
|
it 'should delegate to the manager\'s queue' do
|
75
|
-
@
|
74
|
+
@manager.queue.should_receive(:push).with(*args)
|
76
75
|
|
77
76
|
push
|
78
77
|
end
|
@@ -205,14 +205,14 @@ describe MicroQ::Middleware::Chain do
|
|
205
205
|
end
|
206
206
|
|
207
207
|
describe 'defaults' do
|
208
|
-
[MicroQ::Middleware::Server::Retry].each do |klass|
|
208
|
+
[MicroQ::Middleware::Server::Retry, MicroQ::Middleware::Server::Connection].each do |klass|
|
209
209
|
it "should include #{klass}" do
|
210
210
|
subject.server.entries.should include(klass)
|
211
211
|
end
|
212
212
|
end
|
213
213
|
|
214
|
-
it 'should be
|
215
|
-
subject.server.entries.should have(
|
214
|
+
it 'should be 2 items long' do
|
215
|
+
subject.server.entries.should have(2).items
|
216
216
|
end
|
217
217
|
end
|
218
218
|
end
|
@@ -238,29 +238,40 @@ describe MicroQ::Middleware::Chain do
|
|
238
238
|
class MyWorker
|
239
239
|
end
|
240
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
241
|
describe 'server' do
|
247
242
|
def call
|
248
|
-
subject.server.call(worker, payload)
|
243
|
+
subject.server.call(worker, payload) { }
|
249
244
|
end
|
250
245
|
|
251
|
-
it 'should make a new
|
252
|
-
|
253
|
-
|
254
|
-
|
246
|
+
it 'should make a new retry instance' do
|
247
|
+
MicroQ::Middleware::Server::Retry.should_receive(:new).and_call_original
|
248
|
+
|
249
|
+
call
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'should make a new connections instance' do
|
253
|
+
MicroQ::Middleware::Server::Connection.should_receive(:new).and_call_original
|
255
254
|
|
256
255
|
call
|
257
256
|
end
|
258
257
|
|
259
|
-
it 'should call
|
258
|
+
it 'should call the retry middleware' do
|
259
|
+
@retry = mock(MicroQ::Middleware::Server::Retry)
|
260
|
+
MicroQ::Middleware::Server::Retry.stub(:new).and_return(@retry)
|
261
|
+
|
260
262
|
@retry.should_receive(:call).with(worker, payload)
|
261
263
|
|
262
264
|
call
|
263
265
|
end
|
266
|
+
|
267
|
+
it 'should call the connection middleware' do
|
268
|
+
@connection = mock(MicroQ::Middleware::Server::Connection)
|
269
|
+
MicroQ::Middleware::Server::Connection.stub(:new).and_return(@connection)
|
270
|
+
|
271
|
+
@connection.should_receive(:call).with(worker, payload)
|
272
|
+
|
273
|
+
call
|
274
|
+
end
|
264
275
|
end
|
265
276
|
end
|
266
277
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
describe MicroQ::Middleware::Server::Connection, :middleware => true do
|
5
|
+
describe '#call' do
|
6
|
+
let(:foo) { mock("Foo", :bar => nil) }
|
7
|
+
let(:block) { -> { foo.bar } }
|
8
|
+
|
9
|
+
def call
|
10
|
+
subject.call @worker, @payload, &block
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should execute the block' do
|
14
|
+
foo.should_receive(:bar)
|
15
|
+
|
16
|
+
call
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'active_record' do
|
20
|
+
it 'should clear active connections' do
|
21
|
+
ActiveRecord::Base.should_receive(:clear_active_connections!)
|
22
|
+
|
23
|
+
call
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'when the job raises an exception' do
|
27
|
+
let(:block) { -> { raise } }
|
28
|
+
|
29
|
+
it 'should error' do
|
30
|
+
expect {
|
31
|
+
call
|
32
|
+
}.to raise_error
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should clear active connections' do
|
36
|
+
ActiveRecord::Base.should_receive(:clear_active_connections!)
|
37
|
+
|
38
|
+
safe(:call)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe MicroQ::Middleware::Server::Retry, :middleware => true do
|
4
4
|
describe '#call' do
|
5
5
|
let(:foo) { mock("Foo", :bar => nil) }
|
6
|
-
let(:block) {
|
6
|
+
let(:block) { -> { foo.bar } }
|
7
7
|
|
8
8
|
def call
|
9
9
|
subject.call @worker, @payload, &block
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MicroQ::Proxy::ActionMailer do
|
4
|
+
let(:method) { lambda {|*args| subject.mail_me(*args) } }
|
5
|
+
let(:options) { { :class => MicroQ::Wrapper::ActionMailer, :base => MyMailer } }
|
6
|
+
|
7
|
+
class MyMailer < ActionMailer::Base
|
8
|
+
end
|
9
|
+
|
10
|
+
subject { MicroQ::Proxy::ActionMailer.new(options) }
|
11
|
+
|
12
|
+
describe 'method invocations' do
|
13
|
+
let(:method) { -> { subject.some_method(1, 2) } }
|
14
|
+
|
15
|
+
it 'should push the message' do
|
16
|
+
MicroQ.should_receive(:push)
|
17
|
+
|
18
|
+
method.call
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should have the class' do
|
22
|
+
MicroQ.should_receive(:push).with(hash_including(:class => MicroQ::Wrapper::ActionMailer))
|
23
|
+
|
24
|
+
method.call
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should have the method' do
|
28
|
+
MicroQ.should_receive(:push).with(hash_including(:method => 'perform'))
|
29
|
+
|
30
|
+
method.call
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should have the args' do
|
34
|
+
MicroQ.should_receive(:push).with(hash_including(:args => ['MyMailer', 'some_method', 1, 2]))
|
35
|
+
|
36
|
+
method.call
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'when performing at a specific time' do
|
40
|
+
before do
|
41
|
+
options[:at] = Time.now + 60
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should push with the right \'when\' key' do
|
45
|
+
MicroQ.should_receive(:push).with(anything, :when => (Time.now + 60).to_i)
|
46
|
+
|
47
|
+
method.call
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -13,17 +13,17 @@ describe MicroQ::Queue::Default do
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
describe '#
|
17
|
-
let(:item) { { 'class' => 'MyWorker', 'args' => [] } }
|
16
|
+
describe '#sync_push' do
|
17
|
+
let(:item) { { 'class' => 'MyWorker', 'args' => [4] } }
|
18
18
|
|
19
19
|
it 'should add to the entries' do
|
20
|
-
subject.
|
20
|
+
subject.sync_push(item)
|
21
21
|
|
22
22
|
subject.entries.should include(item)
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'should duplicate the item' do
|
26
|
-
subject.
|
26
|
+
subject.sync_push(item)
|
27
27
|
|
28
28
|
before = item.dup
|
29
29
|
subject.entries.should include(before)
|
@@ -33,11 +33,24 @@ describe MicroQ::Queue::Default do
|
|
33
33
|
subject.entries.should include(before)
|
34
34
|
end
|
35
35
|
|
36
|
+
describe 'client middleware' do
|
37
|
+
it 'should process the middleware chain' do
|
38
|
+
MicroQ.middleware.client.should_receive(:call) do |w, payload|
|
39
|
+
w.should == 'MyWorker'
|
40
|
+
|
41
|
+
payload['class'].should == 'MyWorker'
|
42
|
+
payload['args'].should == [4]
|
43
|
+
end
|
44
|
+
|
45
|
+
subject.sync_push(item)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
36
49
|
describe 'when given the "when" key' do
|
37
50
|
let(:worker) { [item, { 'when' => (Time.now + 100).to_i }] }
|
38
51
|
|
39
52
|
it 'should add to the later' do
|
40
|
-
subject.
|
53
|
+
subject.sync_push(*worker)
|
41
54
|
|
42
55
|
subject.later.should include(
|
43
56
|
'when' => (Time.now + 100).to_i,
|
@@ -46,17 +59,29 @@ describe MicroQ::Queue::Default do
|
|
46
59
|
end
|
47
60
|
|
48
61
|
it 'should not be in the entries' do
|
49
|
-
subject.
|
62
|
+
subject.sync_push(*worker)
|
50
63
|
|
51
64
|
subject.entries.should == []
|
52
65
|
end
|
66
|
+
|
67
|
+
it 'should process the middleware chain' do
|
68
|
+
MicroQ.middleware.client.should_receive(:call) do |w, payload, options|
|
69
|
+
w.should == 'MyWorker'
|
70
|
+
|
71
|
+
payload['class'].should == 'MyWorker'
|
72
|
+
payload['args'].should == [4]
|
73
|
+
options['when'].should == (Time.now + 100).to_i
|
74
|
+
end
|
75
|
+
|
76
|
+
subject.sync_push(*worker)
|
77
|
+
end
|
53
78
|
end
|
54
79
|
|
55
80
|
describe 'when given the symbol :when key' do
|
56
81
|
let(:worker) { [item, { :when => (Time.now + 100).to_i }] }
|
57
82
|
|
58
83
|
it 'should add to the later' do
|
59
|
-
subject.
|
84
|
+
subject.sync_push(*worker)
|
60
85
|
|
61
86
|
subject.later.should include(
|
62
87
|
'when' => (Time.now + 100).to_i,
|
@@ -65,13 +90,28 @@ describe MicroQ::Queue::Default do
|
|
65
90
|
end
|
66
91
|
|
67
92
|
it 'should not be in the entries' do
|
68
|
-
subject.
|
93
|
+
subject.sync_push(*worker)
|
69
94
|
|
70
95
|
subject.entries.should == []
|
71
96
|
end
|
72
97
|
end
|
73
98
|
end
|
74
99
|
|
100
|
+
describe '#push' do
|
101
|
+
let(:item) { { 'class' => 'MyWorker', 'args' => [4] } }
|
102
|
+
|
103
|
+
before do
|
104
|
+
@async = mock(Celluloid::ActorProxy)
|
105
|
+
subject.stub(:async).and_return(@async)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should asynchronously push the item' do
|
109
|
+
@async.should_receive(:sync_push).with(*item)
|
110
|
+
|
111
|
+
subject.push(*item)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
75
115
|
describe '#dequeue' do
|
76
116
|
let(:item) { { 'class' => 'MyWorker', 'args' => [] } }
|
77
117
|
|
@@ -82,7 +122,7 @@ describe MicroQ::Queue::Default do
|
|
82
122
|
|
83
123
|
describe 'when there are entries' do
|
84
124
|
before do
|
85
|
-
subject.
|
125
|
+
subject.sync_push(item)
|
86
126
|
end
|
87
127
|
|
88
128
|
it 'should return the item' do
|
@@ -98,7 +138,7 @@ describe MicroQ::Queue::Default do
|
|
98
138
|
|
99
139
|
describe 'when there are items to be processed later' do
|
100
140
|
before do
|
101
|
-
subject.
|
141
|
+
subject.sync_push(item, 'when' => (Time.now + 5).to_i)
|
102
142
|
end
|
103
143
|
|
104
144
|
it 'should not return the item' do
|
@@ -137,10 +177,10 @@ describe MicroQ::Queue::Default do
|
|
137
177
|
end
|
138
178
|
|
139
179
|
before do
|
140
|
-
items.first(4).each {|item| subject.
|
141
|
-
subject.
|
180
|
+
items.first(4).each {|item| subject.sync_push(item) }
|
181
|
+
subject.sync_push(items.last, 'when' => (Time.now - 2).to_i)
|
142
182
|
|
143
|
-
subject.
|
183
|
+
subject.sync_push(*later_item)
|
144
184
|
end
|
145
185
|
|
146
186
|
it 'should return all the available items' do
|
data/spec/lib/util_spec.rb
CHANGED
@@ -48,4 +48,29 @@ describe MicroQ::Util do
|
|
48
48
|
}
|
49
49
|
end
|
50
50
|
end
|
51
|
+
|
52
|
+
describe '.safe_require' do
|
53
|
+
def req(lib)
|
54
|
+
MicroQ::Util.safe_require(lib)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should require the file' do
|
58
|
+
defined?(CMath).should == nil
|
59
|
+
req('cmath')
|
60
|
+
|
61
|
+
defined?(CMath).should == 'constant'
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'when the library is not available' do
|
65
|
+
it 'should not error' do
|
66
|
+
expect {
|
67
|
+
req('foo-bar-baz')
|
68
|
+
}.not_to raise_error
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should be nil' do
|
72
|
+
req('foo-bar-baz').should == nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
51
76
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MicroQ::Wrapper::ActionMailer do
|
4
|
+
class MyMailer
|
5
|
+
def self.mail_me
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#perform' do
|
10
|
+
let(:mail) { mock("email", :deliver => nil) }
|
11
|
+
|
12
|
+
def perform
|
13
|
+
subject.perform('MyMailer', 'mail_me', 1, 2)
|
14
|
+
end
|
15
|
+
|
16
|
+
before do
|
17
|
+
MyMailer.stub(:mail_me).and_return(mail)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should call the mailer method' do
|
21
|
+
MyMailer.should_receive(:mail_me).and_return(mail)
|
22
|
+
|
23
|
+
perform
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should pass the arguments' do
|
27
|
+
MyMailer.should_receive(:mail_me).with(1, 2).and_return(mail)
|
28
|
+
|
29
|
+
perform
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should deliver the email' do
|
33
|
+
mail.should_receive(:deliver)
|
34
|
+
|
35
|
+
perform
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'when the email does not work' do
|
39
|
+
let(:mail) { mock("email without deliver") }
|
40
|
+
|
41
|
+
before do
|
42
|
+
mail.stub(:respond_to?).with(:deliver).and_return(false)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should not error' do
|
46
|
+
expect {
|
47
|
+
perform
|
48
|
+
}.not_to raise_error
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should not deliver the message' do
|
52
|
+
mail.should_not_receive(:deliver)
|
53
|
+
|
54
|
+
perform
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: micro_q
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: celluloid
|
@@ -107,6 +107,22 @@ dependencies:
|
|
107
107
|
- - ! '>'
|
108
108
|
- !ruby/object:Gem::Version
|
109
109
|
version: 3.2.0
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: actionmailer
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>'
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 3.2.0
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>'
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 3.2.0
|
110
126
|
- !ruby/object:Gem::Dependency
|
111
127
|
name: sqlite3-ruby
|
112
128
|
requirement: !ruby/object:Gem::Requirement
|
@@ -124,8 +140,7 @@ dependencies:
|
|
124
140
|
- !ruby/object:Gem::Version
|
125
141
|
version: '0'
|
126
142
|
description: ''
|
127
|
-
email:
|
128
|
-
- brian.nort@gmail.com
|
143
|
+
email: brian.nort@gmail.com
|
129
144
|
executables: []
|
130
145
|
extensions: []
|
131
146
|
extra_rdoc_files: []
|
@@ -142,13 +157,16 @@ files:
|
|
142
157
|
- lib/micro_q/manager.rb
|
143
158
|
- lib/micro_q/manager/default.rb
|
144
159
|
- lib/micro_q/methods.rb
|
160
|
+
- lib/micro_q/methods/action_mailer.rb
|
145
161
|
- lib/micro_q/methods/active_record.rb
|
146
162
|
- lib/micro_q/methods/class.rb
|
147
163
|
- lib/micro_q/methods/instance.rb
|
148
164
|
- lib/micro_q/middleware.rb
|
149
165
|
- lib/micro_q/middleware/chain.rb
|
166
|
+
- lib/micro_q/middleware/server/connection.rb
|
150
167
|
- lib/micro_q/middleware/server/retry.rb
|
151
168
|
- lib/micro_q/proxies.rb
|
169
|
+
- lib/micro_q/proxies/action_mailer.rb
|
152
170
|
- lib/micro_q/proxies/base.rb
|
153
171
|
- lib/micro_q/proxies/class.rb
|
154
172
|
- lib/micro_q/proxies/instance.rb
|
@@ -158,16 +176,21 @@ files:
|
|
158
176
|
- lib/micro_q/version.rb
|
159
177
|
- lib/micro_q/worker.rb
|
160
178
|
- lib/micro_q/worker/standard.rb
|
179
|
+
- lib/micro_q/wrappers.rb
|
180
|
+
- lib/micro_q/wrappers/action_mailer.rb
|
161
181
|
- micro_q.gemspec
|
162
182
|
- spec/helpers/methods_examples.rb
|
163
183
|
- spec/lib/config_spec.rb
|
164
184
|
- spec/lib/manager/default_spec.rb
|
185
|
+
- spec/lib/methods/action_mailer_spec.rb
|
165
186
|
- spec/lib/methods/active_record_spec.rb
|
166
187
|
- spec/lib/methods/class_spec.rb
|
167
188
|
- spec/lib/methods/instance_spec.rb
|
168
189
|
- spec/lib/micro_q_spec.rb
|
169
190
|
- spec/lib/middleware/chain_spec.rb
|
191
|
+
- spec/lib/middleware/server/connection_spec.rb
|
170
192
|
- spec/lib/middleware/server/retry_spec.rb
|
193
|
+
- spec/lib/proxies/action_mailer_spec.rb
|
171
194
|
- spec/lib/proxies/base_spec.rb
|
172
195
|
- spec/lib/proxies/class_spec.rb
|
173
196
|
- spec/lib/proxies/instance_spec.rb
|
@@ -175,6 +198,7 @@ files:
|
|
175
198
|
- spec/lib/util_spec.rb
|
176
199
|
- spec/lib/worker/standard_spec.rb
|
177
200
|
- spec/spec_helper.rb
|
201
|
+
- spec/wrappers/action_mailer_spec.rb
|
178
202
|
homepage: http://github.com/bnorton/micro-q
|
179
203
|
licenses: []
|
180
204
|
post_install_message:
|
@@ -203,12 +227,15 @@ test_files:
|
|
203
227
|
- spec/helpers/methods_examples.rb
|
204
228
|
- spec/lib/config_spec.rb
|
205
229
|
- spec/lib/manager/default_spec.rb
|
230
|
+
- spec/lib/methods/action_mailer_spec.rb
|
206
231
|
- spec/lib/methods/active_record_spec.rb
|
207
232
|
- spec/lib/methods/class_spec.rb
|
208
233
|
- spec/lib/methods/instance_spec.rb
|
209
234
|
- spec/lib/micro_q_spec.rb
|
210
235
|
- spec/lib/middleware/chain_spec.rb
|
236
|
+
- spec/lib/middleware/server/connection_spec.rb
|
211
237
|
- spec/lib/middleware/server/retry_spec.rb
|
238
|
+
- spec/lib/proxies/action_mailer_spec.rb
|
212
239
|
- spec/lib/proxies/base_spec.rb
|
213
240
|
- spec/lib/proxies/class_spec.rb
|
214
241
|
- spec/lib/proxies/instance_spec.rb
|
@@ -216,4 +243,5 @@ test_files:
|
|
216
243
|
- spec/lib/util_spec.rb
|
217
244
|
- spec/lib/worker/standard_spec.rb
|
218
245
|
- spec/spec_helper.rb
|
246
|
+
- spec/wrappers/action_mailer_spec.rb
|
219
247
|
has_rdoc:
|