= CouchObject

CouchObject is a set of classes to help you talk to CouchDb (http://couchdbwiki.com/) with Ruby.

* Author: Johan Sørensen, Sebastian Probst Eide
* Contact: johan (at) johansorensen DOT com
* Home: http://rubyforge.org/projects/couchobject/
* Source (Git): http://gitorious.org/projects/couchobject

== Creating, opening and deleting databases

CouchObject::Database is the first interaction point to your CouchDb. Creating a CouchDb database:

>> CouchObject::Database.create!("http://localhost:5984", "mydb")
=> "ok"=>true
>> CouchObject::Database.all_databases("http://localhost:5984")
=> ["couchobject", "couchobject_test", "foo", "mydb"]
>> db = CouchObject::Database.open("http://localhost:5984/mydb")
=> #<CouchObject::Database:0x642fa8 @server=#<CouchObject::Server:0x642ef4 @connection=#<Net::HTTP localhost:8888 open=false>, @port=8888, @uri=#<URI::HTTP:0x321612 URL:http://localhost:5984>, @host="localhost">, @uri="http://localhost:5984", @dbname="mydb">
>> db.name
=> "mydb"
>> CouchObject::Database.delete!("http://localhost:5984", "mydb")
=> "ok"=>true
>> CouchObject::Database.all_databases("http://localhost:5984").include?("mydb")
=> false

=== Interacting with the database

>> db.get("_all_docs")
=> #<CouchObject::Response:0x14ed364 @response=#<Net::HTTPOK 200 OK readbody=true>, @parsed_body="view"=>"_all_docs">

Issueing CouchObject::Database#get, CouchObject::Database#post, CouchObject::Database#put and CouchObject::Database#delete requests will return a CouchObject::Response object

>> db.get("_all_docs").body
=> "\"rows\":[\n\n]"
>> db.get("_all_docs").parsed_body
=> "view"=>"_all_docs"
>> db.post("", JSON.unparse(=> "bar"))
=> #<CouchObject::Response:0x14d7780 @response=#<Net::HTTPCreated 201 Created readbody=true>, @parsed_body="_id"=>"1206189E4496409DAD3818D241F5478F", "ok"=>true>
>> db.get("_all_docs").parsed_body
=> "_id"=>"1206189E4496409DAD3818D241F5478F"], "view"=>"_all_docs"}
>> db.get("1206189E4496409DAD3818D241F5478F").parsed_body
=> "_id"=>"1206189E4496409DAD3818D241F5478F", "foo"=>"bar"
>> db.delete("1206189E4496409DAD3818D241F5478F").parsed_body
=> "ok"=>true
>> db.get("_all_docs").parsed_body
=> "view"=>"_all_docs"

== The Couch View Requestor

couch_ruby_view_requestor is a JsServer client for CouchDb, allowing you to query documents with Ruby instead of Javascript! All you need to do is pass in a string with something that reponds to #call with one argument (the document):

>> db.post("_temp_view", "proc { |doc| doc[\"foo\"] =~ /ba/ }").parsed_body["rows"]
=> ["_id"=>"28D568C5992CBD2B4711F57225A19517", "value"=>0, "_id"=>"601D858DB2E298EFC4BBA92A11760D1E", "value"=>0, "_id"=>"CABCEB3F2C8B70B3FE24A03FF6AB7A1E", "value"=>0]
>> pp db.post("_temp_view", "proc { |doc| doc[\"foo\"] =~ /ba/ }").parsed_body["rows"]
["_id"=>"28D568C5992CBD2B4711F57225A19517", "value"=>0,
"_id"=>"601D858DB2E298EFC4BBA92A11760D1E", "value"=>0,
"_id"=>"CABCEB3F2C8B70B3FE24A03FF6AB7A1E", "value"=>0]


== The Document object

CouchObject::Document wraps a few things in a nice api. In particular you can use it if you don't want to deal with hashes all the time (similar to ActiveRecord and so on):

