Wednesday, May 28, 2008

ActiveResource vs SimpleMapper

I wrote SimpleMapper a while back as a replacement for ActiveResource, for two reasons:
1) I needed to work with more than one data source.
2) I needed to use OAuth.

The biggest need was for an ActiveResource with OAuth. I found that it's virtually impossible with the current structure of ActiveResource. (I did get it working, after adding a "callbacks" plugin to ActiveResource and rewriting ARes's core request method.)

So I sat down one morning to write SimpleMapper. The name is rather random -- I like DataMapper, and I wanted to stress that this was just a simple version of a DataMapper / ActiveRecord solution. It took an hour or so to write from scratch the parts of ActiveResource, and I was already happier with my solution than with ARes. The simple structure is what makes it so powerful and extensible.

The structure: I started with a simple Base class, that simply provides methods that bind Connection Adapters with Format Adapters. For example, the Base.get(*args) method simply calls the Connection Adapter's get method with the arguments provided, then extracts individual objects from the returned content by using the Format adapter. This way, the Base is completely independent of the type of Connection and Format being used. I started out by writing an HTTP adapter and an XML adapter, and later I wrote a JSON adapter. Since all the parsing code is in the adapter, it's extremely simple to write new format adapters. You could easily write a yaml, or even csv or plaintext adapter. Or SQL. Write a SQL connection adapter, if you wish (I haven't tackled that yet).

There's one more piece worth mentioning -- ActiveResource does NOT follow the REST way. How? ARes relies on IDs, not URLs. (See Building Web Services the REST Way, down in the section entitled "REST Web Services Characteristics," for authoritative information.) The REST way uses URLs, not IDs. IDs are not reliable. (In thinking about the server side, you should implement a kind of global unique identifier [guid] system for your URLs instead of IDs.)

So SimpleMapper relies only on URLs. The way this works: You call a finder url with the options you need to find some object, a url that returns either a collection or a single object, and go from their. Your API *should* include a URL in the object's properties as the identifier of the object, never an ID. Then to update or delete that resource, it will access it directly via the URL given in the resource's identifier property.

To me, SimpleMapper wins, hands down. If you want to run an ActiveResource-like API, go ahead and clone the http adapter and change things to use an ID to construct-a-URL rather than reading the object's identifier as a URL.

2 comments:

Eric said...

ARes also just uses a URI. By default it uses id, but you can just do self.primary_key = 'email_address' if you wanted the url to looks like /users/:email_address instead of /users/:id.

But ARes has other problems, mainly in the form of bugs and crappy cross versioning compat issues. Im currently using it in a merb app and it would be nice to move to something like SimpleMapper. Maybe ResourceMapper would be a better fit? ;)

daniel said...

@eric - By "ResourceMapper" you mean to suggest a different name? Yes, I see the logic, and I'd probably actually get more noticing for the purpose of replacing ActiveResource, but that's actually not the purpose of SimpleMapper.

The motive *was* originally the need to write a better (and simpler) ActiveResource, but what I really wanted to do was write a simple framework that I could later write my own formats, adapters, is easily plugin-able, uses callbacks, etc. Flexible and easy to weild to the need at hand. It's really just a structure for having persistent models, and the means to the persistence are just combinations of various connection adapters, format adapters, and plugins.

I hope it suits you well. Anyone- feel free to contact me at gems@behindlogic.com if you have any questions about implementing it.