- 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
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!
Image may be NSFW.
Clik here to view.
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.
Image may be NSFW.
Clik here to view.
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 …
Image may be NSFW.
Clik here to view.
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
Image may be NSFW.
Clik here to view.
Clik here to view.
Clik here to view.
Clik here to view.
