CouchDB on Rails (part 4 of ?)
- 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 initIncluding 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
=> #<Cd:0x2b852ffcbfb8 @associations={}, @attributes={:_id=>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 #<Cd:0x2b852ffcbfb8>
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')
=> #<Cd:0x2b9f6de3e120 @callbacks={}, @associations={}, @attributes={:_id=>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')
=> #<Cd:0x2b8a2c7174b0 @callbacks={}, @associations={}, @attributes={:_id=>nil, :_rev=>nil, :title=>"Peaceful, The World Lays Me Down"}, @connection=#<ActiveCouch::Connection:0x2b8a2c71e6c0 @site=#<URI::HTTP:0x2b8a2c71e3f0 URL:http://localhost:5984/>>>
>> cd.save
ActiveCouch::ResourceNotFound: Failed with 404 Object Not FoundAh, it helps to watch the log of CouchDB as we do this.
[info] [<0.602.0>] HTTP Error (code 404): not_found [info] [<0.602.0>] 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')
=> trueThe CouchDB log indicates that the database was created:
[info] [<0.603.0>] 127.0.0.1 - - "PUT /cds" 201
Now maybe we'll have some success.
>> cd.save
=> true
>> cd
=> #<Cd:0x2b339ae93ce0 @callbacks={}, @associations={}, @attributes={:_id=>"29dc9e093c369e13c05c87baf5be00f8", :_rev=>"3659454517", :title=>"Peaceful, The World Lays Me Down"}, @connection=#<ActiveCouch::Connection:0x2b8a2c71e6c0 @site=#<URI::HTTP:0x2b8a2c71e3f0 URL:http://localhost:5984/>, @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!
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:ClassLOL. 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 FoundSee what it's trying to do:
[info] [<0.621.0>] HTTP Error (code 404): {not_found,missing}
[info] [<0.621.0>] 127.0.0.1 - - "GET /cds/_view/by_title/by_title" 404Fine, 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.
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"})
=> [#<Cd:0x2b339ae67fa0 @callbacks={}, @associations={}, @attributes={:_id=>"29dc9e093c369e13c05c87baf5be00f8", :_rev=>"3659454517", :title=>"Peaceful, The World Lays Me Down"}, @connection=#<ActiveCouch::Connection:0x2b339ae9aef0 @site=#<URI::HTTP:0x2b339ae9ac20 URL:http://localhost:5984/>, @default_header={"Content-Type"=>"application/json"}>>]Hey, can we find by ID?
>> Cd.find('29dc9e093c369e13c05c87baf5be00f8')
=> [#<Cd:0x2b339ae67fa0 @callbacks={}, @associations={}, @attributes={:_id=>"29dc9e093c369e13c05c87baf5be00f8", :_rev=>"3659454517", :title=>"Peaceful, The World Lays Me Down"}, @connection=#<ActiveCouch::Connection:0x2b339ae9aef0 @site=#<URI::HTTP:0x2b339ae9ac20 URL:http://localhost:5984/>, @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 …
<!-- app/views/cds/show.html.erb --> <h1>A CD in CouchDB</h1> <p> <strong>Title:</strong> <%= @cd.title %> </p>
I'll need to declare a resource in config/routes.rb …
map.resources :cds
Come on then, fingers crossed …
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
-
motorcycleleatherjackets
-
Wicked Tickets
-
bumperstickers
-
computer keeps freezing
-
Valentine Poems
-
Work at Home
-
Treat Acne
-
Club Penguin Cheats
-
Club Penguin Cheats
-
Craig
-
Referral Code Full Tilt Poker
-
dave8291
-
Neuseeland
-
payday loans
-
digital radio scanners
-
Buy Backlinks
-
IRS Bod
-
HBC
-
simonly
-
Chanel Sunglasses
-
COCA
-
aimee
-
Ariel Monaco
-
arunthampi
-
aimee


