
The dangers of properties with default values...
Reported by falter | May 14th, 2009 @ 05:01 PM
Forgive me for having to double-space this ticket.... everything got wrapped all crazy (who cares about newlines, right?)
At first, I believed this issue was restricted to just the DataMapper::Types::Object class, however it seems to occur in others. It seems that if you define a default value for a property, the default value is only every instantiated once... Direct modifications to the object stored by the property will occur across every object... This is more prevalent in things like hashes and arrays, for which direct object operations are expected... However, it is observable in Strings as well... I'm sure it could be demonstrated for many more...
require 'rubygems'
require 'dm-core'
class Testy
include DataMapper::Resource
property :id, Serial
property :stringy, String, :default => "asdf"
property :hashy, Object, :default => {}
end
DataMapper.setup(:default, "sqlite3::memory:")
DataMapper.auto_migrate!(:default)
test1 = Testy.create
test2 = Testy.create
test1.stringy[0] = "X"
test1.hashy["himom"] = 9999
puts "test1 (#{test1.id}) hashy object id: #{test1.hashy.object_id}, himom: #{test1.hashy["himom"].inspect}"
puts "test2 (#{test2.id}) hashy object id: #{test2.hashy.object_id}, himom: #{test2.hashy["himom"].inspect}"
puts "test1 (#{test1.id}) stringy object id: #{test1.stringy.object_id}, value #{test1.stringy.inspect}"
puts "test2 (#{test2.id}) stringy object id: #{test2.stringy.object_id}, value #{test2.stringy.inspect}"
Output:
test1 (1) hashy object id: 9480490, himom: 9999
test2 (2) hashy object id: 9480490, himom: 9999
test1 (1) stringy object id: 9482920, value "Xsdf"
test2 (2) stringy object id: 9482920, value "Xsdf"
So, yeah.... This made for some confusion on my part, however it totally makes sense, now. Wrapping the default value in lambda {} will cause my value to get evaluated each time the default is set, and this gets me the kind of behavior I expect to see:
require 'rubygems'
require 'dm-core'
class Testy
include DataMapper::Resource
property :id, Serial
property :stringy, String, :default => lambda { "asdf" }
property :hashy, Object, :default => lambda { {} }
end
DataMapper.setup(:default, "sqlite3::memory:")
DataMapper.auto_migrate!(:default)
test1 = Testy.create
test2 = Testy.create
test1.stringy[0] = "X"
test1.hashy["himom"] = 9999
puts "test1 (#{test1.id}) hashy object id: #{test1.hashy.object_id}, himom: #{test1.hashy["himom"].inspect}"
puts "test2 (#{test2.id}) hashy object id: #{test2.hashy.object_id}, himom: #{test2.hashy["himom"].inspect}"
puts "test1 (#{test1.id}) stringy object id: #{test1.stringy.object_id}, value #{test1.stringy.inspect}"
puts "test2 (#{test2.id}) stringy object id: #{test2.stringy.object_id}, value #{test2.stringy.inspect}"
Output:
test1 (1) hashy object id: 9468380, himom: 9999
test2 (2) hashy object id: 9466410, himom: nil
test1 (1) stringy object id: 9468510, value "Xsdf"
test2 (2) stringy object id: 9466620, value "asdf"
Comments and changes to this ticket
-
falter May 14th, 2009 @ 05:23 PM
I now realize that this is probably old news to some of the more experienced DataMapper folks... however, it threw me for a serious loop. It's not very clearly documented here: http://datamapper.org/doku.php?id=docs:properties#setting_defaults.... and though I have now found other posts where folks have wrapped their defaults in a lambda {}, it's not exactly obvious why they do that. One can assume that 'default' would set the value of the property to the default, and not the entire object to the default (if that makes sense).
-
Dan Kubb (dkubb) May 28th, 2009 @ 07:24 PM
- State changed from unconfirmed to accepted
- Assigned user set to Dan Kubb (dkubb)
Can you try dm-core/next and see if the problem presents itself there too? I explicitly use dup() on the value set, so there shouldn't be any problems of this kind.
-
Dan Kubb (dkubb) June 7th, 2009 @ 01:53 AM
- State changed from accepted to resolved
I can confirm this issue is fixed in the dm-core/next branch due to the fact that I use dup to copy the value set as a default into the resource.
Attached is a script that demonstrates the fix.
Please Sign in or create a free account to add a new ticket.
With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.
Create your profile
Help contribute to this project by taking a few moments to create your personal profile. Create your profile »