datamapper / dm-types

DataMapper plugin providing extra data types
http://datamapper.org/
MIT License
55 stars 80 forks source link

YAML data type sometimes badly tracks changes #28

Closed solnic closed 13 years ago

solnic commented 13 years ago

When I am using YAML data type in data_mapper some operations on Arrays and Hashes are not seen by data mapper and not saved into database. Workaround is to use only assign function on Arrays and Hashes saved to database as YAML. Script showing problem: require 'rubygems' require 'dm-core' require 'dm-types'

An in-memory Sqlite3 connection:

DataMapper.setup(:default, 'sqlite3::memory:')

class Category include DataMapper::Resource property :id, Serial property :name, String property :links, DataMapper::Types::Yaml
end

DataMapper.auto_migrate! DataMapper::Logger.new(STDOUT, :debug)

print "Creating new category..." c = Category.new puts "[OK]" c.name = 'Empty' c.links = [] c.save puts "New category links look like #{c.links.to_yaml}" empty_id = c.id c.links << {:a=>12} c.save puts "Changed category links set to look like #{c.links.to_yaml}" puts "After rereading category links look like #{Category.get(empty_id).links.to_yaml}" if Category.get(empty_id).links.size == 0 then puts "There is problem" else puts "Everything is fine" end

Problem appeared both in trunk and in current gem from repository.


Created by gondar - 2009-09-25 13:13:45 UTC

Original Lighthouse ticket: http://datamapper.lighthouseapp.com/projects/20609/tickets/1061

solnic commented 13 years ago

Currently DataMapper only tracks changes caused by calling the mutator methods, not changes using the reference to the value internally.

So for example, if you have a String, and you set the user.name using user.name = "gondar", and then later you use user.name << "other" to append to the String, DataMapper isn’t going to notice the change since you modified the value that it has a reference to.

DataMapper used to track this sort of change, but it incurred a large overhead and was needlessly complex. I removed it until we could figure out a better approach.

I suppose it may be possible if we take a snapshot of the object attributes’ Object#hash values when it is loaded, and then compare those when it comes time to save the object. I’m not yet sure what sort of overhead this will have, but it may be worth looking into later.

For now I am going to mark this as a "suggestion" and we will revisit it prior to 1.0

by Dan Kubb (dkubb)

solnic commented 13 years ago

I wish this ticket were higher on google, but here’s a workaround for anyone else who, like me, finds this type not very useful without this feature. Unfortunately, it will always save the YAML property if it has been loaded at all.

before :save, :dump_yaml

def dump_yaml
  self.yaml_prop = YAML.dump(self.yaml_prop) if attribute_loaded? :yaml_prop
end

by blavender (at gmail)

solnic commented 13 years ago

I added a pending spec for this and sent a pull request, see http://github.com/bhuga/dm-more/commit/e09a0579290a7036ee19f4d9f8c173ebad2a9654

by blavender (at gmail)

jpr5 commented 13 years ago

Note that pull request 33 (https://github.com/datamapper/dm-types/pull/33) would fix this use case.

dkubb commented 13 years ago

@jpr5's pull request was merged which solves this problem. Closing this ticket.

If the problem still persists please add a comment to this ticket, otherwise enjoy!