Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

constantize objects when deserializing so that ActiveSupport can atte…

…mpt its auto loading magic

Closes #65
  • Loading branch information...
commit 4c53af86ee22890e760e780dcc9a9adc33d5aa6f 1 parent d71cd25
@bkeepers bkeepers authored
View
35 lib/delayed/backend/base.rb
@@ -50,11 +50,14 @@ def name
end
def payload_object=(object)
- self['handler'] = object.to_yaml
+ self.handler = object.to_yaml
end
def payload_object
- @payload_object ||= deserialize(self['handler'])
+ @payload_object ||= YAML.load(self.handler)
+ rescue TypeError, LoadError, NameError => e
+ raise DeserializationError,
+ "Job failed to load: #{e.message}. Try to manually require the required file. Handler: #{handler.inspect}"
end
# Moved into its own method so that new_relic can trace it.
@@ -68,34 +71,6 @@ def unlock
self.locked_by = nil
end
- private
-
- def deserialize(source)
- handler = YAML.load(source) rescue nil
-
- unless handler.respond_to?(:perform)
- if handler.nil? && source =~ ParseObjectFromYaml
- handler_class = $1
- end
- attempt_to_load(handler_class || handler.class)
- handler = YAML.load(source)
- end
-
- return handler if handler.respond_to?(:perform)
-
- raise DeserializationError,
- 'Job failed to load: Unknown handler. Try to manually require the appropriate file.'
- rescue TypeError, LoadError, NameError => e
- raise DeserializationError,
- "Job failed to load: #{e.message}. Try to manually require the required file."
- end
-
- # Constantize the object so that ActiveSupport can attempt
- # its auto loading magic. Will raise LoadError if not successful.
- def attempt_to_load(klass)
- klass.constantize
- end
-
protected
def set_default_run_at
View
37 lib/delayed/class_to_yaml.rb
@@ -1,37 +0,0 @@
-require 'yaml'
-
-class Module
- yaml_as "tag:ruby.yaml.org,2002:module"
-
- def Module.yaml_new( klass, tag, val )
- if String === val
- val.split(/::/).inject(Object) {|m, n| m.const_get(n)}
- else
- raise YAML::TypeError, "Invalid Module: " + val.inspect
- end
- end
-
- def to_yaml( opts = {} )
- YAML::quick_emit( nil, opts ) { |out|
- out.scalar( "tag:ruby.yaml.org,2002:module", self.name, :plain )
- }
- end
-end
-
-class Class
- yaml_as "tag:ruby.yaml.org,2002:class"
-
- def Class.yaml_new( klass, tag, val )
- if String === val
- val.split(/::/).inject(Object) {|m, n| m.const_get(n)}
- else
- raise YAML::TypeError, "Invalid Class: " + val.inspect
- end
- end
-
- def to_yaml( opts = {} )
- YAML::quick_emit( nil, opts ) { |out|
- out.scalar( "tag:ruby.yaml.org,2002:class", self.name, :plain )
- }
- end
-end
View
40 lib/delayed/yaml_ext.rb
@@ -0,0 +1,40 @@
+# These extensions allow properly serializing and autoloading of
+# Classes, Modules and Structs
+
+require 'yaml'
+
+class Module
+ yaml_as "tag:ruby.yaml.org,2002:module"
+
+ def self.yaml_new(klass, tag, val)
+ val.constantize
+ end
+
+ def to_yaml( opts = {} )
+ YAML::quick_emit( nil, opts ) { |out|
+ out.scalar(taguri, self.name, :plain)
+ }
+ end
+
+ def yaml_tag_read_class(name)
+ # Constantize the object so that ActiveSupport can attempt
+ # its auto loading magic. Will raise LoadError if not successful.
+ name.constantize
+ name
+ end
+
+end
+
+class Class
+ yaml_as "tag:ruby.yaml.org,2002:class"
+ remove_method :to_yaml # use Module's to_yaml
+end
+
+class Struct
+ def self.yaml_tag_read_class(name)
+ # Constantize the object so that ActiveSupport can attempt
+ # its auto loading magic. Will raise LoadError if not successful.
+ name.constantize
+ "Struct::#{ name }"
+ end
+end
View
2  lib/delayed_job.rb
@@ -2,7 +2,7 @@
require File.dirname(__FILE__) + '/delayed/message_sending'
require File.dirname(__FILE__) + '/delayed/performable_method'
-require File.dirname(__FILE__) + '/delayed/class_to_yaml'
+require File.dirname(__FILE__) + '/delayed/yaml_ext'
require File.dirname(__FILE__) + '/delayed/backend/base'
require File.dirname(__FILE__) + '/delayed/worker'
require File.dirname(__FILE__) + '/delayed/railtie' if defined?(::Rails::Railtie)
View
7 spec/autoloaded/clazz.rb
@@ -0,0 +1,7 @@
+# Make sure this file does not get required manually
+module Autoloaded
+ class Clazz
+ def perform
+ end
+ end
+end
View
7 spec/autoloaded/struct.rb
@@ -0,0 +1,7 @@
+# Make sure this file does not get required manually
+module Autoloaded
+ class Struct < ::Struct.new(nil)
+ def perform
+ end
+ end
+end
View
31 spec/backend/shared_backend_spec.rb
@@ -53,31 +53,22 @@ def create_job(opts = {})
describe "payload_object" do
it "should raise a DeserializationError when the job class is totally unknown" do
job = @backend.new :handler => "--- !ruby/object:JobThatDoesNotExist {}"
- lambda { job.payload_object.perform }.should raise_error(Delayed::Backend::DeserializationError)
- end
-
- it "should try to load the class when it is unknown at the time of the deserialization" do
- job = @backend.new :handler => "--- !ruby/object:JobThatDoesNotExist {}"
- job.should_receive(:attempt_to_load).with('JobThatDoesNotExist').and_return(true)
- lambda { job.payload_object.perform }.should raise_error(Delayed::Backend::DeserializationError)
+ lambda { job.payload_object }.should raise_error(Delayed::Backend::DeserializationError)
end
- it "should try include the namespace when loading unknown objects" do
- job = @backend.new :handler => "--- !ruby/object:Delayed::JobThatDoesNotExist {}"
- job.should_receive(:attempt_to_load).with('Delayed::JobThatDoesNotExist').and_return(true)
- lambda { job.payload_object.perform }.should raise_error(Delayed::Backend::DeserializationError)
+ it "should raise a DeserializationError when the job struct is totally unknown" do
+ job = @backend.new :handler => "--- !ruby/struct:StructThatDoesNotExist {}"
+ lambda { job.payload_object }.should raise_error(Delayed::Backend::DeserializationError)
end
-
- it "should also try to load structs when they are unknown (raises TypeError)" do
- job = @backend.new :handler => "--- !ruby/struct:JobThatDoesNotExist {}"
- job.should_receive(:attempt_to_load).with('JobThatDoesNotExist').and_return(true)
- lambda { job.payload_object.perform }.should raise_error(Delayed::Backend::DeserializationError)
+
+ it "should autoload classes that are unknown at runtime" do
+ job = @backend.new :handler => "--- !ruby/object:Autoloaded::Clazz {}"
+ lambda { job.payload_object }.should_not raise_error(Delayed::Backend::DeserializationError)
end
- it "should try include the namespace when loading unknown structs" do
- job = @backend.new :handler => "--- !ruby/struct:Delayed::JobThatDoesNotExist {}"
- job.should_receive(:attempt_to_load).with('Delayed::JobThatDoesNotExist').and_return(true)
- lambda { job.payload_object.perform }.should raise_error(Delayed::Backend::DeserializationError)
+ it "should autoload structs that are unknown at runtime" do
+ job = @backend.new :handler => "--- !ruby/struct:Autoloaded::Struct {}"
+ lambda { job.payload_object }.should_not raise_error(Delayed::Backend::DeserializationError)
end
end
View
3  spec/spec_helper.rb
@@ -26,3 +26,6 @@
end
Delayed::Worker.backend = BACKENDS.first
+
+# Add this directory so the ActiveSupport autoloading works
+ActiveSupport::Dependencies.load_paths << File.dirname(__FILE__)
Please sign in to comment.
Something went wrong with that request. Please try again.