We are a software consultancy based in Berlin, Germany. We deliver
high quality web apps in short timespans.

Upstream Agile GmbH

New Couch Potato: simple, testable, opinionated.

May 17, 2009 by alex

After my talk about Ruby CouchDB frameworks at Scotland on Rails where I dismissed a few of of the libraries available (including my own Couhch Potato) as not fitting the CouchDB way of doing things, I have been hacking away the past few weeks working on a complete overhaul of Couch Potato.

As a first result I have just released version 0.2 of the framework. Its new goals are simplicity, embracing the CouchDB semantics and testability. In order to achieve this I had to introduce some major changes:

I disconnected models from the database - there are no more save/get/find methods in the models. Instead you can hand the models to a database object that will save/load documents for you - this allows for unit tests that are completely disconnected from the database

I have dropped associations and thrown away all the ActiveRecord like view creation/querying, replacing it with a new, more CouchDB like system. That new system is lighter, simpler and easier to extend.

The following paragraphs will show you how to work with the new Couch Potato.

Saving / loading models

As I said I have decoupled the models from the database, a model doesn’t have permanent access to the database anymore. Instead you instantiate a database object yourself and tell it to load or save a model object. This change isn’t so much about CouchDB as it is about testability. Having the database separated means you can now have true unit test of your models without talking to the database. Here is how you save/load models:

class Book
  include CouchPotato::Persistence
  property :title
end

CouchPotato::Config.database_name = 'my_db' # in Rails this is done for you

db = CouchPotato.database

book = Book.new :title => 'The Passionate Programmer' # good book

db.save! book # saves the book or raises an exception


db.load book._id # the original book

The database is responsible for running the model’s validations and lifecycle callbacks, saving the document to the database and afterwards setting the _id and _rev on the model. This way a lot of the persistence related logic is removed from the models making them more lightweight and most importantly easier to test (see below).

New Views

Overhauling of the views had two main goals:

  • provide a simple and extensible way for saving/querying views that works the way CouchDB works
  • since models don’t have access to the database anymore, find a new way to query them

Here is how you create and query a simple view:

class Book
  view :by_title, :key => :title
  property :title
end

 db = CouchPotato.database
 db.view Book.by_title # no parametters

 db.view Book.by_title(:key => 'The Passionate Programmer', :descending => true) # just use any of the parameters CouchDB accepts

The way views work is now essentially reversed (inversion of control, rings a bell?). Instead of the view method calling the database the new view method creates a specification for a CouchDB view. The view is passed to the actual database in order to query CouchDB. Again this makes testing easier (see next section) but it also gives Couch Potato a clean interface in order to make creating views easier through abstractions. There is now a hierarchy of view specification classes that you can use to more easily create and query views. The above example used the CouchPotato::View::ModelViewSpec which is the default. This spec creates a view whose map function emits one (or an array of) attribute of the model and returns full documents.

Other view specs let you create more customized views, for example the RawViewSpec, which lets you define your own map/reduce functions and return the raw CouchDB results hash.

class Book
  view :title_length, :type => :raw, :map => "function(doc) emit(doc.title.length, null)}"
  property :title
end

db.view Book.title_length # returns something like {:rows => [{:key => 25}]}

For more examples see the Documentation in the CouchPotato::View::*ViewSpec classes.

Testing

As I have mentioned repeatedly decoupling the database from the models makes testing easier. With the new Couch Potato you can unit test your models without hitting the database once. First of all that makes your test lightning fast, and secondly it allows you to more easily test for example your lifecycle callbacks because you can call them directly passing them a stub or mock database.

Here’s an example: (with RSpec)

class Book
  property :title
  property :slug
end

describe Book, 'create' do
  it 'should generate a slug' do
    book = Book.new :title => 'The Passionate Programmer'
    book.run_callbacks :before_create
    book.slug.should == 'the-passionate-programmer'
  end
end

If you don’t like calling the run_callbacks method you can also use the actual database object but without a real connection to CouchDB. Couch Potato is based on the excellent CouchRest - a more low level CouchDB framework. The CouchPotato::Database constructor expects an instance of a CouchRest database which we can replace with a stub:

describe Book, 'create' do
  it 'should generate a slug' do
    book = Book.new :title => 'The Passionate Programmer'
    db = CouchPotato::Database.new stub('couchrest database', :save_doc => {})
    db.save book
    book.slug.should == 'the-passionate-programmer'
  end
end

For more details and examples please see the README, the RDocs and watch this blog. If you want to know all about Couch Potato I encourage you to read through its source code and specs - it’s not that much code actually. Note that all this is still work in progress and time will show how well all of this works. I would be happy to hear your feedback.