micro_q 0.6.1 → 0.6.2
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/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 [](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:
|