namelessjon-couchrest 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. data/LICENSE +176 -0
  2. data/README.md +46 -0
  3. data/Rakefile +69 -0
  4. data/THANKS.md +21 -0
  5. data/couchrest.gemspec +111 -0
  6. data/examples/word_count/markov +38 -0
  7. data/examples/word_count/views/books/chunked-map.js +3 -0
  8. data/examples/word_count/views/books/united-map.js +1 -0
  9. data/examples/word_count/views/markov/chain-map.js +6 -0
  10. data/examples/word_count/views/markov/chain-reduce.js +7 -0
  11. data/examples/word_count/views/word_count/count-map.js +6 -0
  12. data/examples/word_count/views/word_count/count-reduce.js +3 -0
  13. data/examples/word_count/word_count.rb +46 -0
  14. data/examples/word_count/word_count_query.rb +40 -0
  15. data/examples/word_count/word_count_views.rb +26 -0
  16. data/history.txt +145 -0
  17. data/lib/couchrest/commands/generate.rb +71 -0
  18. data/lib/couchrest/commands/push.rb +103 -0
  19. data/lib/couchrest/database.rb +373 -0
  20. data/lib/couchrest/design.rb +80 -0
  21. data/lib/couchrest/document.rb +89 -0
  22. data/lib/couchrest/helper/attachments.rb +29 -0
  23. data/lib/couchrest/helper/pager.rb +103 -0
  24. data/lib/couchrest/helper/streamer.rb +51 -0
  25. data/lib/couchrest/helper/upgrade.rb +52 -0
  26. data/lib/couchrest/json_response.rb +14 -0
  27. data/lib/couchrest/middlewares/logger.rb +263 -0
  28. data/lib/couchrest/monkeypatches.rb +42 -0
  29. data/lib/couchrest/response.rb +35 -0
  30. data/lib/couchrest/rest_api.rb +62 -0
  31. data/lib/couchrest/server.rb +90 -0
  32. data/lib/couchrest/support/inheritable_attributes.rb +107 -0
  33. data/lib/couchrest.rb +127 -0
  34. data/spec/couchrest/couchrest_spec.rb +202 -0
  35. data/spec/couchrest/database_spec.rb +870 -0
  36. data/spec/couchrest/design_spec.rb +158 -0
  37. data/spec/couchrest/document_spec.rb +279 -0
  38. data/spec/couchrest/helpers/pager_spec.rb +123 -0
  39. data/spec/couchrest/helpers/streamer_spec.rb +52 -0
  40. data/spec/couchrest/server_spec.rb +35 -0
  41. data/spec/fixtures/attachments/README +3 -0
  42. data/spec/fixtures/attachments/couchdb.png +0 -0
  43. data/spec/fixtures/attachments/test.html +11 -0
  44. data/spec/fixtures/views/lib.js +3 -0
  45. data/spec/fixtures/views/test_view/lib.js +3 -0
  46. data/spec/fixtures/views/test_view/only-map.js +4 -0
  47. data/spec/fixtures/views/test_view/test-map.js +3 -0
  48. data/spec/fixtures/views/test_view/test-reduce.js +3 -0
  49. data/spec/spec.opts +5 -0
  50. data/spec/spec_helper.rb +44 -0
  51. data/utils/remap.rb +27 -0
  52. data/utils/subset.rb +30 -0
  53. metadata +179 -0
