quebert 0.0.4 → 0.0.6
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/Gemfile +1 -0
- data/README.rdoc +10 -7
- data/VERSION +1 -1
- data/lib/quebert.rb +9 -4
- data/lib/quebert/async_sender/active_record.rb +7 -40
- data/lib/quebert/async_sender/instance.rb +1 -1
- data/lib/quebert/async_sender/object.rb +1 -1
- data/lib/quebert/backend/beanstalk.rb +8 -8
- data/lib/quebert/backend/in_process.rb +3 -3
- data/lib/quebert/backend/sync.rb +1 -1
- data/lib/quebert/{consumer.rb → controller.rb} +3 -3
- data/lib/quebert/{consumer → controller}/base.rb +3 -3
- data/lib/quebert/{consumer → controller}/beanstalk.rb +2 -2
- data/lib/quebert/job.rb +10 -10
- data/lib/quebert/serializer.rb +79 -0
- data/lib/quebert/support.rb +2 -2
- data/lib/quebert/support/registry.rb +19 -0
- data/lib/quebert/worker.rb +12 -5
- data/quebert.gemspec +14 -7
- data/spec/async_sender_spec.rb +9 -29
- data/spec/backend_spec.rb +3 -4
- data/spec/consumer_spec.rb +9 -9
- data/spec/job_spec.rb +10 -2
- data/spec/serializer_spec.rb +60 -0
- data/spec/spec_helper.rb +5 -3
- data/spec/support/active_record.rb +26 -0
- data/spec/{jobs.rb → support/jobs.rb} +0 -0
- data/spec/support_spec.rb +23 -0
- data/spec/worker_spec.rb +3 -3
- metadata +16 -9
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -6,12 +6,15 @@ A worker queue framework designed around Beanstalk. Features include:
|
|
6
6
|
|
7
7
|
= Features
|
8
8
|
|
9
|
-
*
|
10
|
-
*
|
11
|
-
*
|
12
|
-
*
|
9
|
+
* Multiple back-ends (InProcess, Sync, and Beanstalk)
|
10
|
+
* Rails/ActiveRecord integration similar to async_observer
|
11
|
+
* Pluggable exception handling (for Hoptoad integration)
|
12
|
+
* Run workers with pid, log, and config files. These do not daemonize (do it yourself punk!)
|
13
13
|
|
14
|
-
|
14
|
+
Some features that are currently missing that I will soon add include:
|
15
|
+
|
16
|
+
* Rails plugin support (The AR integrations have to be done manually today)
|
17
|
+
* Auto-detecting serializers. Enhanced ClassRegistry to more efficiently look up serializers for objects.
|
15
18
|
|
16
19
|
= How to use
|
17
20
|
|
@@ -31,11 +34,11 @@ Quebert includes a Job class so you can implement how you want certain types of
|
|
31
34
|
|
32
35
|
You can either drop a job in a queue:
|
33
36
|
|
34
|
-
Quebert.backend.put WackyMathWizard
|
37
|
+
Quebert.backend.put WackyMathWizard.new(1, 2, 3)
|
35
38
|
|
36
39
|
Or drop it in right from the job:
|
37
40
|
|
38
|
-
WackyMathWizard.
|
41
|
+
WackyMathWizard.new(4, 5, 6).enqueue
|
39
42
|
|
40
43
|
Then perform the jobs!
|
41
44
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.6
|
data/lib/quebert.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
module Quebert
|
2
|
+
autoload :Serializer, 'quebert/serializer'
|
2
3
|
autoload :Configuration, 'quebert/configuration'
|
3
4
|
autoload :Job, 'quebert/job'
|
4
|
-
autoload :
|
5
|
+
autoload :Controller, 'quebert/controller'
|
5
6
|
autoload :Backend, 'quebert/backend'
|
6
7
|
autoload :Support, 'quebert/support'
|
7
8
|
autoload :Worker, 'quebert/worker'
|
@@ -19,6 +20,10 @@ module Quebert
|
|
19
20
|
@backends ||= Support::Registry.new
|
20
21
|
end
|
21
22
|
|
23
|
+
def serializers
|
24
|
+
@serializers ||= Support::ClassRegistry.new
|
25
|
+
end
|
26
|
+
|
22
27
|
# Make this easier for elsewhere in the app
|
23
28
|
def logger
|
24
29
|
config.logger
|
@@ -26,7 +31,7 @@ module Quebert
|
|
26
31
|
end
|
27
32
|
|
28
33
|
# Register built-in Quebert backends
|
29
|
-
Quebert.backends.register :beanstalk,
|
30
|
-
Quebert.backends.register :in_process,
|
31
|
-
Quebert.backends.register :sync,
|
34
|
+
Quebert.backends.register :beanstalk, Backend::Beanstalk
|
35
|
+
Quebert.backends.register :in_process, Backend::InProcess
|
36
|
+
Quebert.backends.register :sync, Backend::Sync
|
32
37
|
end
|
@@ -1,59 +1,26 @@
|
|
1
1
|
module Quebert
|
2
2
|
module AsyncSender
|
3
|
+
|
3
4
|
# I'm not sure if I want to do this or build serializers per type of object...
|
4
5
|
module ActiveRecord
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
Support.constantize(model_name).find(pk).send(meth, *args)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
# Serialize an unpersisted AR with the attributes on the thing.
|
13
|
-
class UnpersistedRecordJob < Job
|
14
|
-
def perform(model_name, attrs, meth, args)
|
15
|
-
self.class.deserialize(Support.constantize(model_name), attrs).send(meth, *args)
|
16
|
-
end
|
17
|
-
|
18
|
-
# Deal with converting an AR to/from a hash that we can send over the wire.
|
19
|
-
def self.serialize(record)
|
20
|
-
record.attributes.inject({}) do |hash, (attr, val)|
|
21
|
-
hash[attr] = val
|
22
|
-
hash
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.deserialize(model, attrs)
|
27
|
-
record = model.new
|
28
|
-
record.attributes.each do |attr, val|
|
29
|
-
record.send("#{attr}=", attrs[attr])
|
30
|
-
end
|
31
|
-
record
|
6
|
+
class RecordJob < Job
|
7
|
+
def perform(record, meth, args)
|
8
|
+
record.send(meth, *args)
|
32
9
|
end
|
33
10
|
end
|
34
11
|
|
35
12
|
def self.included(base)
|
36
13
|
base.send(:include, InstanceMethods)
|
37
|
-
base.send(:
|
14
|
+
base.send(:include, AsyncSender::Object)
|
38
15
|
end
|
39
16
|
|
40
17
|
module InstanceMethods
|
41
18
|
# The meat of dealing with ActiveRecord instances.
|
42
19
|
def async_send(meth, *args)
|
43
|
-
|
44
|
-
UnpersistedRecordJob.enqueue(self.class.model_name, UnpersistedRecordJob.serialize(self), meth, args)
|
45
|
-
else
|
46
|
-
PersistedRecordJob.enqueue(self.class.model_name, id, meth, args)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
# Get the model name of the Model. Can't just do class.name on this...
|
52
|
-
module ClassMethods
|
53
|
-
def async_send(meth, *args)
|
54
|
-
Object::ObjectJob.enqueue(self.model_name, meth, args)
|
20
|
+
RecordJob.new(self, meth, args).enqueue
|
55
21
|
end
|
56
22
|
end
|
57
23
|
end
|
24
|
+
|
58
25
|
end
|
59
26
|
end
|
@@ -5,24 +5,24 @@ module Quebert
|
|
5
5
|
|
6
6
|
# Manage jobs on a Beanstalk queue out of process
|
7
7
|
class Beanstalk < Beanstalk::Pool
|
8
|
-
def put(job
|
9
|
-
super
|
8
|
+
def put(job)
|
9
|
+
super job.to_json
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
|
12
|
+
def reserve_with_controller
|
13
|
+
Controller::Beanstalk.new(reserve_without_controller, self)
|
14
14
|
end
|
15
|
-
alias :
|
16
|
-
alias :reserve :
|
15
|
+
alias :reserve_without_controller :reserve
|
16
|
+
alias :reserve :reserve_with_controller
|
17
17
|
|
18
18
|
# For testing purposes... I think there's a better way to do this though.
|
19
19
|
def drain!
|
20
20
|
while peek_ready do
|
21
|
-
|
21
|
+
reserve_without_controller.delete
|
22
22
|
end
|
23
23
|
while job = peek_buried do
|
24
24
|
last_conn.kick 1 # what? Why the 1? it kicks them all?
|
25
|
-
|
25
|
+
reserve_without_controller.delete
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
@@ -2,12 +2,12 @@ module Quebert
|
|
2
2
|
module Backend
|
3
3
|
# Drops jobs on an array in-process.
|
4
4
|
class InProcess < Array
|
5
|
-
def put(job
|
6
|
-
unshift
|
5
|
+
def put(job)
|
6
|
+
unshift job.to_json
|
7
7
|
end
|
8
8
|
|
9
9
|
def reserve
|
10
|
-
json = pop and
|
10
|
+
json = pop and Controller::Base.new(Job.from_json(json))
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
data/lib/quebert/backend/sync.rb
CHANGED
@@ -2,8 +2,8 @@ module Quebert
|
|
2
2
|
# The basic glue between a job and the specific queue implementation. This
|
3
3
|
# handles exceptions that may be thrown by the Job and how the Job should
|
4
4
|
# be put back on the queue, if at all.
|
5
|
-
module
|
6
|
-
autoload :Base, 'quebert/
|
7
|
-
autoload :Beanstalk, 'quebert/
|
5
|
+
module Controller
|
6
|
+
autoload :Base, 'quebert/controller/base'
|
7
|
+
autoload :Beanstalk, 'quebert/controller/beanstalk'
|
8
8
|
end
|
9
9
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Quebert
|
2
|
-
module
|
3
|
-
# The most
|
2
|
+
module Controller
|
3
|
+
# The most Controller. Doesn't even accept the queue as an argument because there's nothing
|
4
4
|
# a job can do to be rescheduled, etc.
|
5
5
|
class Base
|
6
6
|
attr_reader :job
|
@@ -11,7 +11,7 @@ module Quebert
|
|
11
11
|
|
12
12
|
def perform
|
13
13
|
begin
|
14
|
-
job.perform
|
14
|
+
job.perform!
|
15
15
|
rescue Job::Action
|
16
16
|
# Nothing to do chief!
|
17
17
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Quebert
|
2
|
-
module
|
2
|
+
module Controller
|
3
3
|
# Handle interactions between a job and a Beanstalk queue.
|
4
4
|
class Beanstalk < Base
|
5
5
|
attr_reader :beanstalk_job, :queue, :job
|
@@ -11,7 +11,7 @@ module Quebert
|
|
11
11
|
|
12
12
|
def perform
|
13
13
|
begin
|
14
|
-
result = job.perform
|
14
|
+
result = job.perform!
|
15
15
|
beanstalk_job.delete
|
16
16
|
result
|
17
17
|
rescue Job::Delete
|
data/lib/quebert/job.rb
CHANGED
@@ -12,7 +12,7 @@ module Quebert
|
|
12
12
|
Delete = Class.new(Action)
|
13
13
|
Release = Class.new(Action)
|
14
14
|
|
15
|
-
def initialize(args
|
15
|
+
def initialize(*args)
|
16
16
|
@args = args.dup.freeze
|
17
17
|
end
|
18
18
|
|
@@ -20,22 +20,22 @@ module Quebert
|
|
20
20
|
raise NotImplemented
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
# Runs the perform method that somebody else should be implementing
|
24
|
+
def perform!
|
25
|
+
perform(*args)
|
25
26
|
end
|
26
27
|
|
27
|
-
def
|
28
|
-
self.class.
|
28
|
+
def enqueue
|
29
|
+
self.class.backend.put self
|
29
30
|
end
|
30
31
|
|
31
|
-
def
|
32
|
-
|
33
|
-
JSON.generate('job' => job.name, 'args' => args)
|
32
|
+
def to_json
|
33
|
+
JSON.generate(Serializer::Job.serialize(self))
|
34
34
|
end
|
35
35
|
|
36
36
|
def self.from_json(json)
|
37
|
-
if
|
38
|
-
|
37
|
+
if hash = JSON.parse(json) and not hash.empty?
|
38
|
+
Serializer::Job.deserialize(hash)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Quebert
|
2
|
+
module Serializer
|
3
|
+
|
4
|
+
# Does this mean you could queue a job that could queue a job? Whoa!
|
5
|
+
class Job
|
6
|
+
def self.serialize(job)
|
7
|
+
{
|
8
|
+
'job' => job.class.name,
|
9
|
+
'args' => serialize_args(job.args)
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.deserialize(hash)
|
14
|
+
hash = Support.stringify_keys(hash)
|
15
|
+
Support.constantize(hash['job']).new(*deserialize_args(hash['args']))
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# Reflect on each arg and see if it has a seralizer
|
21
|
+
def self.serialize_args(args)
|
22
|
+
args.map do |arg|
|
23
|
+
hash = Hash.new
|
24
|
+
if serializer = Quebert.serializers[arg.class]
|
25
|
+
hash['serializer'] = serializer.name
|
26
|
+
hash['payload'] = serializer.serialize(arg)
|
27
|
+
else
|
28
|
+
hash['payload'] = arg
|
29
|
+
end
|
30
|
+
hash
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Find a serializer and/or push out a value
|
35
|
+
def self.deserialize_args(args)
|
36
|
+
args.map do |arg|
|
37
|
+
arg = Support.stringify_keys(arg)
|
38
|
+
if arg.key? 'serializer' and serializer = Support.constantize(arg['serializer'])
|
39
|
+
serializer.deserialize(arg['payload'])
|
40
|
+
else
|
41
|
+
arg['payload']
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Deal with converting an AR to/from a hash that we can send over the wire.
|
48
|
+
class ActiveRecord
|
49
|
+
def self.serialize(record)
|
50
|
+
attrs = record.attributes.inject({}) do |hash, (attr, val)|
|
51
|
+
hash[attr] = val
|
52
|
+
hash
|
53
|
+
end
|
54
|
+
{ 'model' => record.class.model_name, 'attributes' => attrs }
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.deserialize(hash)
|
58
|
+
hash = Support.stringify_keys(hash)
|
59
|
+
model = Support.constantize(hash.delete('model'))
|
60
|
+
if attrs = Support.stringify_keys(hash.delete('attributes'))
|
61
|
+
if id = hash.delete('id')
|
62
|
+
# This has been persisited, so just find it from the db
|
63
|
+
model.find(id)
|
64
|
+
else
|
65
|
+
# Looks like its not around! Better generate it from attributes
|
66
|
+
record = model.new
|
67
|
+
record.attributes.each do |attr, val|
|
68
|
+
record.send("#{attr}=", attrs[attr])
|
69
|
+
end
|
70
|
+
record
|
71
|
+
end
|
72
|
+
else
|
73
|
+
model.new
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
data/lib/quebert/support.rb
CHANGED
@@ -6,9 +6,9 @@ module Quebert
|
|
6
6
|
|
7
7
|
# Borrowed from Rails ActiveSupport
|
8
8
|
def self.constantize(camel_cased_word) #:nodoc:
|
9
|
-
names = camel_cased_word.split('::')
|
9
|
+
names = camel_cased_word.to_s.split('::')
|
10
10
|
names.shift if names.empty? || names.first.empty?
|
11
|
-
|
11
|
+
|
12
12
|
constant = Object
|
13
13
|
names.each do |name|
|
14
14
|
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
@@ -9,5 +9,24 @@ module Quebert
|
|
9
9
|
self.delete(key.to_sym)
|
10
10
|
end
|
11
11
|
end
|
12
|
+
|
13
|
+
# Stores classes at retreives a key for class and subclasses.
|
14
|
+
# TODO
|
15
|
+
# * make this thing match on most specific subclass
|
16
|
+
class ClassRegistry < Registry
|
17
|
+
# Returns a class from a given instance
|
18
|
+
def[](key)
|
19
|
+
case key
|
20
|
+
when Class
|
21
|
+
# Find the class key based on the class or subclass of the incoming key/klass
|
22
|
+
if klass = keys.map{|klass| Support.constantize(klass) }.find{|k| k >= key}
|
23
|
+
# If we find a matching class/subclass then pull this out
|
24
|
+
super klass.name.to_sym
|
25
|
+
end
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
12
31
|
end
|
13
32
|
end
|
data/lib/quebert/worker.rb
CHANGED
@@ -10,19 +10,26 @@ module Quebert
|
|
10
10
|
|
11
11
|
# Start the worker backend and intercept exceptions if a handler is provided
|
12
12
|
def start
|
13
|
+
Signal.trap('TERM'){ stop }
|
14
|
+
|
13
15
|
logger.info "Worker pid##{Process.pid} started with #{backend.class.name} backend"
|
14
|
-
while
|
16
|
+
while controller = backend.reserve do
|
15
17
|
begin
|
16
|
-
log
|
17
|
-
|
18
|
-
log
|
18
|
+
log controller.job, "performing with args #{controller.job.args.inspect}"
|
19
|
+
controller.perform
|
20
|
+
log controller.job, "complete"
|
19
21
|
rescue Exception => e
|
20
|
-
log
|
22
|
+
log controller.job, "fault #{e}", :error
|
21
23
|
exception_handler ? exception_handler.call(e) : raise(e)
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
28
|
+
def stop
|
29
|
+
logger.info "Worker pid##{Process.pid} stopping"
|
30
|
+
exit 0
|
31
|
+
end
|
32
|
+
|
26
33
|
protected
|
27
34
|
# Setup a bunch of stuff with Quebert config defaults the we can override later.
|
28
35
|
def logger
|
data/quebert.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{quebert}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.6"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Brad Gessler"]
|
12
|
-
s.date = %q{2010-10-
|
12
|
+
s.date = %q{2010-10-11}
|
13
13
|
s.default_executable = %q{quebert}
|
14
14
|
s.description = %q{A worker queue framework built around beanstalkd}
|
15
15
|
s.email = %q{brad@bradgessler.com}
|
@@ -39,10 +39,11 @@ Gem::Specification.new do |s|
|
|
39
39
|
"lib/quebert/backend/sync.rb",
|
40
40
|
"lib/quebert/command_line_runner.rb",
|
41
41
|
"lib/quebert/configuration.rb",
|
42
|
-
"lib/quebert/
|
43
|
-
"lib/quebert/
|
44
|
-
"lib/quebert/
|
42
|
+
"lib/quebert/controller.rb",
|
43
|
+
"lib/quebert/controller/base.rb",
|
44
|
+
"lib/quebert/controller/beanstalk.rb",
|
45
45
|
"lib/quebert/job.rb",
|
46
|
+
"lib/quebert/serializer.rb",
|
46
47
|
"lib/quebert/support.rb",
|
47
48
|
"lib/quebert/support/pid_file.rb",
|
48
49
|
"lib/quebert/support/registry.rb",
|
@@ -54,10 +55,13 @@ Gem::Specification.new do |s|
|
|
54
55
|
"spec/configuration_spec.rb",
|
55
56
|
"spec/consumer_spec.rb",
|
56
57
|
"spec/job_spec.rb",
|
57
|
-
"spec/jobs.rb",
|
58
58
|
"spec/quebert_spec.rb",
|
59
|
+
"spec/serializer_spec.rb",
|
59
60
|
"spec/spec.opts",
|
60
61
|
"spec/spec_helper.rb",
|
62
|
+
"spec/support/active_record.rb",
|
63
|
+
"spec/support/jobs.rb",
|
64
|
+
"spec/support_spec.rb",
|
61
65
|
"spec/worker_spec.rb"
|
62
66
|
]
|
63
67
|
s.homepage = %q{http://github.com/bradgessler/quebert}
|
@@ -72,9 +76,12 @@ Gem::Specification.new do |s|
|
|
72
76
|
"spec/configuration_spec.rb",
|
73
77
|
"spec/consumer_spec.rb",
|
74
78
|
"spec/job_spec.rb",
|
75
|
-
"spec/jobs.rb",
|
76
79
|
"spec/quebert_spec.rb",
|
80
|
+
"spec/serializer_spec.rb",
|
77
81
|
"spec/spec_helper.rb",
|
82
|
+
"spec/support/active_record.rb",
|
83
|
+
"spec/support/jobs.rb",
|
84
|
+
"spec/support_spec.rb",
|
78
85
|
"spec/worker_spec.rb"
|
79
86
|
]
|
80
87
|
|
data/spec/async_sender_spec.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
-
require 'active_record'
|
3
2
|
|
4
3
|
describe AsyncSender::Class do
|
5
4
|
|
6
5
|
before(:all) do
|
7
|
-
@q = Backend::InProcess.new
|
6
|
+
@q = Quebert::Backend::InProcess.new
|
8
7
|
Quebert::AsyncSender::Object::ObjectJob.backend = @q
|
9
8
|
Quebert::AsyncSender::Instance::InstanceJob.backend = @q
|
10
9
|
end
|
@@ -39,38 +38,18 @@ end
|
|
39
38
|
|
40
39
|
describe AsyncSender::ActiveRecord do
|
41
40
|
|
42
|
-
ActiveRecord::Base.establish_connection({
|
43
|
-
:adapter => 'sqlite3',
|
44
|
-
:database => ':memory:'
|
45
|
-
})
|
46
|
-
|
47
|
-
ActiveRecord::Schema.define do
|
48
|
-
create_table "users", :force => true do |t|
|
49
|
-
t.column "first_name", :text
|
50
|
-
t.column "last_name", :text
|
51
|
-
t.column "email", :text
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
class User < ActiveRecord::Base
|
56
|
-
include Quebert::AsyncSender::ActiveRecord
|
57
|
-
|
58
|
-
def name
|
59
|
-
"#{first_name} #{last_name}"
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.emailizer(address)
|
63
|
-
address
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
41
|
before(:all) do
|
42
|
+
Quebert.serializers.register :'ActiveRecord::Base', Serializer::ActiveRecord
|
43
|
+
|
68
44
|
@q = Backend::InProcess.new
|
69
|
-
Quebert::AsyncSender::ActiveRecord::
|
70
|
-
Quebert::AsyncSender::ActiveRecord::UnpersistedRecordJob.backend = @q
|
45
|
+
Quebert::AsyncSender::ActiveRecord::RecordJob.backend = @q
|
71
46
|
Quebert::AsyncSender::Object::ObjectJob.backend = @q
|
72
47
|
end
|
73
48
|
|
49
|
+
after(:all) do
|
50
|
+
Quebert.serializers.unregister :'ActiveRecord::Base'
|
51
|
+
end
|
52
|
+
|
74
53
|
context "persisted" do
|
75
54
|
before(:each) do
|
76
55
|
@user = User.create!(:first_name => 'Brad', :last_name => 'Gessler', :email => 'brad@bradgessler.com')
|
@@ -99,4 +78,5 @@ describe AsyncSender::ActiveRecord do
|
|
99
78
|
User.async_send(:emailizer, email)
|
100
79
|
@q.reserve.perform.should eql(email)
|
101
80
|
end
|
81
|
+
|
102
82
|
end
|
data/spec/backend_spec.rb
CHANGED
@@ -23,7 +23,7 @@ describe Backend::InProcess do
|
|
23
23
|
|
24
24
|
it "should put on queue" do
|
25
25
|
3.times do |num|
|
26
|
-
@q.put Adder
|
26
|
+
@q.put Adder.new(num)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -42,7 +42,7 @@ describe Backend::Beanstalk do
|
|
42
42
|
|
43
43
|
it "should put on queue" do
|
44
44
|
3.times do |num|
|
45
|
-
@q.put Adder
|
45
|
+
@q.put Adder.new(num)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -53,7 +53,6 @@ describe Backend::Beanstalk do
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
|
57
56
|
describe Backend::Sync do
|
58
57
|
before(:all) do
|
59
58
|
@q = Backend::Sync.new
|
@@ -61,7 +60,7 @@ describe Backend::Sync do
|
|
61
60
|
|
62
61
|
it "should put on queue" do
|
63
62
|
3.times do |num|
|
64
|
-
@q.put(Adder
|
63
|
+
@q.put(Adder.new(num)).should eql(num)
|
65
64
|
end
|
66
65
|
end
|
67
66
|
|
data/spec/consumer_spec.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
require 'ruby-debug'
|
3
3
|
|
4
|
-
describe
|
4
|
+
describe Controller::Base do
|
5
5
|
it "should perform job" do
|
6
|
-
|
6
|
+
Controller::Base.new(Adder.new(1,2)).perform.should eql(3)
|
7
7
|
end
|
8
8
|
|
9
9
|
it "should rescue all raised job actions" do
|
10
10
|
[ReleaseJob, DeleteJob, BuryJob].each do |job|
|
11
11
|
lambda{
|
12
|
-
|
12
|
+
Controller::Base.new(job.new).perform
|
13
13
|
}.should_not raise_exception
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
describe
|
18
|
+
describe Controller::Beanstalk do
|
19
19
|
before(:all) do
|
20
20
|
@q = Backend::Beanstalk.configure(:host => 'localhost:11300', :tube => 'quebert-test-jobs-actions')
|
21
21
|
end
|
@@ -25,14 +25,14 @@ describe Consumer::Beanstalk do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
it "should delete job off queue after succesful run" do
|
28
|
-
@q.put(
|
28
|
+
@q.put Adder.new(1, 2)
|
29
29
|
@q.peek_ready.should_not be_nil
|
30
30
|
@q.reserve.perform.should eql(3)
|
31
31
|
@q.peek_ready.should be_nil
|
32
32
|
end
|
33
33
|
|
34
34
|
it "should bury job if an exception occurs in job" do
|
35
|
-
@q.put Exceptional
|
35
|
+
@q.put Exceptional.new
|
36
36
|
@q.peek_ready.should_not be_nil
|
37
37
|
lambda{ @q.reserve.perform }.should raise_exception
|
38
38
|
@q.peek_buried.should_not be_nil
|
@@ -40,21 +40,21 @@ describe Consumer::Beanstalk do
|
|
40
40
|
|
41
41
|
context "job actions" do
|
42
42
|
it "should delete job" do
|
43
|
-
@q.put DeleteJob
|
43
|
+
@q.put DeleteJob.new
|
44
44
|
@q.peek_ready.should_not be_nil
|
45
45
|
@q.reserve.perform
|
46
46
|
@q.peek_ready.should be_nil
|
47
47
|
end
|
48
48
|
|
49
49
|
it "should release job" do
|
50
|
-
@q.put ReleaseJob
|
50
|
+
@q.put ReleaseJob.new
|
51
51
|
@q.peek_ready.should_not be_nil
|
52
52
|
@q.reserve.perform
|
53
53
|
@q.peek_ready.should_not be_nil
|
54
54
|
end
|
55
55
|
|
56
56
|
it "should bury job" do
|
57
|
-
@q.put BuryJob
|
57
|
+
@q.put BuryJob.new
|
58
58
|
@q.peek_ready.should_not be_nil
|
59
59
|
@q.peek_buried.should be_nil
|
60
60
|
@q.reserve.perform
|
data/spec/job_spec.rb
CHANGED
@@ -6,6 +6,14 @@ describe Quebert::Job do
|
|
6
6
|
Adder.backend = @q = Quebert::Backend::InProcess.new
|
7
7
|
end
|
8
8
|
|
9
|
+
it "should perform!" do
|
10
|
+
Adder.new(1,2,3).perform!.should eql(6)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should perform 0 arg jobs" do
|
14
|
+
Adder.new.perform!.should eql(0)
|
15
|
+
end
|
16
|
+
|
9
17
|
it "should raise not implemented on base job" do
|
10
18
|
lambda {
|
11
19
|
Job.new.perform
|
@@ -14,7 +22,7 @@ describe Quebert::Job do
|
|
14
22
|
|
15
23
|
it "should convert job to and from JSON" do
|
16
24
|
args = [1,2,3]
|
17
|
-
serialized = Adder.new(args).to_json
|
25
|
+
serialized = Adder.new(*args).to_json
|
18
26
|
unserialized = Adder.from_json(serialized)
|
19
27
|
unserialized.should be_instance_of(Adder)
|
20
28
|
unserialized.args.should eql(args)
|
@@ -44,7 +52,7 @@ describe Quebert::Job do
|
|
44
52
|
context "job queue" do
|
45
53
|
it "should enqueue" do
|
46
54
|
lambda{
|
47
|
-
Adder.
|
55
|
+
Adder.new(1,2,3).enqueue
|
48
56
|
}.should change(@q, :size).by(1)
|
49
57
|
end
|
50
58
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Serializer::ActiveRecord do
|
4
|
+
context "persisted" do
|
5
|
+
before(:all) do
|
6
|
+
@user = User.create!(:first_name => 'Tom', :last_name => 'Jones')
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should serialize" do
|
10
|
+
h = Serializer::ActiveRecord.serialize(@user)
|
11
|
+
h['model'].should eql('User')
|
12
|
+
h['attributes']['first_name'].should eql('Tom')
|
13
|
+
h['attributes']['id'].should eql(@user.id)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should deserialize" do
|
17
|
+
u = Serializer::ActiveRecord.deserialize(Serializer::ActiveRecord.serialize(@user))
|
18
|
+
u.first_name.should eql('Tom')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context "unpersisted" do
|
23
|
+
before(:all) do
|
24
|
+
@user = User.new(:first_name => 'brad')
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should serialize" do
|
28
|
+
h = Serializer::ActiveRecord.serialize(@user)
|
29
|
+
h['model'].should eql('User')
|
30
|
+
h['attributes']['first_name'].should eql('brad')
|
31
|
+
h['attributes']['id'].should be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should deserialize" do
|
35
|
+
u = Serializer::ActiveRecord.deserialize(Serializer::ActiveRecord.serialize(@user))
|
36
|
+
u.first_name.should eql('brad')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe Serializer::Job do
|
42
|
+
before(:all) do
|
43
|
+
@args = [100, User.new(:first_name => 'Brad')]
|
44
|
+
@job = Job.new(*@args)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should serialize job" do
|
48
|
+
h = Serializer::Job.serialize(@job)
|
49
|
+
h['job'].should eql('Quebert::Job')
|
50
|
+
h['args'][0]['payload'].should eql(100)
|
51
|
+
h['args'][1]['payload'].should eql(Serializer::ActiveRecord.serialize(@args[1]))
|
52
|
+
h['args'][1]['serializer'].should eql('Quebert::Serializer::ActiveRecord')
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should deserialize job" do
|
56
|
+
job = Serializer::Job.deserialize(Serializer::Job.serialize(@job))
|
57
|
+
job.args[0].should eql(100)
|
58
|
+
job.args[1].first_name.should eql('Brad')
|
59
|
+
end
|
60
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -15,8 +15,6 @@ end
|
|
15
15
|
include Quebert
|
16
16
|
Quebert.config.logger = Logger.new('/dev/null') # Shhh...
|
17
17
|
|
18
|
-
require 'jobs'
|
19
|
-
|
20
18
|
def clean_file(path, contents=nil, &block)
|
21
19
|
FileUtils.remove_entry(path) if File.exists?(path)
|
22
20
|
FileUtils.mkdir_p(File.dirname(path))
|
@@ -26,4 +24,8 @@ def clean_file(path, contents=nil, &block)
|
|
26
24
|
ensure
|
27
25
|
FileUtils.remove_entry(path) if File.exists?(path) and path != './' # Yeah! This has happened before :(
|
28
26
|
end
|
29
|
-
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Dir[File.join(File.dirname(__FILE__), 'support/*.rb')].each {|file| require file }
|
30
|
+
|
31
|
+
Quebert.serializers.register 'ActiveRecord::Base', Serializer::ActiveRecord
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection({
|
4
|
+
:adapter => 'sqlite3',
|
5
|
+
:database => ':memory:'
|
6
|
+
})
|
7
|
+
|
8
|
+
ActiveRecord::Schema.define do
|
9
|
+
create_table "users", :force => true do |t|
|
10
|
+
t.column "first_name", :text
|
11
|
+
t.column "last_name", :text
|
12
|
+
t.column "email", :text
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class User < ActiveRecord::Base
|
17
|
+
include Quebert::AsyncSender::ActiveRecord
|
18
|
+
|
19
|
+
def name
|
20
|
+
"#{first_name} #{last_name}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.emailizer(address)
|
24
|
+
address
|
25
|
+
end
|
26
|
+
end
|
File without changes
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Support::ClassRegistry do
|
4
|
+
Super = Class.new
|
5
|
+
Sub = Class.new(Super)
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
@registry = Support::ClassRegistry.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should store class symbols" do
|
12
|
+
@registry[:'Super'] = Super
|
13
|
+
@registry[:'Super'].should eql(Super)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should retrieve class keys" do
|
17
|
+
@registry[Super].should eql(Super)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should retrieve subclass keys" do
|
21
|
+
@registry[Sub].should eql(Super)
|
22
|
+
end
|
23
|
+
end
|
data/spec/worker_spec.rb
CHANGED
@@ -14,19 +14,19 @@ describe Worker do
|
|
14
14
|
|
15
15
|
context "pluggable exception handler" do
|
16
16
|
it "should raise exception if nothing is provided" do
|
17
|
-
@q.put Exceptional
|
17
|
+
@q.put Exceptional.new
|
18
18
|
lambda{ @w.start }.should raise_exception
|
19
19
|
end
|
20
20
|
|
21
21
|
it "should default to Quebert.config.worker.exception_handler handler" do
|
22
|
-
@q.put Exceptional
|
22
|
+
@q.put Exceptional.new
|
23
23
|
Quebert.config.worker.exception_handler = Proc.new{|e| e.should be_instance_of(Exception) }
|
24
24
|
@w.exception_handler = Proc.new{|e| e.should be_instance_of(Exception) }
|
25
25
|
lambda{ @w.start }.should_not raise_exception
|
26
26
|
end
|
27
27
|
|
28
28
|
it "should intercept exceptions" do
|
29
|
-
@q.put Exceptional
|
29
|
+
@q.put Exceptional.new
|
30
30
|
@w.exception_handler = Proc.new{|e| e.should be_instance_of(Exception) }
|
31
31
|
lambda{ @w.start }.should_not raise_exception
|
32
32
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quebert
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 6
|
10
|
+
version: 0.0.6
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Brad Gessler
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-10-
|
18
|
+
date: 2010-10-11 00:00:00 -07:00
|
19
19
|
default_executable: quebert
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -92,10 +92,11 @@ files:
|
|
92
92
|
- lib/quebert/backend/sync.rb
|
93
93
|
- lib/quebert/command_line_runner.rb
|
94
94
|
- lib/quebert/configuration.rb
|
95
|
-
- lib/quebert/
|
96
|
-
- lib/quebert/
|
97
|
-
- lib/quebert/
|
95
|
+
- lib/quebert/controller.rb
|
96
|
+
- lib/quebert/controller/base.rb
|
97
|
+
- lib/quebert/controller/beanstalk.rb
|
98
98
|
- lib/quebert/job.rb
|
99
|
+
- lib/quebert/serializer.rb
|
99
100
|
- lib/quebert/support.rb
|
100
101
|
- lib/quebert/support/pid_file.rb
|
101
102
|
- lib/quebert/support/registry.rb
|
@@ -107,10 +108,13 @@ files:
|
|
107
108
|
- spec/configuration_spec.rb
|
108
109
|
- spec/consumer_spec.rb
|
109
110
|
- spec/job_spec.rb
|
110
|
-
- spec/jobs.rb
|
111
111
|
- spec/quebert_spec.rb
|
112
|
+
- spec/serializer_spec.rb
|
112
113
|
- spec/spec.opts
|
113
114
|
- spec/spec_helper.rb
|
115
|
+
- spec/support/active_record.rb
|
116
|
+
- spec/support/jobs.rb
|
117
|
+
- spec/support_spec.rb
|
114
118
|
- spec/worker_spec.rb
|
115
119
|
has_rdoc: true
|
116
120
|
homepage: http://github.com/bradgessler/quebert
|
@@ -153,7 +157,10 @@ test_files:
|
|
153
157
|
- spec/configuration_spec.rb
|
154
158
|
- spec/consumer_spec.rb
|
155
159
|
- spec/job_spec.rb
|
156
|
-
- spec/jobs.rb
|
157
160
|
- spec/quebert_spec.rb
|
161
|
+
- spec/serializer_spec.rb
|
158
162
|
- spec/spec_helper.rb
|
163
|
+
- spec/support/active_record.rb
|
164
|
+
- spec/support/jobs.rb
|
165
|
+
- spec/support_spec.rb
|
159
166
|
- spec/worker_spec.rb
|