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 CHANGED
@@ -9,4 +9,5 @@ group :test do
9
9
  gem 'ruby-debug'
10
10
  gem 'activerecord'
11
11
  gem 'sqlite3-ruby'
12
+ gem 'autotest-fsevent'
12
13
  end
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
- * [x] Multiple back-ends (InProcess, Sync, and Beanstalk)
10
- * [ ] Rails/ActiveRecord integration similar to async_observer
11
- * [ ] Pluggable exception handling (for Hoptoad integration)
12
- * [ ] Reliable daemonization with pidfile support
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
- [x] = Completed, [ ] = Not Started
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, 1, 2, 3
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.enqueue 4, 5, 6
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.4
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 :Consumer, 'quebert/consumer'
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, Backend::Beanstalk
30
- Quebert.backends.register :in_process, Backend::InProcess
31
- Quebert.backends.register :sync, Backend::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
- # Reference an AR with the pk
6
- class PersistedRecordJob < Job
7
- def perform(model_name, pk, meth, args)
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(:extend, ClassMethods)
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
- if self.new_record?
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
@@ -44,7 +44,7 @@ module Quebert
44
44
  end
45
45
 
46
46
  def async_send(meth, *args)
47
- InstanceJob.enqueue(self.class.name, @_init_args, meth, args)
47
+ InstanceJob.new(self.class.name, @_init_args, meth, args).enqueue
48
48
  end
49
49
  end
50
50
  end
@@ -14,7 +14,7 @@ module Quebert
14
14
 
15
15
  module ClassMethods
16
16
  def async_send(meth, *args)
17
- ObjectJob.enqueue(self.name, meth, args)
17
+ ObjectJob.new(self.name, meth, args).enqueue
18
18
  end
19
19
  end
20
20
  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, *args)
9
- super Job.to_json(job, *args)
8
+ def put(job)
9
+ super job.to_json
10
10
  end
11
11
 
12
- def reserve_with_consumer
13
- Consumer::Beanstalk.new(reserve_without_consumer, self)
12
+ def reserve_with_controller
13
+ Controller::Beanstalk.new(reserve_without_controller, self)
14
14
  end
15
- alias :reserve_without_consumer :reserve
16
- alias :reserve :reserve_with_consumer
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
- reserve_without_consumer.delete
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
- reserve_without_consumer.delete
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, *args)
6
- unshift Job.to_json(job, *args)
5
+ def put(job)
6
+ unshift job.to_json
7
7
  end
8
8
 
9
9
  def reserve
10
- json = pop and Consumer::Base.new(Job.from_json(json))
10
+ json = pop and Controller::Base.new(Job.from_json(json))
11
11
  end
12
12
  end
13
13
  end
@@ -4,7 +4,7 @@ module Quebert
4
4
  # or could be used as a fallback if other backends fail to initialize
5
5
  class Sync
6
6
  def put(job, *args)
7
- Consumer::Base.new(job.new(args)).perform
7
+ Controller::Base.new(job).perform
8
8
  end
9
9
  end
10
10
  end
@@ -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 Consumer
6
- autoload :Base, 'quebert/consumer/base'
7
- autoload :Beanstalk, 'quebert/consumer/beanstalk'
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 Consumer
3
- # The most Consumer. Doesn't even accept the queue as an argument because there's nothing
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(*job.args)
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 Consumer
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(*job.args)
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
- def self.enqueue(*args)
24
- backend.put(self, *args)
23
+ # Runs the perform method that somebody else should be implementing
24
+ def perform!
25
+ perform(*args)
25
26
  end
26
27
 
27
- def to_json
28
- self.class.to_json(self)
28
+ def enqueue
29
+ self.class.backend.put self
29
30
  end
30
31
 
31
- def self.to_json(job, *args)
32
- args, job = job.args, job.class if job.respond_to?(:args)
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 data = JSON.parse(json)
38
- Support.constantize(data['job']).new(data['args'])
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
@@ -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
@@ -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 consumer = backend.reserve do
16
+ while controller = backend.reserve do
15
17
  begin
16
- log consumer.job, "performing with args #{consumer.job.args.inspect}"
17
- consumer.perform
18
- log consumer.job, "complete"
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 consumer.job, "fault #{e}", :error
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.4"
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-05}
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/consumer.rb",
43
- "lib/quebert/consumer/base.rb",
44
- "lib/quebert/consumer/beanstalk.rb",
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
 
@@ -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::PersistedRecordJob.backend = @q
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, num
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, num
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, num).should eql(num)
63
+ @q.put(Adder.new(num)).should eql(num)
65
64
  end
66
65
  end
67
66
 
@@ -1,21 +1,21 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
  require 'ruby-debug'
3
3
 
4
- describe Consumer::Base do
4
+ describe Controller::Base do
5
5
  it "should perform job" do
6
- Consumer::Base.new(Adder.new([1,2])).perform.should eql(3)
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
- Consumer::Base.new(job.new).perform
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 Consumer::Beanstalk do
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(Adder, 1, 2)
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.enqueue(1,2,3)
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: 23
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 4
10
- version: 0.0.4
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-05 00:00:00 -07:00
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/consumer.rb
96
- - lib/quebert/consumer/base.rb
97
- - lib/quebert/consumer/beanstalk.rb
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