quebert 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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