>> doc = CouchObject::Document.new({ "foo" => [1,2], "bar" => true })
=> #<CouchObject::Document:0x14a7224 @id=nil, @attributes=2], "bar"=>true, @revision=nil>
>> doc["foo"]
=> [1, 2]
>> doc.foo
=> [1, 2]
>> doc.bar
=> true
>> doc.bar?
=> true

You can also save a document to the database:

>> doc.new?
=> true
>> doc.save(db)
=> #<CouchObject::Response:0x149f358 @response=#<Net::HTTPCreated 201 Created readbody=true>, @parsed_body="_id"=>"CAEADDC895AC4D506542A3796CCA355D", "ok"=>true>
>> doc.id
=> "CAEADDC895AC4D506542A3796CCA355D"

Since CouchObject::Database#get returns a CouchObject::Response object we can convert it into a Document instance easily with CouchObject::Database#to_document:

>> response = db.get(doc.id)
=> #<CouchObject::Response:0x1498b98 @response=#<Net::HTTPOK 200 OK readbody=true>, @parsed_body="_id"=>"CAEADDC895AC4D506542A3796CCA355D", "foo"=>[1, 2], "bar"=>true>
>> the_doc_we_just_saved = response.to_document
=> #<CouchObject::Document:0x148415c @id="CAEADDC895AC4D506542A3796CCA355D", @attributes=2], "bar"=>true, @revision=2030456697>
>> the_doc_we_just_saved.foo
=> [1, 2]
>> doc.foo = "quux"
=> "quux"
>> doc.save(db)
=> #<CouchObject::Response:0x4b0adc @response=#<Net::HTTPCreated 201 Created readbody=true>, @parsed_body="_id"=>"B4077269D2DF8433D145DC0702B9791C", "ok"=>true>


== CouchObject::Persistable

It all started with this module, it maps ruby objects to CouchDb documents.

require 'rubygems'
require 'couch_object'

class RacingCar
include CouchObject::Persistable

def initialize
@races_won = 0
end

def won_a_race
@races_won += 1
end

def lost_a_race
@races_won -= 1
end
end


>> fast_car = RacingCar.new
=> #<RacingCar:0x12b630c @races_won=0>

The first time an object is saved to the database, the database uri
has to be supplied as an argument to the +save+ method:

>> fast_car.save("http://localhost:5984/mydb/")
=> :id=>"B1D0576DA2E7846550DCD61DCC8CDAE4"

>> fast_car.won_a_race
=> 1

Later the object can be saved without supplying a database uri

>> fast_car.save
=> :id=>"B1D0576DA2E7846550DCD61DCC8CDAE4"

We could also use the alias get instead of get_by_id
>> loading_the_fast_car = RacingCar.get_by_id(db_address, "B1D0576DA2E7846550DCD61DCC8CDAE4")
=> #<RacingCar:0x129fddc @races_won=1, @revision="3335068490", @location="http://localhost:5984/mydb", @id="B1D0576DA2E7846550DCD61DCC8CDAE4">

>> loading_the_fast_car.revision == fast_car.revision && loading_the_fast_car.id == fast_car.id
=> true


To omit having to type in the database uri each single time a new object gets saved, it is possible to set the uri to the database in the class itself:

require 'rubygems'
require 'couch_object'

class Balloon
include CouchObject::Persistable
database 'http://localhost:5984/mydb'
end

>> my_balloon = Balloon.new
=> #<Balloon:0x12b4b74>

>> my_balloon.save
=> :id=>"ASD21"

>> other_balloon = Balloon.get_by_id("123")
=> #<Balloon:0x12a6024 @id="123", @location="http://localhost:5984/mydb", @revision="1234">



If you want more control over the way the object is serialized and deserialized (and currently also if the initializer requires arguments) you have to implement and instance method called +to_couch+, which returns a hash of the values you want stored, and a class method called +from_couch+, which should take an array of attributes, which initializes a new object.

require 'rubygems'
require 'couch_object'

class Pizza
include CouchObject::Persistable

def initialize(owner, slices_eaten)
owner == "Sebastian" ? @my_pizza = true : @my_pizza = false
@owner_name = owner
@slices_left = 10 - slices_eaten
end

def my_pizza?
@my_pizza == true
end

def eat
@slices_left -= 1
end

