I am using DataMapper for Database access. My goal is to send the models to an webservice as read-only object. This is my current try:
class User
include DataMapper::Resource
def to_yaml(opts = {})
mini_me = OpenStruct.new
instance_variables.each do |var|
next if /^@_/ =~ var.to_s
mini_me.send("#{var.to_s.gsub(/^@/, '')}=", instance_variable_get(var))
end
mini_me.to_yaml(opts)
end
....
end
YAML::ENGINE.yamler = 'psych'
u = User.get("hulk")
p u.to_yaml
# => "--- !ruby/object:OpenStruct\ntable:\n :uid: hulk\n :uidNumber: 1000\n :gidNumber: 1001\n :email: hulk@example.com\n :dn: uid=hulk,ou=People,o=example\n :name: Hulk\n :displayName: Hulk\n :description: Hulk\n :homeDirectory: /home/hulk\n :accountFlags: ! '[U ]'\n :sambaSID: S-1-5-21-......\nmodifiable: true\n"
p [ u ].to_yaml # TypeError: can't dump anonymous class Class
Any ideas how to make this work and get rid of the exception?
Thanks, krissi
Using to_yaml
is deprecated in Psych, and from my testing it seems to be actually broken in cases like this.
When you call to_yaml
directly on your object, your method gets called and you get the result you expect. When you call it on the array containing your object, Psych serializes it but doesn’t correctly handle your to_yaml
method, and ends up falling back onto the default serialization. In your case this results in an attempt to serialize an anonymous Class which causes the error.
To fix this, you should use the encode_with
method instead. If it’s important that the serialized form is tagged as an OpenStruct object in the generated yaml you can use the represent_object
(that first nil
parameter doesn’t seem to be used):
def encode_with(coder)
mini_me = OpenStruct.new
instance_variables.each do |var|
next if /^@_/ =~ var.to_s
mini_me.send("#{var.to_s.gsub(/^@/, '')}=", instance_variable_get(var))
end
coder.represent_object(nil, mini_me)
end
If you were just using OpenStruct for convenience, an alternative could be something like:
def encode_with(coder)
instance_variables.each do |var|
next if /^@_/ =~ var.to_s
coder[var.to_s.gsub(/^@/, '')]= instance_variable_get(var)
end
end
Note that Datamapper has its own serializer plugin that provides yaml serialization for models, it might be worth looking into.