Blob of README.txt (raw blob data)

1 = CouchObject
2
3 CouchObject is a set of classes to help you talk to CouchDb (http://couchdbwiki.com/) with Ruby.
4
5 * Author: Johan Sørensen, Sebastian Probst Eide
6 * Contact: johan (at) johansorensen DOT com
7 * Home: http://rubyforge.org/projects/couchobject/
8 * Source (Git): http://gitorious.org/projects/couchobject
9
10 == Creating, opening and deleting databases
11
12 CouchObject::Database is the first interaction point to your CouchDb. Creating a CouchDb database:
13
14 >> CouchObject::Database.create!("http://localhost:5984", "mydb")
15 => {"ok"=>true}
16 >> CouchObject::Database.all_databases("http://localhost:5984")
17 => ["couchobject", "couchobject_test", "foo", "mydb"]
18 >> db = CouchObject::Database.open("http://localhost:5984/mydb")
19 => #<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">
20 >> db.name
21 => "mydb"
22 >> CouchObject::Database.delete!("http://localhost:5984", "mydb")
23 => {"ok"=>true}
24 >> CouchObject::Database.all_databases("http://localhost:5984").include?("mydb")
25 => false
26
27 === Interacting with the database
28
29 >> db.get("_all_docs")
30 => #<CouchObject::Response:0x14ed364 @response=#<Net::HTTPOK 200 OK readbody=true>, @parsed_body={"rows"=>[], "view"=>"_all_docs"}>
31
32 Issueing CouchObject::Database#get, CouchObject::Database#post, CouchObject::Database#put and CouchObject::Database#delete requests will return a CouchObject::Response object
33
34 >> db.get("_all_docs").body
35 => "{\"view\":\"_all_docs\", \"rows\":[\n\n]}"
36 >> db.get("_all_docs").parsed_body
37 => {"rows"=>[], "view"=>"_all_docs"}
38 >> db.post("", JSON.unparse({"foo" => "bar"}))
39 => #<CouchObject::Response:0x14d7780 @response=#<Net::HTTPCreated 201 Created readbody=true>, @parsed_body={"_rev"=>-992681820, "_id"=>"1206189E4496409DAD3818D241F5478F", "ok"=>true}>
40 >> db.get("_all_docs").parsed_body
41 => {"rows"=>[{"_rev"=>-992681820, "_id"=>"1206189E4496409DAD3818D241F5478F"}], "view"=>"_all_docs"}
42 >> db.get("1206189E4496409DAD3818D241F5478F").parsed_body
43 => {"_rev"=>-992681820, "_id"=>"1206189E4496409DAD3818D241F5478F", "foo"=>"bar"}
44 >> db.delete("1206189E4496409DAD3818D241F5478F").parsed_body
45 => {"_rev"=>548318611, "ok"=>true}
46 >> db.get("_all_docs").parsed_body
47 => {"rows"=>[], "view"=>"_all_docs"}
48
49 == The Couch View Requestor
50
51 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):
52
53 >> db.post("_temp_view", "proc { |doc| doc[\"foo\"] =~ /ba/ }").parsed_body["rows"]
54 => [{"_rev"=>928806717, "_id"=>"28D568C5992CBD2B4711F57225A19517", "value"=>0}, {"_rev"=>-1696868121, "_id"=>"601D858DB2E298EFC4BBA92A11760D1E", "value"=>0}, {"_rev"=>-2093091288, "_id"=>"CABCEB3F2C8B70B3FE24A03FF6AB7A1E", "value"=>0}]
55 >> pp db.post("_temp_view", "proc { |doc| doc[\"foo\"] =~ /ba/ }").parsed_body["rows"]
56 [{"_rev"=>928806717, "_id"=>"28D568C5992CBD2B4711F57225A19517", "value"=>0},
57 {"_rev"=>-1696868121, "_id"=>"601D858DB2E298EFC4BBA92A11760D1E", "value"=>0},
58 {"_rev"=>-2093091288, "_id"=>"CABCEB3F2C8B70B3FE24A03FF6AB7A1E", "value"=>0}]
59
60
61 == The Document object
62
63 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):
64
65 >> doc = CouchObject::Document.new({ "foo" => [1,2], "bar" => true })
66 => #<CouchObject::Document:0x14a7224 @id=nil, @attributes={"foo"=>[1, 2], "bar"=>true}, @revision=nil>
67 >> doc["foo"]
68 => [1, 2]
69 >> doc.foo
70 => [1, 2]
71 >> doc.bar
72 => true
73 >> doc.bar?
74 => true
75
76 You can also save a document to the database:
77
78 >> doc.new?
79 => true
80 >> doc.save(db)
81 => #<CouchObject::Response:0x149f358 @response=#<Net::HTTPCreated 201 Created readbody=true>, @parsed_body={"_rev"=>2030456697, "_id"=>"CAEADDC895AC4D506542A3796CCA355D", "ok"=>true}>
82 >> doc.id
83 => "CAEADDC895AC4D506542A3796CCA355D"
84
85 Since CouchObject::Database#get returns a CouchObject::Response object we can convert it into a Document instance easily with CouchObject::Database#to_document:
86
87 >> response = db.get(doc.id)
88 => #<CouchObject::Response:0x1498b98 @response=#<Net::HTTPOK 200 OK readbody=true>, @parsed_body={"_rev"=>2030456697, "_id"=>"CAEADDC895AC4D506542A3796CCA355D", "foo"=>[1, 2], "bar"=>true}>
89 >> the_doc_we_just_saved = response.to_document
90 => #<CouchObject::Document:0x148415c @id="CAEADDC895AC4D506542A3796CCA355D", @attributes={"foo"=>[1, 2], "bar"=>true}, @revision=2030456697>
91 >> the_doc_we_just_saved.foo
92 => [1, 2]
93 >> doc.foo = "quux"
94 => "quux"
95 >> doc.save(db)
96 => #<CouchObject::Response:0x4b0adc @response=#<Net::HTTPCreated 201 Created readbody=true>, @parsed_body={"_rev"=>1670064786, "_id"=>"B4077269D2DF8433D145DC0702B9791C", "ok"=>true}>
97
98
99 == CouchObject::Persistable
100
101 It all started with this module, it maps ruby objects to CouchDb documents.
102
103 require 'rubygems'
104 require 'couch_object'
105
106 class RacingCar
107 include CouchObject::Persistable
108
109 def initialize
110 @races_won = 0
111 end
112
113 def won_a_race
114 @races_won += 1
115 end
116
117 def lost_a_race
118 @races_won -= 1
119 end
120 end
121
122
123 >> fast_car = RacingCar.new
124 => #<RacingCar:0x12b630c @races_won=0>
125
126 The first time an object is saved to the database, the database uri
127 has to be supplied as an argument to the +save+ method:
128
129 >> fast_car.save("http://localhost:5984/mydb/")
130 => {:revision=>"2107049750", :id=>"B1D0576DA2E7846550DCD61DCC8CDAE4"}
131
132 >> fast_car.won_a_race
133 => 1
134
135 Later the object can be saved without supplying a database uri
136
137 >> fast_car.save
138 => {:revision=>"3335068490", :id=>"B1D0576DA2E7846550DCD61DCC8CDAE4"}
139
140 >> loading_the_fast_car = RacingCar.get(db_address, "B1D0576DA2E7846550DCD61DCC8CDAE4")
141 => #<RacingCar:0x129fddc @races_won=1, @revision="3335068490", @location="http://localhost:5984/mydb", @id="B1D0576DA2E7846550DCD61DCC8CDAE4">
142
143 >> loading_the_fast_car.revision == fast_car.revision && loading_the_fast_car.id == fast_car.id
144 => true
145
146
147 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:
148
149 require 'rubygems'
150 require 'couch_object'
151
152 class Balloon
153 include CouchObject::Persistable
154 database 'http://localhost:5984/mydb'
155 end
156
157 >> my_balloon = Balloon.new
158 => #<Balloon:0x12b4b74>
159
160 >> my_balloon.save
161 => {:revision=>"1118628227", :id=>"ASD21"}
162
163 >> other_balloon = Balloon.get("123")
164 => #<Balloon:0x12a6024 @id="123", @location="http://localhost:5984/mydb", @revision="1234">
165
166
167
168 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.
169
170 require 'rubygems'
171 require 'couch_object'
172
173 class Pizza
174 include CouchObject::Persistable
175
176 def initialize(owner, slices_eaten)
177 owner == "Sebastian" ? @my_pizza = true : @my_pizza = false
178 @owner_name = owner
179 @slices_left = 10 - slices_eaten
180 end
181
182 def my_pizza?
183 @my_pizza == true
184 end
185
186 def eat
187 @slices_left -= 1
188 end
189
190 def to_couch
191 { :owner => @owner_name,
192 :slices_left => @slices_left }
193 end
194
195 def self.from_couch(attributes)
196 slices_eaten = 10 - attributes["slices_left"]
197 owner = attributes["owner"]
198 new(owner, slices_eaten)
199 end
200 end
201
202
203 >> the_pizza = Pizza.new("Hans",0)
204 => #<Pizza:0x12b2644 @slices_left=10, @owner_name="Hans", @my_pizza=false>
205
206 >> the_pizza.my_pizza?
207 => false
208 Not our pizza... we better eat it fast before the owner comes.
209
210 >> the_pizza.eat
211 >> the_pizza.eat
212 >> the_pizza.eat
213 >> the_pizza.eat
214 >> the_pizza.eat
215 >> the_pizza.eat
216 >> the_pizza.eat
217 >> the_pizza.eat
218 >> the_pizza.eat
219 => 1
220 Let's leave a slice for Hans
221
222 >> the_pizza.save("http://localhost:5984/mydb")
223 => {:revision=>"3412312521", :id=>"88781A212BB4676B48B352B209A4D979"}
224
225 >> hans_pizza = Pizza.get("88781A212BB4676B48B352B209A4D979","http://localhost:5984/mydb")
226 => #<Pizza:0x12989c4 @id="88781A212BB4676B48B352B209A4D979", @slices_left=1, @location="http://localhost:5984/mydb", @owner_name="Hans", @revision="3412312521", @my_pizza=false>
227
228 >> hans_pizza.my_pizza?
229 => false
230 Still not my pizza... damnit
231
232 >> hans_pizza.eat
233 => 0
234
235 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
236 >> hans_pizza.save
237 => {:revision=>"2414021920", :id=>"88781A212BB4676B48B352B209A4D979"}
238
239
240 It is possible to load all objects of a certain type with the +all+ parameter
241
242 require 'rubygems'
243 require 'couch_object'
244
245 class Balloon
246 include CouchObject::Persistable
247 database 'http://localhost:5984/mydb'
248 end
249
250 >> Balloon.new.save
251 >> Balloon.new.save
252 >> Balloon.new.save
253 >> Balloon.new.save
254 >> Balloon.new.save
255 >> Balloon.new.save
256 >> balloons = Balloon.all
257 >> balloons.size
258 => 6
259
260
261 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+.
262
263 require 'rubygems'
264 require 'couch_object'
265
266 class BankAccount
267 include CouchObject::Persistable
268 add_timestamp_for :on_create, :on_update
269 database 'http://localhost:5984/mydb'
270 end
271
272 >> my_account = BankAccount.new
273 => #<BankAccount:0x12b5b00>
274
275 >> my_account.save
276 => {:revision=>"3733197239", :id=>"C7A492BB92B1308E646E6536020EAE27"}
277
278 >> my_account.created_at
279 => Sat Feb 02 17:07:36 -0300 2008
280
281 >> my_account.updated_at
282 => Sat Feb 02 17:07:36 -0300 2008
283
284 >> my_account.save
285 => {:revision=>"486590755", :id=>"C7A492BB92B1308E646E6536020EAE27"}
286
287 >> my_account.created_at
288 => Sat Feb 02 17:07:36 -0300 2008
289
290 >> my_account.updated_at
291 => Sat Feb 02 17:07:53 -0300 2008
292
293
294 === Relations
295
296 There are two different types of relations available for persistable objects:
297
298 1. Sub objects are made a part of the object itself and stored in the same database document, or
299 2. Sub objects are stored to the database as separate documents and only loaded when needed.
300 (3. The hackish pro way)
301
302 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.
303 CouchObject will then take care of serialization and initialization for you.
304
305 Example:
306
307 class House
308 include CouchObject::Persistable
309 database 'http://localhost:5984/mydb'
310
311 def initialize
312 @owner = OwnerPerson.new
313 end
314 attr_accessor :owner
315 end
316
317 class OwnerPerson
318 include CouchObject::Persistable
319
320 def initialize
321 @name = "Unknown owner"
322 end
323 attr_accessor :name
324 end
325
326 >> my_home = House.new
327 >> my_home.owner.name = "Sebastian"
328 => "Sebastian"
329
330 >> my_home.save
331 => {:revision=>"519758193", :id=>"3E8E2F7A0AE7DACC7D537ECE5220C6FF"}
332
333 >> your_home = House.get("3E8E2F7A0AE7DACC7D537ECE5220C6FF")
334 >> your_home.owner.name
335 => "Sebastian"
336
337 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!
338 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!
339
340 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:
341
342 ==== has_many / belongs_to, :as
343
344 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.
345 Relations are specified using the +has_many+ and +belongs_to ,:as+ methods.
346
347 The syntax for relations is as follows:
348
349 has_many :name_of_relation
350 belongs_to :name_of_relation, :as => :name_of_the_has_many_relation
351
352 To work, a relation has to be defined in both the has_many and belongs_to class!
353
354 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.
355 An example says more than a thousand words:
356
357 Example:
358
359 class House
360 include CouchObject::Persistable
361 database 'http://localhost:5984/mydb'
362 has_many :inhabitants
363 has_many :has
364 end
365
366 class Inhabitant
367 include CouchObject::Persistable
368 belongs_to :house, :as => :inhabitants
369 end
370
371 class Book
372 include CouchObject::Persistable
373 belongs_to :is_in, :as => :has
374 end
375
376 class Bed
377 include CouchObject::Persistable
378 belongs_to :is_in, :as => :has
379 end
380
381 The class definitions above allow us to write the following code:
382
383 >> my_home = House.new
384 >> me = Inhabitant.new
385 >> a_book = Book.new
386 >> a_bed = Bed.new
387
388 >> my_home.inhabitants << me
389
390 >> me.house
391 => #<House:0x126ebd8 @couch_object_inhabitants=[#<Inhabitant:0x126cbd0 @couch_object_house=#<House:0x126ebd8 ...>>]>
392
393 >> my_home.has << a_bed
394 >> my_home.has << a_book
395
396 >> a_book.is_in
397 => #<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 ...>>]>
398
399 >> a_bed.is_in
400 => #<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 ...>>]>
401
402 Saving the master object also saves the children.
403 >> my_home.save
404 => {:revision=>"1793606849", :id=>"C65B9837CD572FC66931F12392A3181E"}
405
406
407 >> home_from_db = House.get("C65B9837CD572FC66931F12392A3181E")
408 => #<House:0x1229790 @revision="1793606849", @id="C65B9837CD572FC66931F12392A3181E", @location="http://localhost:5984/mydb">
409
410 All relations are lazily loaded the first time they are needed.
411 >> home_from_db.has
412 => [#<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">]
413
414 >> home_from_db.has.size
415 => 2
416
417 >> home_from_db.inhabitants
418 => [#<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">]
419
420 >> home_from_db.inhabitants.first.house == home_from_db
421 => true
422
423 >> home_from_db == my_home
424 => true
425
426
427 ===== Setting and removing relations
428
429 The following examples use the class definitions used in the example above to show different ways to initiate and end relationships
430
431 >> my_home = House.new
432 >> me = Inhabitant.new
433
434 The relationship can be set either as
435
436 >> my_home.inhabitants << me
437 or
438 >> me.house = my_home
439
440 and ended the following ways:
441
442 >> me.house = nil
443 or
444 >> my_home.inhabitants.remove(me)
445
446
447
448 ==== has_one / belongs_to
449
450 This type of relationship works very much like has_many relationships. The following example shows the difference:
451
452 Example:
453
454 class Person
455 include CouchObject::Persistable
456 database 'http://localhost:5984/mydb'
457 has_one :life
458 end
459
460 class Life
461 include CouchObject::Persistable
462 database 'http://localhost:5984/mydb'
463 belongs_to :person, :as => :life
464 end
465
466 >> me = Person.new
467 => #<Person:0x1270a3c>
468 >> my_life = Life.new
469 => #<Life:0x126eac0>
470
471 >> other_persons_life = Life.new
472 => #<Life:0x126c8b0>
473
474 >> my_life.person == me
475 => true
476
477 Creating the relationship between the "other_persons_life" and "me" removes the connection between "me" and "my_life"
478 >> other_persons_life.person = me
479 => #<Person:0x1270a3c @couch_object_has_one_life=[#<Life:0x126c8b0 @couch_object_person=#<Person:0x1270a3c ...>>]>
480
481 >> me.life == other_persons_life
482 => true
483
484 >> me.life == my_life
485 => false
486
487
488 3) In some cases none of the solutions above completely cover your needs. That's when the last option comes to your aid.
489 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.
490 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.
491
492 The method takes the name of the view as the first parameter and a hash of options as an optional second parameter.
493 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.
494
495 Recommended reading:
496 http://www.couchdbwiki.com/index.php?title=Views
497 http://www.couchdbwiki.com/index.php?title=View_Collation
498
499
500 ==== Stopping relations from being loaded
501
502 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.
503 This method call has to be made before the relations are accessed though, or it wont have any effect at all.
504
505 Example:
506
507 >> home_from_db = House.get("C65B9837CD572FC66931F12392A3181E")
508 => #<House:0x1229790 @revision="1793606849", @id="C65B9837CD572FC66931F12392A3181E", @location="http://localhost:5984/mydb">
509
510 Make sure the relations are NOT loaded
511 >> home_from_db.do_not_load_has_many_relations
512
513 Normally all relations are lazily loaded the first time they are needed.
514 >> home_from_db.has
515 => #<House:0x1229790 @revision="1793606849", @id="C65B9837CD572FC66931F12392A3181E", @location="http://localhost:5984/mydb">
516
517 >> home_from_db.has.size
518 => 0
519
520 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:
521
522 Reactivate loading of relations
523 >> home_from_db.do_load_has_many_relations
524
525 >> home_from_db.has
526 => #<House:0x1229790 @revision="1793606849", @id="C65B9837CD572FC66931F12392A3181E", @location="http://localhost:5984/mydb">
527
528 >> home_from_db.has.size
529 => 0
530
531 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.
532 This feature is helpful in cases like this though:
533
534 >> my_home = House.new
535 => #<House:0x1263f80>
536 >> my_home.inhabitants << Inhabitant.new
537 >> my_home.has << Book.new << Bed.new
538 >> my_home.save
539 => {:id=>"4CDC2355A21DDC4D8887882540A84A14", :revision=>"3659215175"}
540
541 >> loaded_home = House.get("4CDC2355A21DDC4D8887882540A84A14")
542
543 We want to add a book to the "has" relation without loading the already saved book and bed
544 >> loaded_home.do_not_load_has_many_relations
545 => true
546
547 >> loaded_home.has << Book.new
548
549 From here there are two different routes:
550
551 1) We do not reactivate loading of has_many relations
552
553 >> loaded_home.inhabitants.size
554 => 0
555
556 2) We reactivate loading of has_many relations
557
558 >> loaded_home.do_load_has_many_relations
559 => false
560
561 >> loaded_home.inhabitants.size
562 => 1
563
564
565
566 ==== Deleting objects
567
568 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!
569
570
571 ==== Saving relations
572
573 When saving an object that has has_many relations, all its relations are also saved.
574 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.
575 When saving an object that is in a belongs_to relationship with a previously saved object only the child gets saved.
576
577
578 ===== Callbacks.
579
580 CouchObject::Persistable performs callbacks by calling the following methods before and after save, create, update and delete:
581
582 +before_save+
583 +after_save+
584 +before_create+
585 +after_create+
586 +before_update+
587 +after_update+
588 +before_delte+
589 +after_delete+
590
591 Example:
592
593 class WithCallbacks
594 include CouchObject::Persistable
595 database 'foo'
596
597 def initialize
598 @calls = []
599 end
600 attr_accessor :calls
601
602 def before_save
603 @calls << "before_save"
604 end
605 def after_save
606 @calls << "after_save"
607 end
608 def before_create
609 @calls << "before_create"
610 end
611 def after_create
612 @calls << "after_create"
613 end
614 def before_update
615 @calls << "before_update"
616 end
617 def after_update
618 @calls << "after_update"
619 end
620 def before_delete
621 @calls << "before_delete"
622 end
623 def after_delete
624 @calls << "after_delete"
625 end
626 end
627
628 wc = WithCallbacks.new
629 wc.save
630 wc.save
631 wc.delete
632 wc.calls.should == ["before_save",
633 "before_create",
634 "after_create",
635 "after_save",
636 "before_save",
637 "before_update",
638 "after_update",
639 "after_save",
640 "before_delete",
641 "after_delete"]
642
643 === Smart save
644
645 The way a CouchObject instance acts by default is to save itself and all
646 its relatives regardless of if it needs to be saved or not when the +save+
647 method is called.
648
649 When activating smart save each instance contains a copy of its original
650 state, and can, based on that, make smart decisions regarding wether or
651 not it actually needs to save.
652
653 Smart save should be used selectively though because keeping a copy of it's
654 original state will increase the memory usage drastically for objects that
655 contain a lot of data! There is also an overhead while initializing the
656 object when the state is created, although that will in most cases be
657 rather insignificant. These factors add up quickly though if you are working
658 with many objects at the same time.
659
660 Smart save can either be defined in the class definition:
661
662 class SmartClass
663 include CouchObject::Persistable
664 smart_save
665 end
666
667 instance_with_smart_save_activated = SmartClass.get("foo")
668
669 where all instances will receive the smart saving feature by default,
670 or it can be toggled manually:
671
672 class NotSoSmartClass
673 include CouchObject::Persistable
674 end
675
676 instance_WITHOUT_smart_save_activated = NotSoSmartClass.get("foo")
677
678 NotSoSmartClass.smart_save
679
680 instance_WITH_smart_save_activated = NotSoSmartClass.get("bar")
681
682 NotSoSmartClass.deactivate_smart_save
683
684 instance_WITHOUT_smart_save_activated_2 = NotSoSmartClass.get("dong")
685
686 The same as in the example above can be achieved with the convenience
687 method +get_with_smart_save+ which takes the same parameters as +get+.
688 The same example could therefor also be written as:
689
690 class NotSoSmartClass
691 include CouchObject::Persistable
692 end
693
694 instance_WITHOUT_smart_save_activated = NotSoSmartClass.get("foo")
695
696 instance_WITH_smart_save_activated = NotSoSmartClass.get_with_smart_save("bar")
697
698 instance_WITHOUT_smart_save_activated_2 = NotSoSmartClass.get("dong")