couchrest_extended_document 1.0.0.beta5

Sign up to get free protection for your applications and to get access to all the features.
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