Couch Potato unleashed – a couchdb persistence layer in ruby (updated)
Update: the gem is now available, see the installation instructions below.
After several weeks of incubating on my computer it’s finally time to get real: I have just open sourced Couch Potato under the MIT license. You can get Couch Potato on github now. For an introduction to CouchDB and ruby please read my previous blog post A CouchDB primer for an ActiveRecord mindset. The following is a very short introduction into using Couch Potato. If you want to know more you can start with the README.
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.
Installation
Couch Potato is available as a gem from http://gems.github.com, so you can just do
$ sudo gem source –add http://gems.github.com # if you haven’t alread
$ sudo gem install langalex-couch_potato
$ irb
>> require ‘rubygems’
>> gem ‘couch_potato’
>> require ‘couch_potato’
>> CouchPotato::Config.database_name = ‘name of the db’
Alternatively you can download the sources from github. If you are using rails just copy the files into vendor/plugins, create a RAILS_ROOT/config/couchdb.yml file (see the README for the format) and you are ready to go. For other applications you will have to require the lib/couch_potato.rb file and then set the database name by calling CouchPotato::Config.database_name = 'name of the db'.
As Couch Potato is still very young you can expect its feature set to grow quite a bit in the near future. What you can download now is the very core together with a few features giving you a glimpse of what is about to come:
Persistence
Create a new class and make its instances persistable by including the Persistence module. As there is no schema in a CouchDB you have to declare the properties you want to persist:
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
user._id # => “02097f33a0046123f1ebc0ebb6937269″
user.created_at # => Fri Oct 24 19:05:54 +0200 2008
You can of course also retrieve your instance:
User.get “02097f33a0046123f1ebc0ebb6937269″ # => < #User 0x3075>
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 0x987>
user.addresses.create! # raises an exception as street is blank
user.addresses.first.user == user # => true
When saving an object all associated objects are automatically saved as well. All these save operations are sent to CouchDB in one operation which means the whole process is atomic across all objects saved, plus only one database roundtrip is required making it much faster.
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.
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
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
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
Conclusion
Couch Potato is very young and it’s open source, so if you find any bugs (you most definitely will) please go to the bug tracker and file a ticket. If you want to contribute please create a fork on github. If you have ideas for improvements please email me or comment on this post.
Tags: couchdb, couchpotato, persistence, ruby




October 27th, 2008 at 11:00
Congrats on the release!
October 27th, 2008 at 11:09
Wow, congratulations on the release. This looks really great!
October 27th, 2008 at 11:34
wow… sounds awesome!! – congrats!
hope I can get my head deeper into couchdb soon!
October 27th, 2008 at 12:31
thanks @all – i count on you testing and forkin this
November 9th, 2008 at 18:03
News at Couch – November 2008…
Welcome to another installment of News at Couch, our review of what’s new with, on and around CouchDB.
What’s the most interesting new idea you’ve seen in the field of web development in the past year?
New database architectur…
December 8th, 2008 at 05:31
This is a great start!
I set it up and was playing around with it.
The associations work as advertised. I really like the idea of being able to add in couchdb. Seems like a very cool way to store data.
I think I did find a few bugs however (I think)
for example
If I add in a a new property called address_id and add the association belongs_to :address it is looking for a class named Order::Addres. Not sure if this is something I would have to fix (eg, Inflections).
I love how you can mix in normal relations!
class Order
CouchPotato::Config.database_name = 'development_db_name'
include CouchPotato::Persistence
#include CouchPotato::Versioning
property :address_id
property :user_id
property :email_id
# property :billing_address_id
# property :email_id
# property :person_id
# property
#validates_presence_of :address_id
belongs_to :address
belongs_to :user
belongs_to :email
end
This is awesome! Really diggin’ couchdb. This is going to revolutionize the way we do data!
Thanks so much for putting this together.
Cheers.
December 8th, 2008 at 10:23
you don’t have to declare address_id, this is created automatically when declaring the belongs_to :address. the belongs_to looks for a class Address – seems like you don’t have that. the error message is maybe a bit misleading here. you don’t have to put address inside of order of course.
great you like it. if you want to contribute feel free to fork the repo on github.
in the meantime if have added some features and incorporated various bugifxes. these are not in the gem yet so please check out the sources from github.
February 11th, 2009 at 14:00
[...] wanted to use CouchDB but found the existing libraries (such as RelaxDB, CouchREST, ActiveCouch and CouchPotato) lacking – not that they are, as such, but they didn’t fit George’s needs. George wanted something [...]
February 12th, 2009 at 17:20
[...] wanted to use CouchDB but found the existing libraries (such as RelaxDB, CouchREST, ActiveCouch and CouchPotato) lacking – not that they are, as such, but they didn’t fit George’s needs. George wanted something [...]
February 13th, 2009 at 13:50
[...] wanted to use CouchDB but found the existing libraries (such as RelaxDB, CouchREST, ActiveCouch and CouchPotato) lacking – not that they are, as such, but they didn’t fit George’s needs. George [...]
February 20th, 2009 at 10:53
I think that the use of rails is CouchDB prospects, interesting approach to a database.
February 27th, 2009 at 11:06
Great feature lineup you’ve got there. I will definitely try this out.
March 20th, 2009 at 09:21
Und ich war bei deinem Vortrag beim Railscamo nicht da….. oh menno, bekomme ich eine Privataudienz?
May 12th, 2009 at 21:02
Hi
I’ve been testing Couch Potato and CouchDB for a while, and I think it is working great. But now I’ve run into an problem. I’m fairly new to both ruby/rails and couchdb, so it might be some easy stuff. This is my issue:
I try to update my object and get the following error:
undefined method `dirty?’ for #
The object is created with success, but when I try to update the error appears. The objects:
class CouchMovie
include CouchPotato::Persistence
has_many :couchCredits, :stored => :inline
…
end
class CouchCredit
include CouchPotato::Persistence
belongs_to :couchMovie
…
end
Any ideas on what might be wrong?
May 12th, 2009 at 21:04
sorry – the error message got “tagged”, so I try again:
undefined method `dirty?’ for #<CouchPotato::Persistence::InlineCollection:0×18ed644>
May 13th, 2009 at 00:24
sorry it seems you are using the old 0.1.x version of couch potato which isn’t supported anymore. please upgrade to 0.2.x
November 5th, 2009 at 07:52
Pretty awesome stuff, I love it hah
November 20th, 2009 at 23:09
some attention on these types of things. I prefer a organic environment for me and my kids.