Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

YAML can't properly deserialize Mongoid instances with embedded objects #187

Closed
scottburton11 opened this Issue Jan 10, 2011 · 4 comments

Comments

Projects
None yet
6 participants

Here's something I noticed working with Delayed Job and Mongoid instances, and I believe it fits into the "YAML can't deserialize this" camp, for whatever reason.

When I have a class with embedded documents and create a delayed job in a callback of any kind, I wind up with a handler that YAML can't deserialize:

class Foobatz
 include Mongoid::Document
 field :name, :type => String
 before_save :do_something_awesome_later

 embeds_many :monkey_beavers

 def do_something_awesome_later
   delay.do_something_awesome
 end

 private

 def do_something_awesome
   # Something awesome happens here
 end

end


class MonkeyBeaver
 include Mongoid::Document
 field :name, :type => String
 field :size, :type => String

 embedded_in :foobatz, :inverse_of => :monkey_beavers
end

Calling #do_something_awesome_later on an object right out of the database works fine:

f  = Foobatz.last
=> #<Foobatz _id: 4d2a561c44939bb67f000001, name: "Horkable", awesome: true> 
f.do_something_awesome_later
=> #<Delayed::Backend::ActiveRecord::Job id: 812, priority: 0, attempts: 0, handler: "--- !ruby/struct:Delayed::PerformableMethod \nobject...", last_error: nil, run_at: "2011-01-10 01:00:40", locked_at: nil, failed_at: nil, locked_by: nil, created_at: "2011-01-10 01:00:40", updated_at: "2011-01-10 01:00:40"> 
Delayed::Job.last.payload_object
=> #<struct Delayed::PerformableMethod object=#<Foobatz _id: 4d2a561c44939bb67f000001, name: "Horkable", awesome: true>, method_name=:do_something_awesome, args=[]> 

but creating the job using the #before_save callback adds something to the serialized job handler that YAML can't handle:

f.save
=> true 
Delayed::Job.last.payload_object
Delayed::DeserializationError: Job failed to load: uninitialized constant Syck::Syck. Handler: "--- !ruby/struct:Delayed::PerformableMethod \nobject: &id002 !ruby/object:Foobatz \n  accessed: {}\n\n  attributes: \n    _id: !ruby/object:BSON::ObjectId \n      data: \n      - 77\n      - 42\n      - 86\n      - 28\n      - 68\n      - 147\n      - 155\n      - 182\n      - 127\n      - 0\n      - 0\n      - 1\n    awesome: true\n    monkey_beavers: \n    - !map:BSON::OrderedHash \n      _id: &id001 !ruby/object:BSON::ObjectId \n        data: \n        - 77\n        - 42\n        - 86\n        - 68\n        - 68\n        - 147\n        - 155\n        - 182\n        - 127\n        - 0\n        - 0\n        - 2\n      name: Clarance\n      size: \"22\"\n    name: Horkable\n  errors: !omap []\n\n  modifications: {}\n\n  monkey_beavers: \n  - !ruby/object:MonkeyBeaver \n    _index: 0\n    _parent: *id002\n    accessed: {}\n\n    attributes: \n      _id: *id001\n      name: Clarance\n      size: \"22\"\n    errors: !omap []\n\n    metadata: !map:Mongoid::Relations::Metadata \n      :relation: !ruby/class Mongoid::Relations::Embedded::Many\n      :extend: \n      :inverse_class_name: Foobatz\n      :name: :monkey_beavers\n    modifications: {}\n\n    previous_modifications: {}\n\n    validation_context: \n  previous_modifications: {}\n\n  validation_context: \nmethod_name: :do_something_awesome\nargs: []\n\n"
from /Users/scott/.rvm/gems/ruby-1.9.2-p0/gems/delayed_job-2.1.2/lib/delayed/backend/base.rb:77:in 'rescue in payload_object'
from /Users/scott/.rvm/gems/ruby-1.9.2-p0/gems/delayed_job-2.1.2/lib/delayed/backend/base.rb:75:in 'payload_object'
from (irb):8
from /Users/scott/.rvm/gems/ruby-1.9.2-p0/gems/railties-3.0.3/lib/rails/commands/console.rb:44:in 'start'
from /Users/scott/.rvm/gems/ruby-1.9.2-p0/gems/railties-3.0.3/lib/rails/commands/console.rb:8:in 'start'
from /Users/scott/.rvm/gems/ruby-1.9.2-p0/gems/railties-3.0.3/lib/rails/commands.rb:23:in '<top (required)>'
from script/rails:6:in 'require'
from script/rails:6:in '<main>'

I'm not entirely sure what Syck has to do with it. Incidentally, if the object instead has a relation, or a memoized method, DelayedJob rescues a different error:
Delayed::DeserializationError: Job failed to load: undefined method `fields' for nil:NilClass.

The YAML-ized handler in the failing (callback-initiated) version has a BSON::OrderedHash for the embedded collection.

I know these are YAML issues, not DelayedJob issues, but I'm curious to know if I can implement a callback-initiated job in some other way. One alternative was to simply re-instantiate the object (using #find, for example) - in which case the handler YAML doesn't contain a reference to the embedded collection or anything else fishy - but I don't like having to resort to the extra queries.

Any ideas?

rubish commented Mar 14, 2011

I also faced the same problem. Did you found any workaround for it?

I have the same for the Couchrest casted properties.

You need to override the find method of your embedded document to helps DelayedJob to deserialize it.
You can use this gist, which also supports multiple nesting of documents: https://gist.github.com/cblavier/7889042

noniq commented Mar 5, 2014

I had the same problem – @cblavier’s gist solved it for me. Any chance of getting that merged into delayed_job or delayed_job_mongoid?

@albus522 albus522 closed this Sep 24, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment