couchrest 0.12.4 → 0.23

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/README.md +33 -8
  2. data/Rakefile +11 -2
  3. data/examples/model/example.rb +19 -13
  4. data/lib/couchrest.rb +70 -11
  5. data/lib/couchrest/core/database.rb +121 -62
  6. data/lib/couchrest/core/design.rb +7 -17
  7. data/lib/couchrest/core/document.rb +42 -30
  8. data/lib/couchrest/core/response.rb +16 -0
  9. data/lib/couchrest/core/server.rb +47 -10
  10. data/lib/couchrest/helper/upgrade.rb +51 -0
  11. data/lib/couchrest/mixins.rb +4 -0
  12. data/lib/couchrest/mixins/attachments.rb +31 -0
  13. data/lib/couchrest/mixins/callbacks.rb +483 -0
  14. data/lib/couchrest/mixins/class_proxy.rb +108 -0
  15. data/lib/couchrest/mixins/design_doc.rb +90 -0
  16. data/lib/couchrest/mixins/document_queries.rb +44 -0
  17. data/lib/couchrest/mixins/extended_attachments.rb +68 -0
  18. data/lib/couchrest/mixins/extended_document_mixins.rb +7 -0
  19. data/lib/couchrest/mixins/properties.rb +129 -0
  20. data/lib/couchrest/mixins/validation.rb +242 -0
  21. data/lib/couchrest/mixins/views.rb +169 -0
  22. data/lib/couchrest/monkeypatches.rb +81 -6
  23. data/lib/couchrest/more/casted_model.rb +28 -0
  24. data/lib/couchrest/more/extended_document.rb +215 -0
  25. data/lib/couchrest/more/property.rb +40 -0
  26. data/lib/couchrest/support/blank.rb +42 -0
  27. data/lib/couchrest/support/class.rb +176 -0
  28. data/lib/couchrest/validation/auto_validate.rb +163 -0
  29. data/lib/couchrest/validation/contextual_validators.rb +78 -0
  30. data/lib/couchrest/validation/validation_errors.rb +118 -0
  31. data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
  32. data/lib/couchrest/validation/validators/confirmation_validator.rb +99 -0
  33. data/lib/couchrest/validation/validators/format_validator.rb +117 -0
  34. data/lib/couchrest/validation/validators/formats/email.rb +66 -0
  35. data/lib/couchrest/validation/validators/formats/url.rb +43 -0
  36. data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
  37. data/lib/couchrest/validation/validators/length_validator.rb +134 -0
  38. data/lib/couchrest/validation/validators/method_validator.rb +89 -0
  39. data/lib/couchrest/validation/validators/numeric_validator.rb +104 -0
  40. data/lib/couchrest/validation/validators/required_field_validator.rb +109 -0
  41. data/spec/couchrest/core/database_spec.rb +189 -124
  42. data/spec/couchrest/core/design_spec.rb +13 -6
  43. data/spec/couchrest/core/document_spec.rb +231 -177
  44. data/spec/couchrest/core/server_spec.rb +35 -0
  45. data/spec/couchrest/helpers/pager_spec.rb +1 -1
  46. data/spec/couchrest/more/casted_extended_doc_spec.rb +40 -0
  47. data/spec/couchrest/more/casted_model_spec.rb +98 -0
  48. data/spec/couchrest/more/extended_doc_attachment_spec.rb +130 -0
  49. data/spec/couchrest/more/extended_doc_spec.rb +509 -0
  50. data/spec/couchrest/more/extended_doc_subclass_spec.rb +98 -0
  51. data/spec/couchrest/more/extended_doc_view_spec.rb +355 -0
  52. data/spec/couchrest/more/property_spec.rb +136 -0
  53. data/spec/fixtures/more/article.rb +34 -0
  54. data/spec/fixtures/more/card.rb +20 -0
  55. data/spec/fixtures/more/course.rb +14 -0
  56. data/spec/fixtures/more/event.rb +6 -0
  57. data/spec/fixtures/more/invoice.rb +17 -0
  58. data/spec/fixtures/more/person.rb +8 -0
  59. data/spec/fixtures/more/question.rb +6 -0
  60. data/spec/fixtures/more/service.rb +12 -0
  61. data/spec/spec_helper.rb +13 -7
  62. metadata +58 -4
  63. data/lib/couchrest/core/model.rb +0 -613
  64. data/spec/couchrest/core/model_spec.rb +0 -855
