samlown-couchrest 0.35

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 (105) hide show
  1. data/LICENSE +176 -0
  2. data/README.md +46 -0
  3. data/Rakefile +67 -0
  4. data/THANKS.md +19 -0
  5. data/examples/model/example.rb +144 -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 +114 -0
  17. data/lib/couchrest/commands/generate.rb +71 -0
  18. data/lib/couchrest/commands/push.rb +103 -0
  19. data/lib/couchrest/core/adapters/restclient.rb +35 -0
  20. data/lib/couchrest/core/database.rb +377 -0
  21. data/lib/couchrest/core/design.rb +79 -0
  22. data/lib/couchrest/core/document.rb +84 -0
  23. data/lib/couchrest/core/http_abstraction.rb +48 -0
  24. data/lib/couchrest/core/response.rb +16 -0
  25. data/lib/couchrest/core/rest_api.rb +49 -0
  26. data/lib/couchrest/core/server.rb +88 -0
  27. data/lib/couchrest/core/view.rb +4 -0
  28. data/lib/couchrest/helper/pager.rb +103 -0
  29. data/lib/couchrest/helper/streamer.rb +51 -0
  30. data/lib/couchrest/helper/upgrade.rb +51 -0
  31. data/lib/couchrest/middlewares/logger.rb +263 -0
  32. data/lib/couchrest/mixins/attachments.rb +31 -0
  33. data/lib/couchrest/mixins/attribute_protection.rb +74 -0
  34. data/lib/couchrest/mixins/callbacks.rb +532 -0
  35. data/lib/couchrest/mixins/class_proxy.rb +124 -0
  36. data/lib/couchrest/mixins/collection.rb +260 -0
  37. data/lib/couchrest/mixins/design_doc.rb +103 -0
  38. data/lib/couchrest/mixins/document_queries.rb +80 -0
  39. data/lib/couchrest/mixins/extended_attachments.rb +70 -0
  40. data/lib/couchrest/mixins/extended_document_mixins.rb +9 -0
  41. data/lib/couchrest/mixins/properties.rb +154 -0
  42. data/lib/couchrest/mixins/validation.rb +246 -0
  43. data/lib/couchrest/mixins/views.rb +173 -0
  44. data/lib/couchrest/mixins.rb +4 -0
  45. data/lib/couchrest/monkeypatches.rb +113 -0
  46. data/lib/couchrest/more/casted_model.rb +58 -0
  47. data/lib/couchrest/more/extended_document.rb +310 -0
  48. data/lib/couchrest/more/property.rb +50 -0
  49. data/lib/couchrest/more/typecast.rb +175 -0
  50. data/lib/couchrest/support/blank.rb +42 -0
  51. data/lib/couchrest/support/class.rb +190 -0
  52. data/lib/couchrest/support/rails.rb +42 -0
  53. data/lib/couchrest/validation/auto_validate.rb +157 -0
  54. data/lib/couchrest/validation/contextual_validators.rb +78 -0
  55. data/lib/couchrest/validation/validation_errors.rb +125 -0
  56. data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
  57. data/lib/couchrest/validation/validators/confirmation_validator.rb +107 -0
  58. data/lib/couchrest/validation/validators/format_validator.rb +122 -0
  59. data/lib/couchrest/validation/validators/formats/email.rb +66 -0
  60. data/lib/couchrest/validation/validators/formats/url.rb +43 -0
  61. data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
  62. data/lib/couchrest/validation/validators/length_validator.rb +139 -0
  63. data/lib/couchrest/validation/validators/method_validator.rb +89 -0
  64. data/lib/couchrest/validation/validators/numeric_validator.rb +109 -0
  65. data/lib/couchrest/validation/validators/required_field_validator.rb +114 -0
  66. data/lib/couchrest.rb +162 -0
  67. data/spec/couchrest/core/couchrest_spec.rb +184 -0
  68. data/spec/couchrest/core/database_spec.rb +840 -0
  69. data/spec/couchrest/core/design_spec.rb +138 -0
  70. data/spec/couchrest/core/document_spec.rb +275 -0
  71. data/spec/couchrest/core/server_spec.rb +35 -0
  72. data/spec/couchrest/helpers/pager_spec.rb +122 -0
  73. data/spec/couchrest/helpers/streamer_spec.rb +52 -0
  74. data/spec/couchrest/more/attribute_protection_spec.rb +150 -0
  75. data/spec/couchrest/more/casted_extended_doc_spec.rb +79 -0
  76. data/spec/couchrest/more/casted_model_spec.rb +406 -0
  77. data/spec/couchrest/more/extended_doc_attachment_spec.rb +135 -0
  78. data/spec/couchrest/more/extended_doc_inherited_spec.rb +40 -0
  79. data/spec/couchrest/more/extended_doc_spec.rb +797 -0
  80. data/spec/couchrest/more/extended_doc_subclass_spec.rb +98 -0
  81. data/spec/couchrest/more/extended_doc_view_spec.rb +456 -0
  82. data/spec/couchrest/more/property_spec.rb +628 -0
  83. data/spec/fixtures/attachments/README +3 -0
  84. data/spec/fixtures/attachments/couchdb.png +0 -0
  85. data/spec/fixtures/attachments/test.html +11 -0
  86. data/spec/fixtures/more/article.rb +35 -0
  87. data/spec/fixtures/more/card.rb +22 -0
  88. data/spec/fixtures/more/cat.rb +20 -0
  89. data/spec/fixtures/more/course.rb +22 -0
  90. data/spec/fixtures/more/event.rb +8 -0
  91. data/spec/fixtures/more/invoice.rb +17 -0
  92. data/spec/fixtures/more/person.rb +9 -0
  93. data/spec/fixtures/more/question.rb +6 -0
  94. data/spec/fixtures/more/service.rb +12 -0
  95. data/spec/fixtures/more/user.rb +22 -0
  96. data/spec/fixtures/views/lib.js +3 -0
  97. data/spec/fixtures/views/test_view/lib.js +3 -0
  98. data/spec/fixtures/views/test_view/only-map.js +4 -0
  99. data/spec/fixtures/views/test_view/test-map.js +3 -0
  100. data/spec/fixtures/views/test_view/test-reduce.js +3 -0
  101. data/spec/spec.opts +6 -0
  102. data/spec/spec_helper.rb +49 -0
  103. data/utils/remap.rb +27 -0
  104. data/utils/subset.rb +30 -0
  105. metadata +223 -0
