Write your own custom DataMapper adapter

If you read this blog, you probably know that Merb‘s best ORM friend is DataMapper.

compounds in basil oil have potent antioxidant and is used for supplementary treatment of stress

compounds in basil oil have potent antioxidant and is used for supplementary treatment of stress

Merb works very well with ActiveRecord and Sequel but most of the Merbivores get excited about DataMapper.

DataMapper has a lot of cool stuff going for it. I’m planning on writingi few articles about what I particularily like with DM and some of the misconceptions.

I’m going to give a talk about DataMapper during MerbCamp and something I want to cover is the fact that you can write DM adapters for virtually anything. From an adapter for couchdb (available in dm-more) to an adapter for SalesForce API. That’s the kind of stuff that gets me excited, a bit like what Ambition does but built-in in DM.

So, I decided to take some advise from Yehuda and dkubb and wrote my own adapter for Google Video. I had just finished a gem to retrieve google videos for a given google user and thought it would be a perfect exercise to mix a http-scraper with a DM adapter.

Based on the advise I received, I started by defining the API I want to use:

I matched the API calls to the underlying methods I would need to make. I then modified my original gem to support conditional calls.

Once that was done I implemented the required methods for my models to support the Model.first and Model.all calls with conditions.

A custom adapter inherits from AbstractAdapter and can define the default adapter methods:

      def create(resources)
        raise NotImplementedError
      def read_many(query)
        raise NotImplementedError
      def read_one(query)
        raise NotImplementedError
      def update(attributes, query)
        raise NotImplementedError
      def delete(query)
        raise NotImplementedError

Since my adapter only needs to read data, I just had to implement #read_one and #read_many. I implemented a #read private method accessed by #read_one and #read_many as you can see here.

Because DM offers a clean and consistent API, things were pretty easy and you can check the specs to have a better understanding of how things are expected to work.

As you can see with less than 80LOC, I implemented an adapter that I can use and reuse cleanly in my apps. And on top of that, the adapter uses a standard API known by everyone using DM.

Even though, this adapter has a very limited scope, I hope this example will inspire you and at your turn, will write some more cool adapters to share with the rest of us.


Sam Smoot, also known as Mr DataMapper made a very good comment. I should explain a bit more how you create a collection of objects to return when a user does a .all or .first call.

The whole collection creation is a bit strange at first.

In my read method, “set” is a DataMapper::Collection instance that is passed by the #read_one or #read_many method.

The collection needs to be loaded with an array of ordered params.

For instance in this case, to create a Video collection I need to load the collection with an ordered array of params like docid, title etc..  However, some params might be lazy loaded and in some instance, the query might be in the form of Video.all(:fields => :title)

To figure out what fields/params we need, I used:

properties = query.fields

Which retrieves the required fields. Once you have the fields you need to return the structured data and that’s when I used the #result_values method which basically loop through the fields and retrieves the data by sending the param as a method to the result:

properties.map { |p| result.send(p.field(repository_name)) }

The values once retrieved get loaded, but here is a trick I took from wycat’s saleforce API:

arr ? set.load(values) : (break set.load(values, query))

This snippet is quite simple if we set arr as true, that means we want to return an array so we will keep on looping (used by read_more when we want to return an array of objects). Otherwise we use the break operator which will stop the loop but also return a value. (yes, Ruby is awesome). By returning a single object we do exactly what’s expected and don’t have to call #first on a returned array. Pretty slick

Similar Posts

, , ,

  1. #1 by Glenn Gillen - September 29th, 2008 at 04:59

    Hey that’s awesome! I’ve only just started getting myself properly dirty with DM and Merb in the past fortnight, this could well make a good pet project to test my new foo out on.


  2. #2 by Sam Smoot - September 29th, 2008 at 10:13

    One thing you did Matt I want to point out since there’s some confusion on it:

    Do like Matt did and use Query#fields http://github.com/mattetti/dm-gvideo-adapter/tree/master/lib/dm-gvideo-adapter.rb#L48 to get the list of properties to retrieve/load. Don’t use Model#properties directly.

    This way Lazy-Loads are taken care of for you automatically in other parts of DM and you don’t need to do anything else in your adapters.


  3. #3 by admin - September 29th, 2008 at 12:06

    @ Sam very good point, I updated my blog post to explain a bit more the process to return a Collection object.


  4. #4 by Eric - October 6th, 2008 at 10:38

    Thanks for the concrete writeup. Really enjoying these posts!

Comments are closed.