@@ -1,855 +0,0 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
2
-
3
- class Basic < CouchRest::Model
4
- end
5
-
6
- class BasicWithValidation < CouchRest::Model
7
-
8
- before :save, :validate
9
- key_accessor :name
10
-
11
- def validate
12
- throw(:halt, false) unless name
13
- end
14
- end
15
-
16
- class WithTemplateAndUniqueID < CouchRest::Model
17
- unique_id do |model|
18
- model['important-field']
19
- end
20
- set_default({
21
- :preset => 'value',
22
- 'more-template' => [1,2,3]
23
- })
24
- key_accessor :preset
25
- key_accessor :has_no_default
26
- end
27
-
28
- class Question < CouchRest::Model
29
- key_accessor :q, :a
30
- couchrest_type = 'Question'
31
- end
32
-
33
- class Person < CouchRest::Model
34
- key_accessor :name
35
- def last_name
36
- name.last
37
- end
38
- end
39
-
40
- class Course < CouchRest::Model
41
- key_accessor :title
42
- cast :questions, :as => ['Question']
43
- cast :professor, :as => 'Person'
44
- cast :final_test_at, :as => 'Time'
45
- view_by :title
46
- view_by :dept, :ducktype => true
47
- end
48
-
49
- class Article < CouchRest::Model
50
- use_database CouchRest.database!('http://127.0.0.1:5984/couchrest-model-test')
51
- unique_id :slug
52
-
53
- view_by :date, :descending => true
54
- view_by :user_id, :date
55
-
56
- view_by :tags,
57
- :map =>
58
- "function(doc) {
59
- if (doc['couchrest-type'] == 'Article' && doc.tags) {
60
- doc.tags.forEach(function(tag){
61
- emit(tag, 1);
62
- });
63
- }
64
- }",
65
- :reduce =>
66
- "function(keys, values, rereduce) {
67
- return sum(values);
68
- }"
69
-
70
- key_writer :date
71
- key_reader :slug, :created_at, :updated_at
72
- key_accessor :title, :tags
73
-
74
- timestamps!
75
-
76
- before(:save, :generate_slug_from_title)
77
- def generate_slug_from_title
78
- self['slug'] = title.downcase.gsub(/[^a-z0-9]/,'-').squeeze('-').gsub(/^\-|\-$/,'') if new_document?
79
- end
80
- end
81
-
82
- class Player < CouchRest::Model
83
- unique_id :email
84
-
85
- key_accessor :email, :name, :str, :coord, :int, :con, :spirit, :level, :xp, :points, :coins, :date, :items, :loc
86
-
87
- view_by :name, :descending => true
88
- view_by :loc
89
-
90
- timestamps!
91
- end
92
-
93
- class Event < CouchRest::Model
94
- key_accessor :subject, :occurs_at
95
-
96
- cast :occurs_at, :as => 'Time', :send => 'parse'
97
- end
98
-
99
- describe "save bug" do
100
- before(:each) do
101
- CouchRest::Model.default_database = reset_test_db!
102
- end
103
-
104
- it "should fix" do
105
- @p = Player.new
106
- @p.email = 'insane@fakestreet.com'
107
- @p.save
108
- end
109
- end
110
-
111
-
112
- describe CouchRest::Model do
113
- before(:all) do
114
- @cr = CouchRest.new(COUCHHOST)
115
- @db = @cr.database(TESTDB)
116
- @db.delete! rescue nil
117
- @db = @cr.create_db(TESTDB) rescue nil
118
- @adb = @cr.database('couchrest-model-test')
119
- @adb.delete! rescue nil
120
- CouchRest.database!('http://127.0.0.1:5984/couchrest-model-test')
121
- CouchRest::Model.default_database = CouchRest.database!('http://127.0.0.1:5984/couchrest-test')
122
- end
123
-
124
- it "should use the default database" do
125
- Basic.database.info['db_name'].should == 'couchrest-test'
126
- end
127
-
128
- it "should override the default db" do
129
- Article.database.info['db_name'].should == 'couchrest-model-test'
130
- end
131
-
132
- describe "a new model" do
133
- it "should be a new_record" do
134
- @obj = Basic.new
135
- @obj.rev.should be_nil
136
- @obj.should be_a_new_record
137
- end
138
- end
139
-
140
- describe "a model with key_accessors" do
141
- it "should allow reading keys" do
142
- @art = Article.new
143
- @art['title'] = 'My Article Title'
144
- @art.title.should == 'My Article Title'
145
- end
146
- it "should allow setting keys" do
147
- @art = Article.new
148
- @art.title = 'My Article Title'
149
- @art['title'].should == 'My Article Title'
150
- end
151
- end
152
-
153
- describe "a model with key_writers" do
154
- it "should allow setting keys" do
155
- @art = Article.new
156
- t = Time.now
157
- @art.date = t
158
- @art['date'].should == t
159
- end
160
- it "should not allow reading keys" do
161
- @art = Article.new
162
- t = Time.now
163
- @art.date = t
164
- lambda{@art.date}.should raise_error
165
- end
166
- end
167
-
168
- describe "a model with key_readers" do
169
- it "should allow reading keys" do
170
- @art = Article.new
171
- @art['slug'] = 'my-slug'
172
- @art.slug.should == 'my-slug'
173
- end
174
- it "should not allow setting keys" do
175
- @art = Article.new
176
- lambda{@art.slug = 'My Article Title'}.should raise_error
177
- end
178
- end
179
-
180
- describe "update attributes without saving" do
181
- before(:each) do
182
- a = Article.get "big-bad-danger" rescue nil
183
- a.destroy if a
184
- @art = Article.new(:title => "big bad danger")
185
- @art.save
186
- end
187
- it "should work for attribute= methods" do
188
- @art['title'].should == "big bad danger"
189
- @art.update_attributes('date' => Time.now, :title => "super danger")
190
- @art['title'].should == "super danger"
191
- end
192
-
193
- it "should flip out if an attribute= method is missing" do
194
- lambda {
195
- @art.update_attributes('slug' => "new-slug", :title => "super danger")
196
- }.should raise_error
197
- end
198
-
199
- it "should not change other attributes if there is an error" do
200
- lambda {
201
- @art.update_attributes('slug' => "new-slug", :title => "super danger")
202
- }.should raise_error
203
- @art['title'].should == "big bad danger"
204
- end
205
-
206
- end
207
-
208
- describe "update attributes" do
209
- before(:each) do
210
- a = Article.get "big-bad-danger" rescue nil
211
- a.destroy if a
212
- @art = Article.new(:title => "big bad danger")
213
- @art.save
214
- end
215
- it "should save" do
216
- @art['title'].should == "big bad danger"
217
- @art.update_attributes('date' => Time.now, :title => "super danger")
218
- loaded = Article.get @art.id
219
- loaded['title'].should == "super danger"
220
- end
221
- end
222
-
223
- describe "a model with template values" do
224
- before(:all) do
225
- @tmpl = WithTemplateAndUniqueID.new
226
- @tmpl2 = WithTemplateAndUniqueID.new(:preset => 'not_value', 'important-field' => '1')
227
- end
228
- it "should have fields set when new" do
229
- @tmpl.preset.should == 'value'
230
- end
231
- it "shouldn't override explicitly set values" do
232
- @tmpl2.preset.should == 'not_value'
233
- end
234
- it "shouldn't override existing documents" do
235
- @tmpl2.save
236
- tmpl2_reloaded = WithTemplateAndUniqueID.get(@tmpl2.id)
237
- @tmpl2.preset.should == 'not_value'
238
- tmpl2_reloaded.preset.should == 'not_value'
239
- end
240
- it "shouldn't fill in existing documents" do
241
- @tmpl2.save
242
- # If user adds a new default value, shouldn't be retroactively applied to
243
- # documents upon fetching
244
- WithTemplateAndUniqueID.set_default({:has_no_default => 'giraffe'})
245
-
246
- tmpl2_reloaded = WithTemplateAndUniqueID.get(@tmpl2.id)
247
- @tmpl2.has_no_default.should be_nil
248
- tmpl2_reloaded.has_no_default.should be_nil
249
- WithTemplateAndUniqueID.new.has_no_default.should == 'giraffe'
250
- end
251
- end
252
-
253
- describe "getting a model" do
254
- before(:all) do
255
- @art = Article.new(:title => 'All About Getting')
256
- @art.save
257
- end
258
- it "should load and instantiate it" do
259
- foundart = Article.get @art.id
260
- foundart.title.should == "All About Getting"
261
- end
262
- end
263
-
264
- describe "getting a model with a subobjects array" do
265
- before(:all) do
266
- course_doc = {
267
- "title" => "Metaphysics 200",
268
- "questions" => [
269
- {
270
- "q" => "Carve the ___ of reality at the ___.",
271
- "a" => ["beast","joints"]
272
- },{
273
- "q" => "Who layed the smack down on Leibniz's Law?",
274
- "a" => "Willard Van Orman Quine"
275
- }
276
- ]
277
- }
278
- r = Course.database.save course_doc
279
- @course = Course.get r['id']
280
- end
281
- it "should load the course" do
282
- @course.title.should == "Metaphysics 200"
283
- end
284
- it "should instantiate them as such" do
285
- @course["questions"][0].a[0].should == "beast"
286
- end
287
- end
288
-
289
- describe "finding all instances of a model" do
290
- before(:all) do
291
- WithTemplateAndUniqueID.new('important-field' => '1').save
292
- WithTemplateAndUniqueID.new('important-field' => '2').save
293
- WithTemplateAndUniqueID.new('important-field' => '3').save
294
- WithTemplateAndUniqueID.new('important-field' => '4').save
295
- end
296
- it "should make the design doc" do
297
- WithTemplateAndUniqueID.all
298
- d = WithTemplateAndUniqueID.design_doc
299
- d['views']['all']['map'].should include('WithTemplateAndUniqueID')
300
- end
301
- it "should find all" do
302
- rs = WithTemplateAndUniqueID.all
303
- rs.length.should == 4
304
- end
305
- end
306
-
307
- describe "finding the first instance of a model" do
308
- before(:each) do
309
- @db = reset_test_db!
310
- WithTemplateAndUniqueID.new('important-field' => '1').save
311
- WithTemplateAndUniqueID.new('important-field' => '2').save
312
- WithTemplateAndUniqueID.new('important-field' => '3').save
313
- WithTemplateAndUniqueID.new('important-field' => '4').save
314
- end
315
- it "should make the design doc" do
316
- WithTemplateAndUniqueID.all
317
- d = WithTemplateAndUniqueID.design_doc
318
- d['views']['all']['map'].should include('WithTemplateAndUniqueID')
319
- end
320
- it "should find first" do
321
- rs = WithTemplateAndUniqueID.first
322
- rs['important-field'].should == "1"
323
- end
324
- it "should return nil if no instances are found" do
325
- WithTemplateAndUniqueID.all.each {|obj| obj.destroy }
326
- WithTemplateAndUniqueID.first.should be_nil
327
- end
328
- end
329
-
330
- describe "getting a model with a subobject field" do
331
- before(:all) do
332
- course_doc = {
333
- "title" => "Metaphysics 410",
334
- "professor" => {
335
- "name" => ["Mark", "Hinchliff"]
336
- },
337
- "final_test_at" => "2008/12/19 13:00:00 +0800"
338
- }
339
- r = Course.database.save course_doc
340
- @course = Course.get r['id']
341
- end
342
- it "should load the course" do
343
- @course["professor"]["name"][1].should == "Hinchliff"
344
- end
345
- it "should instantiate the professor as a person" do
346
- @course['professor'].last_name.should == "Hinchliff"
347
- end
348
- it "should instantiate the final_test_at as a Time" do
349
- @course['final_test_at'].should == Time.parse("2008/12/19 13:00:00 +0800")
350
- end
351
- end
352
-
353
- describe "cast keys to any type" do
354
- before(:all) do
355
- event_doc = { :subject => "Some event", :occurs_at => Time.now }
356
- e = Event.database.save event_doc
357
-
358
- @event = Event.get e['id']
359
- end
360
- it "should cast created_at to Time" do
361
- @event['occurs_at'].should be_an_instance_of(Time)
362
- end
363
- end
364
-
365
- describe "saving a model" do
366
- before(:all) do
367
- @obj = Basic.new
368
- @obj.save.should == true
369
- end
370
-
371
- it "should save the doc" do
372
- doc = @obj.database.get @obj.id
373
- doc['_id'].should == @obj.id
374
- end
375
-
376
- it "should be set for resaving" do
377
- rev = @obj.rev
378
- @obj['another-key'] = "some value"
379
- @obj.save
380
- @obj.rev.should_not == rev
381
- end
382
-
383
- it "should set the id" do
384
- @obj.id.should be_an_instance_of(String)
385
- end
386
-
387
- it "should set the type" do
388
- @obj['couchrest-type'].should == 'Basic'
389
- end
390
- end
391
-
392
- describe "saving a model with validation hooks added as extlib" do
393
- before(:all) do
394
- @obj = BasicWithValidation.new
395
- end
396
-
397
- it "save should return false is the model doesn't save as expected" do
398
- @obj.save.should be_false
399
- end
400
-
401
- it "save! should raise and exception if the model doesn't save" do
402
- lambda{ @obj.save!}.should raise_error("#{@obj.inspect} failed to save")
403
- end
404
-
405
- end
406
-
407
- describe "saving a model with a unique_id configured" do
408
- before(:each) do
409
- @art = Article.new
410
- @old = Article.database.get('this-is-the-title') rescue nil
411
- Article.database.delete(@old) if @old
412
- end
413
-
414
- it "should be a new document" do
415
- @art.should be_a_new_document
416
- @art.title.should be_nil
417
- end
418
-
419
- it "should require the title" do
420
- lambda{@art.save}.should raise_error
421
- @art.title = 'This is the title'
422
- @art.save.should == true
423
- end
424
-
425
- it "should not change the slug on update" do
426
- @art.title = 'This is the title'
427
- @art.save.should == true
428
- @art.title = 'new title'
429
- @art.save.should == true
430
- @art.slug.should == 'this-is-the-title'
431
- end
432
-
433
- it "should raise an error when the slug is taken" do
434
- @art.title = 'This is the title'
435
- @art.save.should == true
436
- @art2 = Article.new(:title => 'This is the title!')
437
- lambda{@art2.save}.should raise_error
438
- end
439
-
440
- it "should set the slug" do
441
- @art.title = 'This is the title'
442
- @art.save.should == true
443
- @art.slug.should == 'this-is-the-title'
444
- end
445
-
446
- it "should set the id" do
447
- @art.title = 'This is the title'
448
- @art.save.should == true
449
- @art.id.should == 'this-is-the-title'
450
- end
451
- end
452
-
453
- describe "saving a model with a unique_id lambda" do
454
- before(:each) do
455
- @templated = WithTemplateAndUniqueID.new
456
- @old = WithTemplateAndUniqueID.get('very-important') rescue nil
457
- @old.destroy if @old
458
- end
459
-
460
- it "should require the field" do
461
- lambda{@templated.save}.should raise_error
462
- @templated['important-field'] = 'very-important'
463
- @templated.save.should == true
464
- end
465
-
466
- it "should save with the id" do
467
- @templated['important-field'] = 'very-important'
468
- @templated.save.should == true
469
- t = WithTemplateAndUniqueID.get('very-important')
470
- t.should == @templated
471
- end
472
-
473
- it "should not change the id on update" do
474
- @templated['important-field'] = 'very-important'
475
- @templated.save.should == true
476
- @templated['important-field'] = 'not-important'
477
- @templated.save.should == true
478
- t = WithTemplateAndUniqueID.get('very-important')
479
- t.should == @templated
480
- end
481
-
482
- it "should raise an error when the id is taken" do
483
- @templated['important-field'] = 'very-important'
484
- @templated.save.should == true
485
- lambda{WithTemplateAndUniqueID.new('important-field' => 'very-important').save}.should raise_error
486
- end
487
-
488
- it "should set the id" do
489
- @templated['important-field'] = 'very-important'
490
- @templated.save.should == true
491
- @templated.id.should == 'very-important'
492
- end
493
- end
494
-
495
- describe "a model with timestamps" do
496
- before(:each) do
497
- oldart = Article.get "saving-this" rescue nil
498
- oldart.destroy if oldart
499
- @art = Article.new(:title => "Saving this")
500
- @art.save
501
- end
502
- it "should set the time on create" do
503
- (Time.now - @art.created_at).should < 2
504
- foundart = Article.get @art.id
505
- foundart.created_at.should == foundart.updated_at
506
- end
507
- it "should set the time on update" do
508
- @art.save
509
- @art.created_at.should < @art.updated_at
510
- end
511
- end
512
-
513
- describe "a model with simple views and a default param" do
514
- before(:all) do
515
- written_at = Time.now - 24 * 3600 * 7
516
- @titles = ["this and that", "also interesting", "more fun", "some junk"]
517
- @titles.each do |title|
518
- a = Article.new(:title => title)
519
- a.date = written_at
520
- a.save
521
- written_at += 24 * 3600
522
- end
523
- end
524
-
525
- it "should have a design doc" do
526
- Article.design_doc["views"]["by_date"].should_not be_nil
527
- end
528
-
529
- it "should save the design doc" do
530
- Article.by_date #rescue nil
531
- doc = Article.database.get Article.design_doc.id
532
- doc['views']['by_date'].should_not be_nil
533
- end
534
-
535
- it "should return the matching raw view result" do
536
- view = Article.by_date :raw => true
537
- view['rows'].length.should == 4
538
- end
539
-
540
- it "should not include non-Articles" do
541
- Article.database.save({"date" => 1})
542
- view = Article.by_date :raw => true
543
- view['rows'].length.should == 4
544
- end
545
-
546
- it "should return the matching objects (with default argument :descending => true)" do
547
- articles = Article.by_date
548
- articles.collect{|a|a.title}.should == @titles.reverse
549
- end
550
-
551
- it "should allow you to override default args" do
552
- articles = Article.by_date :descending => false
553
- articles.collect{|a|a.title}.should == @titles
554
- end
555
- end
556
-
557
- describe "another model with a simple view" do
558
- before(:all) do
559
- Course.database.delete! rescue nil
560
- @db = @cr.create_db(TESTDB) rescue nil
561
- %w{aaa bbb ddd eee}.each do |title|
562
- Course.new(:title => title).save
563
- end
564
- end
565
- it "should make the design doc upon first query" do
566
- Course.by_title
567
- doc = Course.design_doc
568
- doc['views']['all']['map'].should include('Course')
569
- end
570
- it "should can query via view" do
571
- # register methods with method-missing, for local dispatch. method
572
- # missing lookup table, no heuristics.
573
- view = Course.view :by_title
574
- designed = Course.by_title
575
- view.should == designed
576
- end
577
- it "should get them" do
578
- rs = Course.by_title
579
- rs.length.should == 4
580
- end
581
- it "should yield" do
582
- courses = []
583
- rs = Course.by_title # remove me
584
- Course.view(:by_title) do |course|
585
- courses << course
586
- end
587
- courses[0]["doc"]["title"].should =='aaa'
588
- end
589
- end
590
-
591
-
592
- describe "a ducktype view" do
593
- before(:all) do
594
- @id = @db.save({:dept => true})['id']
595
- end
596
- it "should setup" do
597
- duck = Course.get(@id) # from a different db
598
- duck["dept"].should == true
599
- end
600
- it "should make the design doc" do
601
- @as = Course.by_dept
602
- @doc = Course.design_doc
603
- @doc["views"]["by_dept"]["map"].should_not include("couchrest")
604
- end
605
- it "should not look for class" do |variable|
606
- @as = Course.by_dept
607
- @as[0]['_id'].should == @id
608
- end
609
- end
610
-
611
- describe "a model with a compound key view" do
612
- before(:all) do
613
- written_at = Time.now - 24 * 3600 * 7
614
- @titles = ["uniq one", "even more interesting", "less fun", "not junk"]
615
- @user_ids = ["quentin", "aaron"]
616
- @titles.each_with_index do |title,i|
617
- u = i % 2
618
- a = Article.new(:title => title, :user_id => @user_ids[u])
619
- a.date = written_at
620
- a.save
621
- written_at += 24 * 3600
622
- end
623
- end
624
- it "should create the design doc" do
625
- Article.by_user_id_and_date rescue nil
626
- doc = Article.design_doc
627
- doc['views']['by_date'].should_not be_nil
628
- end
629
- it "should sort correctly" do
630
- articles = Article.by_user_id_and_date
631
- articles.collect{|a|a['user_id']}.should == ['aaron', 'aaron', 'quentin',
632
- 'quentin']
633
- articles[1].title.should == 'not junk'
634
- end
635
- it "should be queryable with couchrest options" do
636
- articles = Article.by_user_id_and_date :limit => 1, :startkey => 'quentin'
637
- articles.length.should == 1
638
- articles[0].title.should == "even more interesting"
639
- end
640
- end
641
-
642
- describe "with a custom view" do
643
- before(:all) do
644
- @titles = ["very uniq one", "even less interesting", "some fun",
645
- "really junk", "crazy bob"]
646
- @tags = ["cool", "lame"]
647
- @titles.each_with_index do |title,i|
648
- u = i % 2
649
- a = Article.new(:title => title, :tags => [@tags[u]])
650
- a.save
651
- end
652
- end
653
- it "should be available raw" do
654
- view = Article.by_tags :raw => true
655
- view['rows'].length.should == 5
656
- end
657
-
658
- it "should be default to :reduce => false" do
659
- ars = Article.by_tags
660
- ars.first.tags.first.should == 'cool'
661
- end
662
-
663
- it "should be raw when reduce is true" do
664
- view = Article.by_tags :reduce => true, :group => true
665
- view['rows'].find{|r|r['key'] == 'cool'}['value'].should == 3
666
- end
667
- end
668
-
669
- # TODO: moved to Design, delete
670
- describe "adding a view" do
671
- before(:each) do
672
- Article.by_date
673
- @design_docs = Article.database.documents :startkey => "_design/",
674
- :endkey => "_design/\u9999"
675
- end
676
- it "should not create a design doc on view definition" do
677
- Article.view_by :created_at
678
- newdocs = Article.database.documents :startkey => "_design/",
679
- :endkey => "_design/\u9999"
680
- newdocs["rows"].length.should == @design_docs["rows"].length
681
- end
682
- it "should create a new design document on view access" do
683
- Article.view_by :updated_at
684
- Article.by_updated_at
685
- newdocs = Article.database.documents :startkey => "_design/",
686
- :endkey => "_design/\u9999"
687
- # puts @design_docs.inspect
688
- # puts newdocs.inspect
689
- newdocs["rows"].length.should == @design_docs["rows"].length + 1
690
- end
691
- end
692
-
693
- describe "with a lot of designs left around" do
694
- before(:each) do
695
- Article.by_date
696
- Article.view_by :field
697
- Article.by_field
698
- end
699
- it "should clean them up" do
700
- Article.view_by :stream
701
- Article.by_stream
702
- ddocs = Article.all_design_doc_versions
703
- ddocs["rows"].length.should > 1
704
- Article.cleanup_design_docs!
705
- ddocs = Article.all_design_doc_versions
706
- ddocs["rows"].length.should == 1
707
- end
708
- end
709
-
710
- describe "destroying an instance" do
711
- before(:each) do
712
- @obj = Basic.new
713
- @obj.save.should == true
714
- end
715
- it "should return true" do
716
- result = @obj.destroy
717
- result.should == true
718
- end
719
- it "should be resavable" do
720
- @obj.destroy
721
- @obj.rev.should be_nil
722
- @obj.id.should be_nil
723
- @obj.save.should == true
724
- end
725
- it "should make it go away" do
726
- @obj.destroy
727
- lambda{Basic.get(@obj.id)}.should raise_error
728
- end
729
- end
730
-
731
- describe "#has_attachment?" do
732
- before(:each) do
733
- @obj = Basic.new
734
- @obj.save.should == true
735
- @file = File.open(FIXTURE_PATH + '/attachments/test.html')
736
- @attachment_name = 'my_attachment'
737
- @obj.create_attachment(:file => @file, :name => @attachment_name)
738
- end
739
-
740
- it 'should return false if there is no attachment' do
741
- @obj.has_attachment?('bogus').should be_false
742
- end
743
-
744
- it 'should return true if there is an attachment' do
745
- @obj.has_attachment?(@attachment_name).should be_true
746
- end
747
-
748
- it 'should return true if an object with an attachment is reloaded' do
749
- @obj.save.should be_true
750
- reloaded_obj = Basic.get(@obj.id)
751
- reloaded_obj.has_attachment?(@attachment_name).should be_true
752
- end
753
-
754
- it 'should return false if an attachment has been removed' do
755
- @obj.delete_attachment(@attachment_name)
756
- @obj.has_attachment?(@attachment_name).should be_false
757
- end
758
- end
759
-
760
- describe "creating an attachment" do
761
- before(:each) do
762
- @obj = Basic.new
763
- @obj.save.should == true
764
- @file_ext = File.open(FIXTURE_PATH + '/attachments/test.html')
765
- @file_no_ext = File.open(FIXTURE_PATH + '/attachments/README')
766
- @attachment_name = 'my_attachment'
767
- @content_type = 'media/mp3'
768
- end
769
-
770
- it "should create an attachment from file with an extension" do
771
- @obj.create_attachment(:file => @file_ext, :name => @attachment_name)
772
- @obj.save.should == true
773
- reloaded_obj = Basic.get(@obj.id)
774
- reloaded_obj['_attachments'][@attachment_name].should_not be_nil
775
- end
776
-
777
- it "should create an attachment from file without an extension" do
778
- @obj.create_attachment(:file => @file_no_ext, :name => @attachment_name)
779
- @obj.save.should == true
780
- reloaded_obj = Basic.get(@obj.id)
781
- reloaded_obj['_attachments'][@attachment_name].should_not be_nil
782
- end
783
-
784
- it 'should raise ArgumentError if :file is missing' do
785
- lambda{ @obj.create_attachment(:name => @attachment_name) }.should raise_error
786
- end
787
-
788
- it 'should raise ArgumentError if :name is missing' do
789
- lambda{ @obj.create_attachment(:file => @file_ext) }.should raise_error
790
- end
791
-
792
- it 'should set the content-type if passed' do
793
- @obj.create_attachment(:file => @file_ext, :name => @attachment_name, :content_type => @content_type)
794
- @obj['_attachments'][@attachment_name]['content-type'].should == @content_type
795
- end
796
- end
797
-
798
- describe 'reading, updating, and deleting an attachment' do
799
- before(:each) do
800
- @obj = Basic.new
801
- @file = File.open(FIXTURE_PATH + '/attachments/test.html')
802
- @attachment_name = 'my_attachment'
803
- @obj.create_attachment(:file => @file, :name => @attachment_name)
804
- @obj.save.should == true
805
- @file.rewind
806
- @content_type = 'media/mp3'
807
- end
808
-
809
- it 'should read an attachment that exists' do
810
- @obj.read_attachment(@attachment_name).should == @file.read
811
- end
812
-
813
- it 'should update an attachment that exists' do
814
- file = File.open(FIXTURE_PATH + '/attachments/README')
815
- @file.should_not == file
816
- @obj.update_attachment(:file => file, :name => @attachment_name)
817
- @obj.save
818
- reloaded_obj = Basic.get(@obj.id)
819
- file.rewind
820
- reloaded_obj.read_attachment(@attachment_name).should_not == @file.read
821
- reloaded_obj.read_attachment(@attachment_name).should == file.read
822
- end
823
-
824
- it 'should se the content-type if passed' do
825
- file = File.open(FIXTURE_PATH + '/attachments/README')
826
- @file.should_not == file
827
- @obj.update_attachment(:file => file, :name => @attachment_name, :content_type => @content_type)
828
- @obj['_attachments'][@attachment_name]['content-type'].should == @content_type
829
- end
830
-
831
- it 'should delete an attachment that exists' do
832
- @obj.delete_attachment(@attachment_name)
833
- @obj.save
834
- lambda{Basic.get(@obj.id).read_attachment(@attachment_name)}.should raise_error
835
- end
836
- end
837
-
838
- describe "#attachment_url" do
839
- before(:each) do
840
- @obj = Basic.new
841
- @file = File.open(FIXTURE_PATH + '/attachments/test.html')
842
- @attachment_name = 'my_attachment'
843
- @obj.create_attachment(:file => @file, :name => @attachment_name)
844
- @obj.save.should == true
845
- end
846
-
847
- it 'should return nil if attachment does not exist' do
848
- @obj.attachment_url('bogus').should be_nil
849
- end
850
-
851
- it 'should return the attachment URL as specified by CouchDB HttpDocumentApi' do
852
- @obj.attachment_url(@attachment_name).should == "#{Basic.database}/#{@obj.id}/#{@attachment_name}"
853
- end
854
- end
855
- end