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 +8 -127
- data/Rakefile +20 -36
- data/THANKS.md +2 -1
- data/history.txt +25 -0
- data/lib/couchrest.rb +5 -4
- data/lib/couchrest/core/database.rb +26 -21
- data/lib/couchrest/core/document.rb +4 -3
- data/lib/couchrest/helper/streamer.rb +11 -4
- data/lib/couchrest/mixins/attribute_protection.rb +74 -0
- 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/extended_document_mixins.rb +1 -0
- 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 +73 -25
- data/lib/couchrest/more/property.rb +20 -1
- data/lib/couchrest/support/class.rb +81 -67
- data/lib/couchrest/support/rails.rb +12 -5
- data/lib/couchrest/validation/auto_validate.rb +5 -9
- data/lib/couchrest/validation/validators/confirmation_validator.rb +11 -3
- data/lib/couchrest/validation/validators/format_validator.rb +8 -3
- data/lib/couchrest/validation/validators/length_validator.rb +10 -5
- data/lib/couchrest/validation/validators/numeric_validator.rb +6 -1
- data/lib/couchrest/validation/validators/required_field_validator.rb +8 -3
- data/spec/couchrest/core/couchrest_spec.rb +48 -2
- data/spec/couchrest/core/database_spec.rb +22 -10
- data/spec/couchrest/core/document_spec.rb +9 -1
- data/spec/couchrest/helpers/streamer_spec.rb +31 -2
- data/spec/couchrest/more/attribute_protection_spec.rb +94 -0
- data/spec/couchrest/more/casted_extended_doc_spec.rb +2 -4
- data/spec/couchrest/more/casted_model_spec.rb +230 -1
- data/spec/couchrest/more/extended_doc_attachment_spec.rb +2 -2
- data/spec/couchrest/more/extended_doc_spec.rb +173 -15
- data/spec/couchrest/more/extended_doc_view_spec.rb +17 -10
- data/spec/couchrest/more/property_spec.rb +97 -3
- data/spec/fixtures/more/article.rb +4 -3
- data/spec/fixtures/more/card.rb +1 -1
- data/spec/fixtures/more/cat.rb +5 -3
- data/spec/fixtures/more/event.rb +4 -1
- data/spec/fixtures/more/invoice.rb +2 -2
- data/spec/fixtures/more/person.rb +1 -0
- data/spec/fixtures/more/user.rb +22 -0
- metadata +46 -13
|
@@ -65,22 +65,27 @@ describe CouchRest::Database do
|
|
|
65
65
|
|
|
66
66
|
describe "saving a view" do
|
|
67
67
|
before(:each) do
|
|
68
|
-
@view = {'test' => {'map' =>
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
@view = {'test' => {'map' => <<-JS
|
|
69
|
+
function(doc) {
|
|
70
|
+
var reg = new RegExp("\\\\W");
|
|
71
|
+
if (doc.word && !reg.test(doc.word)) {
|
|
72
|
+
emit(doc.word,null);
|
|
73
|
+
}
|
|
71
74
|
}
|
|
72
|
-
|
|
75
|
+
JS
|
|
76
|
+
}}
|
|
73
77
|
@db.save_doc({
|
|
74
78
|
"_id" => "_design/test",
|
|
75
79
|
:views => @view
|
|
76
80
|
})
|
|
77
81
|
end
|
|
78
82
|
it "should work properly" do
|
|
79
|
-
@db.bulk_save([
|
|
83
|
+
r = @db.bulk_save([
|
|
80
84
|
{"word" => "once"},
|
|
81
85
|
{"word" => "and again"}
|
|
82
86
|
])
|
|
83
|
-
@db.view('test/test')
|
|
87
|
+
r = @db.view('test/test')
|
|
88
|
+
r['total_rows'].should == 1
|
|
84
89
|
end
|
|
85
90
|
it "should round trip" do
|
|
86
91
|
@db.get("_design/test")['views'].should == @view
|
|
@@ -131,9 +136,16 @@ describe CouchRest::Database do
|
|
|
131
136
|
rs = @db.view('first/test', :include_docs => true) do |row|
|
|
132
137
|
rows << row
|
|
133
138
|
end
|
|
134
|
-
rows.length.should ==
|
|
139
|
+
rows.length.should == 3
|
|
135
140
|
rs["total_rows"].should == 3
|
|
136
141
|
end
|
|
142
|
+
it "should accept a block with several params" do
|
|
143
|
+
rows = []
|
|
144
|
+
rs = @db.view('first/test', :include_docs => true, :limit => 2) do |row|
|
|
145
|
+
rows << row
|
|
146
|
+
end
|
|
147
|
+
rows.length.should == 2
|
|
148
|
+
end
|
|
137
149
|
end
|
|
138
150
|
|
|
139
151
|
describe "GET (document by id) when the doc exists" do
|
|
@@ -253,7 +265,7 @@ describe CouchRest::Database do
|
|
|
253
265
|
describe "PUT attachment from file" do
|
|
254
266
|
before(:each) do
|
|
255
267
|
filename = FIXTURE_PATH + '/attachments/couchdb.png'
|
|
256
|
-
@file = File.open(filename)
|
|
268
|
+
@file = File.open(filename, "rb")
|
|
257
269
|
end
|
|
258
270
|
after(:each) do
|
|
259
271
|
@file.close
|
|
@@ -552,7 +564,7 @@ describe CouchRest::Database do
|
|
|
552
564
|
newdoc['artist'].should == 'Zappa'
|
|
553
565
|
end
|
|
554
566
|
it "should fail without an _id" do
|
|
555
|
-
lambda{@db.
|
|
567
|
+
lambda{@db.copy_doc({"not"=>"a real doc"})}.should raise_error(ArgumentError)
|
|
556
568
|
end
|
|
557
569
|
end
|
|
558
570
|
describe "to an existing location" do
|
|
@@ -711,4 +723,4 @@ describe CouchRest::Database do
|
|
|
711
723
|
end
|
|
712
724
|
|
|
713
725
|
|
|
714
|
-
end
|
|
726
|
+
end
|
|
@@ -83,6 +83,14 @@ describe CouchRest::Document do
|
|
|
83
83
|
@doc.id.should == @resp["id"]
|
|
84
84
|
@doc.rev.should == @resp["rev"]
|
|
85
85
|
end
|
|
86
|
+
it "should generate a correct URI" do
|
|
87
|
+
@doc.uri.should == "#{@db.root}/#{@doc.id}"
|
|
88
|
+
URI.parse(@doc.uri).to_s.should == @doc.uri
|
|
89
|
+
end
|
|
90
|
+
it "should generate a correct URI with revision" do
|
|
91
|
+
@doc.uri(true).should == "#{@db.root}/#{@doc.id}?rev=#{@doc.rev}"
|
|
92
|
+
URI.parse(@doc.uri(true)).to_s.should == @doc.uri(true)
|
|
93
|
+
end
|
|
86
94
|
end
|
|
87
95
|
|
|
88
96
|
describe "bulk saving" do
|
|
@@ -264,4 +272,4 @@ describe "dealing with attachments" do
|
|
|
264
272
|
end
|
|
265
273
|
end
|
|
266
274
|
|
|
267
|
-
end
|
|
275
|
+
end
|
|
@@ -9,6 +9,14 @@ describe CouchRest::Streamer do
|
|
|
9
9
|
@streamer = CouchRest::Streamer.new(@db)
|
|
10
10
|
@docs = (1..1000).collect{|i| {:integer => i, :string => i.to_s}}
|
|
11
11
|
@db.bulk_save(@docs)
|
|
12
|
+
@db.save_doc({
|
|
13
|
+
"_id" => "_design/first",
|
|
14
|
+
:views => {
|
|
15
|
+
:test => {
|
|
16
|
+
:map => "function(doc){for(var w in doc){ if(!w.match(/^_/))emit(w,doc[w])}}"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
})
|
|
12
20
|
end
|
|
13
21
|
|
|
14
22
|
it "should yield each row in a view" do
|
|
@@ -19,5 +27,26 @@ describe CouchRest::Streamer do
|
|
|
19
27
|
end
|
|
20
28
|
count.should == 1001
|
|
21
29
|
end
|
|
22
|
-
|
|
23
|
-
|
|
30
|
+
|
|
31
|
+
it "should accept several params" do
|
|
32
|
+
count = 0
|
|
33
|
+
@streamer.view("_design/first/_view/test", :include_docs => true, :limit => 5) do |row|
|
|
34
|
+
count += 1
|
|
35
|
+
end
|
|
36
|
+
count.should == 5
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should accept both view formats" do
|
|
40
|
+
count = 0
|
|
41
|
+
@streamer.view("_design/first/_view/test") do |row|
|
|
42
|
+
count += 1
|
|
43
|
+
end
|
|
44
|
+
count.should == 2000
|
|
45
|
+
count = 0
|
|
46
|
+
@streamer.view("first/test") do |row|
|
|
47
|
+
count += 1
|
|
48
|
+
end
|
|
49
|
+
count.should == 2000
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require File.expand_path("../../../spec_helper", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "ExtendedDocument", "no declarations" do
|
|
4
|
+
class NoProtection < CouchRest::ExtendedDocument
|
|
5
|
+
use_database TEST_SERVER.default_database
|
|
6
|
+
property :name
|
|
7
|
+
property :phone
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should not protect anything through new" do
|
|
11
|
+
user = NoProtection.new(:name => "will", :phone => "555-5555")
|
|
12
|
+
|
|
13
|
+
user.name.should == "will"
|
|
14
|
+
user.phone.should == "555-5555"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should not protect anything through attributes=" do
|
|
18
|
+
user = NoProtection.new
|
|
19
|
+
user.attributes = {:name => "will", :phone => "555-5555"}
|
|
20
|
+
|
|
21
|
+
user.name.should == "will"
|
|
22
|
+
user.phone.should == "555-5555"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe "ExtendedDocument", "accessible flag" do
|
|
27
|
+
class WithAccessible < CouchRest::ExtendedDocument
|
|
28
|
+
use_database TEST_SERVER.default_database
|
|
29
|
+
property :name, :accessible => true
|
|
30
|
+
property :admin, :default => false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should recognize accessible properties" do
|
|
34
|
+
props = WithAccessible.accessible_properties.map { |prop| prop.name}
|
|
35
|
+
props.should include("name")
|
|
36
|
+
props.should_not include("admin")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should protect non-accessible properties set through new" do
|
|
40
|
+
user = WithAccessible.new(:name => "will", :admin => true)
|
|
41
|
+
|
|
42
|
+
user.name.should == "will"
|
|
43
|
+
user.admin.should == false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "should protect non-accessible properties set through attributes=" do
|
|
47
|
+
user = WithAccessible.new
|
|
48
|
+
user.attributes = {:name => "will", :admin => true}
|
|
49
|
+
|
|
50
|
+
user.name.should == "will"
|
|
51
|
+
user.admin.should == false
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "ExtendedDocument", "protected flag" do
|
|
56
|
+
class WithProtected < CouchRest::ExtendedDocument
|
|
57
|
+
use_database TEST_SERVER.default_database
|
|
58
|
+
property :name
|
|
59
|
+
property :admin, :default => false, :protected => true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should recognize protected properties" do
|
|
63
|
+
props = WithProtected.protected_properties.map { |prop| prop.name}
|
|
64
|
+
props.should_not include("name")
|
|
65
|
+
props.should include("admin")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "should protect non-accessible properties set through new" do
|
|
69
|
+
user = WithProtected.new(:name => "will", :admin => true)
|
|
70
|
+
|
|
71
|
+
user.name.should == "will"
|
|
72
|
+
user.admin.should == false
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "should protect non-accessible properties set through attributes=" do
|
|
76
|
+
user = WithProtected.new
|
|
77
|
+
user.attributes = {:name => "will", :admin => true}
|
|
78
|
+
|
|
79
|
+
user.name.should == "will"
|
|
80
|
+
user.admin.should == false
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
describe "ExtendedDocument", "protected flag" do
|
|
85
|
+
class WithBoth < CouchRest::ExtendedDocument
|
|
86
|
+
use_database TEST_SERVER.default_database
|
|
87
|
+
property :name, :accessible => true
|
|
88
|
+
property :admin, :default => false, :protected => true
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "should raise an error when both are set" do
|
|
92
|
+
lambda { WithBoth.new }.should raise_error
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -43,16 +43,14 @@ describe "assigning a value to casted attribute after initializing an object" do
|
|
|
43
43
|
@car.driver.should be_nil
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
# Note that this isn't casting the attribute, it's just assigning it a value
|
|
47
|
-
# (see "should not cast attribute")
|
|
48
46
|
it "should let you assign the value" do
|
|
49
47
|
@car.driver = @driver
|
|
50
48
|
@car.driver.name.should == 'Matt'
|
|
51
49
|
end
|
|
52
50
|
|
|
53
|
-
it "should
|
|
51
|
+
it "should cast attribute" do
|
|
54
52
|
@car.driver = JSON.parse(JSON.generate(@driver))
|
|
55
|
-
@car.driver.
|
|
53
|
+
@car.driver.should be_instance_of(Driver)
|
|
56
54
|
end
|
|
57
55
|
|
|
58
56
|
end
|
|
@@ -4,6 +4,8 @@ require File.expand_path('../../../spec_helper', __FILE__)
|
|
|
4
4
|
require File.join(FIXTURE_PATH, 'more', 'card')
|
|
5
5
|
require File.join(FIXTURE_PATH, 'more', 'cat')
|
|
6
6
|
require File.join(FIXTURE_PATH, 'more', 'person')
|
|
7
|
+
require File.join(FIXTURE_PATH, 'more', 'question')
|
|
8
|
+
require File.join(FIXTURE_PATH, 'more', 'course')
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
class WithCastedModelMixin < Hash
|
|
@@ -21,6 +23,26 @@ class DummyModel < CouchRest::ExtendedDocument
|
|
|
21
23
|
property :keywords, :cast_as => ["String"]
|
|
22
24
|
end
|
|
23
25
|
|
|
26
|
+
class CastedCallbackDoc < CouchRest::ExtendedDocument
|
|
27
|
+
use_database TEST_SERVER.default_database
|
|
28
|
+
raise "Default DB not set" if TEST_SERVER.default_database.nil?
|
|
29
|
+
property :callback_model, :cast_as => 'WithCastedCallBackModel'
|
|
30
|
+
end
|
|
31
|
+
class WithCastedCallBackModel < Hash
|
|
32
|
+
include CouchRest::CastedModel
|
|
33
|
+
include CouchRest::Validation
|
|
34
|
+
property :name
|
|
35
|
+
property :run_before_validate
|
|
36
|
+
property :run_after_validate
|
|
37
|
+
|
|
38
|
+
before_validate do |object|
|
|
39
|
+
object.run_before_validate = true
|
|
40
|
+
end
|
|
41
|
+
after_validate do |object|
|
|
42
|
+
object.run_after_validate = true
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
24
46
|
describe CouchRest::CastedModel do
|
|
25
47
|
|
|
26
48
|
describe "A non hash class including CastedModel" do
|
|
@@ -106,7 +128,40 @@ describe CouchRest::CastedModel do
|
|
|
106
128
|
@obj.keywords.should be_an_instance_of(Array)
|
|
107
129
|
@obj.keywords.first.should == 'couch'
|
|
108
130
|
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
describe "update attributes without saving" do
|
|
134
|
+
before(:each) do
|
|
135
|
+
@question = Question.new(:q => "What is your quest?", :a => "To seek the Holy Grail")
|
|
136
|
+
end
|
|
137
|
+
it "should work for attribute= methods" do
|
|
138
|
+
@question.q.should == "What is your quest?"
|
|
139
|
+
@question['a'].should == "To seek the Holy Grail"
|
|
140
|
+
@question.update_attributes_without_saving(:q => "What is your favorite color?", 'a' => "Blue")
|
|
141
|
+
@question['q'].should == "What is your favorite color?"
|
|
142
|
+
@question.a.should == "Blue"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it "should also work for attributes= alias" do
|
|
146
|
+
@question.respond_to?(:attributes=).should be_true
|
|
147
|
+
@question.attributes = {:q => "What is your favorite color?", 'a' => "Blue"}
|
|
148
|
+
@question['q'].should == "What is your favorite color?"
|
|
149
|
+
@question.a.should == "Blue"
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
it "should flip out if an attribute= method is missing" do
|
|
153
|
+
lambda {
|
|
154
|
+
@q.update_attributes_without_saving('foo' => "something", :a => "No green")
|
|
155
|
+
}.should raise_error(NoMethodError)
|
|
156
|
+
end
|
|
109
157
|
|
|
158
|
+
it "should not change any attributes if there is an error" do
|
|
159
|
+
lambda {
|
|
160
|
+
@q.update_attributes_without_saving('foo' => "something", :a => "No green")
|
|
161
|
+
}.should raise_error(NoMethodError)
|
|
162
|
+
@question.q.should == "What is your quest?"
|
|
163
|
+
@question.a.should == "To seek the Holy Grail"
|
|
164
|
+
end
|
|
110
165
|
end
|
|
111
166
|
|
|
112
167
|
describe "saved document with casted models" do
|
|
@@ -154,6 +209,10 @@ describe CouchRest::CastedModel do
|
|
|
154
209
|
toy = CatToy.new :name => "Mouse"
|
|
155
210
|
@cat.toys.push(toy)
|
|
156
211
|
@cat.save.should be_true
|
|
212
|
+
@cat = Cat.get @cat.id
|
|
213
|
+
@cat.toys.class.should == CastedArray
|
|
214
|
+
@cat.toys.first.class.should == CatToy
|
|
215
|
+
@cat.toys.first.should === toy
|
|
157
216
|
end
|
|
158
217
|
|
|
159
218
|
it "should fail because name is not present" do
|
|
@@ -165,13 +224,183 @@ describe CouchRest::CastedModel do
|
|
|
165
224
|
|
|
166
225
|
it "should not fail if the casted model doesn't have validation" do
|
|
167
226
|
Cat.property :masters, :cast_as => ['Person'], :default => []
|
|
168
|
-
Cat.
|
|
227
|
+
Cat.validates_presence_of :name
|
|
169
228
|
cat = Cat.new(:name => 'kitty')
|
|
170
229
|
cat.should be_valid
|
|
171
230
|
cat.masters.push Person.new
|
|
172
231
|
cat.should be_valid
|
|
173
232
|
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
describe "calling valid?" do
|
|
236
|
+
before :each do
|
|
237
|
+
@cat = Cat.new
|
|
238
|
+
@toy1 = CatToy.new
|
|
239
|
+
@toy2 = CatToy.new
|
|
240
|
+
@toy3 = CatToy.new
|
|
241
|
+
@cat.favorite_toy = @toy1
|
|
242
|
+
@cat.toys << @toy2
|
|
243
|
+
@cat.toys << @toy3
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
describe "on the top document" do
|
|
247
|
+
it "should put errors on all invalid casted models" do
|
|
248
|
+
@cat.should_not be_valid
|
|
249
|
+
@cat.errors.should_not be_empty
|
|
250
|
+
@toy1.errors.should_not be_empty
|
|
251
|
+
@toy2.errors.should_not be_empty
|
|
252
|
+
@toy3.errors.should_not be_empty
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
it "should not put errors on valid casted models" do
|
|
256
|
+
@toy1.name = "Feather"
|
|
257
|
+
@toy2.name = "Twine"
|
|
258
|
+
@cat.should_not be_valid
|
|
259
|
+
@cat.errors.should_not be_empty
|
|
260
|
+
@toy1.errors.should be_empty
|
|
261
|
+
@toy2.errors.should be_empty
|
|
262
|
+
@toy3.errors.should_not be_empty
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
describe "on a casted model property" do
|
|
267
|
+
it "should only validate itself" do
|
|
268
|
+
@toy1.should_not be_valid
|
|
269
|
+
@toy1.errors.should_not be_empty
|
|
270
|
+
@cat.errors.should be_empty
|
|
271
|
+
@toy2.errors.should be_empty
|
|
272
|
+
@toy3.errors.should be_empty
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
describe "on a casted model inside a casted collection" do
|
|
277
|
+
it "should only validate itself" do
|
|
278
|
+
@toy2.should_not be_valid
|
|
279
|
+
@toy2.errors.should_not be_empty
|
|
280
|
+
@cat.errors.should be_empty
|
|
281
|
+
@toy1.errors.should be_empty
|
|
282
|
+
@toy3.errors.should be_empty
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
describe "calling new? on a casted model" do
|
|
288
|
+
before :each do
|
|
289
|
+
reset_test_db!
|
|
290
|
+
@cat = Cat.new(:name => 'Sockington')
|
|
291
|
+
@favorite_toy = CatToy.new(:name => 'Catnip Ball')
|
|
292
|
+
@cat.favorite_toy = @favorite_toy
|
|
293
|
+
@cat.toys << CatToy.new(:name => 'Fuzzy Stick')
|
|
294
|
+
end
|
|
174
295
|
|
|
296
|
+
it "should be true on new" do
|
|
297
|
+
CatToy.new.should be_new
|
|
298
|
+
CatToy.new.new_record?.should be_true
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
it "should be true after assignment" do
|
|
302
|
+
@cat.should be_new
|
|
303
|
+
@cat.favorite_toy.should be_new
|
|
304
|
+
@cat.toys.first.should be_new
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it "should not be true after create or save" do
|
|
308
|
+
@cat.create
|
|
309
|
+
@cat.save
|
|
310
|
+
@cat.favorite_toy.should_not be_new
|
|
311
|
+
@cat.toys.first.should_not be_new
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
it "should not be true after get from the database" do
|
|
315
|
+
@cat.save
|
|
316
|
+
@cat = Cat.get(@cat.id)
|
|
317
|
+
@cat.favorite_toy.should_not be_new
|
|
318
|
+
@cat.toys.first.should_not be_new
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
it "should still be true after a failed create or save" do
|
|
322
|
+
@cat.name = nil
|
|
323
|
+
@cat.create.should be_false
|
|
324
|
+
@cat.save.should be_false
|
|
325
|
+
@cat.favorite_toy.should be_new
|
|
326
|
+
@cat.toys.first.should be_new
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
describe "calling base_doc from a nested casted model" do
|
|
331
|
+
before :each do
|
|
332
|
+
@course = Course.new(:title => 'Science 101')
|
|
333
|
+
@professor = Person.new(:name => 'Professor Plum')
|
|
334
|
+
@cat = Cat.new(:name => 'Scratchy')
|
|
335
|
+
@toy1 = CatToy.new
|
|
336
|
+
@toy2 = CatToy.new
|
|
337
|
+
@course.professor = @professor
|
|
338
|
+
@professor.pet = @cat
|
|
339
|
+
@cat.favorite_toy = @toy1
|
|
340
|
+
@cat.toys << @toy2
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
it "should reference the top document for" do
|
|
344
|
+
@course.base_doc.should === @course
|
|
345
|
+
@professor.casted_by.should === @course
|
|
346
|
+
@professor.base_doc.should === @course
|
|
347
|
+
@cat.base_doc.should === @course
|
|
348
|
+
@toy1.base_doc.should === @course
|
|
349
|
+
@toy2.base_doc.should === @course
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
it "should call setter on top document" do
|
|
353
|
+
@toy1.base_doc.should_not be_nil
|
|
354
|
+
@toy1.base_doc.title = 'Tom Foolery'
|
|
355
|
+
@course.title.should == 'Tom Foolery'
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
it "should return nil if not yet casted" do
|
|
359
|
+
person = Person.new
|
|
360
|
+
person.base_doc.should == nil
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
describe "calling base_doc.save from a nested casted model" do
|
|
365
|
+
before :each do
|
|
366
|
+
reset_test_db!
|
|
367
|
+
@cat = Cat.new(:name => 'Snowball')
|
|
368
|
+
@toy = CatToy.new
|
|
369
|
+
@cat.favorite_toy = @toy
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
it "should not save parent document when casted model is invalid" do
|
|
373
|
+
@toy.should_not be_valid
|
|
374
|
+
@toy.base_doc.save.should be_false
|
|
375
|
+
lambda{@toy.base_doc.save!}.should raise_error
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
it "should save parent document when nested casted model is valid" do
|
|
379
|
+
@toy.name = "Mr Squeaks"
|
|
380
|
+
@toy.should be_valid
|
|
381
|
+
@toy.base_doc.save.should be_true
|
|
382
|
+
lambda{@toy.base_doc.save!}.should_not raise_error
|
|
383
|
+
end
|
|
175
384
|
end
|
|
176
385
|
|
|
386
|
+
describe "callbacks" do
|
|
387
|
+
before(:each) do
|
|
388
|
+
@doc = CastedCallbackDoc.new
|
|
389
|
+
@model = WithCastedCallBackModel.new
|
|
390
|
+
@doc.callback_model = @model
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
describe "validate" do
|
|
394
|
+
it "should run before_validate before validating" do
|
|
395
|
+
@model.run_before_validate.should be_nil
|
|
396
|
+
@model.should be_valid
|
|
397
|
+
@model.run_before_validate.should be_true
|
|
398
|
+
end
|
|
399
|
+
it "should run after_validate after validating" do
|
|
400
|
+
@model.run_after_validate.should be_nil
|
|
401
|
+
@model.should be_valid
|
|
402
|
+
@model.run_after_validate.should be_true
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
end
|
|
177
406
|
end
|