def to_couch
{ :owner => @owner_name,
:slices_left => @slices_left }
end

def self.from_couch(attributes)
slices_eaten = 10 - attributes["slices_left"]
owner = attributes["owner"]
new(owner, slices_eaten)
end
end


>> the_pizza = Pizza.new("Hans",0)
=> #<Pizza:0x12b2644 @slices_left=10, @owner_name="Hans", @my_pizza=false>

>> the_pizza.my_pizza?
=> false
Not our pizza... we better eat it fast before the owner comes.

>> the_pizza.eat
>> the_pizza.eat
>> the_pizza.eat
>> the_pizza.eat
>> the_pizza.eat
>> the_pizza.eat
>> the_pizza.eat
>> the_pizza.eat
>> the_pizza.eat
=> 1
Let's leave a slice for Hans

>> the_pizza.save("http://localhost:5984/mydb")
=> :id=>"88781A212BB4676B48B352B209A4D979"

>> hans_pizza = Pizza.get_by_id("88781A212BB4676B48B352B209A4D979","http://localhost:5984/mydb")
=> #<Pizza:0x12989c4 @id="88781A212BB4676B48B352B209A4D979", @slices_left=1, @location="http://localhost:5984/mydb", @owner_name="Hans", @revision="3412312521", @my_pizza=false>

>> hans_pizza.my_pizza?
=> false
Still not my pizza... damnit

>> hans_pizza.eat
=> 0

Objects that are loaded from the database automatically know where they are stored and therefore don't need the database address as a parameter for the save method
>> hans_pizza.save
=> :id=>"88781A212BB4676B48B352B209A4D979"


You can also have timestamps added automatically to your class using the +add_timestamp_for+ method which takes the values +:on_create+ and +:on_update+.

require 'rubygems'
require 'couch_object'

class BankAccount
include CouchObject::Persistable
add_timestamp_for :on_create, :on_update
database 'http://localhost:5984/mydb'
end

>> my_account = BankAccount.new
=> #<BankAccount:0x12b5b00>

>> my_account.save
=> :id=>"C7A492BB92B1308E646E6536020EAE27"

>> my_account.created_at
=> Sat Feb 02 17:07:36 -0300 2008

>> my_account.updated_at
=> Sat Feb 02 17:07:36 -0300 2008

>> my_account.save
=> :id=>"C7A492BB92B1308E646E6536020EAE27"

>> my_account.created_at
=> Sat Feb 02 17:07:36 -0300 2008

>> my_account.updated_at
=> Sat Feb 02 17:07:53 -0300 2008


=== Relations

There are two different types of relations available for persistable objects:

1. Sub objects are made a part of the object itself and stored in the same database document, or
2. Sub objects are stored to the database as separate documents and only loaded when needed.
(3. The hackish pro way)

1) If you want to use the first option, you have to make sure each sub class either responds to a +to_json+ method or includes the CouchObject::Persistable module so they can be serialized.
CouchObject will then take care of serialization and initialization for you.

Example:

class House
include CouchObject::Persistable
database 'http://localhost:5984/mydb'

def initialize
@owner = OwnerPerson.new
end
attr_accessor :owner
end

class OwnerPerson
include CouchObject::Persistable

def initialize
@name = "Unknown owner"
end
attr_accessor :name
end

>> my_home = House.new
>> my_home.owner.name = "Sebastian"
=> "Sebastian"

>> my_home.save
=> :id=>"3E8E2F7A0AE7DACC7D537ECE5220C6FF"

>> your_home = House.get_by_id("3E8E2F7A0AE7DACC7D537ECE5220C6FF")
>> your_home.owner.name
=> "Sebastian"

Make sure to understand the fact that the OwnerPerson object is stored as part of the House document in the database. The OwnerObject will therefore always be read and initialized when the House is loaded from the database!
If your application has a BlogEntry class and each entry can potentially have thousands of comments and you sometimes want to load the blog entries without loading all the comments at the same time then option number 2, described under, is a better choice for you!

2) There are two types of relations where the sub object(s) are NOT stored in the same database document: +has_one+ relationships and +has_many+ relationships:

