Skip to content

Instantly share code, notes, and snippets.

@paul
Created May 1, 2009 18:37
Show Gist options
  • Select an option

  • Save paul/105179 to your computer and use it in GitHub Desktop.

Select an option

Save paul/105179 to your computer and use it in GitHub Desktop.

The problems with Types in DataMapper

Over the past few months, I've written many adapters for DataMapper, interfacing with a wide variety of data stores. A ReST-flavored one, TokyoTyrant, and some internal ones that I can't share. It's my second or third iteration on each, and I've been changing the Adapter API in dm-core as a result of my experiences. However, they're still not as polished or featureful as I'd like, due to still more deficiencies in the API. The most significant of these is the current types implementation.

Can't detect the type of a property

Currently, its extremely difficult to inspect the type of a property. Consider the following code:

require 'dm-core'

DataMapper.setup(:default, 
	     :adapter => :in_memory)
class Person
  include DataMapper::Resource

  property :id, Serial
  property :name, String

  property :created_at, DateTime
end

Person.properties.each do |p|
  case p.type
when String then puts p.inspect
  end
end

# no output

This is because property.type === String returns false, because its not really a String, is a DM::Types::String. We could add #===, but that would require everyone implementing a custom type to do the same.

Non-adapter specific type de/serialization

The "native" dm types currently do no serialization at all. In most of my adapters, I serialize Time-like properties as iso8601 datetimes. I like it because it is concise, and human-readable. Currently, though, dm-core doesn't do any typecasting at all on native types. So when I save a DateTime to the data store, then load it again, I get it back as a string, not a DateTime. My preferred way would be to use a case statement, to detect if I'm loading a DateTime, and parse it appropriately, but that obviously doesn't work. It's also far from ideal.

Writing new types is non-trivial.

Currently, anything used as a type has to to written as a DM::Type subclass. You also need to set a primitive, and methods for serializing/deserialize the value into something understandable by SQL. This is less than ideal for writing new types, and also completely unworkable for non-SQL adapter.

Proposal

I have started a branch to address some of these issues. Some of the important features:

There's no such thing as a datamapper Types. The type attribute of a property is just a normal ruby class, and can be absolutely anything. This makes ruby-native adapters, such as in-mem and yaml, trivially easy to do, because no typecasting need be done. Writing new "types" is also easy, you just make a class.

The "custom" type is just a regular type, that sets some default options on the Property when its used. An example of the Serial type:

class Serial < Integer
  include DataMapper::Type

  default_options[:key]    = true 
  default_options[:serial] = true 
end

Just include the DataMapper::Type to get the default_options method, and set the Property options you want. When you do property :foo, Serial, those options will be automatically set on the property.

Types are just the ruby class, which means that case statements and === work just like normal.

De/Serialization now become part of the responsibility of the adapters. I would like to have a mechanism where a type would be registered with the adapter. Some examples of how that might be accomplished:

class PostgresqlAdapter
  add_save(UUID) { |val| val.to_s }
  add_load(UUID) { |val| UUID.parse(val) }

end

This would allow custom, adapter-specific types to be implemented easily.

Conclusion

DataMapper, as it stands now, is completely unworkable for writing adapters that are non-trivial, and depart to far from the RDBMS mindset. Fixing and simplifying types will be a huge step in making it more plausible. My proposal is a pretty major departure from what we have now, but we get to greatly simplify some of the more complex code in DataMapper but doing this. Dan Kubb has is own ideas on how Types should be handled, but I'm not sure they're any better from the point-of-view of an adapter author. I hope to get some debate going over which is better, because I need better features in my adapters soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment