【转】Serialize / Deserialize nested objects in ActiveRecord
Monday, November 29, 2010Using ActiveRecord’s serialize method you can save Ruby objects in a single field of your model. The serialization is done through YAML. Today I tried to save a nested object structure with this 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 which saves a PhotoStyle object:
class Photo < ActiveRecord::Base serialize :style, PhotoStyle end
This first approach didn’t quite work out. Saving the model worked fine, but when I loaded it only PhotoStyle got deserialized to the original type. The nested StyleElement didn’t:
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">>
Note the YAML object assigned to the @image instance variable. I’m not sure if that’s a bug in Rails3 or if that is by design. You can however “fix” it by running this code snippet when your application starts up:
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
Save it e.g. in yaml_autoloader.rb in the config/initializers folder of your app. Now all types will autoload:
#<PhotoStyle:0xb73dcb08 @image=#<StyleElement:0xb73dd058 @selector="#theimage", @style={"border"=>"0", "box-shadow"=>"1px 3px 15px #555"}>>
I found above code snippet here. Thanks! Read more about ActiveRecord’s serialize method here: Simple user preferences for your Rails app.
Update: Still not sure if that’s a bug, but I submitted a lighthouse ticket.
Update 2: Apparently this is not a bug in Rails, but a problem in my code :-) Please see Aaron’s comment in above ticket. http://blog.project-sierra.de/archives/1539