openlogic-couchrest_model 1.0.0
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/.gitignore +11 -0
- data/.rspec +4 -0
- data/Gemfile +4 -0
- data/LICENSE +176 -0
- data/README.md +137 -0
- data/Rakefile +38 -0
- data/THANKS.md +21 -0
- data/VERSION +1 -0
- data/benchmarks/dirty.rb +118 -0
- data/couchrest_model.gemspec +36 -0
- data/history.md +309 -0
- data/init.rb +1 -0
- data/lib/couchrest/model.rb +10 -0
- data/lib/couchrest/model/associations.rb +231 -0
- data/lib/couchrest/model/base.rb +129 -0
- data/lib/couchrest/model/callbacks.rb +28 -0
- data/lib/couchrest/model/casted_array.rb +83 -0
- data/lib/couchrest/model/casted_by.rb +33 -0
- data/lib/couchrest/model/casted_hash.rb +84 -0
- data/lib/couchrest/model/class_proxy.rb +135 -0
- data/lib/couchrest/model/collection.rb +273 -0
- data/lib/couchrest/model/configuration.rb +67 -0
- data/lib/couchrest/model/connection.rb +70 -0
- data/lib/couchrest/model/core_extensions/hash.rb +9 -0
- data/lib/couchrest/model/core_extensions/time_parsing.rb +66 -0
- data/lib/couchrest/model/design_doc.rb +128 -0
- data/lib/couchrest/model/designs.rb +91 -0
- data/lib/couchrest/model/designs/view.rb +513 -0
- data/lib/couchrest/model/dirty.rb +39 -0
- data/lib/couchrest/model/document_queries.rb +99 -0
- data/lib/couchrest/model/embeddable.rb +78 -0
- data/lib/couchrest/model/errors.rb +25 -0
- data/lib/couchrest/model/extended_attachments.rb +83 -0
- data/lib/couchrest/model/persistence.rb +178 -0
- data/lib/couchrest/model/properties.rb +228 -0
- data/lib/couchrest/model/property.rb +114 -0
- data/lib/couchrest/model/property_protection.rb +71 -0
- data/lib/couchrest/model/proxyable.rb +183 -0
- data/lib/couchrest/model/support/couchrest_database.rb +13 -0
- data/lib/couchrest/model/support/couchrest_design.rb +33 -0
- data/lib/couchrest/model/typecast.rb +154 -0
- data/lib/couchrest/model/validations.rb +80 -0
- data/lib/couchrest/model/validations/casted_model.rb +16 -0
- data/lib/couchrest/model/validations/locale/en.yml +5 -0
- data/lib/couchrest/model/validations/uniqueness.rb +69 -0
- data/lib/couchrest/model/views.rb +151 -0
- data/lib/couchrest/railtie.rb +24 -0
- data/lib/couchrest_model.rb +66 -0
- data/lib/rails/generators/couchrest_model.rb +16 -0
- data/lib/rails/generators/couchrest_model/config/config_generator.rb +18 -0
- data/lib/rails/generators/couchrest_model/config/templates/couchdb.yml +21 -0
- data/lib/rails/generators/couchrest_model/model/model_generator.rb +27 -0
- data/lib/rails/generators/couchrest_model/model/templates/model.rb +2 -0
- data/spec/.gitignore +1 -0
- data/spec/fixtures/attachments/README +3 -0
- data/spec/fixtures/attachments/couchdb.png +0 -0
- data/spec/fixtures/attachments/test.html +11 -0
- data/spec/fixtures/config/couchdb.yml +10 -0
- data/spec/fixtures/models/article.rb +36 -0
- data/spec/fixtures/models/base.rb +164 -0
- data/spec/fixtures/models/card.rb +19 -0
- data/spec/fixtures/models/cat.rb +23 -0
- data/spec/fixtures/models/client.rb +6 -0
- data/spec/fixtures/models/course.rb +27 -0
- data/spec/fixtures/models/event.rb +8 -0
- data/spec/fixtures/models/invoice.rb +14 -0
- data/spec/fixtures/models/key_chain.rb +5 -0
- data/spec/fixtures/models/membership.rb +4 -0
- data/spec/fixtures/models/person.rb +11 -0
- data/spec/fixtures/models/project.rb +6 -0
- data/spec/fixtures/models/question.rb +7 -0
- data/spec/fixtures/models/sale_entry.rb +9 -0
- data/spec/fixtures/models/sale_invoice.rb +14 -0
- data/spec/fixtures/models/service.rb +10 -0
- data/spec/fixtures/models/user.rb +22 -0
- data/spec/fixtures/views/lib.js +3 -0
- data/spec/fixtures/views/test_view/lib.js +3 -0
- data/spec/fixtures/views/test_view/only-map.js +4 -0
- data/spec/fixtures/views/test_view/test-map.js +3 -0
- data/spec/fixtures/views/test_view/test-reduce.js +3 -0
- data/spec/functional/validations_spec.rb +8 -0
- data/spec/spec_helper.rb +60 -0
- data/spec/unit/active_model_lint_spec.rb +30 -0
- data/spec/unit/assocations_spec.rb +242 -0
- data/spec/unit/attachment_spec.rb +176 -0
- data/spec/unit/base_spec.rb +537 -0
- data/spec/unit/casted_spec.rb +72 -0
- data/spec/unit/class_proxy_spec.rb +167 -0
- data/spec/unit/collection_spec.rb +86 -0
- data/spec/unit/configuration_spec.rb +77 -0
- data/spec/unit/connection_spec.rb +148 -0
- data/spec/unit/core_extensions/time_parsing.rb +77 -0
- data/spec/unit/design_doc_spec.rb +241 -0
- data/spec/unit/designs/view_spec.rb +831 -0
- data/spec/unit/designs_spec.rb +134 -0
- data/spec/unit/dirty_spec.rb +436 -0
- data/spec/unit/embeddable_spec.rb +498 -0
- data/spec/unit/inherited_spec.rb +33 -0
- data/spec/unit/persistence_spec.rb +481 -0
- data/spec/unit/property_protection_spec.rb +192 -0
- data/spec/unit/property_spec.rb +481 -0
- data/spec/unit/proxyable_spec.rb +376 -0
- data/spec/unit/subclass_spec.rb +85 -0
- data/spec/unit/typecast_spec.rb +521 -0
- data/spec/unit/validations_spec.rb +140 -0
- data/spec/unit/view_spec.rb +367 -0
- metadata +301 -0
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require "spec_helper"
|
|
3
|
+
|
|
4
|
+
class WithCastedModelMixin
|
|
5
|
+
include CouchRest::Model::Embeddable
|
|
6
|
+
property :name
|
|
7
|
+
property :no_value
|
|
8
|
+
property :details, Object, :default => {}
|
|
9
|
+
property :casted_attribute, WithCastedModelMixin
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class OldFashionedMixin < Hash
|
|
13
|
+
include CouchRest::Model::CastedModel
|
|
14
|
+
property :name
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class DummyModel < CouchRest::Model::Base
|
|
18
|
+
use_database TEST_SERVER.default_database
|
|
19
|
+
raise "Default DB not set" if TEST_SERVER.default_database.nil?
|
|
20
|
+
property :casted_attribute, WithCastedModelMixin
|
|
21
|
+
property :keywords, [String]
|
|
22
|
+
property :old_casted_attribute, OldFashionedMixin
|
|
23
|
+
property :sub_models do |child|
|
|
24
|
+
child.property :title
|
|
25
|
+
end
|
|
26
|
+
property :param_free_sub_models do
|
|
27
|
+
property :title
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class WithCastedCallBackModel
|
|
32
|
+
include CouchRest::Model::Embeddable
|
|
33
|
+
property :name
|
|
34
|
+
property :run_before_validation
|
|
35
|
+
property :run_after_validation
|
|
36
|
+
|
|
37
|
+
validates_presence_of :run_before_validation
|
|
38
|
+
|
|
39
|
+
before_validation do |object|
|
|
40
|
+
object.run_before_validation = true
|
|
41
|
+
end
|
|
42
|
+
after_validation do |object|
|
|
43
|
+
object.run_after_validation = true
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class CastedCallbackDoc < CouchRest::Model::Base
|
|
48
|
+
use_database TEST_SERVER.default_database
|
|
49
|
+
raise "Default DB not set" if TEST_SERVER.default_database.nil?
|
|
50
|
+
property :callback_model, WithCastedCallBackModel
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe CouchRest::Model::Embeddable do
|
|
54
|
+
|
|
55
|
+
describe "isolated" do
|
|
56
|
+
before(:each) do
|
|
57
|
+
@obj = WithCastedModelMixin.new
|
|
58
|
+
end
|
|
59
|
+
it "should automatically include the property mixin and define getters and setters" do
|
|
60
|
+
@obj.name = 'Matt'
|
|
61
|
+
@obj.name.should == 'Matt'
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "should allow override of default" do
|
|
65
|
+
@obj = WithCastedModelMixin.new(:name => 'Eric', :details => {'color' => 'orange'})
|
|
66
|
+
@obj.name.should == 'Eric'
|
|
67
|
+
@obj.details['color'].should == 'orange'
|
|
68
|
+
end
|
|
69
|
+
it "should always return base_doc? as false" do
|
|
70
|
+
@obj.base_doc?.should be_false
|
|
71
|
+
end
|
|
72
|
+
it "should call after_initialize callback if available" do
|
|
73
|
+
klass = Class.new do
|
|
74
|
+
include CouchRest::Model::CastedModel
|
|
75
|
+
after_initialize :set_name
|
|
76
|
+
property :name
|
|
77
|
+
def set_name; self.name = "foobar"; end
|
|
78
|
+
end
|
|
79
|
+
@obj = klass.new
|
|
80
|
+
@obj.name.should eql("foobar")
|
|
81
|
+
end
|
|
82
|
+
it "should allow override of initialize with super" do
|
|
83
|
+
klass = Class.new do
|
|
84
|
+
include CouchRest::Model::Embeddable
|
|
85
|
+
after_initialize :set_name
|
|
86
|
+
property :name
|
|
87
|
+
def set_name; self.name = "foobar"; end
|
|
88
|
+
def initialize(attrs = {}); super(); end
|
|
89
|
+
end
|
|
90
|
+
@obj = klass.new
|
|
91
|
+
@obj.name.should eql("foobar")
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
describe "casted as an attribute, but without a value" do
|
|
96
|
+
before(:each) do
|
|
97
|
+
@obj = DummyModel.new
|
|
98
|
+
@casted_obj = @obj.casted_attribute
|
|
99
|
+
end
|
|
100
|
+
it "should be nil" do
|
|
101
|
+
@casted_obj.should == nil
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe "anonymous sub casted models" do
|
|
106
|
+
before :each do
|
|
107
|
+
@obj = DummyModel.new
|
|
108
|
+
end
|
|
109
|
+
it "should be empty initially" do
|
|
110
|
+
@obj.sub_models.should_not be_nil
|
|
111
|
+
@obj.sub_models.should be_empty
|
|
112
|
+
end
|
|
113
|
+
it "should be updatable using a hash" do
|
|
114
|
+
@obj.sub_models << {:title => 'test'}
|
|
115
|
+
@obj.sub_models.first.title.should eql('test')
|
|
116
|
+
end
|
|
117
|
+
it "should be empty intitally (without params)" do
|
|
118
|
+
@obj.param_free_sub_models.should_not be_nil
|
|
119
|
+
@obj.param_free_sub_models.should be_empty
|
|
120
|
+
end
|
|
121
|
+
it "should be updatable using a hash (without params)" do
|
|
122
|
+
@obj.param_free_sub_models << {:title => 'test'}
|
|
123
|
+
@obj.param_free_sub_models.first.title.should eql('test')
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
describe "casted as attribute" do
|
|
128
|
+
before(:each) do
|
|
129
|
+
casted = {:name => 'not whatever'}
|
|
130
|
+
@obj = DummyModel.new(:casted_attribute => {:name => 'whatever', :casted_attribute => casted})
|
|
131
|
+
@casted_obj = @obj.casted_attribute
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "should be available from its parent" do
|
|
135
|
+
@casted_obj.should be_an_instance_of(WithCastedModelMixin)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it "should have the getters defined" do
|
|
139
|
+
@casted_obj.name.should == 'whatever'
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "should know who casted it" do
|
|
143
|
+
@casted_obj.casted_by.should == @obj
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "should know which property casted it" do
|
|
147
|
+
@casted_obj.casted_by_property.should == @obj.properties.detect{|p| p.to_s == 'casted_attribute'}
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it "should return nil for the 'no_value' attribute" do
|
|
151
|
+
@casted_obj.no_value.should be_nil
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it "should return nil for the unknown attribute" do
|
|
155
|
+
@casted_obj["unknown"].should be_nil
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it "should return {} for the hash attribute" do
|
|
159
|
+
@casted_obj.details.should == {}
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it "should cast its own attributes" do
|
|
163
|
+
@casted_obj.casted_attribute.should be_instance_of(WithCastedModelMixin)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "should raise an error if save or update_attributes called" do
|
|
167
|
+
expect { @casted_obj.casted_attribute.save }.to raise_error(NoMethodError)
|
|
168
|
+
expect { @casted_obj.casted_attribute.update_attributes(:name => "Fubar") }.to raise_error(NoMethodError)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Basic testing for an old fashioned casted hash
|
|
173
|
+
describe "old hash casted as attribute" do
|
|
174
|
+
before :each do
|
|
175
|
+
@obj = DummyModel.new(:old_casted_attribute => {:name => 'Testing'})
|
|
176
|
+
@casted_obj = @obj.old_casted_attribute
|
|
177
|
+
end
|
|
178
|
+
it "should be available from its parent" do
|
|
179
|
+
@casted_obj.should be_an_instance_of(OldFashionedMixin)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it "should have the getters defined" do
|
|
183
|
+
@casted_obj.name.should == 'Testing'
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
it "should know who casted it" do
|
|
187
|
+
@casted_obj.casted_by.should == @obj
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
it "should know which property casted it" do
|
|
191
|
+
@casted_obj.casted_by_property.should == @obj.properties.detect{|p| p.to_s == 'old_casted_attribute'}
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "should return nil for the unknown attribute" do
|
|
195
|
+
@casted_obj["unknown"].should be_nil
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
describe "casted as an array of a different type" do
|
|
200
|
+
before(:each) do
|
|
201
|
+
@obj = DummyModel.new(:keywords => ['couch', 'sofa', 'relax', 'canapé'])
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
it "should cast the array properly" do
|
|
205
|
+
@obj.keywords.should be_kind_of(Array)
|
|
206
|
+
@obj.keywords.first.should == 'couch'
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
describe "update attributes without saving" do
|
|
211
|
+
before(:each) do
|
|
212
|
+
@question = Question.new(:q => "What is your quest?", :a => "To seek the Holy Grail")
|
|
213
|
+
end
|
|
214
|
+
it "should work for attribute= methods" do
|
|
215
|
+
@question.q.should == "What is your quest?"
|
|
216
|
+
@question['a'].should == "To seek the Holy Grail"
|
|
217
|
+
@question.update_attributes_without_saving(:q => "What is your favorite color?", 'a' => "Blue")
|
|
218
|
+
@question['q'].should == "What is your favorite color?"
|
|
219
|
+
@question.a.should == "Blue"
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it "should also work for attributes= alias" do
|
|
223
|
+
@question.respond_to?(:attributes=).should be_true
|
|
224
|
+
@question.attributes = {:q => "What is your favorite color?", 'a' => "Blue"}
|
|
225
|
+
@question['q'].should == "What is your favorite color?"
|
|
226
|
+
@question.a.should == "Blue"
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it "should flip out if an attribute= method is missing" do
|
|
230
|
+
lambda {
|
|
231
|
+
@q.update_attributes_without_saving('foo' => "something", :a => "No green")
|
|
232
|
+
}.should raise_error(NoMethodError)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it "should not change any attributes if there is an error" do
|
|
236
|
+
lambda {
|
|
237
|
+
@q.update_attributes_without_saving('foo' => "something", :a => "No green")
|
|
238
|
+
}.should raise_error(NoMethodError)
|
|
239
|
+
@question.q.should == "What is your quest?"
|
|
240
|
+
@question.a.should == "To seek the Holy Grail"
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
describe "saved document with casted models" do
|
|
246
|
+
before(:each) do
|
|
247
|
+
reset_test_db!
|
|
248
|
+
@obj = DummyModel.new(:casted_attribute => {:name => 'whatever'})
|
|
249
|
+
@obj.save.should be_true
|
|
250
|
+
@obj = DummyModel.get(@obj.id)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
it "should be able to load with the casted models" do
|
|
254
|
+
casted_obj = @obj.casted_attribute
|
|
255
|
+
casted_obj.should_not be_nil
|
|
256
|
+
casted_obj.should be_an_instance_of(WithCastedModelMixin)
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
it "should have defined getters for the casted model" do
|
|
260
|
+
casted_obj = @obj.casted_attribute
|
|
261
|
+
casted_obj.name.should == "whatever"
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
it "should have defined setters for the casted model" do
|
|
265
|
+
casted_obj = @obj.casted_attribute
|
|
266
|
+
casted_obj.name = "test"
|
|
267
|
+
casted_obj.name.should == "test"
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
it "should retain an override of a casted model attribute's default" do
|
|
271
|
+
casted_obj = @obj.casted_attribute
|
|
272
|
+
casted_obj.details['color'] = 'orange'
|
|
273
|
+
@obj.save
|
|
274
|
+
casted_obj = DummyModel.get(@obj.id).casted_attribute
|
|
275
|
+
casted_obj.details['color'].should == 'orange'
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
describe "saving document with array of casted models and validation" do
|
|
281
|
+
before :each do
|
|
282
|
+
@cat = Cat.new :name => "felix"
|
|
283
|
+
@cat.save
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
it "should save" do
|
|
287
|
+
toy = CatToy.new :name => "Mouse"
|
|
288
|
+
@cat.toys.push(toy)
|
|
289
|
+
@cat.save.should be_true
|
|
290
|
+
@cat = Cat.get @cat.id
|
|
291
|
+
@cat.toys.class.should == CouchRest::Model::CastedArray
|
|
292
|
+
@cat.toys.first.class.should == CatToy
|
|
293
|
+
@cat.toys.first.should === toy
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
it "should fail because name is not present" do
|
|
297
|
+
toy = CatToy.new
|
|
298
|
+
@cat.toys.push(toy)
|
|
299
|
+
@cat.should_not be_valid
|
|
300
|
+
@cat.save.should be_false
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
it "should not fail if the casted model doesn't have validation" do
|
|
304
|
+
Cat.property :masters, [Person], :default => []
|
|
305
|
+
Cat.validates_presence_of :name
|
|
306
|
+
cat = Cat.new(:name => 'kitty')
|
|
307
|
+
cat.should be_valid
|
|
308
|
+
cat.masters.push Person.new
|
|
309
|
+
cat.should be_valid
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
describe "calling valid?" do
|
|
314
|
+
before :each do
|
|
315
|
+
@cat = Cat.new
|
|
316
|
+
@toy1 = CatToy.new
|
|
317
|
+
@toy2 = CatToy.new
|
|
318
|
+
@toy3 = CatToy.new
|
|
319
|
+
@cat.favorite_toy = @toy1
|
|
320
|
+
@cat.toys << @toy2
|
|
321
|
+
@cat.toys << @toy3
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
describe "on the top document" do
|
|
325
|
+
it "should put errors on all invalid casted models" do
|
|
326
|
+
@cat.should_not be_valid
|
|
327
|
+
@cat.errors.should_not be_empty
|
|
328
|
+
@toy1.errors.should_not be_empty
|
|
329
|
+
@toy2.errors.should_not be_empty
|
|
330
|
+
@toy3.errors.should_not be_empty
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
it "should not put errors on valid casted models" do
|
|
334
|
+
@toy1.name = "Feather"
|
|
335
|
+
@toy2.name = "Twine"
|
|
336
|
+
@cat.should_not be_valid
|
|
337
|
+
@cat.errors.should_not be_empty
|
|
338
|
+
@toy1.errors.should be_empty
|
|
339
|
+
@toy2.errors.should be_empty
|
|
340
|
+
@toy3.errors.should_not be_empty
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
it "should not use dperecated ActiveModel options" do
|
|
344
|
+
ActiveSupport::Deprecation.should_not_receive(:warn)
|
|
345
|
+
@cat.should_not be_valid
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
describe "on a casted model property" do
|
|
350
|
+
it "should only validate itself" do
|
|
351
|
+
@toy1.should_not be_valid
|
|
352
|
+
@toy1.errors.should_not be_empty
|
|
353
|
+
@cat.errors.should be_empty
|
|
354
|
+
@toy2.errors.should be_empty
|
|
355
|
+
@toy3.errors.should be_empty
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
describe "on a casted model inside a casted collection" do
|
|
360
|
+
it "should only validate itself" do
|
|
361
|
+
@toy2.should_not be_valid
|
|
362
|
+
@toy2.errors.should_not be_empty
|
|
363
|
+
@cat.errors.should be_empty
|
|
364
|
+
@toy1.errors.should be_empty
|
|
365
|
+
@toy3.errors.should be_empty
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
describe "calling new? on a casted model" do
|
|
371
|
+
before :each do
|
|
372
|
+
reset_test_db!
|
|
373
|
+
@cat = Cat.new(:name => 'Sockington')
|
|
374
|
+
@favorite_toy = CatToy.new(:name => 'Catnip Ball')
|
|
375
|
+
@cat.favorite_toy = @favorite_toy
|
|
376
|
+
@cat.toys << CatToy.new(:name => 'Fuzzy Stick')
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
it "should be true on new" do
|
|
380
|
+
CatToy.new.should be_new
|
|
381
|
+
CatToy.new.new_record?.should be_true
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
it "should be true after assignment" do
|
|
385
|
+
@cat.should be_new
|
|
386
|
+
@cat.favorite_toy.should be_new
|
|
387
|
+
@cat.toys.first.should be_new
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
it "should not be true after create or save" do
|
|
391
|
+
@cat.create
|
|
392
|
+
@cat.save
|
|
393
|
+
@cat.favorite_toy.should_not be_new
|
|
394
|
+
@cat.toys.first.casted_by.should eql(@cat)
|
|
395
|
+
@cat.toys.first.should_not be_new
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
it "should not be true after get from the database" do
|
|
399
|
+
@cat.save
|
|
400
|
+
@cat = Cat.get(@cat.id)
|
|
401
|
+
@cat.favorite_toy.should_not be_new
|
|
402
|
+
@cat.toys.first.should_not be_new
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
it "should still be true after a failed create or save" do
|
|
406
|
+
@cat.name = nil
|
|
407
|
+
@cat.create.should be_false
|
|
408
|
+
@cat.save.should be_false
|
|
409
|
+
@cat.favorite_toy.should be_new
|
|
410
|
+
@cat.toys.first.should be_new
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
describe "calling base_doc from a nested casted model" do
|
|
415
|
+
before :each do
|
|
416
|
+
@course = Course.new(:title => 'Science 101')
|
|
417
|
+
@professor = Person.new(:name => ['Professor', 'Plum'])
|
|
418
|
+
@cat = Cat.new(:name => 'Scratchy')
|
|
419
|
+
@toy1 = CatToy.new
|
|
420
|
+
@toy2 = CatToy.new
|
|
421
|
+
@course.professor = @professor
|
|
422
|
+
@professor.pet = @cat
|
|
423
|
+
@cat.favorite_toy = @toy1
|
|
424
|
+
@cat.toys << @toy2
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
it 'should let you copy over casted arrays' do
|
|
428
|
+
question = Question.new
|
|
429
|
+
@course.questions << question
|
|
430
|
+
new_course = Course.new
|
|
431
|
+
new_course.questions = @course.questions
|
|
432
|
+
new_course.questions.should include(question)
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
it "should reference the top document for" do
|
|
436
|
+
@course.base_doc.should === @course
|
|
437
|
+
@professor.casted_by.should === @course
|
|
438
|
+
@professor.base_doc.should === @course
|
|
439
|
+
@cat.base_doc.should === @course
|
|
440
|
+
@toy1.base_doc.should === @course
|
|
441
|
+
@toy2.base_doc.should === @course
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
it "should call setter on top document" do
|
|
445
|
+
@toy1.base_doc.should_not be_nil
|
|
446
|
+
@toy1.base_doc.title = 'Tom Foolery'
|
|
447
|
+
@course.title.should == 'Tom Foolery'
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
it "should return nil if not yet casted" do
|
|
451
|
+
person = Person.new
|
|
452
|
+
person.base_doc.should == nil
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
describe "calling base_doc.save from a nested casted model" do
|
|
457
|
+
before :each do
|
|
458
|
+
reset_test_db!
|
|
459
|
+
@cat = Cat.new(:name => 'Snowball')
|
|
460
|
+
@toy = CatToy.new
|
|
461
|
+
@cat.favorite_toy = @toy
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
it "should not save parent document when casted model is invalid" do
|
|
465
|
+
@toy.should_not be_valid
|
|
466
|
+
@toy.base_doc.save.should be_false
|
|
467
|
+
lambda{@toy.base_doc.save!}.should raise_error
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
it "should save parent document when nested casted model is valid" do
|
|
471
|
+
@toy.name = "Mr Squeaks"
|
|
472
|
+
@toy.should be_valid
|
|
473
|
+
@toy.base_doc.save.should be_true
|
|
474
|
+
lambda{@toy.base_doc.save!}.should_not raise_error
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
describe "callbacks" do
|
|
479
|
+
before(:each) do
|
|
480
|
+
@doc = CastedCallbackDoc.new
|
|
481
|
+
@model = WithCastedCallBackModel.new
|
|
482
|
+
@doc.callback_model = @model
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
describe "validate" do
|
|
486
|
+
it "should run before_validation before validating" do
|
|
487
|
+
@model.run_before_validation.should be_nil
|
|
488
|
+
@model.should be_valid
|
|
489
|
+
@model.run_before_validation.should be_true
|
|
490
|
+
end
|
|
491
|
+
it "should run after_validation after validating" do
|
|
492
|
+
@model.run_after_validation.should be_nil
|
|
493
|
+
@model.should be_valid
|
|
494
|
+
@model.run_after_validation.should be_true
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
end
|