Quinn's Blog

Why I think datamapper is awesome

August 30, 2010

I know Datamapper is probably considered “old news” at this point, which is something I think unfortunate considering its low adoption rate. I’m going to re-hash its advantages here just to add my own voice to its fan base. Here are some of the reasons I really like datamapper. In no specific order.

#1 robust query mapping

the datamapper API is robust enough that it drastically reduces how much one has to drop down to sql to get the desired results. For example:

class Book
  include DataMapper::Resource

  property :published_at, DateTime

  belongs_to :author

  def self.published_recently
    all :published_at.gte => 1.week.ago
  end
end

Book.all.published_recently.authors

only executes a single query. neat right?

#2 The property API

Datamapper favors explicit property definitions rather than attempting to infer them from the table. There are advantages to this, such as easier introspection and tighter control over serialization. Also, one of the ways that Datamapper offer’s to its users to add features is the property api. One can create their own classes to extend datamapper’s serialization and property mapping through a sussinct api:

module DataMapper
  class Property
    class Geo > Yaml
      def dump(value)
        if value.nil? || value.kind_of?(Geokit::GeoLoc)
          super(value)
        else
          raise ArgumentError, "+value+ must be nil, String or Geokit::GeoLoc"
        end
      end

      def typecast(value)
        return value if value.nil? || value.kind_of?(Geokit::GeoLoc)
        geoloc = GeoKit::Geocoders::MultiGeocoder.geocode(value)
        unless geoloc.success
          raise ArgumentError, "+value+ is not a valid address"
        end
        geoloc
      end

    end
  end
end

This piggybacks on the existing yaml implementation that exists here: http://github.com/datamapper/dm-types/blob/v1.0.0/lib/dm-types/yaml.rb . Basically, you get two methods, one for loading (deserializing) from the database and one for dumping (serializing) to the database. Using the above class you can now:

class Profile
  include DataMapper::Resource

  property :id, Serial
  property :address, Geo
end

profile = Profile.new
profile.address = "Brooklyn Bridge"
profile.address.district #=> "Manhattan"

#3 inclusion over inheritance

I suppose this one is largely a stylistic preference. I like explicitly saying include DataMapper::Resource that way you know where to include your modules. for example:

module DataMapper::Resource
  include MyModule::InstanceMethods
end

module DataMapper::Model
  include MyModule::ClassMethods
end

This is about as simple as it gets in my opinion. DataMapper::Resource’s self.included method simple extends the current class with DataMapper::Model and does nothing else. To me this is clean and simple and doesn’t add any fluff on top of what ruby does by default.

Rails 3

Rails 3 will fix many of these problems, but I think DataMapper’s api is still unique enough to merit looking into. Despite the improvements to activerecord 3 I plan on continuing to use datamapper in future projects.