CouchDB on Rails (part 5 of ?)

  1. An introduction to CouchDB
  2. Installing CouchDB
  3. Experimenting with CouchDB's web interface
  4. Integrating with Rails using ActiveCouch
  5. Integrating with Rails using RelaxDB
  6. Getting to scaffolding using RelaxDB
  7. Installing CouchDB from Subversion source code
  8. Trying out couchrest and topfunky’s basic_model

So today i'm going to try using the RelaxDB plugin. I am fortunate in that there is a tutorial already written, which might make it easier. However, the tutorial is for Merb, so it might not work at all on Rails. We'll see.

Start a new Rails project

I have deleted my previous project and i'm starting again. The initial steps are the same as last time to create the project, create a Git repository, and bring in RSpec.

rails cd_collection
cd cd_collection
git init
git submodule add git://github.com/dchelimsky/rspec.git vendor/plugins/rspec
git submodule add git://github.com/dchelimsky/rspec-rails.git vendor/plugins/rspec-rails
./script/generate rspec

I'm slightly confused as to whether you're supposed to install RelaxDB as a gem, or whether it is okay just to include it as a plugin. For now i'm going to try as a plugin.

git submodule add git://github.com/paulcarey/relaxdb.git vendor/plugins/relaxdb

Spec tests

Like all good projects, RelaxDB has some RSpec tests, but before i can run them i need a few more gems such as extlib, json, uuid. Just use 'gem install' as root to install them. The specs will not run until you meet all the requirements.

I also found that the specs require CouchDB to be running on http://localhost:5984. As before, i use the su impersonate command to get it running.

sudo -i -u couchdb couchdb

And now for the moment of truth …

rake spec
..................................................................................................................
Finished in 6.620036 seconds
114 examples, 0 failures

Amazing! I'm feeling very hopeful about this! So let's jump straight in!

Configuration

A big win over ActiveCouch, RelaxDB has the option to set the database connection details. First i have to require the RelaxDB plugin … i'm doing this in config/environment.rb

require File.join(File.dirname(__FILE__), '../vendor/plugins/relaxdb/lib/relaxdb')

I suspect if i'd installed it as a gem i would have been able to just do this:

require 'relaxdb'

Never mind. It's fine. Now i can set my database connection details:

RelaxDB.configure(:host => 'localhost', :port => 5984)
RelaxDB.use_db 'cd_collection'

Create a model

The key thing today is to inherit from RelaxDB::Document. It seems i also have to set up the fields (in RelaxDB they are called properties). For now i'll just start with a title.

# app/models/cd.rb
class Cd < RelaxDB::Document
  property :title
end

Use script/console to ease us in gently

./script/console
Loading development environment (Rails 2.1.0)
>> cd = Cd.new
=> #<Cd:23965060258520, _id: "deadea40-6256-012b-aeb2-001a9205e793">
>> cd.title = 'Made Of Bricks'
=> "Made Of Bricks"
>> cd.save
=> #<Cd:23965060258520, _id: "deadea40-6256-012b-aeb2-001a9205e793", _rev: "4255803143", title: "Made Of Bricks">

It is my lucky day!! Let's see if that actually did what i think it did. The CouchDB log file looks promising:

[info] [<0.1970.0>] 127.0.0.1 - - "GET /cd_collection" 200
[info] [<0.1972.0>] 127.0.0.1 - - "PUT /cd_collection/deadea40-6256-012b-aeb2-001a9205e793" 201

And yes, sure enough, here's my new document sitting happily in the database:

A new CouchDB document created via RelaxDB

I notice the 'class' field has been inserted with the value 'Cd'. This is clearly a special field that RelaxDB uses to map the document to a model in Rails. It's a much better idea than ActiveCouch which seemed to want a separate database per model.

What else can we do? Find our CDs again? In ActiveRecord you'd do Cd.find(:all) but in RelaxDB it's just Cd.all

