New Adventures with DataMapper

About a year ago I started working on a new Rails project. I’d previously done all Rails jobs with ActiveRecord but decided to give DataMapper a try this time as I heard it scaled particularly well and was classed as a “more complete ORM”.

It’s been a challenging experience at times and I thought I’d share some of the gotchas I came across in the past 12 months.

Lack of documentation
The fact that I have to write this article at all is testament to this. The documentation on the DataMapper website is okay-ish but it feels more like an introduction and certainly not the place for in-depth reference.

The community is also considerably smaller than ActiveRecords which means if you’re really stumped then there’s less helping hands available, especially for those really awkward bugs – which brings me nicely onto:

Many to many relationships issues
The documentation on associations specifies that you may use the :through => Resource syntax to create n-to-n associations between models. This means that if you have a model User and a model Activity then DataMapper will create a activity_user link table for you automatically. It also creates a link model which is loaded when Rails fires up.

Thus when you go to add a link between the data model (eg. adding an activity to a user), you’re really actually interacting with an ActivityUser model – DataMapper is just doing the heavy lifting for you.

This is all well and good unless you have configured cache_classes to false in your environment.rb file (which most people will in development, its one of the big benefits of using Rails and similar frameworks). In this case, the join model doesn’t get reloaded and when you go to update the relationship between two objects DataMapper will try to create a relationship where it should not.

The issue is spoken about in more detail here but the best way to mitigate this issue is to avoid using :through => Resource and define your join tables explicitly.

Validation works fine in DataMapper until you try to persisting an object with new relationships at the same time. Let’s say I have a User object with contains a Location; so on creation of the user, I create my new Location and set it on the new User object.

If the location fails validation when I try to persist the user then the save will fail but the errors array on the user will be empty. This becomes quite frustrating when working with a model with multiple relationships and you have to check validation on each of them individually.

Integration with third-party libraries
It’s fair to say that ActiveRecord is considered the standard ORM for Rails. This means that a lot of gems (eg. paperclip) will integrate seamlessly with AR but oftentimes you’ll have to search for an alternative for DataMapper or write your own.

I guess this article should have been titled “Bad adventures with Datamapper” since I didn’t really list any positives. There are quite a few though:

  • it’s really sweet not having to write migrations
  • configurable eager/lazy loading is cool (this may exist on AR now, not sure)
  • it’s fast. Really fast.
  • it’s actually an ORM (rather than a wrapper)

DataMapper is undoubtedly a useful ORM and the choice really comes down to your personal preference and what the project entails. If you have any questions you think I could help out with then feel free to contact me on Twitter @modernprogrammr

2 Comments New Adventures with DataMapper

  1. postmodern

    Some DataMapper protips:

    You almost never want :through => Resource. Instead, you should create an explicit join Model, just in case you want to add timestamps or other properties to the join Model.

    You can tell DataMapper to raise validation errors with DataMapper::Model.raise_on_save_failure = true or MyModel.raise_on_save_failure = true.

    Rails3 introduced ActiveModel, to abstract away how different ORM Models behave. Newer Rails plugins should support DataMapper. If not, you’ll have to find a DM friendly version (ex: dm-paperclip, carrierwave-datamapper). DataMapper already has plugins for most of the trivial ActiveRecord plugins (ex: dm-is-state_machine, dm-is-tree, etc).

  2. gearoid

    Yep would totally agree on not using :through => Resource.

    I did try the raise_on_save_failure before and it didn’t work for propagating errors up the models.

    For sure there are plenty of dm-equivalent gems but the integration experience wasn’t as smooth as with AR.

    Thanks for the tips though :-)


Leave a Reply

Your email address will not be published. Required fields are marked *