@@ -0,0 +1,797 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path("../../../spec_helper", __FILE__)
4
+ require File.join(FIXTURE_PATH, 'more', 'article')
5
+ require File.join(FIXTURE_PATH, 'more', 'course')
6
+ require File.join(FIXTURE_PATH, 'more', 'cat')
7
+
8
+ describe "ExtendedDocument" do
9
+
10
+ class WithDefaultValues < CouchRest::ExtendedDocument
11
+ use_database TEST_SERVER.default_database
12
+ property :preset, :type => 'Object', :default => {:right => 10, :top_align => false}
13
+ property :set_by_proc, :default => Proc.new{Time.now}, :cast_as => 'Time'
14
+ property :tags, :type => ['String'], :default => []
15
+ property :read_only_with_default, :default => 'generic', :read_only => true
16
+ property :default_false, :type => 'Boolean', :default => false
17
+ property :name
18
+ timestamps!
19
+ end
20
+
21
+ class WithCallBacks < CouchRest::ExtendedDocument
22
+ include ::CouchRest::Validation
23
+ use_database TEST_SERVER.default_database
24
+ property :name
25
+ property :run_before_validate
26
+ property :run_after_validate
27
+ property :run_before_save
28
+ property :run_after_save
29
+ property :run_before_create
30
+ property :run_after_create
31
+ property :run_before_update
32
+ property :run_after_update
33
+
34
+ before_validate do |object|
35
+ object.run_before_validate = true
36
+ end
37
+ after_validate do |object|
38
+ object.run_after_validate = true
39
+ end
40
+ before_save do |object|
41
+ object.run_before_save = true
42
+ end
43
+ after_save do |object|
44
+ object.run_after_save = true
45
+ end
46
+ before_create do |object|
47
+ object.run_before_create = true
48
+ end
49
+ after_create do |object|
50
+ object.run_after_create = true
51
+ end
52
+ before_update do |object|
53
+ object.run_before_update = true
54
+ end
55
+ after_update do |object|
56
+ object.run_after_update = true
57
+ end
58
+
59
+ property :run_one
60
+ property :run_two
61
+ property :run_three
62
+
63
+ before_save :run_one_method, :run_two_method do |object|
64
+ object.run_three = true
65
+ end
66
+ def run_one_method
67
+ self.run_one = true
68
+ end
69
+ def run_two_method
70
+ self.run_two = true
71
+ end
72
+
73
+ attr_accessor :run_it
74
+ property :conditional_one
75
+ property :conditional_two
76
+
77
+ before_save :conditional_one_method, :conditional_two_method, :if => proc { self.run_it }
78
+ def conditional_one_method
79
+ self.conditional_one = true
80
+ end
81
+ def conditional_two_method
82
+ self.conditional_two = true
83
+ end
84
+ end
85
+
86
+ class WithTemplateAndUniqueID < CouchRest::ExtendedDocument
87
+ use_database TEST_SERVER.default_database
88
+ unique_id do |model|
89
+ model['important-field']
90
+ end
91
+ property :preset, :default => 'value'
92
+ property :has_no_default
93
+ end
94
+
95
+ class WithGetterAndSetterMethods < CouchRest::ExtendedDocument
96
+ use_database TEST_SERVER.default_database
97
+
98
+ property :other_arg
99
+ def arg
100
+ other_arg
101
+ end
102
+
103
+ def arg=(value)
104
+ self.other_arg = "foo-#{value}"
105
+ end
106
+ end
107
+
108
+ class WithAfterInitializeMethod < CouchRest::ExtendedDocument
109
+ use_database TEST_SERVER.default_database
110
+
111
+ property :some_value
112
+
113
+ def after_initialize
114
+ self.some_value ||= "value"
115
+ end
116
+
117
+ end
118
+
119
+
120
+ before(:each) do
121
+ @obj = WithDefaultValues.new
122
+ end
123
+
124
+ describe "instance database connection" do
125
+ it "should use the default database" do
126
+ @obj.database.name.should == 'couchrest-test'
127
+ end
128
+
129
+ it "should override the default db" do
130
+ @obj.database = TEST_SERVER.database!('couchrest-extendedmodel-test')
131
+ @obj.database.name.should == 'couchrest-extendedmodel-test'
132
+ @obj.database.delete!
133
+ end
134
+ end
135
+
136
+ describe "a new model" do
137
+ it "should be a new document" do
138
+ @obj = Basic.new
139
+ @obj.rev.should be_nil
140
+ @obj.should be_new
141
+ @obj.should be_new_document
142
+ @obj.should be_new_record
143
+ end
144
+
145
+ it "should not failed on a nil value in argument" do
146
+ @obj = Basic.new(nil)
147
+ @obj.should == { 'couchrest-type' => 'Basic' }
148
+ end
149
+ end
150
+
151
+ describe "creating a new document" do
152
+ it "should instantialize and save a document" do
153
+ article = Article.create(:title => 'my test')
154
+ article.title.should == 'my test'
155
+ article.should_not be_new
156
+ end
157
+
158
+ it "should trigger the create callbacks" do
159
+ doc = WithCallBacks.create(:name => 'my other test')
160
+ doc.run_before_create.should be_true
161
+ doc.run_after_create.should be_true
162
+ doc.run_before_save.should be_true
163
+ doc.run_after_save.should be_true
164
+ end
165
+ end
166
+
167
+ describe "update attributes without saving" do
168
+ before(:each) do
169
+ a = Article.get "big-bad-danger" rescue nil
170
+ a.destroy if a
171
+ @art = Article.new(:title => "big bad danger")
172
+ @art.save
173
+ end
174
+ it "should work for attribute= methods" do
175
+ @art['title'].should == "big bad danger"
176
+ @art.update_attributes_without_saving('date' => Time.now, :title => "super danger")
177
+ @art['title'].should == "super danger"
178
+ end
179
+ it "should silently ignore _id" do
180
+ @art.update_attributes_without_saving('_id' => 'foobar')
181
+ @art['_id'].should_not == 'foobar'
182
+ end
183
+ it "should silently ignore _rev" do
184
+ @art.update_attributes_without_saving('_rev' => 'foobar')
185
+ @art['_rev'].should_not == 'foobar'
186
+ end
187
+ it "should silently ignore created_at" do
188
+ @art.update_attributes_without_saving('created_at' => 'foobar')
189
+ @art['created_at'].should_not == 'foobar'
190
+ end
191
+ it "should silently ignore updated_at" do
192
+ @art.update_attributes_without_saving('updated_at' => 'foobar')
193
+ @art['updated_at'].should_not == 'foobar'
194
+ end
195
+ it "should also work using attributes= alias" do
196
+ @art.respond_to?(:attributes=).should be_true
197
+ @art.attributes = {'date' => Time.now, :title => "something else"}
198
+ @art['title'].should == "something else"
199
+ end
200
+
201
+ it "should flip out if an attribute= method is missing" do
202
+ lambda {
203
+ @art.update_attributes_without_saving('slug' => "new-slug", :title => "super danger")
204
+ }.should raise_error
205
+ end
206
+
207
+ it "should not change other attributes if there is an error" do
208
+ lambda {
209
+ @art.update_attributes_without_saving('slug' => "new-slug", :title => "super danger")
210
+ }.should raise_error
211
+ @art['title'].should == "big bad danger"
212
+ end
213
+ end
214
+
215
+ describe "update attributes" do
216
+ before(:each) do
217
+ a = Article.get "big-bad-danger" rescue nil
218
+ a.destroy if a
219
+ @art = Article.new(:title => "big bad danger")
220
+ @art.save
221
+ end
222
+ it "should save" do
223
+ @art['title'].should == "big bad danger"
224
+ @art.update_attributes('date' => Time.now, :title => "super danger")
225
+ loaded = Article.get(@art.id)
226
+ loaded['title'].should == "super danger"
227
+ end
228
+ end
229
+
230
+ describe "with default" do
231
+ it "should have the default value set at initalization" do
232
+ @obj.preset.should == {:right => 10, :top_align => false}
233
+ end
234
+
235
+ it "should have the default false value explicitly assigned" do
236
+ @obj.default_false.should == false
237
+ end
238
+
239
+ it "should automatically call a proc default at initialization" do
240
+ @obj.set_by_proc.should be_an_instance_of(Time)
241
+ @obj.set_by_proc.should == @obj.set_by_proc
242
+ @obj.set_by_proc.should < Time.now
243
+ end
244
+
245
+ it "should let you overwrite the default values" do
246
+ obj = WithDefaultValues.new(:preset => 'test')
247
+ obj.preset = 'test'
248
+ end
249
+
250
+ it "should work with a default empty array" do
251
+ obj = WithDefaultValues.new(:tags => ['spec'])
252
+ obj.tags.should == ['spec']
253
+ end
254
+
255
+ it "should set default value of read-only property" do
256
+ obj = WithDefaultValues.new
257
+ obj.read_only_with_default.should == 'generic'
258
+ end
259
+ end
260
+
261
+ describe "a doc with template values (CR::Model spec)" do
262
+ before(:all) do
263
+ WithTemplateAndUniqueID.all.map{|o| o.destroy(true)}
264
+ WithTemplateAndUniqueID.database.bulk_delete
265
+ @tmpl = WithTemplateAndUniqueID.new
266
+ @tmpl2 = WithTemplateAndUniqueID.new(:preset => 'not_value', 'important-field' => '1')
267
+ end
268
+ it "should have fields set when new" do
269
+ @tmpl.preset.should == 'value'
270
+ end
271
+ it "shouldn't override explicitly set values" do
272
+ @tmpl2.preset.should == 'not_value'
273
+ end
274
+ it "shouldn't override existing documents" do
275
+ @tmpl2.save
276
+ tmpl2_reloaded = WithTemplateAndUniqueID.get(@tmpl2.id)
277
+ @tmpl2.preset.should == 'not_value'
278
+ tmpl2_reloaded.preset.should == 'not_value'
279
+ end
280
+ end
281
+
282
+ describe "getting a model" do
283
+ before(:all) do
284
+ @art = Article.new(:title => 'All About Getting')
285
+ @art.save
286
+ end
287
+ it "should load and instantiate it" do
288
+ foundart = Article.get @art.id
289
+ foundart.title.should == "All About Getting"
290
+ end
291
+
292
+ it "should return nil if `get` is used and the document doesn't exist" do
293
+ foundart = Article.get 'matt aimonetti'
294
+ foundart.should be_nil
295
+ end
296
+
297
+ it "should raise an error if `get!` is used and the document doesn't exist" do
298
+ lambda{foundart = Article.get!('matt aimonetti')}.should raise_error
299
+ end
300
+ end
301
+
302
+ describe "getting a model with a subobjects array" do
303
+ before(:all) do
304
+ course_doc = {
305
+ "title" => "Metaphysics 200",
306
+ "questions" => [
307
+ {
308
+ "q" => "Carve the ___ of reality at the ___.",
309
+ "a" => ["beast","joints"]
310
+ },{
311
+ "q" => "Who layed the smack down on Leibniz's Law?",
312
+ "a" => "Willard Van Orman Quine"
313
+ }
314
+ ]
315
+ }
316
+ r = Course.database.save_doc course_doc
317
+ @course = Course.get r['id']
318
+ end
319
+ it "should load the course" do
320
+ @course.title.should == "Metaphysics 200"
321
+ end
322
+ it "should instantiate them as such" do
323
+ @course["questions"][0].a[0].should == "beast"
324
+ end
325
+ end
326
+
327
+ describe "finding all instances of a model" do
328
+ before(:all) do
329
+ WithTemplateAndUniqueID.design_doc_fresh = false
330
+ WithTemplateAndUniqueID.all.map{|o| o.destroy(true)}
331
+ WithTemplateAndUniqueID.database.bulk_delete
332
+ WithTemplateAndUniqueID.new('important-field' => '1').save
333
+ WithTemplateAndUniqueID.new('important-field' => '2').save
334
+ WithTemplateAndUniqueID.new('important-field' => '3').save
335
+ WithTemplateAndUniqueID.new('important-field' => '4').save
336
+ end
337
+ it "should make the design doc" do
338
+ WithTemplateAndUniqueID.all
339
+ d = WithTemplateAndUniqueID.design_doc
340
+ d['views']['all']['map'].should include('WithTemplateAndUniqueID')
341
+ end
342
+ it "should find all" do
343
+ rs = WithTemplateAndUniqueID.all
344
+ rs.length.should == 4
345
+ end
346
+ end
347
+
348
+ describe "counting all instances of a model" do
349
+ before(:each) do
350
+ @db = reset_test_db!
351
+ WithTemplateAndUniqueID.design_doc_fresh = false
352
+ end
353
+
354
+ it ".count should return 0 if there are no docuemtns" do
355
+ WithTemplateAndUniqueID.count.should == 0
356
+ end
357
+
358
+ it ".count should return the number of documents" do
359
+ WithTemplateAndUniqueID.new('important-field' => '1').save
360
+ WithTemplateAndUniqueID.new('important-field' => '2').save
361
+ WithTemplateAndUniqueID.new('important-field' => '3').save
362
+
363
+ WithTemplateAndUniqueID.count.should == 3
364
+ end
365
+ end
366
+
367
+ describe "finding the first instance of a model" do
368
+ before(:each) do
369
+ @db = reset_test_db!
370
+ WithTemplateAndUniqueID.design_doc_fresh = false
371
+ WithTemplateAndUniqueID.new('important-field' => '1').save
372
+ WithTemplateAndUniqueID.new('important-field' => '2').save
373
+ WithTemplateAndUniqueID.new('important-field' => '3').save
374
+ WithTemplateAndUniqueID.new('important-field' => '4').save
375
+ end
376
+ it "should make the design doc" do
377
+ WithTemplateAndUniqueID.all
378
+ d = WithTemplateAndUniqueID.design_doc
379
+ d['views']['all']['map'].should include('WithTemplateAndUniqueID')
380
+ end
381
+ it "should find first" do
382
+ rs = WithTemplateAndUniqueID.first
383
+ rs['important-field'].should == "1"
384
+ end
385
+ it "should return nil if no instances are found" do
386
+ WithTemplateAndUniqueID.all.each {|obj| obj.destroy }
387
+ WithTemplateAndUniqueID.first.should be_nil
388
+ end
389
+ end
390
+
391
+ describe "getting a model with a subobject field" do
392
+ before(:all) do
393
+ course_doc = {
394
+ "title" => "Metaphysics 410",
395
+ "professor" => {
396
+ "name" => ["Mark", "Hinchliff"]
397
+ },
398
+ "ends_at" => "2008/12/19 13:00:00 +0800"
399
+ }
400
+ r = Course.database.save_doc course_doc
401
+ @course = Course.get r['id']
402
+ end
403
+ it "should load the course" do
404
+ @course["professor"]["name"][1].should == "Hinchliff"
405
+ end
406
+ it "should instantiate the professor as a person" do
407
+ @course['professor'].last_name.should == "Hinchliff"
408
+ end
409
+ it "should instantiate the ends_at as a Time" do
410
+ @course['ends_at'].should == Time.parse("2008/12/19 13:00:00 +0800")
411
+ end
412
+ end
413
+
414
+ describe "timestamping" do
415
+ before(:each) do
416
+ oldart = Article.get "saving-this" rescue nil
417
+ oldart.destroy if oldart
418
+ @art = Article.new(:title => "Saving this")
419
+ @art.save
420
+ end
421
+
422
+ it "should define the updated_at and created_at getters and set the values" do
423
+ @obj.save
424
+ obj = WithDefaultValues.get(@obj.id)
425
+ obj.should be_an_instance_of(WithDefaultValues)
426
+ obj.created_at.should be_an_instance_of(Time)
427
+ obj.updated_at.should be_an_instance_of(Time)
428
+ obj.created_at.to_s.should == @obj.updated_at.to_s
429
+ end
430
+ it "should set the time on create" do
431
+ (Time.now - @art.created_at).should < 2
432
+ foundart = Article.get @art.id
433
+ foundart.created_at.should == foundart.updated_at
434
+ end
435
+ it "should set the time on update" do
436
+ @art.save
437
+ @art.created_at.should < @art.updated_at
438
+ end
439
+ end
440
+
441
+ describe "basic saving and retrieving" do
442
+ it "should work fine" do
443
+ @obj.name = "should be easily saved and retrieved"
444
+ @obj.save
445
+ saved_obj = WithDefaultValues.get(@obj.id)
446
+ saved_obj.should_not be_nil
447
+ end
448
+
449
+ it "should parse the Time attributes automatically" do
450
+ @obj.name = "should parse the Time attributes automatically"
451
+ @obj.set_by_proc.should be_an_instance_of(Time)
452
+ @obj.save
453
+ @obj.set_by_proc.should be_an_instance_of(Time)
454
+ saved_obj = WithDefaultValues.get(@obj.id)
455
+ saved_obj.set_by_proc.should be_an_instance_of(Time)
456
+ end
457
+ end
458
+
459
+ describe "saving a model" do
460
+ before(:all) do
461
+ @sobj = Basic.new
462
+ @sobj.save.should == true
463
+ end
464
+
465
+ it "should save the doc" do
466
+ doc = Basic.get(@sobj.id)
467
+ doc['_id'].should == @sobj.id
468
+ end
469
+
470
+ it "should be set for resaving" do
471
+ rev = @obj.rev
472
+ @sobj['another-key'] = "some value"
473
+ @sobj.save
474
+ @sobj.rev.should_not == rev
475
+ end
476
+
477
+ it "should set the id" do
478
+ @sobj.id.should be_an_instance_of(String)
479
+ end
480
+
481
+ it "should set the type" do
482
+ @sobj['couchrest-type'].should == 'Basic'
483
+ end
484
+
485
+ describe "save!" do
486
+
487
+ before(:each) do
488
+ @sobj = Card.new(:first_name => "Marcos", :last_name => "Tapajós")
489
+ end
490
+
491
+ it "should return true if save the document" do
492
+ @sobj.save!.should == true
493
+ end
494
+
495
+ it "should raise error if don't save the document" do
496
+ @sobj.first_name = nil
497
+ lambda { @sobj.save!.should == true }.should raise_error(RuntimeError)
498
+ end
499
+
500
+ end
501
+
502
+
503
+ end
504
+
505
+ describe "saving a model with a unique_id configured" do
506
+ before(:each) do
507
+ @art = Article.new
508
+ @old = Article.database.get('this-is-the-title') rescue nil
509
+ Article.database.delete_doc(@old) if @old
510
+ end
511
+
512
+ it "should be a new document" do
513
+ @art.should be_new
514
+ @art.title.should be_nil
515
+ end
516
+
517
+ it "should require the title" do
518
+ lambda{@art.save}.should raise_error
519
+ @art.title = 'This is the title'
520
+ @art.save.should == true
521
+ end
522
+
523
+ it "should not change the slug on update" do
524
+ @art.title = 'This is the title'
525
+ @art.save.should == true
526
+ @art.title = 'new title'
527
+ @art.save.should == true
528
+ @art.slug.should == 'this-is-the-title'
529
+ end
530
+
531
+ it "should raise an error when the slug is taken" do
532
+ @art.title = 'This is the title'
533
+ @art.save.should == true
534
+ @art2 = Article.new(:title => 'This is the title!')
535
+ lambda{@art2.save}.should raise_error
536
+ end
537
+
538
+ it "should set the slug" do
539
+ @art.title = 'This is the title'
540
+ @art.save.should == true
541
+ @art.slug.should == 'this-is-the-title'
542
+ end
543
+
544
+ it "should set the id" do
545
+ @art.title = 'This is the title'
546
+ @art.save.should == true
547
+ @art.id.should == 'this-is-the-title'
548
+ end
549
+ end
550
+
551
+ describe "saving a model with a unique_id lambda" do
552
+ before(:each) do
553
+ @templated = WithTemplateAndUniqueID.new
554
+ @old = WithTemplateAndUniqueID.get('very-important') rescue nil
555
+ @old.destroy if @old
556
+ end
557
+
558
+ it "should require the field" do
559
+ lambda{@templated.save}.should raise_error
560
+ @templated['important-field'] = 'very-important'
561
+ @templated.save.should == true
562
+ end
563
+
564
+ it "should save with the id" do
565
+ @templated['important-field'] = 'very-important'
566
+ @templated.save.should == true
567
+ t = WithTemplateAndUniqueID.get('very-important')
568
+ t.should == @templated
569
+ end
570
+
571
+ it "should not change the id on update" do
572
+ @templated['important-field'] = 'very-important'
573
+ @templated.save.should == true
574
+ @templated['important-field'] = 'not-important'
575
+ @templated.save.should == true
576
+ t = WithTemplateAndUniqueID.get('very-important')
577
+ t.should == @templated
578
+ end
579
+
580
+ it "should raise an error when the id is taken" do
581
+ @templated['important-field'] = 'very-important'
582
+ @templated.save.should == true
583
+ lambda{WithTemplateAndUniqueID.new('important-field' => 'very-important').save}.should raise_error
584
+ end
585
+
586
+ it "should set the id" do
587
+ @templated['important-field'] = 'very-important'
588
+ @templated.save.should == true
589
+ @templated.id.should == 'very-important'
590
+ end
591
+ end
592
+
593
+ describe "destroying an instance" do
594
+ before(:each) do
595
+ @dobj = Basic.new
596
+ @dobj.save.should == true
597
+ end
598
+ it "should return true" do
599
+ result = @dobj.destroy
600
+ result.should == true
601
+ end
602
+ it "should be resavable" do
603
+ @dobj.destroy
604
+ @dobj.rev.should be_nil
605
+ @dobj.id.should be_nil
606
+ @dobj.save.should == true
607
+ end
608
+ it "should make it go away" do
609
+ @dobj.destroy
610
+ lambda{Basic.get!(@dobj.id)}.should raise_error
611
+ end
612
+ end
613
+
614
+
615
+ describe "callbacks" do
616
+
617
+ before(:each) do
618
+ @doc = WithCallBacks.new
619
+ end
620
+
621
+
622
+ describe "validate" do
623
+ it "should run before_validate before validating" do
624
+ @doc.run_before_validate.should be_nil
625
+ @doc.should be_valid
626
+ @doc.run_before_validate.should be_true
627
+ end
628
+ it "should run after_validate after validating" do
629
+ @doc.run_after_validate.should be_nil
630
+ @doc.should be_valid
631
+ @doc.run_after_validate.should be_true
632
+ end
633
+ end
634
+ describe "save" do
635
+ it "should run the after filter after saving" do
636
+ @doc.run_after_save.should be_nil
637
+ @doc.save.should be_true
638
+ @doc.run_after_save.should be_true
639
+ end
640
+ it "should run the grouped callbacks before saving" do
641
+ @doc.run_one.should be_nil
642
+ @doc.run_two.should be_nil
643
+ @doc.run_three.should be_nil
644
+ @doc.save.should be_true
645
+ @doc.run_one.should be_true
646
+ @doc.run_two.should be_true
647
+ @doc.run_three.should be_true
648
+ end
649
+ it "should not run conditional callbacks" do
650
+ @doc.run_it = false
651
+ @doc.save.should be_true
652
+ @doc.conditional_one.should be_nil
653
+ @doc.conditional_two.should be_nil
654
+ end
655
+ it "should run conditional callbacks" do
656
+ @doc.run_it = true
657
+ @doc.save.should be_true
658
+ @doc.conditional_one.should be_true
659
+ @doc.conditional_two.should be_true
660
+ end
661
+ end
662
+ describe "create" do
663
+ it "should run the before save filter when creating" do
664
+ @doc.run_before_save.should be_nil
665
+ @doc.create.should_not be_nil
666
+ @doc.run_before_save.should be_true
667
+ end
668
+ it "should run the before create filter" do
669
+ @doc.run_before_create.should be_nil
670
+ @doc.create.should_not be_nil
671
+ @doc.create
672
+ @doc.run_before_create.should be_true
673
+ end
674
+ it "should run the after create filter" do
675
+ @doc.run_after_create.should be_nil
676
+ @doc.create.should_not be_nil
677
+ @doc.create
678
+ @doc.run_after_create.should be_true
679
+ end
680
+ end
681
+ describe "update" do
682
+
683
+ before(:each) do
684
+ @doc.save
685
+ end
686
+ it "should run the before update filter when updating an existing document" do
687
+ @doc.run_before_update.should be_nil
688
+ @doc.update
689
+ @doc.run_before_update.should be_true
690
+ end
691
+ it "should run the after update filter when updating an existing document" do
692
+ @doc.run_after_update.should be_nil
693
+ @doc.update
694
+ @doc.run_after_update.should be_true
695
+ end
696
+ it "should run the before update filter when saving an existing document" do
697
+ @doc.run_before_update.should be_nil
698
+ @doc.save
699
+ @doc.run_before_update.should be_true
700
+ end
701
+
702
+ end
703
+ end
704
+
705
+ describe "getter and setter methods" do
706
+ it "should try to call the arg= method before setting :arg in the hash" do
707
+ @doc = WithGetterAndSetterMethods.new(:arg => "foo")
708
+ @doc['arg'].should be_nil
709
+ @doc[:arg].should be_nil
710
+ @doc.other_arg.should == "foo-foo"
711
+ end
712
+ end
713
+
714
+ describe "initialization" do
715
+ it "should call after_initialize method if available" do
716
+ @doc = WithAfterInitializeMethod.new
717
+ @doc['some_value'].should eql('value')
718
+ end
719
+ end
720
+
721
+ describe "recursive validation on an extended document" do
722
+ before :each do
723
+ reset_test_db!
724
+ @cat = Cat.new(:name => 'Sockington')
725
+ end
726
+
727
+ it "should not save if a nested casted model is invalid" do
728
+ @cat.favorite_toy = CatToy.new
729
+ @cat.should_not be_valid
730
+ @cat.save.should be_false
731
+ lambda{@cat.save!}.should raise_error
732
+ end
733
+
734
+ it "should save when nested casted model is valid" do
735
+ @cat.favorite_toy = CatToy.new(:name => 'Squeaky')
736
+ @cat.should be_valid
737
+ @cat.save.should be_true
738
+ lambda{@cat.save!}.should_not raise_error
739
+ end
740
+
741
+ it "should not save when nested collection contains an invalid casted model" do
742
+ @cat.toys = [CatToy.new(:name => 'Feather'), CatToy.new]
743
+ @cat.should_not be_valid
744
+ @cat.save.should be_false
745
+ lambda{@cat.save!}.should raise_error
746
+ end
747
+
748
+ it "should save when nested collection contains valid casted models" do
749
+ @cat.toys = [CatToy.new(:name => 'feather'), CatToy.new(:name => 'ball-o-twine')]
750
+ @cat.should be_valid
751
+ @cat.save.should be_true
752
+ lambda{@cat.save!}.should_not raise_error
753
+ end
754
+
755
+ it "should not fail if the nested casted model doesn't have validation" do
756
+ Cat.property :trainer, :cast_as => 'Person'
757
+ Cat.validates_presence_of :name
758
+ cat = Cat.new(:name => 'Mr Bigglesworth')
759
+ cat.trainer = Person.new
760
+ cat.trainer.validatable?.should be_false
761
+ cat.should be_valid
762
+ cat.save.should be_true
763
+ end
764
+ end
765
+
766
+ describe "searching the contents of an extended document" do
767
+ before :each do
768
+ @db = reset_test_db!
769
+
770
+ names = ["Fuzzy", "Whiskers", "Mr Bigglesworth", "Sockington", "Smitty", "Sammy", "Samson", "Simon"]
771
+ names.each { |name| Cat.create(:name => name) }
772
+
773
+ search_function = { 'defaults' => {'store' => 'no', 'index' => 'analyzed_no_norms'},
774
+ 'index' => "function(doc) { ret = new Document(); ret.add(doc['name'], {'field':'name'}); return ret; }" }
775
+ @db.save_doc({'_id' => '_design/search', 'fulltext' => {'cats' => search_function}})
776
+ end
777
+
778
+ it "should be able to paginate through a large set of search results" do
779
+ if couchdb_lucene_available?
780
+ names = []
781
+ Cat.paginated_each(:design_doc => "search", :view_name => "cats",
782
+ :q => 'name:S*', :search => true, :include_docs => true, :per_page => 3) do |cat|
783
+ cat.should_not be_nil
784
+ names << cat.name
785
+ end
786
+
787
+ names.size.should == 5
788
+ names.should include('Sockington')
789
+ names.should include('Smitty')
790
+ names.should include('Sammy')
791
+ names.should include('Samson')
792
+ names.should include('Simon')
793
+ end
794
+ end
795
+ end
796
+
797
+ end