==== has_many / belongs_to, :as

In cases where your object has many related sub objects that you might not always need when accessing the object this is the route to go.
Relations are specified using the +has_many+ and +belongs_to ,:as+ methods.

The syntax for relations is as follows:

has_many :name_of_relation
belongs_to :name_of_relation, :as => :name_of_the_has_many_relation

To work, a relation has to be defined in both the has_many and belongs_to class!

As CouchDB doesn't care what we put in it's documents neither does CouchObject. What you name your relations is therefore completely up to you.
An example says more than a thousand words:

Example:

class House
include CouchObject::Persistable
database 'http://localhost:5984/mydb'
has_many :inhabitants
has_many :has
end

class Inhabitant
include CouchObject::Persistable
belongs_to :house, :as => :inhabitants
end

class Book
include CouchObject::Persistable
belongs_to :is_in, :as => :has
end

class Bed
include CouchObject::Persistable
belongs_to :is_in, :as => :has
end

The class definitions above allow us to write the following code:

>> my_home = House.new
>> me = Inhabitant.new
>> a_book = Book.new
>> a_bed = Bed.new

>> my_home.inhabitants << me

>> me.house
=> #<House:0x126ebd8 @couch_object_inhabitants=[#<Inhabitant:0x126cbd0 @couch_object_house=#<House:0x126ebd8 ...>>]>

>> my_home.has << a_bed
>> my_home.has << a_book

>> a_book.is_in
=> #<House:0x126ebd8 @couch_object_has=[#<Bed:0x1268dc8 @couch_object_is_in=#<House:0x126ebd8 ...>>, #<Book:0x126ac90 @couch_object_is_in=#<House:0x126ebd8 ...>>], @couch_object_inhabitants=[#<Inhabitant:0x126cbd0 @couch_object_house=#<House:0x126ebd8 ...>>]>

>> a_bed.is_in
=> #<House:0x126ebd8 @couch_object_has=[#<Bed:0x1268dc8 @couch_object_is_in=#<House:0x126ebd8 ...>>, #<Book:0x126ac90 @couch_object_is_in=#<House:0x126ebd8 ...>>], @couch_object_inhabitants=[#<Inhabitant:0x126cbd0 @couch_object_house=#<House:0x126ebd8 ...>>]>

Saving the master object also saves the children.
>> my_home.save
=> :id=>"C65B9837CD572FC66931F12392A3181E"


>> home_from_db = House.get_by_id("C65B9837CD572FC66931F12392A3181E")
=> #<House:0x1229790 @revision="1793606849", @id="C65B9837CD572FC66931F12392A3181E", @location="http://localhost:5984/mydb">

