This project is archived and is in readonly mode.
Nested objects don't deserialize completely (Rails 3 beta 4)
Reported by Stefan Siebel | June 29th, 2010 @ 11:55 AM
I tried to save a nested object structure with the ActiveRecord serialize method. These are the classes:
class PhotoStyle
attr_accessor :image
def initialize
@image = StyleElement.new
end
end
class StyleElement
attr_accessor :selector, :style
def initialize
@selector = '*'
@style = {}
end
end
The model:
class Photo < ActiveRecord::Base
serialize :style, PhotoStyle
end
When I save a Photo instance, and load it again, the style looks
like this:
p = Photo.last
p.style
=> #<photostyle :0xb70a906c @image=#<YAML::Object:0xb72079a4 @ivars={"selector"=>"#theimage", "style"=>{"border"=>"0", "box-shadow"=>"1px 3px 15px #555"}}, @class="StyleElement">}, @class="PhotoStyle">>
The StyleElement doesn't get deserialized.
I'm not sure if that's by design or a bug.
I was able to "fix" this by adding below code to an initializer of my rails3 beta 4 app:
YAML::Syck::Resolver.class_eval do
def transfer_with_autoload(type, val)
match = type.match(/object:(\w+(?:::\w+)*)/)
match && match[1].constantize
transfer_without_autoload(type, val)
end
alias_method_chain :transfer, :autoload
end
After restarting the app deserialization works fine:
#<PhotoStyle :0xb73dcb08 @image=#<StyleElement:0xb73dd058 @selector="#theimage", @style={"border"=>"0", "box-shadow"=>"1px 3px 15px #555"}>>
Comments and changes to this ticket
-
Stefan Siebel June 29th, 2010 @ 12:00 PM
- Title changed from Nested objects don't deserialized completely (Rails 3 beta 4) to Nested objects don't deserialize completely (Rails 3 beta 4)
-
Aaron Patterson June 30th, 2010 @ 12:45 AM
- Assigned user set to Aaron Patterson
- Importance changed from to Low
Could you possibly upload an app that has this error (and a test)? I'd like to take a look.
-
Stefan Siebel June 30th, 2010 @ 09:01 PM
Hi Aaron,
cool, thank you! Please see attached file. It looks like the issue is a little bit weird. I tried to reproduce it with a test, but apparently the test works fine.
That's how you can reproduce the issue:
1) Create a new user object through the UI (localhost:3000/users)
2) load the rails console, type in:u = User.last u.preferences.test.kind_of? Something => false
Another to reproduce it, only with rails console, without web UI:
a = User.new a.save b = User.last b.preferences.test.kind_of? Something => true reload! b = User.last b.preferences.test.kind_of? Something => false
Does that help?
-Stefan
-
Aaron Patterson June 30th, 2010 @ 11:50 PM
- Tag changed from activerecord rails3, serialize to activerecord rails3, serialize, yaml
- State changed from new to invalid
Hey Stefan!
Thanks for the sample application! I was indeed able to reproduce the problem, but unfortunately the problem isn't from Rails. I'll show you the fix for your code, then go in to detail about the problem.
Just add this to the top of your photo_style.rb file:
require 'style_element'
The problem is that your class isn't defined at the time the YAML gets deserialized. You've taught Rails how to load the top level constant when you made the
serialize :preferences, UserPreferences
call. Unfortunately, it doesn't know how to load the other class. We can reproduce this behavior with a Ruby program like this:require 'yaml' class UserPreferences end p YAML.load DATA.read __END__ --- !ruby/object:UserPreferences test: !ruby/object:Something a: 1 b: 2 c: 3
You'll note that the output of this shows the same problem that the rails code is experiencing. If you were using Marshal to serialize and deserialize these objects, this case would actually result in an exception being raised. The reason an exception is raised with marshal is because it doesn't know the definition of that object, so it can't properly deserialize it.
I believe this difference between Marshal and YAML to be a bug. The newer YAML parser in ruby 1.9 (psych) will raise an exception in this case, matching the behavior of Marshal. For example:
require 'yaml' YAML::ENGINE.yamler = 'psych' class UserPreferences end p YAML.load DATA.read __END__ --- !ruby/object:UserPreferences test: !ruby/object:Something a: 1 b: 2 c: 3
If you run the above code using Ruby 1.9.2 you'll see the exception raised. That exception can give rails a chance to load the missing constant.
I hope this explanation helps!
-
Stefan Siebel July 1st, 2010 @ 09:45 AM
Excellent Aaron! Thank you!
I already sort of expected it's something I'm doing wrong since I'm still pretty new to Ruby / Rails :-)
Thanks again, I really appreciate!
-Stefan.
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »
<h2 style="font-size: 14px">Tickets have moved to Github</h2>
The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>
People watching this ticket
Attachments
Referenced by
- 5525 After update to Rails 3.0.0 release, serialization seems to be broken? H Aaron, I set you as "who is responsible" because you he...