Unexpected behavior with YAML

Hi, I'm trying to figure out why I can't dump and load a ActiveRecord object with YAML.

Please see the gist below for how to replicate, and the exact error I'm getting.

Basically I create a new project, new database, and new model.

Create an instance of the model, call to_yaml on it, store that, and then try and load an object from that.

This general idea, of reconstituting a model from it's YAML representation works in some of my projects, but not in any new ones.

I have no idea why this would be the case.

Changing the type of the yaml dump from !ruby/object:Dummy to !ruby/ActiveRecord:Dummy helps a little bit, but still returns something weird.

ruby-1.9.2-p0 > tom => "--- !ruby/object:Dummy \nattributes: \n id: 1\n name: Tim\n created_at: 2011-01-27 21:12:33.327345\n updated_at: 2011-01-27 21:12:33.327345\nattributes_cache: {}\n\nchanged_attributes: {}\n \ndestroyed: false\nmarked_for_destruction: false\npersisted: true \npreviously_changed: {}\n\nreadonly: false\n" ruby-1.9.2-p0 > bill = tom.gsub("object", 'ActiveRecord') => "--- !ruby/ActiveRecord:Dummy \nattributes: \n id: 1\n name: Tim \n created_at: 2011-01-27 21:12:33.327345\n updated_at: 2011-01-27 21:12:33.327345\nattributes_cache: {}\n\nchanged_attributes: {}\n \ndestroyed: false\nmarked_for_destruction: false\npersisted: true \npreviously_changed: {}\n\nreadonly: false\n" ruby-1.9.2-p0 > YAML.load(bill) => #<Syck::DomainType:0x00000100c634b8 @domain="ruby.yaml.org,2002", @type_id="ActiveRecord:Dummy", @value={"attributes"=>{"id"=>1, "name"=>"Tim", "created_at"=>"2011-01-27 21:12:33.327345", "updated_at"=>"2011-01-27 21:12:33.327345"}, "attributes_cache"=>{}, "changed_attributes"=>{}, "destroyed"=>false, "marked_for_destruction"=>false, "persisted"=>true, "previously_changed"=>{}, "readonly"=>false}>

bob wrote in post #977980:

Hi, I'm trying to figure out why I can't dump and load a ActiveRecord object with YAML.

[...]

Er, why? You already have a serialized representation of it in the database...or are you trying to do some sort of backup and restore (in which case you might want to look at yaml_db)?

Best,

Hi, I'm trying to figure out why I can't dump and load a ActiveRecord object with YAML.

Not sure about your error, but you could do...

tom = Dummy.new(:name => 'Tom') yml = toml.attributes.to_yaml new_tom = Dummy.new(YAML.load(yml))

or something similar...

This general idea, of reconstituting a model from it's YAML representation works in some of my projects, but not in any new ones.

I have no idea why this would be the case.

At a guess it looks like during the yaml loading process it calls (or causes to be called) respond_to?, however respond_to for an AR object depends on the internal state (because of the dynamically generated methods) of the object, and respond_to is being called when that state is inconsistent (I'd guess that @attributes hasn't been restored yet, so is nil). Changing Dummy to ActiveRecord:Dummy changes things because yaml no longer finds your class and so it doesn't get AR's respond_to (what you change it to is probably irrelevant, as long as you don't change it to some other AR class. I seem to recall that historically, although method_missing etc was overriden to add dynamic methods, respond_to wasn't, which didn't make AR a very good citizen, which may be why this works with projects using old versions of rails

Fred

Frederick Cheung wrote in post #978021: [...]

I seem to recall that historically, although method_missing etc was overriden to add dynamic methods, respond_to wasn't, which didn't make AR a very good citizen, which may be why this works with projects using old versions of rails

How is that possible? respond_to? shouldn't need to be overridden to take method_missing into account, should it?

Fred

Best,

It seems to me like you ought to be able to call .to_yaml on a activerecord object and then use YAML.load to reconstitute it.

I'm trying to figure out if something is wrong with my ruby / rails installation or if this is something that everyone experiences.

Can anyone else verify that after creating a simple new rails project and model, an instance of the model cannot make a round trip to_yaml and back?

Frederick Cheung wrote in post #978021: [...]

> I seem to recall that > historically, although method_missing etc was overriden to add dynamic > methods, respond_to wasn't, which didn't make AR a very good citizen, > which may be why this works with projects using old versions of rails

How is that possible? respond_to? shouldn't need to be overridden to take method_missing into account, should it?

The default implementation of respond_to? doesn't know that although a method (like a attribute accessor) doesn't exist yet, if you were to call it then method_missing would create it, so respond_to would return false, even though you would be able to call the method. Need is a vague word. At a basic level you don't need to override respond_to, however there was a strong enough feeling that this wasn't consistent, especially as you'd do things lile

foo.respond_to? :name #=> false foo.name #=> 'Bob' foo.respond_to? :name #=> true

Fred

Frederick Cheung wrote in post #978098:

Frederick Cheung wrote in post #978021: [...]

> I seem to recall that > historically, although method_missing etc was overriden to add dynamic > methods, respond_to wasn't, which didn't make AR a very good citizen, > which may be why this works with projects using old versions of rails

How is that possible? respond_to? shouldn't need to be overridden to take method_missing into account, should it?

The default implementation of respond_to? doesn't know that although a method (like a attribute accessor) doesn't exist yet, if you were to call it then method_missing would create it, so respond_to would return false, even though you would be able to call the method.

Really? I thought I'd used respond_to? in that context successfully. Perhaps I'm wrong or it was overridden.

Need is a vague word. At a basic level you don't need to override respond_to, however there was a strong enough feeling that this wasn't consistent, especially as you'd do things lile

foo.respond_to? :name #=> false foo.name #=> 'Bob' foo.respond_to? :name #=> true

Right.

Fred

Best,

It seems to me like you ought to be able to call .to_yaml on a activerecord object and then use YAML.load to reconstitute it.

I'm trying to figure out if something is wrong with my ruby / rails installation or if this is something that everyone experiences.

Can anyone else verify that after creating a simple new rails project and model, an instance of the model cannot make a round trip to_yaml and back?

Confirmed. Rails 3.0.3. Same error you get.

Thanks for verifying that Philip.

Guess I'll try and dive into the code and figure out how to fix it this weekend.

If anybody has any tips or ideas, let me know.