>> Cd.all
=> [#<Cd:23965059931020, _id: "deadea40-6256-012b-aeb2-001a9205e793", _rev: "4255803143", title: "Made Of Bricks">]

I wonder if i can trick it into finding the other CDs in my database, if i add the 'class' field to them all …?

>> Cd.all
=> [#<Cd:23965059900720, _id: "1", _rev: "1785545430", title: "Unwritten">, #<Cd:23965059892540, _id: "2", _rev: "1917382848", title: "Screaming Serenades">, #<Cd:23965059874120, _id: "3", _rev: "548309184", title: "Harry Potter and the Order of the Phoenix">, #<Cd:23965059858260, _id: "4", _rev: "288823659", title: "N.B.">, #<Cd:23965059840120, _id: "5", _rev: "374227142", title: "Vampire Weekend">, #<Cd:23965059821740, _id: "deadea40-6256-012b-aeb2-001a9205e793", _rev: "4255803143", title: "Made Of Bricks">]

Sure enough, yes! It works! And look what else i've just seen:

RelaxDB made its own view

RelaxDB helpfully created the view that it needed to search for CDs. I am very impressed.

Searching by ID is slightly different from ActiveRecord. Instead of Cd.find(id) we need RelaxDB.load(id)

>> RelaxDB.load(1)
=> #<Cd:24003693527840, _id: "1", _rev: "1785545430", title: "Unwritten", artist: "Natasha Bedingfield">

Oh, by the way, i added 'artist' as a property in the model. That works too! :)

Remember that most documents will be looked up using a UUID that RelaxDB will generate for us. That first CD just happened to be ID 1 because i gave it that ID the other day.

>> RelaxDB.load("deadea40-6256-012b-aeb2-001a9205e793")
=> #<Cd:24003693442460, _id: "deadea40-6256-012b-aeb2-001a9205e793", _rev: "4255803143", title: "Made Of Bricks">

There doesn't seem to be a 'find' method. I don't know how we would find the first CD, or search by title.

RelaxDB can delete documents using a 'destroy!' method:

>> cd = Cd.new(:title => "Delete me")
=> #<Cd:23749309141020, _id: "77d7a1f0-625b-012b-aeb4-001a9205e793", title: "Delete me">
>> cd.save
=> #<Cd:23749309141020, _id: "77d7a1f0-625b-012b-aeb4-001a9205e793", _rev: "1029394382", title: "Delete me">
>> RelaxDB.load("77d7a1f0-625b-012b-aeb4-001a9205e793")
=> #<Cd:23749309081880, _id: "77d7a1f0-625b-012b-aeb4-001a9205e793", _rev: "1029394382", title: "Delete me">
>> cd.destroy!
=> #<Cd:23749309141020, _id: "77d7a1f0-625b-012b-aeb4-001a9205e793", _rev: "1029394382", title: "Delete me">
>> RelaxDB.load("77d7a1f0-625b-012b-aeb4-001a9205e793")
RuntimeError: 404:Object Not Found
METHOD:GET
URI:/cd_collection/77d7a1f0-625b-012b-aeb4-001a9205e793
{"error":"not_found","reason":"deleted"}

Dead end

Unfortunately, i seem to have come to a dead end here. Although i think i have everything needed for some basic web app scaffolding, i keep getting the following error when i try to load a page in a web browser:

Rails error when trying to connect to CouchDB

You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.reverse

This happens even if i have a completely empty action in the controller.

The error seems to be coming from ActiveRecord, so i think something is still calling ActiveRecord when it shouldn't. Anyone got any ideas? Why would it even bother to load ActiveRecord when the model inherits from RelaxDB?

Anyway, i'm not finished with RelaxDB yet. I like what i've seen so far. I'll publish this progress and hope to get some feedback on the error. My next post will hopefully be a tutorial about creating the Rails scaffold.

Edit to add: The problem comes when i include relaxdb in the environment.rb. Is there a way to get around this?

Edit again to add: It does seem to work if i put the include statement into the application controller. I think i'm back on track. Stay tuned for the next installment!

Part 6: Getting to scaffolding using RelaxDB

Related posts (automatically calculated)

  1. CouchDB and data storage
  2. Rails on Ubuntu in 14 minutes

  • Claes Nygren
    A few things:
    I think you now have to specify a design_doc in RelaxDB.configure such as:

    RelaxDB.configure :host => "localhost", :port => 5984, :design_doc => "app"

    also you may have to do

    rake db:migrate

    just to create a db/schema.rb, even if you are not using sqlite before you do

    rake spec
  • very helpful . thank you
  • Great writeup! Aimee - I agree, that setting the properties explicitly is a pain.

    I would imagine that creating a view explicitly in CouchDB would let you do a search by title - sort of like creating a stored procedure (but I haven't read parts 6+ yet, so maybe I'm jumping the gun!) Not Rails-y, but it's not designed for Rails...
  • Bruno
    Hi Aimee,
    I configured the app like you sad. But when rails initialize it keeps calling the database.yml. I tried to turn off in enviroment.rb:
    config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
    There's a bug in rails that it doens't work very well. I think I'm missing something here. Do you have your source code anywhere so I can look at it?
    thanks
  • Hi Bruno,

    I don't think you can use database.yml together with CouchDB. There is no couchdb adapter that i know of.

    If you're using RelaxDB you could try putting the details into your config/environment.rb:

    RelaxDB.configure(:host => 'localhost', :port => 5984)
    RelaxDB.use_db 'sysx_development'


    I can't remember whether i maybe had to create an empty MySQL database just so that the database.yml didn't cry.
  • Bruno
    Hi Aimee. First of all congratulations on your blog. I'm learning a lot.
    Now I need some help with running the rspec in my project.
    I'm getting the error:
    rake aborted!
    #42000Unknown database 'sysx_development'
    when i run "rake rspec". This is the database that is in my database.yml file and is setted to mysql. I tried to configure the file like this:
    development:
    adapter: couchdb
    database: sysx_development
    host: 127.0.0.1
    port: 5984
    After that, executing rspec asks for a gem called activerecord-couchdb-adapter. But it doesn't exist. Am I going throw the wrong way?
  • Hi Randy, the specs are working just fine for me with the RelaxDB. Is your CouchDB server running?
  • Randy
    If you are having problems running the spec, you might need to update your gems.

    This is the error I was getting:

    undefined method `generate' for "0322b6d0-6270-012b-24ea-0017f2d556c5":String
  • @Ben, thank you for your encouragement.

    @Chris, i think the most important and useful feature would be to make use of CouchDB's dynamic document structure. If you can make a library that doesn't try to apologise for CouchDB's lack of schema, that would be awesome! :) I think the ability to create its own views when it needs them is a very strong feature.
  • It's really helpful to see the contrast between these frameworks. I'm beginning to think about how best to layer an application-model style wrapper on top of CouchRest. It's good to see what's helpful and what gets in the way, or is missing from these other libraries. Keep up the good work, looking forward to what's next!
  • benatkin
    It's interesting to see how RelaxDB builds on CouchDB. Since CouchDB is so simple, there are many ways to use it, and it's nice to have a library like RelaxDB that provides an example of one way to use it. It looks like RelaxDB makes it easier for people who are used to thinking in terms of tables to begin using CouchDB.

    Nice article. I like how you show every step you take, because I can pick up on little unrelated bits of information (like the "-i" option that can be passed to sudo). It feels like I'm watching a screencast, except I can learn at my own pace and headphones aren't required.
blog comments powered by Disqus
who-am-i
A web programmer in my late-late-twenties, fanatical about open source, free software, loving my job working daily with Ruby on Rails, RSpec, Cucumber and Git.

I am very proud to have created MyChores.co.uk, an online team based tracking system for household chores and other recurring tasks.
Search



Flickr