#851 ✓resolved
falter

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

    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)

    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)

    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.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

People watching this ticket

Attachments

Pages