CouchDB on Rails (part 4 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

A grand moment has come. It is time to attempt to get Rails talking to my CouchDB database.

I have come across four plugins that i intend to try out. ActiveCouch, RelaxDB, couchrest and couchobject. They are all hosted on GitHub because GitHub is made of love.

Today i am going to try ActiveCouch. My main reason for picking it first is that it seems to be inspired by ActiveRecord. It is quite evidently intended for Rails. It seems quite close to what i’m used to. RelaxDB may also be, but it is written for Merb so i may run into troubles that are simply differences between Rails and Merb.

I have some doubts about ActiveCouch, particularly that it hasn’t been updated since July. It also seems to have some odd syntax … but we’ll see how it goes.

A new Rails app

Nothing unusual to begin with …

rails cd_collection
cd cd_collection
git init

Including RSpec comes as second nature to me now … i have been well trained!

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

So i’m guessing the next thing to do is include ActiveCouch as a submodule too.

git submodule add git://github.com/arunthampi/activecouch.git vendor/plugins/activecouch

Looking at the README i see that i need a json gem, which i don’t think i have. There are some other requirements too, so if you’re following along at home, make sure you have them all!

sudo gem install json
gem list

I ran ‘gem list’ to confirm that i now have the json gem, version 1.1.3.

ActiveCouch spec tests

So i presumably want to run the specs that are included with ActiveCouch. I tried this:

cd vendor/plugins/activecouch
rake

… which was probably the wrong thing to do! It has created a pkg directory and zipped the whole lot up into a gem, a tgz and a zip file. Not exactly what i had in mind! What i should have done was check what the Rakefile actually does!

rake -T
rake clobber_package  # Remove package products
rake gem              # Build the gem file activecouch-0.1.9.gem
rake package          # Build all the packages
rake repackage        # Force a rebuild of the package files

So i think i’ll go ahead and clobber the package! But how do you run the specs?

ruby spec/connection/initialize_spec.rb
...

Finished in 0.065529 seconds

3 examples, 0 failures

Well, that’s a good start! But surely i don’t have to go through every file one at a time? This is not filling me with joy. Unfortunately i am revealing a gap in my knowledge. I have never run specs on a plugin before, and i don’t know how to write a Rake file to do it.

Well this is a little bit hacktastic, but let’s give it a go …

# vendor/plugins/activecouch/spec/runner.rb
files = File.join(File.dirname(__FILE__), "*/*.rb")
Dir[files].each do |file|
  puts `ruby #{file}`
end

Great! Loads of stuff fails! Stuff like “Failed with 500 Internal Server Error”. I have ensured that the CouchDB is running on port 5984, as the specs expect. What about “Error creating database – got HTTP response 200”. If it can’t even create a database, that would explain a lot of the other errors. Here’s an odd one:

'ActiveCouch::Base #after_save method with an Object (which implements after_save) as argument should call before_save in the object passed as a param to after_delete' FAILED
expected: true,
     got: false (using ==)

Oh dear, it’s not looking good for ActiveCouch. I am discouraged by a comment i’ve found in the STATUS file: “Version 0.2.0 and upwards will support CouchDB 0.8.0 and upwards.” – i am on CouchDB version 0.8.1. I can’t find a version 0.2.0 of ActiveCouch anywhere. I’m guessing CouchDB has moved on a lot and ActiveCouch has failed to keep up.

Still, on the bright side, quite a lot of the specs did actually pass. I’m determined to get at least something working so that Part 4 is not a total disaster!

I know what, i’ll create a model, but instead of inheriting from ActiveRecord::Base i will inherit from ActiveCouch::Base.

# app/models/cd.rb
class Cd < ActiveCouch::Base
end

Now let’s see what it can do.

script/console FTW

./script/console
Loading development environment (Rails 2.1.0)
>> cd = Cd.new
=> #nil, :_rev=>nil}, @callbacks={}, @connection=nil>

Interesting! I have a CD document! It has the id and rev attributes that i saw last night in the CouchDB web interface. I wonder whether i can add some fields to it.

>> cd.title = 'Peaceful, The World Lays Me Down'
NoMethodError: undefined method `title=' for #

Useless. That’s the kind of error i’d expect from ActiveRecord when it can’t find a field in the database. The whole point of CouchDB is it’s document-oriented and you can put more-or-less whatever fields you like into each document. But wait a minute, here’s an idea:

class Cd < ActiveCouch::Base
  has :title
end

Now let’s try.

>> cd = Cd.new(:title => 'Peaceful, The World Lays Me Down')
=> #nil, :_rev=>nil, :title=>"Peaceful, The World Lays Me Down"}, @connection=nil>

Better. So we have to define our attributes in the model. I can live with that.

>> cd.save
NoMethodError: You have a nil object when you didn't expect it!
The error occurred while evaluating nil.post

Yeah, i wasn’t really expecting that to work! I haven’t told Rails anything at all about my database! Normally we’d do it in config/database.yml of course, but i don’t know how to set up a CouchDB adapter. Fortunately i do know how to trawl through specs (in the absence of any documentation) to look for clues!

class Cd < ActiveCouch::Base
  site 'http://localhost:5984/'
  has :title
end

Please forgive the hard-coding for the moment! Now, let’s try again.

>> cd = Cd.new(:title => 'Peaceful, The World Lays Me Down')
=> #nil, :_rev=>nil, :title=>"Peaceful, The World Lays Me Down"}, @connection=#<activeCouch::Connection:0x2b8a2c71e6c0 @site=#>>
>> cd.save
ActiveCouch::ResourceNotFound: Failed with 404 Object Not Found

Ah, it helps to watch the log of CouchDB as we do this.

[info] [] HTTP Error (code 404): not_found
[info] [] 127.0.0.1 - - "POST /cds" 404

ActiveCouch seems to have made an assumption that the database is called cds. I’m not sure i’m very happy about this. Does this mean there is a separate database per model? Surely that’s non-optimal. Am i misunderstanding something here? Well, let’s try creating the database and see if it keeps ActiveCouch happy.

>> ActiveCouch::Migrator.create_database('http://localhost:5984/', 'cds')
=> true

The CouchDB log indicates that the database was created:

[info] [] 127.0.0.1 - - "PUT /cds" 201

Now maybe we’ll have some success.

>> cd.save
=> true
>> cd
=> #"29dc9e093c369e13c05c87baf5be00f8", :_rev=>"3659454517", :title=>"Peaceful, The World Lays Me Down"}, @connection=#<activeCouch::Connection:0x2b8a2c71e6c0 @site=#, @default_header={"Content-Type"=>"application/json"}>>

Good. It has generated a UUID and a revision for me, so i guess this has been saved.

Sure enough, i can now see the document through the CouchDB web interface. Apparently i am winning!

A document saved to CouchDB from Rails

Finding documents

Unfortunately i can’t seem to find a CD again after i created it.

>> Cd.find(:all)
NoMethodError: You have a nil object when you didn't expect it!
The error occurred while evaluating nil.keys
>> Cd.find(:first)
NoMethodError: You have a nil object when you didn't expect it!
The error occurred while evaluating nil.keys
>> Cd.find(:last)
=> nil
>> Cd.find_by_title("Peaceful, The World Lays Me Down")
NoMethodError: undefined method `find_by_title' for Cd:Class

LOL. ActiveRecord this is not!

I think this would work, if i had a view:

>> Cd.find(:all, :params => {:title => "Peaceful, The World Lays Me Down"})
ActiveCouch::ResourceNotFound: Failed with 404 Object Not Found

See what it’s trying to do:

[info] [] HTTP Error (code 404): {not_found,missing}
[info] [] 127.0.0.1 - - "GET /cds/_view/by_title/by_title" 404

Fine, i’ll create the view. I don’t know how to do it with ActiveCouch so i’ll just do it through the web interface.

Created a view to find CDs by title

Well, that’s good. Yesterday i didn’t think you could create views with underscores in the ID. So let’s try again.

>> Cd.find(:all, :params => {:title => "Peaceful, The World Lays Me Down"})
=> [#"29dc9e093c369e13c05c87baf5be00f8", :_rev=>"3659454517", :title=>"Peaceful, The World Lays Me Down"}, @connection=#<activeCouch::Connection:0x2b339ae9aef0 @site=#, @default_header={"Content-Type"=>"application/json"}>>]

Hey, can we find by ID?

>> Cd.find('29dc9e093c369e13c05c87baf5be00f8')
=> [#"29dc9e093c369e13c05c87baf5be00f8", :_rev=>"3659454517", :title=>"Peaceful, The World Lays Me Down"}, @connection=#<activeCouch::Connection:0x2b339ae9aef0 @site=#, @default_header={"Content-Type"=>"application/json"}>>]

Excellent! This is a bit more promising.

So let’s scaffold up a web interface

# app/controllers/cds_controller.rb
class CdsController < ApplicationController
  def show
    @cd = Cd.find(params[:id])
  end
end

And of course, the view to go with it …


A CD in CouchDB

Title:

I’ll need to declare a resource in config/routes.rb …

map.resources :cds

Come on then, fingers crossed …

Rails using CouchDB!

My gosh, it worked!! :D Yeah yeah, i know it doesn’t look like much, but it has connected to the database, found the document, loaded it into ActiveCouch and output it for me.

My verdict

So i have proved that you can do it. Hurrah! But i don’t like the way i had to do it. I don’t like having the database named after the model (what if, God forbid, you have more than one model?!) I don’t like having to create view called by_title/by_title if i want to search by title. I don’t know why i can’t do Cd.find(:all).

Of course, i may have got it entirely wrong and maybe somebody who knows about ActiveCouch will be able to tell me exactly how i should have done it. But for now, i’ve gone as far as i want to go. I’ve shown how you can do it, and if you’re interested you could probably take it further. But stay tuned, because i think there are better things yet to come in this series …! :)

Thank you if you stuck with me through this long and rambling exploration of ActiveCouch!

Part 5: Integrating with Rails using RelaxDB

Advertisements

37 comments on “CouchDB on Rails (part 4 of ?)

  1. thanks for writing all this up — this kind of work could lead to some cool dbstore patterns

  2. Aimee, this is again, excellent, thanks for taking the time to write this, and keep it up!

    The find/view issue, in my opinion shows that AR is probably not the best model to use with CouchDB. I’m not saying it can’t be made to work together, but I think we’d need to bend both parts to something they weren’t designed to do. Why is that?

    The difference between CouchDB and an RDBMS (that AR is ‘just’ and object representation of) can be summed up as “RDBMS: Static data & dynamic queries vs CouchDB: Dynamic data & fixed queries”. So providing dynamic queries is not exactly CouchDB’s strength. This is not a bad thing though, you still can get all the info you need for your application out of CouchDB. Just in a different way. That makes AR probably not the first choice for an OO abstraction layer on top of CouchDB.

    To fix the API issues of ActiveCouch, see http://wiki.apache.org/couchdb/BreakingChanges for what has changed in CouchDB.

    Again, brilliant content, please continue!

    Cheers
    Jan

  3. Great series of articles. You’ve hit a perfect balance between code examples and explanation which has really got me interested in investigating CouchDB. Thanks very much

  4. @Jan … Thank you for your encouragement, and it is really great to hear your opinions. I’m not particularly impressed with ActiveCouch and i don’t want to be the one to fix its issues. I have a feeling the RelaxDB Merb plugin will prove to be much more flexible and more suited to CouchDB.

    @SD … Thanks, it’s lovely to hear your feedback. I’m glad to be getting other people interested in the Couch! :)

  5. Jup, my comment definitely missed a “In case you want to fix AactiveCouch”… Sorry :-) (New ep tonight? :)

  6. Yes, i’m getting on with some more CouchDB tonight. I feel i’m in a race with TopFunky to see if i can get my next part out before the PeepCode! :) Although i’m really very interested to see what Geoffrey has to say about CouchDB and Rails …!

  7. Personally, I think some of these ActiveRecord-ish libraries are overkill. I wrote a simple 60-line wrapper for CouchRest that does most of what you need, and takes advantage of CouchDB’s strengths (flexible schema, etc.)

  8. Excellent, thank you Geoffrey! I will take a look.

    I’m inclined to agree with you, but to get started i wanted something
    that feels familiar for me.

  9. Hi Aimee – Just stumbled upon this blog post. I’m making changes to ActiveCouch this weekend to make it compatible with 0.8.x as well as more Rails-friendly (so a ‘couchify’ command which will automagically add config files for ActiveCouch to use in your Rails project, etc.)

    Been really busy at work, so haven’t gotten a chance to catch up with CouchDB awesomeness.

    Stay tuned, though :)

  10. Hi Aimee – Just an update, the master branch of ActiveCouch now supports 0.8.0 and upwards. I’m still working on the couchify command (as you may notice from the code) so please give it another go!

    Thanks for your feedback.

    Cheers,
    Arun

  11. Ariel, you are very welcome. I’m sure it’s obvious that they are very much exploratory on my part, but i’m glad to know that they are of help to other people.

  12. Pingback: A little place of calm » Blog Archive » CouchDB and data storage

  13. Hi Aimee – Just stumbled upon this blog post. I'm making changes to ActiveCouch this weekend to make it compatible with 0.8.x as well as more Rails-friendly (so a 'couchify' command which will automagically add config files for ActiveCouch to use in your Rails project, etc.)

  14. Hi Aimee – Just stumbled upon this blog post. I'm making changes to ActiveCouch this weekend to make it compatible with 0.8.x as well as more Rails-friendly (so a 'couchify' command which will automagically add config files for ActiveCouch to use in your Rails project, etc.)

  15. It is not uncommon to have to deal with several constant stores in a single application. A very common approach is to use a relational database that stores paths pointing to files that are stored in a file system.So you might think as CouchDB as a special “file system” for a special part of your data model.Also, in larger applications, multiple stores and complex physical architectures are quite common, so don't be shy of using more than one persistent store for your models.faxless payday loan

  16. Hi Aimee – Just an update, the master branch of ActiveCouch now supports 0.8.0 and upwards. I'm still working on the couchify command (as you may notice from the code) so please give it another go!

  17. Considerably, the mesothelioma article is in reality the greatest on this noteworthy topic. I agree with your conclusions and will eagerly look forward to your next acai berry updates. Saying thanks will not just be sufficient, for best weight loss pills the wonderful clarity in your writing. I will immediately grab your rss feed to stay privy of any updates. Pleasant work and much success in your business dealings!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s