couchrest_extended_document 1.0.0.beta5

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 (71) hide show
  1. data/LICENSE +176 -0
  2. data/README.md +68 -0
  3. data/Rakefile +68 -0
  4. data/THANKS.md +19 -0
  5. data/examples/model/example.rb +144 -0
  6. data/history.txt +159 -0
  7. data/lib/couchrest/casted_array.rb +25 -0
  8. data/lib/couchrest/casted_model.rb +55 -0
  9. data/lib/couchrest/extended_document.rb +323 -0
  10. data/lib/couchrest/mixins/attribute_protection.rb +74 -0
  11. data/lib/couchrest/mixins/callbacks.rb +532 -0
  12. data/lib/couchrest/mixins/class_proxy.rb +120 -0
  13. data/lib/couchrest/mixins/collection.rb +260 -0
  14. data/lib/couchrest/mixins/design_doc.rb +127 -0
  15. data/lib/couchrest/mixins/document_queries.rb +82 -0
  16. data/lib/couchrest/mixins/extended_attachments.rb +73 -0
  17. data/lib/couchrest/mixins/properties.rb +162 -0
  18. data/lib/couchrest/mixins/validation.rb +245 -0
  19. data/lib/couchrest/mixins/views.rb +148 -0
  20. data/lib/couchrest/mixins.rb +11 -0
  21. data/lib/couchrest/property.rb +50 -0
  22. data/lib/couchrest/support/couchrest.rb +19 -0
  23. data/lib/couchrest/support/rails.rb +42 -0
  24. data/lib/couchrest/typecast.rb +175 -0
  25. data/lib/couchrest/validation/auto_validate.rb +156 -0
  26. data/lib/couchrest/validation/contextual_validators.rb +78 -0
  27. data/lib/couchrest/validation/validation_errors.rb +125 -0
  28. data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
  29. data/lib/couchrest/validation/validators/confirmation_validator.rb +107 -0
  30. data/lib/couchrest/validation/validators/format_validator.rb +122 -0
  31. data/lib/couchrest/validation/validators/formats/email.rb +66 -0
  32. data/lib/couchrest/validation/validators/formats/url.rb +43 -0
  33. data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
  34. data/lib/couchrest/validation/validators/length_validator.rb +139 -0
  35. data/lib/couchrest/validation/validators/method_validator.rb +89 -0
  36. data/lib/couchrest/validation/validators/numeric_validator.rb +109 -0
  37. data/lib/couchrest/validation/validators/required_field_validator.rb +114 -0
  38. data/lib/couchrest/validation.rb +245 -0
  39. data/lib/couchrest_extended_document.rb +21 -0
  40. data/spec/couchrest/attribute_protection_spec.rb +150 -0
  41. data/spec/couchrest/casted_extended_doc_spec.rb +79 -0
  42. data/spec/couchrest/casted_model_spec.rb +406 -0
  43. data/spec/couchrest/extended_doc_attachment_spec.rb +148 -0
  44. data/spec/couchrest/extended_doc_inherited_spec.rb +40 -0
  45. data/spec/couchrest/extended_doc_spec.rb +868 -0
  46. data/spec/couchrest/extended_doc_subclass_spec.rb +99 -0
  47. data/spec/couchrest/extended_doc_view_spec.rb +529 -0
  48. data/spec/couchrest/property_spec.rb +648 -0
  49. data/spec/fixtures/attachments/README +3 -0
  50. data/spec/fixtures/attachments/couchdb.png +0 -0
  51. data/spec/fixtures/attachments/test.html +11 -0
  52. data/spec/fixtures/more/article.rb +35 -0
  53. data/spec/fixtures/more/card.rb +22 -0
  54. data/spec/fixtures/more/cat.rb +22 -0
  55. data/spec/fixtures/more/course.rb +25 -0
  56. data/spec/fixtures/more/event.rb +8 -0
  57. data/spec/fixtures/more/invoice.rb +17 -0
  58. data/spec/fixtures/more/person.rb +9 -0
  59. data/spec/fixtures/more/question.rb +6 -0
  60. data/spec/fixtures/more/service.rb +12 -0
  61. data/spec/fixtures/more/user.rb +22 -0
  62. data/spec/fixtures/views/lib.js +3 -0
  63. data/spec/fixtures/views/test_view/lib.js +3 -0
  64. data/spec/fixtures/views/test_view/only-map.js +4 -0
  65. data/spec/fixtures/views/test_view/test-map.js +3 -0
  66. data/spec/fixtures/views/test_view/test-reduce.js +3 -0
  67. data/spec/spec.opts +5 -0
  68. data/spec/spec_helper.rb +49 -0
  69. data/utils/remap.rb +27 -0
  70. data/utils/subset.rb +30 -0
  71. metadata +200 -0
@@ -0,0 +1,648 @@
1
+ # encoding: utf-8
2
+ require File.expand_path('../../spec_helper', __FILE__)
3
+ require File.join(FIXTURE_PATH, 'more', 'person')
4
+ require File.join(FIXTURE_PATH, 'more', 'card')
5
+ require File.join(FIXTURE_PATH, 'more', 'invoice')
6
+ require File.join(FIXTURE_PATH, 'more', 'service')
7
+ require File.join(FIXTURE_PATH, 'more', 'event')
8
+ require File.join(FIXTURE_PATH, 'more', 'cat')
9
+ require File.join(FIXTURE_PATH, 'more', 'user')
10
+ require File.join(FIXTURE_PATH, 'more', 'course')
11
+
12
+
13
+ describe "ExtendedDocument properties" do
14
+
15
+ before(:each) do
16
+ reset_test_db!
17
+ @card = Card.new(:first_name => "matt")
18
+ end
19
+
20
+ it "should be accessible from the object" do
21
+ @card.properties.should be_an_instance_of(Array)
22
+ @card.properties.map{|p| p.name}.should include("first_name")
23
+ end
24
+
25
+ it "should let you access a property value (getter)" do
26
+ @card.first_name.should == "matt"
27
+ end
28
+
29
+ it "should let you set a property value (setter)" do
30
+ @card.last_name = "Aimonetti"
31
+ @card.last_name.should == "Aimonetti"
32
+ end
33
+
34
+ it "should not let you set a property value if it's read only" do
35
+ lambda{@card.read_only_value = "test"}.should raise_error
36
+ end
37
+
38
+ it "should let you use an alias for an attribute" do
39
+ @card.last_name = "Aimonetti"
40
+ @card.family_name.should == "Aimonetti"
41
+ @card.family_name.should == @card.last_name
42
+ end
43
+
44
+ it "should let you use an alias for a casted attribute" do
45
+ @card.cast_alias = Person.new(:name => "Aimonetti")
46
+ @card.cast_alias.name.should == ["Aimonetti"]
47
+ @card.calias.name.should == ["Aimonetti"]
48
+ card = Card.new(:first_name => "matt", :cast_alias => {:name => "Aimonetti"})
49
+ card.cast_alias.name.should == ["Aimonetti"]
50
+ card.calias.name.should == ["Aimonetti"]
51
+ end
52
+
53
+ it "should be auto timestamped" do
54
+ @card.created_at.should be_nil
55
+ @card.updated_at.should be_nil
56
+ @card.save.should be_true
57
+ @card.created_at.should_not be_nil
58
+ @card.updated_at.should_not be_nil
59
+ end
60
+
61
+
62
+ describe "mass assignment protection" do
63
+
64
+ it "should not store protected attribute using mass assignment" do
65
+ cat_toy = CatToy.new(:name => "Zorro")
66
+ cat = Cat.create(:name => "Helena", :toys => [cat_toy], :favorite_toy => cat_toy, :number => 1)
67
+ cat.number.should be_nil
68
+ cat.number = 1
69
+ cat.save
70
+ cat.number.should == 1
71
+ end
72
+
73
+ it "should not store protected attribute when 'declare accessible poperties, assume all the rest are protected'" do
74
+ user = User.create(:name => "Marcos Tapajós", :admin => true)
75
+ user.admin.should be_nil
76
+ end
77
+
78
+ it "should not store protected attribute when 'declare protected properties, assume all the rest are accessible'" do
79
+ user = SpecialUser.create(:name => "Marcos Tapajós", :admin => true)
80
+ user.admin.should be_nil
81
+ end
82
+
83
+ end
84
+
85
+ describe "validation" do
86
+ before(:each) do
87
+ @invoice = Invoice.new(:client_name => "matt", :employee_name => "Chris", :location => "San Diego, CA")
88
+ end
89
+
90
+ it "should be able to be validated" do
91
+ @card.valid?.should == true
92
+ end
93
+
94
+ it "should let you validate the presence of an attribute" do
95
+ @card.first_name = nil
96
+ @card.should_not be_valid
97
+ @card.errors.should_not be_empty
98
+ @card.errors.on(:first_name).should == ["First name must not be blank"]
99
+ end
100
+
101
+ it "should let you look up errors for a field by a string name" do
102
+ @card.first_name = nil
103
+ @card.should_not be_valid
104
+ @card.errors.on('first_name').should == ["First name must not be blank"]
105
+ end
106
+
107
+ it "should validate the presence of 2 attributes" do
108
+ @invoice.clear
109
+ @invoice.should_not be_valid
110
+ @invoice.errors.should_not be_empty
111
+ @invoice.errors.on(:client_name).first.should == "Client name must not be blank"
112
+ @invoice.errors.on(:employee_name).should_not be_empty
113
+ end
114
+
115
+ it "should let you set an error message" do
116
+ @invoice.location = nil
117
+ @invoice.valid?
118
+ @invoice.errors.on(:location).should == ["Hey stupid!, you forgot the location"]
119
+ end
120
+
121
+ it "should validate before saving" do
122
+ @invoice.location = nil
123
+ @invoice.should_not be_valid
124
+ @invoice.save.should be_false
125
+ @invoice.should be_new
126
+ end
127
+ end
128
+
129
+ describe "autovalidation" do
130
+ before(:each) do
131
+ @service = Service.new(:name => "Coumpound analysis", :price => 3_000)
132
+ end
133
+
134
+ it "should be valid" do
135
+ @service.should be_valid
136
+ end
137
+
138
+ it "should not respond to properties not setup" do
139
+ @service.respond_to?(:client_name).should be_false
140
+ end
141
+
142
+ describe "property :name, :length => 4...20" do
143
+
144
+ it "should autovalidate the presence when length is set" do
145
+ @service.name = nil
146
+ @service.should_not be_valid
147
+ @service.errors.should_not be_nil
148
+ @service.errors.on(:name).first.should == "Name must be between 4 and 19 characters long"
149
+ end
150
+
151
+ it "should autovalidate the correct length" do
152
+ @service.name = "a"
153
+ @service.should_not be_valid
154
+ @service.errors.should_not be_nil
155
+ @service.errors.on(:name).first.should == "Name must be between 4 and 19 characters long"
156
+ end
157
+ end
158
+ end
159
+
160
+ describe "casting" do
161
+ before(:each) do
162
+ @course = Course.new(:title => 'Relaxation')
163
+ end
164
+
165
+ describe "when value is nil" do
166
+ it "leaves the value unchanged" do
167
+ @course.title = nil
168
+ @course['title'].should == nil
169
+ end
170
+ end
171
+
172
+ describe "when type primitive is an Object" do
173
+ it "it should not cast given value" do
174
+ @course.participants = [{}, 'q', 1]
175
+ @course['participants'].should == [{}, 'q', 1]
176
+ end
177
+
178
+ it "should cast started_on to Date" do
179
+ @course.started_on = Date.today
180
+ @course['started_on'].should be_an_instance_of(Date)
181
+ end
182
+ end
183
+
184
+ describe "when type primitive is a String" do
185
+ it "keeps string value unchanged" do
186
+ value = "1.0"
187
+ @course.title = value
188
+ @course['title'].should equal(value)
189
+ end
190
+
191
+ it "it casts to string representation of the value" do
192
+ @course.title = 1.0
193
+ @course['title'].should eql("1.0")
194
+ end
195
+ end
196
+
197
+ describe 'when type primitive is a Float' do
198
+ it 'returns same value if a float' do
199
+ value = 24.0
200
+ @course.estimate = value
201
+ @course['estimate'].should equal(value)
202
+ end
203
+
204
+ it 'returns float representation of a zero string integer' do
205
+ @course.estimate = '0'
206
+ @course['estimate'].should eql(0.0)
207
+ end
208
+
209
+ it 'returns float representation of a positive string integer' do
210
+ @course.estimate = '24'
211
+ @course['estimate'].should eql(24.0)
212
+ end
213
+
214
+ it 'returns float representation of a negative string integer' do
215
+ @course.estimate = '-24'
216
+ @course['estimate'].should eql(-24.0)
217
+ end
218
+
219
+ it 'returns float representation of a zero string float' do
220
+ @course.estimate = '0.0'
221
+ @course['estimate'].should eql(0.0)
222
+ end
223
+
224
+ it 'returns float representation of a positive string float' do
225
+ @course.estimate = '24.35'
226
+ @course['estimate'].should eql(24.35)
227
+ end
228
+
229
+ it 'returns float representation of a negative string float' do
230
+ @course.estimate = '-24.35'
231
+ @course['estimate'].should eql(-24.35)
232
+ end
233
+
234
+ it 'returns float representation of a zero string float, with no leading digits' do
235
+ @course.estimate = '.0'
236
+ @course['estimate'].should eql(0.0)
237
+ end
238
+
239
+ it 'returns float representation of a positive string float, with no leading digits' do
240
+ @course.estimate = '.41'
241
+ @course['estimate'].should eql(0.41)
242
+ end
243
+
244
+ it 'returns float representation of a zero integer' do
245
+ @course.estimate = 0
246
+ @course['estimate'].should eql(0.0)
247
+ end
248
+
249
+ it 'returns float representation of a positive integer' do
250
+ @course.estimate = 24
251
+ @course['estimate'].should eql(24.0)
252
+ end
253
+
254
+ it 'returns float representation of a negative integer' do
255
+ @course.estimate = -24
256
+ @course['estimate'].should eql(-24.0)
257
+ end
258
+
259
+ it 'returns float representation of a zero decimal' do
260
+ @course.estimate = BigDecimal('0.0')
261
+ @course['estimate'].should eql(0.0)
262
+ end
263
+
264
+ it 'returns float representation of a positive decimal' do
265
+ @course.estimate = BigDecimal('24.35')
266
+ @course['estimate'].should eql(24.35)
267
+ end
268
+
269
+ it 'returns float representation of a negative decimal' do
270
+ @course.estimate = BigDecimal('-24.35')
271
+ @course['estimate'].should eql(-24.35)
272
+ end
273
+
274
+ [ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
275
+ it "does not typecast non-numeric value #{value.inspect}" do
276
+ @course.estimate = value
277
+ @course['estimate'].should equal(value)
278
+ end
279
+ end
280
+ end
281
+
282
+ describe 'when type primitive is a Integer' do
283
+ it 'returns same value if an integer' do
284
+ value = 24
285
+ @course.hours = value
286
+ @course['hours'].should equal(value)
287
+ end
288
+
289
+ it 'returns integer representation of a zero string integer' do
290
+ @course.hours = '0'
291
+ @course['hours'].should eql(0)
292
+ end
293
+
294
+ it 'returns integer representation of a positive string integer' do
295
+ @course.hours = '24'
296
+ @course['hours'].should eql(24)
297
+ end
298
+
299
+ it 'returns integer representation of a negative string integer' do
300
+ @course.hours = '-24'
301
+ @course['hours'].should eql(-24)
302
+ end
303
+
304
+ it 'returns integer representation of a zero string float' do
305
+ @course.hours = '0.0'
306
+ @course['hours'].should eql(0)
307
+ end
308
+
309
+ it 'returns integer representation of a positive string float' do
310
+ @course.hours = '24.35'
311
+ @course['hours'].should eql(24)
312
+ end
313
+
314
+ it 'returns integer representation of a negative string float' do
315
+ @course.hours = '-24.35'
316
+ @course['hours'].should eql(-24)
317
+ end
318
+
319
+ it 'returns integer representation of a zero string float, with no leading digits' do
320
+ @course.hours = '.0'
321
+ @course['hours'].should eql(0)
322
+ end
323
+
324
+ it 'returns integer representation of a positive string float, with no leading digits' do
325
+ @course.hours = '.41'
326
+ @course['hours'].should eql(0)
327
+ end
328
+
329
+ it 'returns integer representation of a zero float' do
330
+ @course.hours = 0.0
331
+ @course['hours'].should eql(0)
332
+ end
333
+
334
+ it 'returns integer representation of a positive float' do
335
+ @course.hours = 24.35
336
+ @course['hours'].should eql(24)
337
+ end
338
+
339
+ it 'returns integer representation of a negative float' do
340
+ @course.hours = -24.35
341
+ @course['hours'].should eql(-24)
342
+ end
343
+
344
+ it 'returns integer representation of a zero decimal' do
345
+ @course.hours = '0.0'
346
+ @course['hours'].should eql(0)
347
+ end
348
+
349
+ it 'returns integer representation of a positive decimal' do
350
+ @course.hours = '24.35'
351
+ @course['hours'].should eql(24)
352
+ end
353
+
354
+ it 'returns integer representation of a negative decimal' do
355
+ @course.hours = '-24.35'
356
+ @course['hours'].should eql(-24)
357
+ end
358
+
359
+ [ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
360
+ it "does not typecast non-numeric value #{value.inspect}" do
361
+ @course.hours = value
362
+ @course['hours'].should equal(value)
363
+ end
364
+ end
365
+ end
366
+
367
+ describe 'when type primitive is a BigDecimal' do
368
+ it 'returns same value if a decimal' do
369
+ value = BigDecimal('24.0')
370
+ @course.profit = value
371
+ @course['profit'].should equal(value)
372
+ end
373
+
374
+ it 'returns decimal representation of a zero string integer' do
375
+ @course.profit = '0'
376
+ @course['profit'].should eql(BigDecimal('0.0'))
377
+ end
378
+
379
+ it 'returns decimal representation of a positive string integer' do
380
+ @course.profit = '24'
381
+ @course['profit'].should eql(BigDecimal('24.0'))
382
+ end
383
+
384
+ it 'returns decimal representation of a negative string integer' do
385
+ @course.profit = '-24'
386
+ @course['profit'].should eql(BigDecimal('-24.0'))
387
+ end
388
+
389
+ it 'returns decimal representation of a zero string float' do
390
+ @course.profit = '0.0'
391
+ @course['profit'].should eql(BigDecimal('0.0'))
392
+ end
393
+
394
+ it 'returns decimal representation of a positive string float' do
395
+ @course.profit = '24.35'
396
+ @course['profit'].should eql(BigDecimal('24.35'))
397
+ end
398
+
399
+ it 'returns decimal representation of a negative string float' do
400
+ @course.profit = '-24.35'
401
+ @course['profit'].should eql(BigDecimal('-24.35'))
402
+ end
403
+
404
+ it 'returns decimal representation of a zero string float, with no leading digits' do
405
+ @course.profit = '.0'
406
+ @course['profit'].should eql(BigDecimal('0.0'))
407
+ end
408
+
409
+ it 'returns decimal representation of a positive string float, with no leading digits' do
410
+ @course.profit = '.41'
411
+ @course['profit'].should eql(BigDecimal('0.41'))
412
+ end
413
+
414
+ it 'returns decimal representation of a zero integer' do
415
+ @course.profit = 0
416
+ @course['profit'].should eql(BigDecimal('0.0'))
417
+ end
418
+
419
+ it 'returns decimal representation of a positive integer' do
420
+ @course.profit = 24
421
+ @course['profit'].should eql(BigDecimal('24.0'))
422
+ end
423
+
424
+ it 'returns decimal representation of a negative integer' do
425
+ @course.profit = -24
426
+ @course['profit'].should eql(BigDecimal('-24.0'))
427
+ end
428
+
429
+ it 'returns decimal representation of a zero float' do
430
+ @course.profit = 0.0
431
+ @course['profit'].should eql(BigDecimal('0.0'))
432
+ end
433
+
434
+ it 'returns decimal representation of a positive float' do
435
+ @course.profit = 24.35
436
+ @course['profit'].should eql(BigDecimal('24.35'))
437
+ end
438
+
439
+ it 'returns decimal representation of a negative float' do
440
+ @course.profit = -24.35
441
+ @course['profit'].should eql(BigDecimal('-24.35'))
442
+ end
443
+
444
+ [ Object.new, true, '00.0', '0.', '-.0', 'string' ].each do |value|
445
+ it "does not typecast non-numeric value #{value.inspect}" do
446
+ @course.profit = value
447
+ @course['profit'].should equal(value)
448
+ end
449
+ end
450
+ end
451
+
452
+ describe 'when type primitive is a DateTime' do
453
+ describe 'and value given as a hash with keys like :year, :month, etc' do
454
+ it 'builds a DateTime instance from hash values' do
455
+ @course.updated_at = {
456
+ :year => '2006',
457
+ :month => '11',
458
+ :day => '23',
459
+ :hour => '12',
460
+ :min => '0',
461
+ :sec => '0'
462
+ }
463
+ result = @course['updated_at']
464
+
465
+ result.should be_kind_of(DateTime)
466
+ result.year.should eql(2006)
467
+ result.month.should eql(11)
468
+ result.day.should eql(23)
469
+ result.hour.should eql(12)
470
+ result.min.should eql(0)
471
+ result.sec.should eql(0)
472
+ end
473
+ end
474
+
475
+ describe 'and value is a string' do
476
+ it 'parses the string' do
477
+ @course.updated_at = 'Dec, 2006'
478
+ @course['updated_at'].month.should == 12
479
+ end
480
+ end
481
+
482
+ it 'does not typecast non-datetime values' do
483
+ @course.updated_at = 'not-datetime'
484
+ @course['updated_at'].should eql('not-datetime')
485
+ end
486
+ end
487
+
488
+ describe 'when type primitive is a Date' do
489
+ describe 'and value given as a hash with keys like :year, :month, etc' do
490
+ it 'builds a Date instance from hash values' do
491
+ @course.started_on = {
492
+ :year => '2007',
493
+ :month => '3',
494
+ :day => '25'
495
+ }
496
+ result = @course['started_on']
497
+
498
+ result.should be_kind_of(Date)
499
+ result.year.should eql(2007)
500
+ result.month.should eql(3)
501
+ result.day.should eql(25)
502
+ end
503
+ end
504
+
505
+ describe 'and value is a string' do
506
+ it 'parses the string' do
507
+ @course.started_on = 'Dec 20th, 2006'
508
+ @course.started_on.month.should == 12
509
+ @course.started_on.day.should == 20
510
+ @course.started_on.year.should == 2006
511
+ end
512
+ end
513
+
514
+ it 'does not typecast non-date values' do
515
+ @course.started_on = 'not-date'
516
+ @course['started_on'].should eql('not-date')
517
+ end
518
+ end
519
+
520
+ describe 'when type primitive is a Time' do
521
+ describe 'and value given as a hash with keys like :year, :month, etc' do
522
+ it 'builds a Time instance from hash values' do
523
+ @course.ends_at = {
524
+ :year => '2006',
525
+ :month => '11',
526
+ :day => '23',
527
+ :hour => '12',
528
+ :min => '0',
529
+ :sec => '0'
530
+ }
531
+ result = @course['ends_at']
532
+
533
+ result.should be_kind_of(Time)
534
+ result.year.should eql(2006)
535
+ result.month.should eql(11)
536
+ result.day.should eql(23)
537
+ result.hour.should eql(12)
538
+ result.min.should eql(0)
539
+ result.sec.should eql(0)
540
+ end
541
+ end
542
+
543
+ describe 'and value is a string' do
544
+ it 'parses the string' do
545
+ t = Time.now
546
+ @course.ends_at = t.strftime('%Y/%m/%d %H:%M:%S %z')
547
+ @course['ends_at'].year.should eql(t.year)
548
+ @course['ends_at'].month.should eql(t.month)
549
+ @course['ends_at'].day.should eql(t.day)
550
+ @course['ends_at'].hour.should eql(t.hour)
551
+ @course['ends_at'].min.should eql(t.min)
552
+ @course['ends_at'].sec.should eql(t.sec)
553
+ end
554
+ end
555
+
556
+ it 'does not typecast non-time values' do
557
+ @course.ends_at = 'not-time'
558
+ @course['ends_at'].should eql('not-time')
559
+ end
560
+ end
561
+
562
+ describe 'when type primitive is a Class' do
563
+ it 'returns same value if a class' do
564
+ value = Course
565
+ @course.klass = value
566
+ @course['klass'].should equal(value)
567
+ end
568
+
569
+ it 'returns the class if found' do
570
+ @course.klass = 'Course'
571
+ @course['klass'].should eql(Course)
572
+ end
573
+
574
+ it 'does not typecast non-class values' do
575
+ @course.klass = 'NoClass'
576
+ @course['klass'].should eql('NoClass')
577
+ end
578
+ end
579
+
580
+ describe 'when type primitive is a Boolean' do
581
+
582
+ [ true, 'true', 'TRUE', '1', 1, 't', 'T' ].each do |value|
583
+ it "returns true when value is #{value.inspect}" do
584
+ @course.active = value
585
+ @course['active'].should be_true
586
+ end
587
+ end
588
+
589
+ [ false, 'false', 'FALSE', '0', 0, 'f', 'F' ].each do |value|
590
+ it "returns false when value is #{value.inspect}" do
591
+ @course.active = value
592
+ @course['active'].should be_false
593
+ end
594
+ end
595
+
596
+ [ 'string', 2, 1.0, BigDecimal('1.0'), DateTime.now, Time.now, Date.today, Class, Object.new, ].each do |value|
597
+ it "does not typecast value #{value.inspect}" do
598
+ @course.active = value
599
+ @course['active'].should equal(value)
600
+ end
601
+ end
602
+
603
+ it "should respond to requests with ? modifier" do
604
+ @course.active = nil
605
+ @course.active?.should be_false
606
+ @course.active = false
607
+ @course.active?.should be_false
608
+ @course.active = true
609
+ @course.active?.should be_true
610
+ end
611
+
612
+ it "should respond to requests with ? modifier on TrueClass" do
613
+ @course.very_active = nil
614
+ @course.very_active?.should be_false
615
+ @course.very_active = false
616
+ @course.very_active?.should be_false
617
+ @course.very_active = true
618
+ @course.very_active?.should be_true
619
+ end
620
+ end
621
+
622
+ end
623
+ end
624
+
625
+ describe "a casted model retrieved from the database" do
626
+ before(:each) do
627
+ reset_test_db!
628
+ @cat = Cat.new(:name => 'Stimpy')
629
+ @cat.favorite_toy = CatToy.new(:name => 'Stinky')
630
+ @cat.toys << CatToy.new(:name => 'Feather')
631
+ @cat.toys << CatToy.new(:name => 'Mouse')
632
+ @cat.save
633
+ @cat = Cat.get(@cat.id)
634
+ end
635
+
636
+ describe "as a casted property" do
637
+ it "should already be casted_by its parent" do
638
+ @cat.favorite_toy.casted_by.should === @cat
639
+ end
640
+ end
641
+
642
+ describe "from a casted collection" do
643
+ it "should already be casted_by its parent" do
644
+ @cat.toys[0].casted_by.should === @cat
645
+ @cat.toys[1].casted_by.should === @cat
646
+ end
647
+ end
648
+ end
@@ -0,0 +1,3 @@
1
+ This is an example README file.
2
+
3
+ More of the README, whee.
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Test</title>
5
+ </head>
6
+ <body>
7
+ <p>
8
+ Test
9
+ </p>
10
+ </body>
11
+ </html>
@@ -0,0 +1,35 @@
1
+ class Article < CouchRest::ExtendedDocument
2
+ use_database DB
3
+ unique_id :slug
4
+
5
+ provides_collection :article_details, 'Article', 'by_date', :descending => true, :include_docs => true
6
+ view_by :date, :descending => true
7
+ view_by :user_id, :date
8
+
9
+ view_by :tags,
10
+ :map =>
11
+ "function(doc) {
12
+ if (doc['couchrest-type'] == 'Article' && doc.tags) {
13
+ doc.tags.forEach(function(tag){
14
+ emit(tag, 1);
15
+ });
16
+ }
17
+ }",
18
+ :reduce =>
19
+ "function(keys, values, rereduce) {
20
+ return sum(values);
21
+ }"
22
+
23
+ property :date, :type => 'Date'
24
+ property :slug, :read_only => true
25
+ property :title
26
+ property :tags, :type => ['String']
27
+
28
+ timestamps!
29
+
30
+ before_save :generate_slug_from_title
31
+
32
+ def generate_slug_from_title
33
+ self['slug'] = title.downcase.gsub(/[^a-z0-9]/,'-').squeeze('-').gsub(/^\-|\-$/,'') if new?
34
+ end
35
+ end