All relations are lazily loaded the first time they are needed.
>> home_from_db.has
=> [#<Bed:0x1224614 @couch_object_is_in=#<House:0x1229790 @revision="1793606849", @couch_object_has=[...], @id="C65B9837CD572FC66931F12392A3181E", @location="http://localhost:5984/mydb">, @revision="1572453885", @id="37A414CE5440B095C2DF46CEFCBBAF33", @belongs_to_id="C65B9837CD572FC66931F12392A3181E">, #<Book:0x1224358 @couch_object_is_in=#<House:0x1229790 @revision="1793606849", @couch_object_has=[...], @id="C65B9837CD572FC66931F12392A3181E", @location="http://localhost:5984/mydb">, @revision="1604857972", @id="DA67DCB6F4BECE8D8DD9F87F814E8340", @belongs_to_id="C65B9837CD572FC66931F12392A3181E">]

>> home_from_db.has.size
=> 2

>> home_from_db.inhabitants
=> [#<Inhabitant:0x121f7e0 @couch_object_house=#<House:0x1229790 @revision="1793606849", @couch_object_has=[#<Bed:0x1224614 @couch_object_is_in=#<House:0x1229790 ...>, @revision="1572453885", @id="37A414CE5440B095C2DF46CEFCBBAF33", @belongs_to_id="C65B9837CD572FC66931F12392A3181E">, #<Book:0x1224358 @couch_object_is_in=#<House:0x1229790 ...>, @revision="1604857972", @id="DA67DCB6F4BECE8D8DD9F87F814E8340", @belongs_to_id="C65B9837CD572FC66931F12392A3181E">], @couch_object_inhabitants=[...], @id="C65B9837CD572FC66931F12392A3181E", @location="http://localhost:5984/mydb">, @revision="1062171341", @id="ED5DFCF2E28C2D01AE9D4945434CE587", @belongs_to_id="C65B9837CD572FC66931F12392A3181E">]

>> home_from_db.inhabitants.first.house == home_from_db
=> true

>> home_from_db == my_home
=> true


===== Setting and removing relations

The following examples use the class definitions used in the example above to show different ways to initiate and end relationships

>> my_home = House.new
>> me = Inhabitant.new

The relationship can be set either as

>> my_home.inhabitants << me
or
>> me.house = my_home

and ended the following ways:

>> me.house = nil
or
>> my_home.inhabitants.remove(me)



==== has_one / belongs_to

This type of relationship works very much like has_many relationships. The following example shows the difference:

Example:

class Person
include CouchObject::Persistable
database 'http://localhost:5984/mydb'
has_one :life
end

class Life
include CouchObject::Persistable
database 'http://localhost:5984/mydb'
belongs_to :person, :as => :life
end

>> me = Person.new
=> #<Person:0x1270a3c>
>> my_life = Life.new
=> #<Life:0x126eac0>

>> other_persons_life = Life.new
=> #<Life:0x126c8b0>

>> my_life.person == me
=> true

Creating the relationship between the "other_persons_life" and "me" removes the connection between "me" and "my_life"
>> other_persons_life.person = me
=> #<Person:0x1270a3c @couch_object_has_one_life=[#<Life:0x126c8b0 @couch_object_person=#<Person:0x1270a3c ...>>]>

>> me.life == other_persons_life
=> true

>> me.life == my_life
=> false


3) In some cases none of the solutions above completely cover your needs. That's when the last option comes to your aid.
It might be that you need to load a huge number of different types of relations in one swoop, or you want to load objects that aren't strictly related to your class at all.
By using the class level method +get_from_view+ from within your class you can load an arbitrary number of objects you can then do whatever you please with.

The method takes the name of the view as the first parameter and a hash of options as an optional second parameter.
The options can be any of the querying options supported by CouchDB (http://www.couchdbwiki.com/index.php?title=HTTP_View_API) in addition to the database uri if it hasn't been specified on the class level.

Recommended reading:
http://www.couchdbwiki.com/index.php?title=Views
http://www.couchdbwiki.com/index.php?title=View_Collation


==== Stopping relations from being loaded

When adding an object to a has_many relationship all the other objects in the relationship are automatically loaded. In cases where you don't want to access the other relations at all and just want to add a new object, you can use the +do_not_load_has_many_relations+ instance method, which tells the object not to load its relations.
This method call has to be made before the relations are accessed though, or it wont have any effect at all.

Example:

>> home_from_db = House.get_by_id("C65B9837CD572FC66931F12392A3181E")
=> #<House:0x1229790 @revision="1793606849", @id="C65B9837CD572FC66931F12392A3181E", @location="http://localhost:5984/mydb">

Make sure the relations are NOT loaded
>> home_from_db.do_not_load_has_many_relations

Normally all relations are lazily loaded the first time they are needed.
>> home_from_db.has
=> #<House:0x1229790 @revision="1793606849", @id="C65B9837CD572FC66931F12392A3181E", @location="http://localhost:5984/mydb">

>> home_from_db.has.size
=> 0

There is also a method called +do_load_has_many_relations+. But please be aware that relations are only loaded the first time the relations are accessed. We therefore get behavior like this when continuing with the code example from above:

Reactivate loading of relations
>> home_from_db.do_load_has_many_relations

>> home_from_db.has
=> #<House:0x1229790 @revision="1793606849", @id="C65B9837CD572FC66931F12392A3181E", @location="http://localhost:5984/mydb">

>> home_from_db.has.size
=> 0

As the relations are only loaded the first time they are accessed they wont be loaded again later although loading of relations has been reactivated.
This feature is helpful in cases like this though:

>> my_home = House.new
=> #<House:0x1263f80>
>> my_home.inhabitants << Inhabitant.new
>> my_home.has << Book.new << Bed.new
>> my_home.save
=> :revision=>"3659215175"

>> loaded_home = House.get_by_id("4CDC2355A21DDC4D8887882540A84A14")

We want to add a book to the "has" relation without loading the already saved book and bed
>> loaded_home.do_not_load_has_many_relations
=> true

>> loaded_home.has << Book.new

From here there are two different routes:

1) We do not reactivate loading of has_many relations

>> loaded_home.inhabitants.size
=> 0

2) We reactivate loading of has_many relations

