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.

https://gist.github.com/84f44da695b218ae9474

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.