@@ -0,0 +1,870 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+
3
+ describe CouchRest::Database do
4
+ before(:each) do
5
+ @cr = CouchRest.new(COUCHHOST)
6
+ @db = @cr.database(TESTDB)
7
+ @db.delete! rescue nil
8
+ @db = @cr.create_db(TESTDB) rescue nil
9
+ end
10
+
11
+ describe "database name including slash" do
12
+ it "should escape the name in the URI" do
13
+ db = @cr.database("foo/bar")
14
+ db.name.should == "foo/bar"
15
+ db.root.should == "#{COUCHHOST}/foo%2Fbar"
16
+ db.uri.should == "/foo%2Fbar"
17
+ end
18
+ end
19
+
20
+ describe "map query with _temp_view in Javascript" do
21
+ before(:each) do
22
+ @db.bulk_save([
23
+ {"wild" => "and random"},
24
+ {"mild" => "yet local"},
25
+ {"another" => ["set","of","keys"]}
26
+ ])
27
+ @temp_view = {:map => "function(doc){for(var w in doc){ if(!w.match(/^_/))emit(w,doc[w])}}"}
28
+ end
29
+ it "should return the result of the temporary function" do
30
+ rs = @db.temp_view(@temp_view)
31
+ rs['rows'].select{|r|r['key'] == 'wild' && r['value'] == 'and random'}.length.should == 1
32
+ end
33
+ it "should work with a range" do
34
+ rs = @db.temp_view(@temp_view, :startkey => "b", :endkey => "z")
35
+ rs['rows'].length.should == 2
36
+ end
37
+ it "should work with a key" do
38
+ rs = @db.temp_view(@temp_view, :key => "wild")
39
+ rs['rows'].length.should == 1
40
+ end
41
+ it "should work with a limit" do
42
+ rs = @db.temp_view(@temp_view, :limit => 1)
43
+ rs['rows'].length.should == 1
44
+ end
45
+ it "should work with multi-keys" do
46
+ rs = @db.temp_view(@temp_view, :keys => ["another", "wild"])
47
+ rs['rows'].length.should == 2
48
+ end
49
+ end
50
+
51
+ describe "map/reduce query with _temp_view in Javascript" do
52
+ before(:each) do
53
+ @db.bulk_save([
54
+ {"beverage" => "beer", :count => 4},
55
+ {"beverage" => "beer", :count => 2},
56
+ {"beverage" => "tea", :count => 3}
57
+ ])
58
+ end
59
+ it "should return the result of the temporary function" do
60
+ rs = @db.temp_view(:map => "function(doc){emit(doc.beverage, doc.count)}", :reduce => "function(beverage,counts){return sum(counts)}")
61
+ # rs.should == 'x'
62
+ rs['rows'][0]['value'].should == 9
63
+ end
64
+ end
65
+
66
+ describe "saving a view" do
67
+ before(:each) do
68
+ @view = {'test' => {'map' => <<-JS
69
+ function(doc) {
70
+ var reg = new RegExp("\\\\W");
71
+ if (doc.word && !reg.test(doc.word)) {
72
+ emit(doc.word,null);
73
+ }
74
+ }
75
+ JS
76
+ }}
77
+ @db.save_doc({
78
+ "_id" => "_design/test",
79
+ :views => @view
80
+ })
81
+ end
82
+ it "should work properly" do
83
+ r = @db.bulk_save([
84
+ {"word" => "once"},
85
+ {"word" => "and again"}
86
+ ])
87
+ r = @db.view('test/test')
88
+ r['total_rows'].should == 1
89
+ end
90
+ it "should round trip" do
91
+ @db.get("_design/test")['views'].should == @view
92
+ end
93
+ end
94
+
95
+ describe "select from an existing view" do
96
+ before(:each) do
97
+ r = @db.save_doc({
98
+ "_id" => "_design/first",
99
+ :views => {
100
+ :test => {
101
+ :map => "function(doc){for(var w in doc){ if(!w.match(/^_/))emit(w,doc[w])}}"
102
+ }
103
+ }
104
+ })
105
+ @db.bulk_save([
106
+ {"wild" => "and random"},
107
+ {"mild" => "yet local"},
108
+ {"another" => ["set","of","keys"]}
109
+ ])
110
+ end
111
+ it "should have the view" do
112
+ @db.get('_design/first')['views']['test']['map'].should include("for(var w in doc)")
113
+ end
114
+ it "should list from the view" do
115
+ rs = @db.view('first/test')
116
+ rs['rows'].select{|r|r['key'] == 'wild' && r['value'] == 'and random'}.length.should == 1
117
+ end
118
+ it "should work with a range" do
119
+ rs = @db.view('first/test', :startkey => "b", :endkey => "z")
120
+ rs['rows'].length.should == 2
121
+ end
122
+ it "should work with a key" do
123
+ rs = @db.view('first/test', :key => "wild")
124
+ rs['rows'].length.should == 1
125
+ end
126
+ it "should work with a limit" do
127
+ rs = @db.view('first/test', :limit => 1)
128
+ rs['rows'].length.should == 1
129
+ end
130
+ it "should work with multi-keys" do
131
+ rs = @db.view('first/test', :keys => ["another", "wild"])
132
+ rs['rows'].length.should == 2
133
+ end
134
+ it "should accept a block" do
135
+ rows = []
136
+ rs = @db.view('first/test', :include_docs => true) do |row|
137
+ rows << row
138
+ end
139
+ rows.length.should == 3
140
+ rs["total_rows"].should == 3
141
+ end
142
+ it "should accept a block with several params" do
143
+ rows = []
144
+ rs = @db.view('first/test', :include_docs => true, :limit => 2) do |row|
145
+ rows << row
146
+ end
147
+ rows.length.should == 2
148
+ end
149
+ end
150
+
151
+ describe "GET (document by id) when the doc exists" do
152
+ before(:each) do
153
+ @r = @db.save_doc({'lemons' => 'from texas', 'and' => 'spain'})
154
+ @docid = "http://example.com/stuff.cgi?things=and%20stuff"
155
+ @db.save_doc({'_id' => @docid, 'will-exist' => 'here'})
156
+ end
157
+ it "should get the document" do
158
+ doc = @db.get(@r['id'])
159
+ doc['lemons'].should == 'from texas'
160
+ end
161
+ it "should work with a funky id" do
162
+ @db.get(@docid)['will-exist'].should == 'here'
163
+ end
164
+ end
165
+
166
+ describe "POST (adding bulk documents)" do
167
+ it "should add them without ids" do
168
+ rs = @db.bulk_save([
169
+ {"wild" => "and random"},
170
+ {"mild" => "yet local"},
171
+ {"another" => ["set","of","keys"]}
172
+ ])
173
+ rs.each do |r|
174
+ @db.get(r['id']).rev.should == r["rev"]
175
+ end
176
+ end
177
+
178
+ it "should use uuids when ids aren't provided" do
179
+ @db.server.stub!(:next_uuid).and_return('asdf6sgadkfhgsdfusdf')
180
+
181
+ docs = [{'key' => 'value'}, {'_id' => 'totally-uniq'}]
182
+ id_docs = [{'key' => 'value', '_id' => 'asdf6sgadkfhgsdfusdf'}, {'_id' => 'totally-uniq'}]
183
+ CouchRest.should_receive(:post).with("#{COUCHHOST}/couchrest-test/_bulk_docs", {:docs => id_docs})
184
+
185
+ @db.bulk_save(docs)
186
+ end
187
+
188
+ it "should add them with uniq ids" do
189
+ rs = @db.bulk_save([
190
+ {"_id" => "oneB", "wild" => "and random"},
191
+ {"_id" => "twoB", "mild" => "yet local"},
192
+ {"another" => ["set","of","keys"]}
193
+ ])
194
+ rs.each do |r|
195
+ @db.get(r['id']).rev.should == r["rev"]
196
+ end
197
+ end
198
+
199
+ it "should empty the bulk save cache if no documents are given" do
200
+ @db.save_doc({"_id" => "bulk_cache_1", "val" => "test"}, true)
201
+ lambda do
202
+ @db.get('bulk_cache_1')
203
+ end.should raise_error(RestClient::ResourceNotFound)
204
+ @db.bulk_save
205
+ @db.get("bulk_cache_1")["val"].should == "test"
206
+ end
207
+
208
+ it "should raise an error that is useful for recovery" do
209
+ @r = @db.save_doc({"_id" => "taken", "field" => "stuff"})
210
+ begin
211
+ rs = @db.bulk_save([
212
+ {"_id" => "taken", "wild" => "and random"},
213
+ {"_id" => "free", "mild" => "yet local"},
214
+ {"another" => ["set","of","keys"]}
215
+ ])
216
+ rescue RestClient::RequestFailed => e
217
+ # soon CouchDB will provide _which_ docs conflicted
218
+ JSON.parse(e.response.body)['error'].should == 'conflict'
219
+ end
220
+ end
221
+ end
222
+
223
+ describe "new document without an id" do
224
+ it "should start empty" do
225
+ @db.documents["total_rows"].should == 0
226
+ end
227
+ it "should create the document and return the id" do
228
+ r = @db.save_doc({'lemons' => 'from texas', 'and' => 'spain'})
229
+ r2 = @db.get(r['id'])
230
+ r2["lemons"].should == "from texas"
231
+ end
232
+ it "should use PUT with UUIDs" do
233
+ CouchRest.should_receive(:put).and_return({"ok" => true, "id" => "100", "rev" => "55"})
234
+ r = @db.save_doc({'just' => ['another document']})
235
+ end
236
+
237
+ end
238
+
239
+ describe "fetch_attachment" do
240
+ before do
241
+ @attach = "<html><head><title>My Doc</title></head><body><p>Has words.</p></body></html>"
242
+ @doc = {
243
+ "_id" => "mydocwithattachment",
244
+ "field" => ["some value"],
245
+ "_attachments" => {
246
+ "test.html" => {
247
+ "type" => "text/html",
248
+ "data" => @attach
249
+ }
250
+ }
251
+ }
252
+ @db.save_doc(@doc)
253
+ end
254
+
255
+ # Depreacated
256
+ # it "should get the attachment with the doc's _id" do
257
+ # @db.fetch_attachment("mydocwithattachment", "test.html").should == @attach
258
+ # end
259
+
260
+ it "should get the attachment with the doc itself" do
261
+ @db.fetch_attachment(@db.get('mydocwithattachment'), 'test.html').should == @attach
262
+ end
263
+ end
264
+
265
+ describe "PUT attachment from file" do
266
+ before(:each) do
267
+ filename = FIXTURE_PATH + '/attachments/couchdb.png'
268
+ @file = File.open(filename, "rb")
269
+ end
270
+ after(:each) do
271
+ @file.close
272
+ end
273
+ it "should save the attachment to a new doc" do
274
+ r = @db.put_attachment({'_id' => 'attach-this'}, 'couchdb.png', image = @file.read, {:content_type => 'image/png'})
275
+ r['ok'].should == true
276
+ doc = @db.get("attach-this")
277
+ attachment = @db.fetch_attachment(doc,"couchdb.png")
278
+ if attachment.respond_to?(:net_http_res)
279
+ attachment.net_http_res.body.should == image
280
+ else
281
+ attachment.should == image
282
+ end
283
+ end
284
+ end
285
+
286
+ describe "PUT document with attachment" do
287
+ before(:each) do
288
+ @attach = "<html><head><title>My Doc</title></head><body><p>Has words.</p></body></html>"
289
+ doc = {
290
+ "_id" => "mydocwithattachment",
291
+ "field" => ["some value"],
292
+ "_attachments" => {
293
+ "test.html" => {
294
+ "type" => "text/html",
295
+ "data" => @attach
296
+ }
297
+ }
298
+ }
299
+ @db.save_doc(doc)
300
+ @doc = @db.get("mydocwithattachment")
301
+ end
302
+ it "should save and be indicated" do
303
+ @doc['_attachments']['test.html']['length'].should == @attach.length
304
+ end
305
+ it "should be there" do
306
+ attachment = @db.fetch_attachment(@doc,"test.html")
307
+ attachment.should == @attach
308
+ end
309
+ end
310
+
311
+ describe "PUT document with attachment stub" do
312
+ before(:each) do
313
+ @attach = "<html><head><title>My Doc</title></head><body><p>Has words.</p></body></html>"
314
+ doc = {
315
+ '_id' => 'mydocwithattachment',
316
+ 'field' => ['some_value'],
317
+ '_attachments' => {
318
+ 'test.html' => {
319
+ 'type' => 'text/html', 'data' => @attach
320
+ }
321
+ }
322
+ }
323
+ @db.save_doc(doc)
324
+ doc['_rev'].should_not be_nil
325
+ doc['field'] << 'another value'
326
+ @db.save_doc(doc)["ok"].should be_true
327
+ end
328
+
329
+ it 'should be there' do
330
+ doc = @db.get('mydocwithattachment')
331
+ attachment = @db.fetch_attachment(doc, 'test.html')
332
+ attachment.unpack('m').first == @attach
333
+ end
334
+ end
335
+
336
+ describe "PUT document with multiple attachments" do
337
+ before(:each) do
338
+ @attach = "<html><head><title>My Doc</title></head><body><p>Has words.</p></body></html>"
339
+ @attach2 = "<html><head><title>Other Doc</title></head><body><p>Has more words.</p></body></html>"
340
+ @doc = {
341
+ "_id" => "mydocwithattachment",
342
+ "field" => ["some value"],
343
+ "_attachments" => {
344
+ "test.html" => {
345
+ "type" => "text/html",
346
+ "data" => @attach
347
+ },
348
+ "other.html" => {
349
+ "type" => "text/html",
350
+ "data" => @attach2
351
+ }
352
+ }
353
+ }
354
+ @db.save_doc(@doc)
355
+ @doc = @db.get("mydocwithattachment")
356
+ end
357
+ it "should save and be indicated" do
358
+ @doc['_attachments']['test.html']['length'].should == @attach.length
359
+ @doc['_attachments']['other.html']['length'].should == @attach2.length
360
+ end
361
+ it "should be there" do
362
+ attachment = @db.fetch_attachment(@doc,"test.html")
363
+ attachment.should == @attach
364
+ end
365
+ it "should be there" do
366
+ attachment = @db.fetch_attachment(@doc,"other.html")
367
+ attachment.should == @attach2
368
+ end
369
+ end
370
+
371
+ describe "DELETE an attachment directly from the database" do
372
+ before(:each) do
373
+ doc = {
374
+ '_id' => 'mydocwithattachment',
375
+ '_attachments' => {
376
+ 'test.html' => {
377
+ 'type' => 'text/html',
378
+ 'data' => "<html><head><title>My Doc</title></head><body><p>Has words.</p></body></html>"
379
+ }
380
+ }
381
+ }
382
+ @db.save_doc(doc)
383
+ @doc = @db.get('mydocwithattachment')
384
+ end
385
+ it "should delete the attachment" do
386
+ lambda { @db.fetch_attachment(@doc,'test.html') }.should_not raise_error
387
+ @db.delete_attachment(@doc, "test.html")
388
+ @doc = @db.get('mydocwithattachment') # avoid getting a 409
389
+ lambda{ @db.fetch_attachment(@doc,'test.html')}.should raise_error
390
+ end
391
+
392
+ it "should force a delete even if we get a 409" do
393
+ @doc['new_attribute'] = 'something new'
394
+ @db.put_attachment(@doc, 'test', File.open(File.join(FIXTURE_PATH, 'attachments', 'test.html')).read)
395
+ # at this point the revision number changed, if we try to save doc one more time
396
+ # we would get a 409.
397
+ lambda{ @db.save_doc(@doc) }.should raise_error
398
+ lambda{ @db.delete_attachment(@doc, "test", true) }.should_not raise_error
399
+ end
400
+ end
401
+
402
+ describe "POST document with attachment (with funky name)" do
403
+ before(:each) do
404
+ @attach = "<html><head><title>My Funky Doc</title></head><body><p>Has words.</p></body></html>"
405
+ @doc = {
406
+ "field" => ["some other value"],
407
+ "_attachments" => {
408
+ "http://example.com/stuff.cgi?things=and%20stuff" => {
409
+ "type" => "text/html",
410
+ "data" => @attach
411
+ }
412
+ }
413
+ }
414
+ @docid = @db.save_doc(@doc)['id']
415
+ end
416
+ it "should save and be indicated" do
417
+ doc = @db.get(@docid)
418
+ doc['_attachments']['http://example.com/stuff.cgi?things=and%20stuff']['length'].should == @attach.length
419
+ end
420
+ it "should be there" do
421
+ doc = @db.get(@docid)
422
+ attachment = @db.fetch_attachment(doc,"http://example.com/stuff.cgi?things=and%20stuff")
423
+ attachment.should == @attach
424
+ end
425
+ end
426
+
427
+ describe "PUT (new document with url id)" do
428
+ it "should create the document" do
429
+ @docid = "http://example.com/stuff.cgi?things=and%20stuff"
430
+ @db.save_doc({'_id' => @docid, 'will-exist' => 'here'})
431
+ lambda{@db.save_doc({'_id' => @docid})}.should raise_error(RestClient::Request::RequestFailed)
432
+ @db.get(@docid)['will-exist'].should == 'here'
433
+ end
434
+ end
435
+
436
+ describe "PUT (new document with id)" do
437
+ it "should start without the document" do
438
+ # r = @db.save_doc({'lemons' => 'from texas', 'and' => 'spain'})
439
+ @db.documents['rows'].each do |doc|
440
+ doc['id'].should_not == 'my-doc'
441
+ end
442
+ # should_not include({'_id' => 'my-doc'})
443
+ # this needs to be a loop over docs on content with the post
444
+ # or instead make it return something with a fancy <=> method
445
+ end
446
+ it "should create the document" do
447
+ @db.save_doc({'_id' => 'my-doc', 'will-exist' => 'here'})
448
+ lambda{@db.save_doc({'_id' => 'my-doc'})}.should raise_error(RestClient::Request::RequestFailed)
449
+ end
450
+ end
451
+
452
+ describe "PUT (existing document with rev)" do
453
+ before(:each) do
454
+ @db.save_doc({'_id' => 'my-doc', 'will-exist' => 'here'})
455
+ @doc = @db.get('my-doc')
456
+ @docid = "http://example.com/stuff.cgi?things=and%20stuff"
457
+ @db.save_doc({'_id' => @docid, 'now' => 'save'})
458
+ end
459
+ it "should start with the document" do
460
+ @doc['will-exist'].should == 'here'
461
+ @db.get(@docid)['now'].should == 'save'
462
+ end
463
+ it "should save with url id" do
464
+ doc = @db.get(@docid)
465
+ doc['yaml'] = ['json', 'word.']
466
+ @db.save_doc doc
467
+ @db.get(@docid)['yaml'].should == ['json', 'word.']
468
+ end
469
+ it "should fail to resave without the rev" do
470
+ @doc['them-keys'] = 'huge'
471
+ @doc['_rev'] = 'wrong'
472
+ # @db.save_doc(@doc)
473
+ lambda {@db.save_doc(@doc)}.should raise_error
474
+ end
475
+ it "should update the document" do
476
+ @doc['them-keys'] = 'huge'
477
+ @db.save_doc(@doc)
478
+ now = @db.get('my-doc')
479
+ now['them-keys'].should == 'huge'
480
+ end
481
+ end
482
+
483
+ describe "cached bulk save" do
484
+ it "stores documents in a database-specific cache" do
485
+ td = {"_id" => "btd1", "val" => "test"}
486
+ @db.save_doc(td, true)
487
+ @db.instance_variable_get("@bulk_save_cache").should == [td]
488
+
489
+ end
490
+
491
+ it "doesn't save to the database until the configured cache size is exceded" do
492
+ @db.bulk_save_cache_limit = 3
493
+ td1 = {"_id" => "td1", "val" => true}
494
+ td2 = {"_id" => "td2", "val" => 4}
495
+ @db.save_doc(td1, true)
496
+ @db.save_doc(td2, true)
497
+ lambda do
498
+ @db.get(td1["_id"])
499
+ end.should raise_error(RestClient::ResourceNotFound)
500
+ lambda do
501
+ @db.get(td2["_id"])
502
+ end.should raise_error(RestClient::ResourceNotFound)
503
+ td3 = {"_id" => "td3", "val" => "foo"}
504
+ @db.save_doc(td3, true)
505
+ @db.get(td1["_id"])["val"].should == td1["val"]
506
+ @db.get(td2["_id"])["val"].should == td2["val"]
507
+ @db.get(td3["_id"])["val"].should == td3["val"]
508
+ end
509
+
510
+ it "clears the bulk save cache the first time a non bulk save is requested" do
511
+ td1 = {"_id" => "blah", "val" => true}
512
+ td2 = {"_id" => "steve", "val" => 3}
513
+ @db.bulk_save_cache_limit = 50
514
+ @db.save_doc(td1, true)
515
+ lambda do
516
+ @db.get(td1["_id"])
517
+ end.should raise_error(RestClient::ResourceNotFound)
518
+ @db.save_doc(td2)
519
+ @db.get(td1["_id"])["val"].should == td1["val"]
520
+ @db.get(td2["_id"])["val"].should == td2["val"]
521
+ end
522
+ end
523
+
524
+ describe "DELETE existing document" do
525
+ before(:each) do
526
+ @r = @db.save_doc({'lemons' => 'from texas', 'and' => 'spain'})
527
+ @docid = "http://example.com/stuff.cgi?things=and%20stuff"
528
+ @db.save_doc({'_id' => @docid, 'will-exist' => 'here'})
529
+ end
530
+ it "should work" do
531
+ doc = @db.get(@r['id'])
532
+ doc['and'].should == 'spain'
533
+ @db.delete_doc doc
534
+ lambda{@db.get @r['id']}.should raise_error
535
+ end
536
+ it "should work with uri id" do
537
+ doc = @db.get(@docid)
538
+ @db.delete_doc doc
539
+ lambda{@db.get @docid}.should raise_error
540
+ end
541
+ it "should fail without an _id" do
542
+ lambda{@db.delete_doc({"not"=>"a real doc"})}.should raise_error(ArgumentError)
543
+ end
544
+ it "should defer actual deletion when using bulk save" do
545
+ doc = @db.get(@docid)
546
+ @db.delete_doc doc, true
547
+ lambda{@db.get @docid}.should_not raise_error
548
+ @db.bulk_save
549
+ lambda{@db.get @docid}.should raise_error
550
+ end
551
+
552
+ end
553
+
554
+ describe "UPDATE existing document" do
555
+ before :each do
556
+ @id = @db.save_doc({
557
+ 'article' => 'Pete Doherty Kicked Out For Nazi Anthem',
558
+ 'upvotes' => 10,
559
+ 'link' => 'http://beatcrave.com/2009-11-30/pete-doherty-kicked-out-for-nazi-anthem/'})['id']
560
+ end
561
+ it "should work under normal conditions" do
562
+ @db.update_doc @id do |doc|
563
+ doc['upvotes'] += 1
564
+ doc
565
+ end
566
+ @db.get(@id)['upvotes'].should == 11
567
+ end
568
+ it "should fail if update_limit is reached" do
569
+ lambda do
570
+ @db.update_doc @id do |doc|
571
+ # modify and save the doc so that a collision happens
572
+ conflicting_doc = @db.get @id
573
+ conflicting_doc['upvotes'] += 1
574
+ @db.save_doc conflicting_doc
575
+
576
+ # then try saving it through the update
577
+ doc['upvotes'] += 1
578
+ doc
579
+ end
580
+ end.should raise_error(RestClient::RequestFailed)
581
+ end
582
+ it "should not fail if update_limit is not reached" do
583
+ limit = 5
584
+ lambda do
585
+ @db.update_doc @id do |doc|
586
+ # same as the last spec except we're only forcing 5 conflicts
587
+ if limit > 0
588
+ conflicting_doc = @db.get @id
589
+ conflicting_doc['upvotes'] += 1
590
+ @db.save_doc conflicting_doc
591
+ limit -= 1
592
+ end
593
+ doc['upvotes'] += 1
594
+ doc
595
+ end
596
+ end.should_not raise_error
597
+ @db.get(@id)['upvotes'].should == 16
598
+ end
599
+ end
600
+
601
+ describe "COPY existing document" do
602
+ before :each do
603
+ @r = @db.save_doc({'artist' => 'Zappa', 'title' => 'Muffin Man'})
604
+ @docid = 'tracks/zappa/muffin-man'
605
+ @doc = @db.get(@r['id'])
606
+ end
607
+ describe "to a new location" do
608
+ it "should work" do
609
+ @db.copy_doc @doc, @docid
610
+ newdoc = @db.get(@docid)
611
+ newdoc['artist'].should == 'Zappa'
612
+ end
613
+ it "should fail without an _id" do
614
+ lambda{@db.copy_doc({"not"=>"a real doc"})}.should raise_error(ArgumentError)
615
+ end
616
+ end
617
+ describe "to an existing location" do
618
+ before :each do
619
+ @db.save_doc({'_id' => @docid, 'will-exist' => 'here'})
620
+ end
621
+ it "should fail without a rev" do
622
+ lambda{@db.copy_doc @doc, @docid}.should raise_error(RestClient::RequestFailed)
623
+ end
624
+ it "should succeed with a rev" do
625
+ @to_be_overwritten = @db.get(@docid)
626
+ @db.copy_doc @doc, "#{@docid}?rev=#{@to_be_overwritten['_rev']}"
627
+ newdoc = @db.get(@docid)
628
+ newdoc['artist'].should == 'Zappa'
629
+ end
630
+ it "should succeed given the doc to overwrite" do
631
+ @to_be_overwritten = @db.get(@docid)
632
+ @db.copy_doc @doc, @to_be_overwritten
633
+ newdoc = @db.get(@docid)
634
+ newdoc['artist'].should == 'Zappa'
635
+ end
636
+ end
637
+ end
638
+
639
+
640
+ it "should list documents" do
641
+ 5.times do
642
+ @db.save_doc({'another' => 'doc', 'will-exist' => 'anywhere'})
643
+ end
644
+ ds = @db.documents
645
+ ds['rows'].should be_an_instance_of(Array)
646
+ ds['rows'][0]['id'].should_not be_nil
647
+ ds['total_rows'].should == 5
648
+ end
649
+
650
+ describe "documents / _all_docs" do
651
+ before(:each) do
652
+ 9.times do |i|
653
+ @db.save_doc({'_id' => "doc#{i}",'another' => 'doc', 'will-exist' => 'here'})
654
+ end
655
+ end
656
+ it "should list documents with keys and such" do
657
+ ds = @db.documents
658
+ ds['rows'].should be_an_instance_of(Array)
659
+ ds['rows'][0]['id'].should == "doc0"
660
+ ds['total_rows'].should == 9
661
+ end
662
+ it "should take query params" do
663
+ ds = @db.documents(:startkey => 'doc0', :endkey => 'doc3')
664
+ ds['rows'].length.should == 4
665
+ ds = @db.documents(:key => 'doc0')
666
+ ds['rows'].length.should == 1
667
+ end
668
+ it "should work with multi-key" do
669
+ rs = @db.documents :keys => ["doc0", "doc7"]
670
+ rs['rows'].length.should == 2
671
+ end
672
+ it "should work with include_docs" do
673
+ ds = @db.documents(:startkey => 'doc0', :endkey => 'doc3', :include_docs => true)
674
+ ds['rows'][0]['doc']['another'].should == "doc"
675
+ end
676
+ it "should have the bulk_load macro" do
677
+ rs = @db.bulk_load ["doc0", "doc7"]
678
+ rs['rows'].length.should == 2
679
+ rs['rows'][0]['doc']['another'].should == "doc"
680
+ end
681
+ end
682
+
683
+
684
+ describe "compacting a database" do
685
+ it "should compact the database" do
686
+ db = @cr.database('couchrest-test')
687
+ # r =
688
+ db.compact!
689
+ # r['ok'].should == true
690
+ end
691
+ end
692
+
693
+ describe "deleting a database" do
694
+ it "should start with the test database" do
695
+ @cr.databases.should include('couchrest-test')
696
+ end
697
+ it "should delete the database" do
698
+ db = @cr.database('couchrest-test')
699
+ # r =
700
+ db.delete!
701
+ # r['ok'].should == true
702
+ @cr.databases.should_not include('couchrest-test')
703
+ end
704
+ end
705
+
706
+ describe "simply replicating a database" do
707
+ before(:each) do
708
+ @db.save_doc({'_id' => 'test_doc', 'some-value' => 'foo'})
709
+ @other_db = @cr.database(REPLICATIONDB)
710
+ end
711
+
712
+ shared_examples_for "simply replicated" do
713
+ it "contains the document from the original database" do
714
+ doc = @other_db.get('test_doc')
715
+ doc['some-value'].should == 'foo'
716
+ end
717
+ end
718
+
719
+ describe "via pulling" do
720
+ before(:each) do
721
+ @other_db.recreate!
722
+ @other_db.replicate_from @db
723
+ end
724
+
725
+ it_should_behave_like "simply replicated"
726
+ end
727
+
728
+ describe "via pushing" do
729
+ before(:each) do
730
+ @other_db.recreate!
731
+ @db.replicate_to @other_db
732
+ end
733
+
734
+ it_should_behave_like "simply replicated"
735
+ end
736
+
737
+ describe "implicitly creating target" do
738
+ describe "via pulling" do
739
+ before(:each) do
740
+ @other_db.replicate_from(@db, false, true)
741
+ end
742
+
743
+ it_should_behave_like "simply replicated"
744
+ end
745
+
746
+ describe "via pushing" do
747
+ before(:each) do
748
+ @db.replicate_to(@other_db, false, true)
749
+ end
750
+
751
+ it_should_behave_like "simply replicated"
752
+ end
753
+ end
754
+ end
755
+
756
+ describe "continuously replicating a database" do
757
+ before(:each) do
758
+ @db.save_doc({'_id' => 'test_doc', 'some-value' => 'foo'})
759
+ @other_db = @cr.database(REPLICATIONDB)
760
+ end
761
+
762
+ shared_examples_for "continuously replicated" do
763
+ it "contains the document from the original database" do
764
+ sleep(1.5) # Allow some time to replicate
765
+ doc = @other_db.get('test_doc')
766
+ doc['some-value'].should == 'foo'
767
+ end
768
+
769
+ it "contains documents saved after replication initiated" do
770
+ @db.save_doc({'_id' => 'test_doc_after', 'some-value' => 'bar'})
771
+ sleep(1.5) # Allow some time to replicate
772
+ doc = @other_db.get('test_doc_after')
773
+ doc['some-value'].should == 'bar'
774
+ end
775
+ end
776
+
777
+ describe "via pulling" do
778
+ before(:each) do
779
+ @other_db.recreate!
780
+ @other_db.replicate_from(@db, true)
781
+ end
782
+
783
+ it_should_behave_like "continuously replicated"
784
+ end
785
+
786
+ describe "via pushing" do
787
+ before(:each) do
788
+ @other_db.recreate!
789
+ @db.replicate_to(@other_db, true)
790
+ end
791
+
792
+ it_should_behave_like "continuously replicated"
793
+ end
794
+
795
+ describe "implicitly creating target" do
796
+ before(:each) do
797
+ @other_db.replicate_from(@db, true, true)
798
+ end
799
+
800
+ after(:each) do
801
+ @other_db.delete!
802
+ end
803
+
804
+ describe "via pulling" do
805
+ it_should_behave_like "continuously replicated"
806
+ end
807
+
808
+ describe "via pushing" do
809
+ it_should_behave_like "continuously replicated"
810
+ end
811
+ end
812
+ end
813
+
814
+ describe "creating a database" do
815
+ before(:each) do
816
+ @db = @cr.database('couchrest-test-db_to_create')
817
+ @db.delete! if @cr.databases.include?('couchrest-test-db_to_create')
818
+ end
819
+
820
+ it "should just work fine" do
821
+ @cr.databases.should_not include('couchrest-test-db_to_create')
822
+ @db.create!
823
+ @cr.databases.should include('couchrest-test-db_to_create')
824
+ end
825
+ end
826
+
827
+ describe "recreating a database" do
828
+ before(:each) do
829
+ @db = @cr.database('couchrest-test-db_to_create')
830
+ @db2 = @cr.database('couchrest-test-db_to_recreate')
831
+ @cr.databases.include?(@db.name) ? nil : @db.create!
832
+ @cr.databases.include?(@db2.name) ? @db2.delete! : nil
833
+ end
834
+
835
+ it "should drop and recreate a database" do
836
+ @cr.databases.should include(@db.name)
837
+ @db.recreate!
838
+ @cr.databases.should include(@db.name)
839
+ end
840
+
841
+ it "should recreate a db even tho it doesn't exist" do
842
+ @cr.databases.should_not include(@db2.name)
843
+ @db2.recreate!
844
+ @cr.databases.should include(@db2.name)
845
+ end
846
+
847
+ end
848
+
849
+ describe "searching a database" do
850
+ before(:each) do
851
+ search_function = { 'defaults' => {'store' => 'no', 'index' => 'analyzed_no_norms'},
852
+ 'index' => "function(doc) { ret = new Document(); ret.add(doc['name'], {'field':'name'}); ret.add(doc['age'], {'field':'age'}); return ret; }" }
853
+ @db.save_doc({'_id' => '_design/search', 'fulltext' => {'people' => search_function}})
854
+ @db.save_doc({'_id' => 'john', 'name' => 'John', 'age' => '31'})
855
+ @db.save_doc({'_id' => 'jack', 'name' => 'Jack', 'age' => '32'})
856
+ @db.save_doc({'_id' => 'dave', 'name' => 'Dave', 'age' => '33'})
857
+ end
858
+
859
+ it "should be able to search a database using couchdb-lucene" do
860
+ if couchdb_lucene_available?
861
+ result = @db.search('search/people', :q => 'name:J*')
862
+ doc_ids = result['rows'].collect{ |row| row['id'] }
863
+ doc_ids.size.should == 2
864
+ doc_ids.should include('john')
865
+ doc_ids.should include('jack')
866
+ end
867
+ end
868
+ end
869
+
870
+ end