Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
ruby persistence layer for CouchDB.
Ruby JavaScript

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.


Couch Potato

… is a persistence layer written in ruby for CouchDB.


The goal of Couch Potato is to create a migration path for users of ActiveRecord and other object relational mappers to port their applications to CouchDB. It therefore offers a basic set of the functionality provided by most ORMs and adds functionality unique to CouchDB on top.

Core Features

Couch Potato is a work in progress so this list will hopefully grow over time.

  • persisting objects by including the CouchPotato::Persistence module
  • has_many/belongs_to relationships between persistant objects
  • atomic write operations spanning multiple objects using bulk save queues
  • extensive spec suite
  • included versioning support via the CouchPotato::Versioning module
  • included ordered lists support via the CouchPotato::Ordering module


Couch Potato is hosted as a gem on github which you can install like this:

sudo gem source —add # if you haven’t alread sudo gem install langalex-couch_potato

Using with your ruby application:

require ‘rubygems’ gem ‘couch_potato’ require ‘couch_potato’ CouchPotato::Config.database_name = ‘name of the db’

Alternatively you can download or clone the source repository and then require lib/couhc_potato.rb.

Using with Rails

Add to your config/environment.rb:

config.gem ‘langalex-couch_potato’, :lib => ‘couch_potato’, :source => ‘’

Then create a config/couchdb.yml:

development: development_db_name test: test_db_name production: production_db_name

Alternatively you can also install Couch Potato directly as a plugin.


This is a basic tutorial on how to use Couch Potato. If you want to know all the details feel free to read the specs.

Save, load objects

First you need a class.

class User end

To make instances of this class persistent include the persistence module:

class User include CouchPotato::Persistence end

If you want to store any properties you have to declare them:

class User include CouchPotato::Persistence property :name end

Now you can save your objects:

user = :name => ‘joe’ # or save!

Properties: # => ‘joe’ = {:first => [‘joe’, ‘joey’], :last => ‘doe’, :middle => ’J’} # you can set any ruby object that responds_to :to_json (includes all core objects) user._id # => “02097f33a0046123f1ebc0ebb6937269” user._rev # => “2769180384” user.created_at # => Fri Oct 24 19:05:54 +0200 2008 user.updated_at # => Fri Oct 24 19:05:54 +0200 2008 user.new_document? # => false, for compatibility new_record? will work as well

You can of course also retrieve your instance:

User.get “02097f33a0046123f1ebc0ebb6937269” # => <#User 0×3075>

Object validations

Couch Potato uses the validatable library for vaidation (\

class User property :name validates_presence_of :name end user = user.valid? # => false user.errors.on(:name) # => [:name, ’can’t be blank’]

Finding stuff

For running basic finds (e.g. creating/querying views) you can either use the CouchPotato::Persistence::Finder class:

joe = User.create! :first_name => ‘joe’, :position => 1 jane = User.create! :first_name => ‘jane’, :position => 2 User, :first_name => ‘joe’ # => [joe] User, :first_name => [‘joe’, ‘jane’] # => [joe, jane] User, :position => 1..2 # => [joe, jane]

You can also count:

user = User.create! :first_name => ‘joe’ User, :first_name => ‘joe’ # => 1

Or you can call first/all/count on a class persistent class which will call the Finder.find, e.g.:

User.first :login => ‘alex’ User.all User.count :activated => true

Be warned though that executing these finder methods will generate a new view for every new combination of class and attribute names it gets called with, so you really don’t want to use this in a console on a production system.

Support for more sophisticated views will be added later.


As of now has_many and belongs_to are supported. By default the associated objects are stored in separate documents linked via foreign keys just like in relational databases.

class User has_many :addresses, :dependent => :destroy end class Address belongs_to :user property :street end user = :street => ‘potato way’ user.addresses.first # => <#Address 0×987> user.addresses.create! # raises an exception as street is blank user.addresses.first.user == user # => true

As CouchDB can not only store flat structures you also store associations inline:

class User has_many :addresses, :stored => :inline end

This will store the addresses of the user as an array within your CouchDB document.

You can also find stuff on associations (not the :stored => :inline):

user.addresses.all :street => ‘potato way’

… returns only adresses belonging to this user instance and also matching the given conditions. You can call #count a well, #first and #last are not implemented here since those collide with the methods in Enumerable.


Couch Potato supports the usual lifecycle callbacks known from ActiveRecord:

class User include CouchPotato::Persistence before_create :do_something_before_create after_update :do_something_else end

This will call the method do_something_before_create before creating an object and do_something_else after updating one. Supported callbacks are: :before_validation_on_create, :before_validation_on_update, :before_validation_on_save, :before_create, :after_create, :before_update, :after_update, :before_save, :after_save, :before_destroy, :after_destroy

If you want to do any CouchDB update/create/delete operation in your callback methods…

class User include CouchPotato::Persistence has_many :comments before_update :create_a_comment private def create_a_comment comments.create :body => ‘i was updated’ end end

… and you want the entire operation including its hooks to be atomic you can do this:

class User include CouchPotato::Persistence has_many :comments before_update :create_a_comment private def create_a_comment bulk_save_queue << => ‘i was updated’) end end


Couch Potato supports versioning your objects, very similar to the popular acts_as_versioned plugin for ActiveRecord. To use it include the module:

class Document include CouchPotato::Persistence include CouchPotato::Versioning end

After that your object will have a version that gets incremented on each save.

doc = Document.create doc.version # => 1 doc.version # => 2

You can access the older versions via the versions method.

doc.versions.first.version # => 1

When passing a version number the version method will only return that version:

doc.versions(1).version # => 1

You can set a condition for when to create a new version:

class Document attr_accessor :update_version include CouchPotato::Persistence include CouchPotato::Versioning set_version_condition lambda {|doc| doc.update_version} end doc = Document.create doc.update_version = false doc.version # => 1 doc.version # => 1 doc.update_version = true doc.version # => 2

Ordered Lists

Couch Potato supports ordered lists for has_many relationships (with the :stored => :separately option only), very similar to the popular acts_as_list plugin for ActiveRecord. To use it include the module:

class PersistenArray include CouchPotato::Persistence has_many :items end class Item include CouchPotato::Ordering belongs_to :persistent_array set_ordering_scope :persistent_array_id end array = item1 = array.items.create! item1.position # => 1 item2 = array.items.create! item2.position # => 2

You can move items up and down simply by changing the position:

item2.position = 1! item1.position # => 2

And you can insert new items at any position you want:

item3 = array.items.create! :position => 2 item1.position # => 3

And remove:

item3.destroy item1.position # => 2

Helping out

Please fix bugs, add more specs, implement new features by forking the github repo at

You can run all the specs by calling ‘rake spec_unit’ and ‘rake spec_functional’ in the root folder of Couch Potato. The specs require a running CouchDB instance at http://localhost:5984

I will only accept patches that are covered by specs – sorry.


If you have any questions/suggestions etc. please contact me at alex at or @langalex on twitter.

Something went wrong with that request. Please try again.