YAML dump Class and ActiveRecord::Base objects natively, refs #1
This fixes a long-standing issue with not being able to properly serialize AR objects that are in arrays or other data structures. Now that we're using native YAML methods for doing the serialization, rather than dumping specially-formatted "LOAD;N" strings, it works properly. I've left the load_for_delayed_job stuff in for now, for backwards compatibility with jobs already in the queue. Change-Id: I109f364b91c4becc8fc17ea1d9f041eb3c18281f Reviewed-on: https://gerrit.instructure.com/2389 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Bracken Mosbacker <bracken@instructure.com>
This commit is contained in:
parent
2f9e0088fc
commit
b86fb7fd83
|
@ -8,13 +8,6 @@ class ActiveRecord::Base
|
|||
super
|
||||
end
|
||||
end
|
||||
|
||||
def dump_for_delayed_job
|
||||
if id.nil?
|
||||
raise("Can't serialize unsaved ActiveRecord object for delayed job: #{self.inspect}")
|
||||
end
|
||||
"#{self.class};#{id}"
|
||||
end
|
||||
end
|
||||
|
||||
module Delayed
|
||||
|
|
|
@ -72,13 +72,13 @@ module Delayed
|
|||
|
||||
def reschedule_at
|
||||
new_time = self.class.db_time_now + (attempts ** 4) + 5
|
||||
if payload_object.respond_to?(:reschedule_at)
|
||||
begin
|
||||
begin
|
||||
if payload_object.respond_to?(:reschedule_at)
|
||||
new_time = payload_object.reschedule_at(
|
||||
self.class.db_time_now, attempts)
|
||||
rescue
|
||||
# TODO: just swallow errors from reschedule_at ?
|
||||
end
|
||||
rescue
|
||||
# TODO: just swallow errors from reschedule_at ?
|
||||
end
|
||||
new_time
|
||||
end
|
||||
|
|
|
@ -1,10 +1,32 @@
|
|||
YAML.add_ruby_type("object:Class") do |type, val|
|
||||
val.constantize
|
||||
end
|
||||
|
||||
class Class
|
||||
def to_yaml(opts = {})
|
||||
YAML.quick_emit(self.object_id, opts) do |out|
|
||||
out.scalar(taguri, name)
|
||||
end
|
||||
end
|
||||
|
||||
def load_for_delayed_job(arg)
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
def dump_for_delayed_job
|
||||
name
|
||||
class ActiveRecord::Base
|
||||
yaml_as "tag:ruby.yaml.org,2002:ActiveRecord"
|
||||
|
||||
def to_yaml(opts = {})
|
||||
YAML.quick_emit(self.object_id, opts) do |out|
|
||||
out.scalar(taguri, id.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def self.yaml_new(klass, tag, val)
|
||||
klass.find(val)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
raise Delayed::Backend::DeserializationError, "Couldn't find #{klass} with id #{val.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -31,16 +53,14 @@ module Delayed
|
|||
self.args = args.map { |a| dump(a) }
|
||||
self.method = method.to_sym
|
||||
|
||||
if object.is_a?(Module)
|
||||
self.tag = "#{object.name}.#{method}"
|
||||
else
|
||||
self.tag = "#{object.class}##{method}"
|
||||
end
|
||||
self.tag = display_name
|
||||
end
|
||||
|
||||
def display_name
|
||||
if STRING_FORMAT === object
|
||||
"#{$1}#{$2 ? '#' : '.'}#{method}"
|
||||
elsif object.is_a?(Module)
|
||||
"#{object}.#{method}"
|
||||
else
|
||||
"#{object.class}##{method}"
|
||||
end
|
||||
|
@ -68,11 +88,7 @@ module Delayed
|
|||
end
|
||||
|
||||
def dump(obj)
|
||||
if obj.respond_to?(:dump_for_delayed_job)
|
||||
"LOAD;#{obj.dump_for_delayed_job}"
|
||||
else
|
||||
obj
|
||||
end
|
||||
obj
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,18 +8,18 @@ end
|
|||
|
||||
describe Delayed::PerformableMethod do
|
||||
|
||||
it "should NOT FREAKING ignore ActiveRecord::RecordNotFound errors because they are NOT ALWAYS permanent" do
|
||||
it "should not ignore ActiveRecord::RecordNotFound errors because they are not always permanent" do
|
||||
story = Story.create :text => 'Once upon...'
|
||||
p = Delayed::PerformableMethod.new(story, :tell, [])
|
||||
story.destroy
|
||||
lambda { p.perform }.should raise_error
|
||||
lambda { YAML.load(p.to_yaml) }.should raise_error
|
||||
end
|
||||
|
||||
it "should store the object as string if its an active record" do
|
||||
it "should store the object using native YAML even if its an active record" do
|
||||
story = Story.create :text => 'Once upon...'
|
||||
p = Delayed::PerformableMethod.new(story, :tell, [])
|
||||
p.class.should == Delayed::PerformableMethod
|
||||
p.object.should == "LOAD;Story;#{story.id}"
|
||||
p.object.should == story
|
||||
p.method.should == :tell
|
||||
p.args.should == []
|
||||
p.perform.should == 'Once upon...'
|
||||
|
@ -30,13 +30,13 @@ describe Delayed::PerformableMethod do
|
|||
lambda { p.send(:load, p.object) }.should_not raise_error
|
||||
end
|
||||
|
||||
it "should store arguments as string if they are active record objects" do
|
||||
it "should store arguments as native YAML if they are active record objects" do
|
||||
story = Story.create :text => 'Once upon...'
|
||||
reader = StoryReader.new
|
||||
p = Delayed::PerformableMethod.new(reader, :read, [story])
|
||||
p.class.should == Delayed::PerformableMethod
|
||||
p.method.should == :read
|
||||
p.args.should == ["LOAD;Story;#{story.id}"]
|
||||
p.args.should == [story]
|
||||
p.perform.should == 'Epilog: Once upon...'
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue