public
Description: ruby persistence layer for CouchDB.
Homepage: http://langalex.github.com/couch_potato
Clone URL: git://github.com/langalex/couch_potato.git
name age message
file CREDITS Tue Feb 03 15:15:24 -0800 2009 more performance improvements for belongs_to as... [langalex]
file INSTALL Loading commit data...
file MIT-LICENSE.txt Fri Oct 24 10:48:11 -0700 2008 added required gems to install file, added MIT ... [Alexander Lang]
file README.textile
file Rakefile
file TODO
file VERSION.yml
file couch_potato.gemspec
file init.rb Sun Nov 16 03:24:44 -0800 2008 added init.rb again so that this also works as ... [Alexander Lang]
directory lib/
directory rails/
directory spec/
README.textile

Couch Potato

… is a persistence layer written in ruby for CouchDB.

Mission

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

Installation

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

sudo gem source —add http://gems.github.com # 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 => ‘http://gems.github.com’

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.

Introduction

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 = User.new :name => ‘joe’ user.save # or save!

Properties:

user.name # => ‘joe’ user.name = {: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 (http://validatable.rubyforge.org/)\

class User property :name validates_presence_of :name end user = User.new 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 Finder.new.find User, :first_name => ‘joe’ # => [joe] Finder.new.find User, :first_name => [‘joe’, ‘jane’] # => [joe, jane] Finder.new.find User, :position => 1..2 # => [joe, jane]

You can also count:

user = User.create! :first_name => ‘joe’ Finder.new.count 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.

Associations

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 = User.new user.addresses.build :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.

callbacks

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 << Comment.new(:body => ‘i was updated’) end end

Custom Views

This is still in very early in development and of limited use

This is useful if you have a set of documents that has not been created with Couch Potato, for example doesn’t have a ruby_class attribute but you still want to be able to load those documents as instances of a Couch Potato class.

Example: Assuming you have a document like this:

{name: "joe"}

And a class:

class User include CouchPotato::Persistence property :name end

To be able to load that user all you have to do is this:

class User … view :everyone end User.everyone

This will load all documents in your database as instances of User and assign their _id and name attributes.

If you have larger structures and you only want to load some attributes you can customize the view:

{name: “joe”, bio: “52 pages of text ….”} class User property :name property :bio view :everyone, :properties => [:name] end User.everyone.first.name # => “joe” User.everyone.first.bio # => nil

Versioning

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.save 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.save doc.version # => 1 doc.update_version = true doc.save 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 = PersistenArray.new 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 item2.save! 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 http://github.com/langalex/couch_potato.

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.

Contact

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