>> loaded_home.do_load_has_many_relations
=> false

>> loaded_home.inhabitants.size
=> 1



==== Deleting objects

Persistable objects can be deleted from the database using the +delete+ method. Deleting an object that has has_many relations will also delete the relations. Deleting an object that is in a belongs_to relation will on the other hand NOT delete its relation!


==== Saving relations

When saving an object that has has_many relations, all its relations are also saved.
When saving an object that is in a belongs_to relationship with a new object, the master object is also saved which then again saves all its has_many relations as well! This is because the child object on which the save command was issued needs its parents ID to be validly saved in a relation, which it can't get until the parent has been saved.
When saving an object that is in a belongs_to relationship with a previously saved object only the child gets saved.


===== Callbacks.

CouchObject::Persistable performs callbacks by calling the following methods before and after save, create, update and delete:

+before_save+
+after_save+
+before_create+
+after_create+
+before_update+
+after_update+
+before_delte+
+after_delete+

Example:

class WithCallbacks
include CouchObject::Persistable
database 'foo'

def initialize
@calls = []
end
attr_accessor :calls

def before_save
@calls << "before_save"
end
def after_save
@calls << "after_save"
end
def before_create
@calls << "before_create"
end
def after_create
@calls << "after_create"
end
def before_update
@calls << "before_update"
end
def after_update
@calls << "after_update"
end
def before_delete
@calls << "before_delete"
end
def after_delete
@calls << "after_delete"
end
end

wc = WithCallbacks.new
wc.save
wc.save
wc.delete
wc.calls.should == ["before_save",
"before_create",
"after_create",
"after_save",
"before_save",
"before_update",
"after_update",
"after_save",
"before_delete",
"after_delete"]

=== Smart save

The way a CouchObject instance acts by default is to save itself and all
its relatives regardless of if it needs to be saved or not when the +save+
method is called.

When activating smart save each instance contains a copy of its original
state, and can, based on that, make smart decisions regarding wether or
not it actually needs to save.

Smart save should be used selectively though because keeping a copy of it's
original state will increase the memory usage drastically for objects that
contain a lot of data! There is also an overhead while initializing the
object when the state is created, although that will in most cases be
rather insignificant. These factors add up quickly though if you are working
with many objects at the same time.

Smart save can either be defined in the class definition:

class SmartClass
include CouchObject::Persistable
smart_save
end

instance_with_smart_save_activated = SmartClass.get("foo")

where all instances will receive the smart saving feature by default,
or it can be toggled manually:

class NotSoSmartClass
include CouchObject::Persistable
end

instance_WITHOUT_smart_save_activated = NotSoSmartClass.get("foo")

NotSoSmartClass.smart_save

instance_WITH_smart_save_activated = NotSoSmartClass.get("bar")

NotSoSmartClass.deactivate_smart_save

instance_WITHOUT_smart_save_activated_2 = NotSoSmartClass.get("dong")

The same as in the example above can be achieved with the convenience
method +get_with_smart_save+ which takes the same parameters as +get_by_id+.
The same example could therefor also be written as:

class NotSoSmartClass
include CouchObject::Persistable
end

instance_WITHOUT_smart_save_activated = NotSoSmartClass.get("foo")

instance_WITH_smart_save_activated = NotSoSmartClass.get_with_smart_save("bar")

instance_WITHOUT_smart_save_activated_2 = NotSoSmartClass.get("dong")