- An introduction to CouchDB
- Installing CouchDB
- Experimenting with CouchDB’s web interface
- Integrating with Rails using ActiveCouch
- Integrating with Rails using RelaxDB
- Getting to scaffolding using RelaxDB
- Installing CouchDB from Subversion source code
- Trying out couchrest and topfunky’s basic_model
Some time has gone by since i last used CouchDB and one method seems to have bubbled up to the top as a good one to use. It is couchrest, helped along by topfunky‘s basic_model wrapper. Today i am going to have a little play with them.
I have been meaning to get back into CouchDB but to be honest, i was tired of the old CD Collection database, and i was waiting for some inspiration. Today that inspiration arrived! I need to write my Christmas cards by the end of the week. I was impressed this afternoon by my auntie’s label printing system, and i decided i should do the same. I did some label printing last week at work, and i quite fancy making a nice little Ruby library for it.
I also think an address book is a pretty neat application for CouchDB as a document oriented database. It’ll work quite well for people who have multiple email addresses, or multiple telephone numbers. The document for each person can expand as big as it needs to be without wasting database space for the people who only have one phone number.
I need to print these address labels by the end of the week. You know that if a programmer has 100 hours to do something, they will generally spend 99 hours figuring out how to do it in 1 hour! Let’s go then! :D
Starting up the Rails project
rails address_book cd address_book
Couchrest specifically tells me to install it as a gem, so i will do just that. A little hint, couchrest requires rest-client and a few other dependencies, so make sure they are installed first.
sudo gem install rest-client json extlib sudo gem install jchris-couchrest -s http://gems.github.com
You can run a ‘gem list’ to ensure that it installed successfully.
gem list | grep 'couchrest' jchris-couchrest (0.9.12)
A quick check that Ruby can find and use the couchrest gem:
irb irb(main):001:0> require 'rubygems' => true irb(main):002:0> require 'couchrest' => true
Getting the basic_model
Geoffrey Grosenbach (topfunky) has written a helpful basic_model wrapper that should allow us to perform a few useful operations on our models. I’m not sure whether we’re supposed to install it as a gem, submodule it as a plugin, or just copy the files by hand. It looks as if it ought to be installable as a gem, but i can’t work out how. I think it should work just as well as a plugin, because i can see the lib there which contains the basic_model.
cd ~/rails/address_book git submodule add git://github.com/topfunky/basic_model.git vendor/plugins/basic_model
Creating a model
Here is the simplest possible model for a Contact in my address book. Notice that it inherits from BasicModel.
class Contact < BasicModel end
One particular feature of BasicModel is that every command needs to know a database to work on. This is good if you are using a different database per user accessing your application, which is a perfectly valid thing to do in CouchDB. Let me give an example.
./script/console Loading development environment (Rails 2.1.2) >> c = Contact.new ArgumentError: wrong number of arguments (0 for 1) from (irb):1:in `initialize' from (irb):1:in `new' from (irb):1
Normally that would work, right? This is how we know that we are inheriting from BasicModel and not just using a standard Ruby class. Now let’s do that again but give it a database name …
./script/console Loading development environment (Rails 2.1.2) >> c = Contact.new('address_book') => # >> c.save => #Tue Dec 09 21:43:15 +0000 2008, "_rev"=>"4254838631", "_id"=>"2421574ce782cf1b41e801fd0c19cf4f", "type"=>"Contact", "created_at"=>Tue Dec 09 21:43:15 +0000 2008}, @database_name="address_book">
Woohoo, look at that! Part of the basic_model gives us a created_at and updated_at, just like we’re used to with Rails! It also adds in a ‘type’ field for this document to show that it is a contact. What’s more, we get to see those CouchDB specific fields like _id and _rev. This is looking good!
It gets better! Look at my Futon utility!
I did not create that address_book database. BasicModel just created it for me! And here is my document, ready to go.
Now, let me think. Who do i want to send a Christmas card to?!
./script/console Loading development environment (Rails 2.1.2) >> c = Contact.new('address_book', :first_name => 'Geoffrey', :last_name => 'Grosenbach') => #"Geoffrey", :last_name=>"Grosenbach"}, @database_name="address_book"> >> c.save => #Tue Dec 09 22:09:58 +0000 2008, "_rev"=>"3194024490", "_id"=>"26e09404188d8bce0b5081e653d137b4", "type"=>"Contact", :first_name=>"Geoffrey", "created_at"=>Tue Dec 09 22:09:58 +0000 2008, :last_name=>"Grosenbach"}, @database_name="address_book">
Oooh, nice, it actually works!
Couchview utility
Couchrest comes with a nice utility called Couchview which lets us create views locally and push them to the database. It’s quite neat to be able to store them in our db directory like we do with migrations on relational databases.
couchview generate db/views contacts Writing db/views/lib.js Writing db/views/contacts/sample-map.js Writing db/views/contacts/sample-reduce.js Writing db/views/contacts/lib.js
It creates a sample map and a sample reduce for me. I’m actually going to write my own map for finding contacts using their last name as the key. The file is saved as db/views/contacts/by_last_name-map.js
function(doc) { if (doc.type == "Contact") { emit(doc.last_name, doc); } }
Please note that double quotes are important. It breaks with single quotes.
If i were interested in knowing how many of my contacts have the same last name, i could now write a reduce to count up the results returned by the map. But for now, i only want the map.
Push this to the database like so:
couchview push db/views/contacts address_book Pushing views from directory db/views/contacts to database http://localhost:5984/address_book creating _design/contacts
Sure enough, here is my view, showing my one contact:
Let’s make a Rails website, shall we?!
Okay, so a contacts controller could now use this view to find and show my contacts.
class ContactsController < ApplicationController def index @contacts = Contact.view(database_name, 'contacts/by_last_name-map') end end
A little tip: i defined database_name in the application controller just to return ‘address_book’ for now.
Here is app/views/contacts/index.html.erb
Contacts
Notice you have to get the rows out of the @contacts. If you raise @contacts.inspect
you’ll see why.
Where did full_name come from, you ask? I just defined it in the Contact model:
def full_name "#{first_name} #{last_name}" end
But what good is a Christmas card list without addresses? Let’s edit our contact and put in an address. It is nice and RESTful so long as you add a resource as usual in your config/routes.rb
map.resources :contacts
Then put this in the view:
Here is the controller action:
def edit @contact = Contact.find(database_name, params[:id]) end
The edit view:
Edit
4 %>
Notice i added in the _rev as a hidden field. I believe CouchDB needs to know that, but i’m not entirely sure.
The update action simply needs to find the record, update it, and return to the index page.
def update @contact = Contact.find(database_name, params[:id]) @contact.save(params[:contact]) redirect_to(contacts_path) end
I can now output the address on my index page:
Contacts
Just quickly, the new and create actions:
def new @contact = Contact.new(database_name) end def create contact = Contact.new(database_name) contact.save(params[:contact]) redirect_to(contacts_path) end
And here we have two contacts!
There, that wasn’t particularly hard. I definitely think the CouchRest and BasicModel are the easiest method i have tried so far. I also really like the method of creating views in files and pushing them to the database. Very neat!
Please note, the addresses used in these examples are easily found on the web! I’m not giving away any secrets!
Update: I have the pushed the code so far to GitHub, as couchdb_address_book. Feel free to take it and use it for your own purposes.
Update 14th December 2008: I did it! I printed my labels and wrote all my Christmas cards! Now i can go on holiday! Although they’re not part of the “CouchDB on Rails” series, see these two related posts if you’re curious how i did it!
Posted in couchdb, ruby on rails
