There are a few ActiveRecord-style wrappers for CouchDB out there these days, but many of them have fallen behind as CouchDB’s powers have increased. I’ve taken an interest in writing regular old web apps again – so now is the time when CouchRest grows wings and flies to the vaulted heights of DataMapper and ActiveRecord. (I could have written a DataMapper adapter for CouchDB, but much of DataMapper’s code is based around SQL-like problems that CouchDB just doesn’t have.) CouchRest::Model provides the ease-of-use Rubyists have come to expect from their ORMs, and it does it in CouchDB style.
Here is an example (taken from the bright shiny new documentation) of what your Models can look like. If you click through to the actual blog post, there is also a longer example pasted as a gist embed which details the capabilities of the view-generation system even more.
class Article < CouchRest::Model
use_database CouchRest.database!('http://localhost:5984/couchrest-model-test')
unique_id :slug
view_by :date, :descending => true
view_by :user_id, :date
view_by :tags,
:map =>
"function(doc) {
if (doc.type == 'Article' && doc.tags) {
doc.tags.forEach(function(tag){
emit(tag, 1);
});
}
}",
:reduce =>
"function(keys, values, rereduce) {
return sum(values);
}"
key_reader :slug, :created_at, :updated_at
key_accessor :title, :tags
# You can make dates work nice without magic or special fields
key_writer :date
def date
Time.parse(@doc['date'])
end
timestamps!
before(:create, :generate_slug_from_title)
def generate_slug_from_title
doc['slug'] = title.downcase.gsub(/[^a-z0-9]/,'-').squeeze('-').gsub(/^\-|\-$/,'')
end
end
You can do the standard CRUD:
@post = Post.get(params[:id])
@post.title = "New Title"
@post.save
The view generation system is what I’m proudest of. It’s designed to be performant, and it strikes a balance between magic and usability. Simple views are as easy to declare as:
view_by :date, :descending => true
They can be queried easily like this:
# get the 10 most recent posts
@posts = Post.by_date :count => 10
# get the raw view
@view = Post.by_date :count => 10, :raw => true
Views defined in this way take all the options of a CouchRest::Database#view call, and those options can be provided either as defaults at definition time, or overriden at query time. There is an additional option, :raw, which defaults to false. When :raw is true, you get back just what CouchDB sends. When :raw is false, CouchRest::Model::MagicViews requests the associated document for each view row, and gives you an array of documents. W00t!
CouchRest::Model views use _design documents, not temp views, so they take advantage of all the performance CouchDB has to offer.
Views can also be manually composed, if you want to write something that’s more complex than the built-in helpers can provide.
If you’re at the permalink page for this post, you’ll see a Gist embed here with an even longer example of CouchRest::Model in action.
Enjoy!
3 comments on CouchRest::Model - ORM, the CouchDB way
Great stuff!!! Keep up with the good workd! I have a few questions. Are you planning on adding relationships and validations? Also, do you know a way of ensuring that an attribute is unique across a Model domain? For example, email attribute for a User model?
Thanx
Do you have a sample rails/merb application that we can look at? I’d like to get a peek at the various init and config files.
Hope you don’t mind the silly question.
Any advice on how best to support mapping a an couchrest-type to a an Object? Id like to get a little meta and it would be cool if I could do this:
Here’s the class I have so far. The views get created but reads fail. Need to figure that out. Ideally the class would be readonly… not sure if this is the simplest way to go about it as type isn’t really a type, if you know what I mean.