mattetti-couchrest 0.33 → 0.34
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.
- data/README.md +29 -10
- data/Rakefile +1 -0
- data/THANKS.md +1 -0
- data/lib/couchrest.rb +2 -1
- data/lib/couchrest/core/database.rb +0 -18
- data/lib/couchrest/core/document.rb +3 -2
- data/lib/couchrest/mixins/callbacks.rb +187 -138
- data/lib/couchrest/mixins/collection.rb +3 -16
- data/lib/couchrest/mixins/extended_attachments.rb +1 -1
- data/lib/couchrest/mixins/properties.rb +71 -44
- data/lib/couchrest/mixins/validation.rb +18 -29
- data/lib/couchrest/more/casted_model.rb +29 -1
- data/lib/couchrest/more/extended_document.rb +56 -17
- data/lib/couchrest/more/property.rb +19 -0
- data/lib/couchrest/support/class.rb +81 -67
- data/lib/couchrest/support/rails.rb +12 -5
- data/spec/couchrest/core/database_spec.rb +2 -2
- data/spec/couchrest/more/casted_extended_doc_spec.rb +2 -4
- data/spec/couchrest/more/casted_model_spec.rb +229 -0
- data/spec/couchrest/more/extended_doc_attachment_spec.rb +2 -2
- data/spec/couchrest/more/extended_doc_spec.rb +167 -16
- data/spec/couchrest/more/extended_doc_view_spec.rb +17 -6
- data/spec/couchrest/more/property_spec.rb +71 -3
- data/spec/fixtures/more/article.rb +2 -2
- data/spec/fixtures/more/cat.rb +1 -0
- data/spec/fixtures/more/event.rb +3 -0
- data/spec/fixtures/more/person.rb +1 -0
- metadata +3 -3
@@ -66,7 +66,7 @@ describe "ExtendedDocument attachments" do
|
|
66
66
|
|
67
67
|
it 'should set the content-type if passed' do
|
68
68
|
@obj.create_attachment(:file => @file_ext, :name => @attachment_name, :content_type => @content_type)
|
69
|
-
@obj['_attachments'][@attachment_name]['
|
69
|
+
@obj['_attachments'][@attachment_name]['content_type'].should == @content_type
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -100,7 +100,7 @@ describe "ExtendedDocument attachments" do
|
|
100
100
|
file = File.open(FIXTURE_PATH + '/attachments/README')
|
101
101
|
@file.should_not == file
|
102
102
|
@obj.update_attachment(:file => file, :name => @attachment_name, :content_type => @content_type)
|
103
|
-
@obj['_attachments'][@attachment_name]['
|
103
|
+
@obj['_attachments'][@attachment_name]['content_type'].should == @content_type
|
104
104
|
end
|
105
105
|
|
106
106
|
it 'should delete an attachment that exists' do
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.expand_path("../../../spec_helper", __FILE__)
|
2
2
|
require File.join(FIXTURE_PATH, 'more', 'article')
|
3
3
|
require File.join(FIXTURE_PATH, 'more', 'course')
|
4
|
-
|
4
|
+
require File.join(FIXTURE_PATH, 'more', 'cat')
|
5
5
|
|
6
6
|
describe "ExtendedDocument" do
|
7
7
|
|
@@ -17,8 +17,11 @@ describe "ExtendedDocument" do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
class WithCallBacks < CouchRest::ExtendedDocument
|
20
|
+
include ::CouchRest::Validation
|
20
21
|
use_database TEST_SERVER.default_database
|
21
22
|
property :name
|
23
|
+
property :run_before_validate
|
24
|
+
property :run_after_validate
|
22
25
|
property :run_before_save
|
23
26
|
property :run_after_save
|
24
27
|
property :run_before_create
|
@@ -26,24 +29,56 @@ describe "ExtendedDocument" do
|
|
26
29
|
property :run_before_update
|
27
30
|
property :run_after_update
|
28
31
|
|
29
|
-
|
32
|
+
before_validate do |object|
|
33
|
+
object.run_before_validate = true
|
34
|
+
end
|
35
|
+
after_validate do |object|
|
36
|
+
object.run_after_validate = true
|
37
|
+
end
|
38
|
+
before_save do |object|
|
30
39
|
object.run_before_save = true
|
31
40
|
end
|
32
|
-
|
41
|
+
after_save do |object|
|
33
42
|
object.run_after_save = true
|
34
43
|
end
|
35
|
-
|
44
|
+
before_create do |object|
|
36
45
|
object.run_before_create = true
|
37
46
|
end
|
38
|
-
|
47
|
+
after_create do |object|
|
39
48
|
object.run_after_create = true
|
40
49
|
end
|
41
|
-
|
50
|
+
before_update do |object|
|
42
51
|
object.run_before_update = true
|
43
52
|
end
|
44
|
-
|
53
|
+
after_update do |object|
|
45
54
|
object.run_after_update = true
|
46
55
|
end
|
56
|
+
|
57
|
+
property :run_one
|
58
|
+
property :run_two
|
59
|
+
property :run_three
|
60
|
+
|
61
|
+
before_save :run_one_method, :run_two_method do |object|
|
62
|
+
object.run_three = true
|
63
|
+
end
|
64
|
+
def run_one_method
|
65
|
+
self.run_one = true
|
66
|
+
end
|
67
|
+
def run_two_method
|
68
|
+
self.run_two = true
|
69
|
+
end
|
70
|
+
|
71
|
+
attr_accessor :run_it
|
72
|
+
property :conditional_one
|
73
|
+
property :conditional_two
|
74
|
+
|
75
|
+
before_save :conditional_one_method, :conditional_two_method, :if => proc { self.run_it }
|
76
|
+
def conditional_one_method
|
77
|
+
self.conditional_one = true
|
78
|
+
end
|
79
|
+
def conditional_two_method
|
80
|
+
self.conditional_two = true
|
81
|
+
end
|
47
82
|
end
|
48
83
|
|
49
84
|
class WithTemplateAndUniqueID < CouchRest::ExtendedDocument
|
@@ -85,15 +120,12 @@ describe "ExtendedDocument" do
|
|
85
120
|
end
|
86
121
|
|
87
122
|
describe "a new model" do
|
88
|
-
it "should be a
|
89
|
-
@obj = Basic.new
|
90
|
-
@obj.rev.should be_nil
|
91
|
-
@obj.should be_a_new_record
|
92
|
-
end
|
93
|
-
it "should be a new_document" do
|
123
|
+
it "should be a new document" do
|
94
124
|
@obj = Basic.new
|
95
125
|
@obj.rev.should be_nil
|
96
|
-
@obj.should
|
126
|
+
@obj.should be_new
|
127
|
+
@obj.should be_new_document
|
128
|
+
@obj.should be_new_record
|
97
129
|
end
|
98
130
|
end
|
99
131
|
|
@@ -101,7 +133,7 @@ describe "ExtendedDocument" do
|
|
101
133
|
it "should instantialize and save a document" do
|
102
134
|
article = Article.create(:title => 'my test')
|
103
135
|
article.title.should == 'my test'
|
104
|
-
article.should_not
|
136
|
+
article.should_not be_new
|
105
137
|
end
|
106
138
|
|
107
139
|
it "should trigger the create callbacks" do
|
@@ -125,6 +157,27 @@ describe "ExtendedDocument" do
|
|
125
157
|
@art.update_attributes_without_saving('date' => Time.now, :title => "super danger")
|
126
158
|
@art['title'].should == "super danger"
|
127
159
|
end
|
160
|
+
it "should silently ignore _id" do
|
161
|
+
@art.update_attributes_without_saving('_id' => 'foobar')
|
162
|
+
@art['_id'].should_not == 'foobar'
|
163
|
+
end
|
164
|
+
it "should silently ignore _rev" do
|
165
|
+
@art.update_attributes_without_saving('_rev' => 'foobar')
|
166
|
+
@art['_rev'].should_not == 'foobar'
|
167
|
+
end
|
168
|
+
it "should silently ignore created_at" do
|
169
|
+
@art.update_attributes_without_saving('created_at' => 'foobar')
|
170
|
+
@art['created_at'].should_not == 'foobar'
|
171
|
+
end
|
172
|
+
it "should silently ignore updated_at" do
|
173
|
+
@art.update_attributes_without_saving('updated_at' => 'foobar')
|
174
|
+
@art['updated_at'].should_not == 'foobar'
|
175
|
+
end
|
176
|
+
it "should also work using attributes= alias" do
|
177
|
+
@art.respond_to?(:attributes=).should be_true
|
178
|
+
@art.attributes = {'date' => Time.now, :title => "something else"}
|
179
|
+
@art['title'].should == "something else"
|
180
|
+
end
|
128
181
|
|
129
182
|
it "should flip out if an attribute= method is missing" do
|
130
183
|
lambda {
|
@@ -409,6 +462,25 @@ describe "ExtendedDocument" do
|
|
409
462
|
it "should set the type" do
|
410
463
|
@sobj['couchrest-type'].should == 'Basic'
|
411
464
|
end
|
465
|
+
|
466
|
+
describe "save!" do
|
467
|
+
|
468
|
+
before(:each) do
|
469
|
+
@sobj = Card.new(:first_name => "Marcos", :last_name => "Tapajós")
|
470
|
+
end
|
471
|
+
|
472
|
+
it "should return true if save the document" do
|
473
|
+
@sobj.save!.should == true
|
474
|
+
end
|
475
|
+
|
476
|
+
it "should raise error if don't save the document" do
|
477
|
+
@sobj.first_name = nil
|
478
|
+
lambda { @sobj.save!.should == true }.should raise_error(RuntimeError)
|
479
|
+
end
|
480
|
+
|
481
|
+
end
|
482
|
+
|
483
|
+
|
412
484
|
end
|
413
485
|
|
414
486
|
describe "saving a model with a unique_id configured" do
|
@@ -419,7 +491,7 @@ describe "ExtendedDocument" do
|
|
419
491
|
end
|
420
492
|
|
421
493
|
it "should be a new document" do
|
422
|
-
@art.should
|
494
|
+
@art.should be_new
|
423
495
|
@art.title.should be_nil
|
424
496
|
end
|
425
497
|
|
@@ -527,12 +599,46 @@ describe "ExtendedDocument" do
|
|
527
599
|
@doc = WithCallBacks.new
|
528
600
|
end
|
529
601
|
|
602
|
+
|
603
|
+
describe "validate" do
|
604
|
+
it "should run before_validate before validating" do
|
605
|
+
@doc.run_before_validate.should be_nil
|
606
|
+
@doc.should be_valid
|
607
|
+
@doc.run_before_validate.should be_true
|
608
|
+
end
|
609
|
+
it "should run after_validate after validating" do
|
610
|
+
@doc.run_after_validate.should be_nil
|
611
|
+
@doc.should be_valid
|
612
|
+
@doc.run_after_validate.should be_true
|
613
|
+
end
|
614
|
+
end
|
530
615
|
describe "save" do
|
531
616
|
it "should run the after filter after saving" do
|
532
617
|
@doc.run_after_save.should be_nil
|
533
618
|
@doc.save.should be_true
|
534
619
|
@doc.run_after_save.should be_true
|
535
620
|
end
|
621
|
+
it "should run the grouped callbacks before saving" do
|
622
|
+
@doc.run_one.should be_nil
|
623
|
+
@doc.run_two.should be_nil
|
624
|
+
@doc.run_three.should be_nil
|
625
|
+
@doc.save.should be_true
|
626
|
+
@doc.run_one.should be_true
|
627
|
+
@doc.run_two.should be_true
|
628
|
+
@doc.run_three.should be_true
|
629
|
+
end
|
630
|
+
it "should not run conditional callbacks" do
|
631
|
+
@doc.run_it = false
|
632
|
+
@doc.save.should be_true
|
633
|
+
@doc.conditional_one.should be_nil
|
634
|
+
@doc.conditional_two.should be_nil
|
635
|
+
end
|
636
|
+
it "should run conditional callbacks" do
|
637
|
+
@doc.run_it = true
|
638
|
+
@doc.save.should be_true
|
639
|
+
@doc.conditional_one.should be_true
|
640
|
+
@doc.conditional_two.should be_true
|
641
|
+
end
|
536
642
|
end
|
537
643
|
describe "create" do
|
538
644
|
it "should run the before save filter when creating" do
|
@@ -585,4 +691,49 @@ describe "ExtendedDocument" do
|
|
585
691
|
@doc.other_arg.should == "foo-foo"
|
586
692
|
end
|
587
693
|
end
|
694
|
+
|
695
|
+
describe "recursive validation on an extended document" do
|
696
|
+
before :each do
|
697
|
+
reset_test_db!
|
698
|
+
@cat = Cat.new(:name => 'Sockington')
|
699
|
+
end
|
700
|
+
|
701
|
+
it "should not save if a nested casted model is invalid" do
|
702
|
+
@cat.favorite_toy = CatToy.new
|
703
|
+
@cat.should_not be_valid
|
704
|
+
@cat.save.should be_false
|
705
|
+
lambda{@cat.save!}.should raise_error
|
706
|
+
end
|
707
|
+
|
708
|
+
it "should save when nested casted model is valid" do
|
709
|
+
@cat.favorite_toy = CatToy.new(:name => 'Squeaky')
|
710
|
+
@cat.should be_valid
|
711
|
+
@cat.save.should be_true
|
712
|
+
lambda{@cat.save!}.should_not raise_error
|
713
|
+
end
|
714
|
+
|
715
|
+
it "should not save when nested collection contains an invalid casted model" do
|
716
|
+
@cat.toys = [CatToy.new(:name => 'Feather'), CatToy.new]
|
717
|
+
@cat.should_not be_valid
|
718
|
+
@cat.save.should be_false
|
719
|
+
lambda{@cat.save!}.should raise_error
|
720
|
+
end
|
721
|
+
|
722
|
+
it "should save when nested collection contains valid casted models" do
|
723
|
+
@cat.toys = [CatToy.new(:name => 'feather'), CatToy.new(:name => 'ball-o-twine')]
|
724
|
+
@cat.should be_valid
|
725
|
+
@cat.save.should be_true
|
726
|
+
lambda{@cat.save!}.should_not raise_error
|
727
|
+
end
|
728
|
+
|
729
|
+
it "should not fail if the nested casted model doesn't have validation" do
|
730
|
+
Cat.property :trainer, :cast_as => 'Person'
|
731
|
+
Cat.validates_present :name
|
732
|
+
cat = Cat.new(:name => 'Mr Bigglesworth')
|
733
|
+
cat.trainer = Person.new
|
734
|
+
cat.trainer.validatable?.should be_false
|
735
|
+
cat.should be_valid
|
736
|
+
cat.save.should be_true
|
737
|
+
end
|
738
|
+
end
|
588
739
|
end
|
@@ -348,12 +348,19 @@ describe "ExtendedDocument views" do
|
|
348
348
|
describe "with a collection" do
|
349
349
|
before(:all) do
|
350
350
|
reset_test_db!
|
351
|
-
|
351
|
+
titles = ["very uniq one", "really interesting", "some fun",
|
352
352
|
"really awesome", "crazy bob", "this rocks", "super rad"]
|
353
|
-
|
353
|
+
titles.each_with_index do |title,i|
|
354
354
|
a = Article.new(:title => title, :date => Date.today)
|
355
355
|
a.save
|
356
356
|
end
|
357
|
+
|
358
|
+
titles = ["yesterday very uniq one", "yesterday really interesting", "yesterday some fun",
|
359
|
+
"yesterday really awesome", "yesterday crazy bob", "yesterday this rocks"]
|
360
|
+
titles.each_with_index do |title,i|
|
361
|
+
a = Article.new(:title => title, :date => Date.today - 1)
|
362
|
+
a.save
|
363
|
+
end
|
357
364
|
end
|
358
365
|
require 'date'
|
359
366
|
it "should return a proxy that looks like an array of 7 Article objects" do
|
@@ -373,10 +380,6 @@ describe "ExtendedDocument views" do
|
|
373
380
|
a.should_not be_nil
|
374
381
|
end
|
375
382
|
end
|
376
|
-
it "should have the amount of paginated pages" do
|
377
|
-
articles = Article.by_date :key => Date.today
|
378
|
-
articles.paginate(:per_page => 3).amount_pages.should == 3
|
379
|
-
end
|
380
383
|
it "should provide a class method to access the collection directly" do
|
381
384
|
articles = Article.collection_proxy_for('Article', 'by_date', :descending => true,
|
382
385
|
:key => Date.today, :include_docs => true)
|
@@ -421,6 +424,14 @@ describe "ExtendedDocument views" do
|
|
421
424
|
lambda{Article.collection_proxy_for('Article', nil)}.should raise_error
|
422
425
|
lambda{Article.paginate(:design_doc => 'Article')}.should raise_error
|
423
426
|
end
|
427
|
+
it "should be able to span multiple keys" do
|
428
|
+
articles = Article.by_date :startkey => Date.today, :endkey => Date.today - 1
|
429
|
+
articles.paginate(:page => 1, :per_page => 3).size.should == 3
|
430
|
+
articles.paginate(:page => 2, :per_page => 3).size.should == 3
|
431
|
+
articles.paginate(:page => 3, :per_page => 3).size.should == 3
|
432
|
+
articles.paginate(:page => 4, :per_page => 3).size.should == 3
|
433
|
+
articles.paginate(:page => 5, :per_page => 3).size.should == 1
|
434
|
+
end
|
424
435
|
end
|
425
436
|
|
426
437
|
end
|
@@ -4,6 +4,7 @@ require File.join(FIXTURE_PATH, 'more', 'card')
|
|
4
4
|
require File.join(FIXTURE_PATH, 'more', 'invoice')
|
5
5
|
require File.join(FIXTURE_PATH, 'more', 'service')
|
6
6
|
require File.join(FIXTURE_PATH, 'more', 'event')
|
7
|
+
require File.join(FIXTURE_PATH, 'more', 'cat')
|
7
8
|
|
8
9
|
|
9
10
|
describe "ExtendedDocument properties" do
|
@@ -94,7 +95,7 @@ describe "ExtendedDocument properties" do
|
|
94
95
|
@invoice.location = nil
|
95
96
|
@invoice.should_not be_valid
|
96
97
|
@invoice.save.should be_false
|
97
|
-
@invoice.should
|
98
|
+
@invoice.should be_new
|
98
99
|
end
|
99
100
|
end
|
100
101
|
|
@@ -132,14 +133,17 @@ describe "ExtendedDocument properties" do
|
|
132
133
|
describe "casting" do
|
133
134
|
describe "cast keys to any type" do
|
134
135
|
before(:all) do
|
135
|
-
event_doc = { :subject => "Some event", :occurs_at => Time.now }
|
136
|
+
event_doc = { :subject => "Some event", :occurs_at => Time.now, :end_date => Date.today }
|
136
137
|
e = Event.database.save_doc event_doc
|
137
138
|
|
138
139
|
@event = Event.get e['id']
|
139
140
|
end
|
140
|
-
it "should cast
|
141
|
+
it "should cast occurs_at to Time" do
|
141
142
|
@event['occurs_at'].should be_an_instance_of(Time)
|
142
143
|
end
|
144
|
+
it "should cast end_date to Date" do
|
145
|
+
@event['end_date'].should be_an_instance_of(Date)
|
146
|
+
end
|
143
147
|
end
|
144
148
|
|
145
149
|
describe "casting to Float object" do
|
@@ -191,5 +195,69 @@ describe "ExtendedDocument properties" do
|
|
191
195
|
end
|
192
196
|
|
193
197
|
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe "a newly created casted model" do
|
201
|
+
before(:each) do
|
202
|
+
reset_test_db!
|
203
|
+
@cat = Cat.new(:name => 'Toonces')
|
204
|
+
@squeaky_mouse = CatToy.new(:name => 'Squeaky')
|
205
|
+
end
|
206
|
+
|
207
|
+
describe "assigned assigned to a casted property" do
|
208
|
+
it "should have casted_by set to its parent" do
|
209
|
+
@squeaky_mouse.casted_by.should be_nil
|
210
|
+
@cat.favorite_toy = @squeaky_mouse
|
211
|
+
@squeaky_mouse.casted_by.should === @cat
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe "appended to a casted collection" do
|
216
|
+
it "should have casted_by set to its parent" do
|
217
|
+
@squeaky_mouse.casted_by.should be_nil
|
218
|
+
@cat.toys << @squeaky_mouse
|
219
|
+
@squeaky_mouse.casted_by.should === @cat
|
220
|
+
@cat.save
|
221
|
+
@cat.toys.first.casted_by.should === @cat
|
222
|
+
end
|
223
|
+
end
|
194
224
|
|
225
|
+
describe "list assigned to a casted collection" do
|
226
|
+
it "should have casted_by set on all elements" do
|
227
|
+
toy1 = CatToy.new(:name => 'Feather')
|
228
|
+
toy2 = CatToy.new(:name => 'Mouse')
|
229
|
+
@cat.toys = [toy1, toy2]
|
230
|
+
toy1.casted_by.should === @cat
|
231
|
+
toy2.casted_by.should === @cat
|
232
|
+
@cat.save
|
233
|
+
@cat = Cat.get(@cat.id)
|
234
|
+
@cat.toys[0].casted_by.should === @cat
|
235
|
+
@cat.toys[1].casted_by.should === @cat
|
236
|
+
end
|
237
|
+
end
|
195
238
|
end
|
239
|
+
|
240
|
+
describe "a casted model retrieved from the database" do
|
241
|
+
before(:each) do
|
242
|
+
reset_test_db!
|
243
|
+
@cat = Cat.new(:name => 'Stimpy')
|
244
|
+
@cat.favorite_toy = CatToy.new(:name => 'Stinky')
|
245
|
+
@cat.toys << CatToy.new(:name => 'Feather')
|
246
|
+
@cat.toys << CatToy.new(:name => 'Mouse')
|
247
|
+
@cat.save
|
248
|
+
@cat = Cat.get(@cat.id)
|
249
|
+
end
|
250
|
+
|
251
|
+
describe "as a casted property" do
|
252
|
+
it "should already be casted_by its parent" do
|
253
|
+
@cat.favorite_toy.casted_by.should === @cat
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
describe "from a casted collection" do
|
258
|
+
it "should already be casted_by its parent" do
|
259
|
+
@cat.toys[0].casted_by.should === @cat
|
260
|
+
@cat.toys[1].casted_by.should === @cat
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
@@ -26,9 +26,9 @@ class Article < CouchRest::ExtendedDocument
|
|
26
26
|
|
27
27
|
timestamps!
|
28
28
|
|
29
|
-
|
29
|
+
before_save :generate_slug_from_title
|
30
30
|
|
31
31
|
def generate_slug_from_title
|
32
|
-
self['slug'] = title.downcase.gsub(/[^a-z0-9]/,'-').squeeze('-').gsub(/^\-|\-$/,'') if
|
32
|
+
self['slug'] = title.downcase.gsub(/[^a-z0-9]/,'-').squeeze('-').gsub(/^\-|\-$/,'') if new?
|
33
33
|
end
|
34
34
|
end
|