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
@@ -38,3 +38,22 @@ module CouchRest
|
|
38
38
|
|
39
39
|
end
|
40
40
|
end
|
41
|
+
|
42
|
+
class CastedArray < Array
|
43
|
+
attr_accessor :casted_by
|
44
|
+
|
45
|
+
def << obj
|
46
|
+
obj.casted_by = self.casted_by if obj.respond_to?(:casted_by)
|
47
|
+
super(obj)
|
48
|
+
end
|
49
|
+
|
50
|
+
def push(obj)
|
51
|
+
obj.casted_by = self.casted_by if obj.respond_to?(:casted_by)
|
52
|
+
super(obj)
|
53
|
+
end
|
54
|
+
|
55
|
+
def []= index, obj
|
56
|
+
obj.casted_by = self.casted_by if obj.respond_to?(:casted_by)
|
57
|
+
super(index, obj)
|
58
|
+
end
|
59
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
# Copyright (c)
|
2
|
-
#
|
1
|
+
# Copyright (c) 2006-2009 David Heinemeier Hansson
|
2
|
+
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
# a copy of this software and associated documentation files (the
|
5
5
|
# "Software"), to deal in the Software without restriction, including
|
@@ -7,10 +7,10 @@
|
|
7
7
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
8
|
# permit persons to whom the Software is furnished to do so, subject to
|
9
9
|
# the following conditions:
|
10
|
-
#
|
10
|
+
#
|
11
11
|
# The above copyright notice and this permission notice shall be
|
12
12
|
# included in all copies or substantial portions of the Software.
|
13
|
-
#
|
13
|
+
#
|
14
14
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
15
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
16
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
@@ -18,79 +18,59 @@
|
|
18
18
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
20
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
-
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
21
|
+
#
|
22
|
+
# Extracted From
|
23
|
+
# http://github.com/rails/rails/commit/971e2438d98326c994ec6d3ef8e37b7e868ed6e2
|
24
|
+
|
25
|
+
# Extends the class object with class and instance accessors for class attributes,
|
26
|
+
# just like the native attr* accessors for instance attributes.
|
27
|
+
#
|
28
|
+
# class Person
|
29
|
+
# cattr_accessor :hair_colors
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# Person.hair_colors = [:brown, :black, :blonde, :red]
|
28
33
|
class Class
|
29
|
-
# Defines class-level and instance-level attribute reader.
|
30
|
-
#
|
31
|
-
# @param *syms<Array> Array of attributes to define reader for.
|
32
|
-
# @return <Array[#to_s]> List of attributes that were made into cattr_readers
|
33
|
-
#
|
34
|
-
# @api public
|
35
|
-
#
|
36
|
-
# @todo Is this inconsistent in that it does not allow you to prevent
|
37
|
-
# an instance_reader via :instance_reader => false
|
38
34
|
def cattr_reader(*syms)
|
39
35
|
syms.flatten.each do |sym|
|
40
36
|
next if sym.is_a?(Hash)
|
41
|
-
class_eval(<<-
|
42
|
-
unless defined? @@#{sym}
|
43
|
-
@@#{sym} = nil
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.#{sym}
|
47
|
-
@@#{sym}
|
48
|
-
end
|
49
|
-
|
50
|
-
def #{sym}
|
51
|
-
@@#{sym}
|
52
|
-
end
|
53
|
-
|
37
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
38
|
+
unless defined? @@#{sym} # unless defined? @@hair_colors
|
39
|
+
@@#{sym} = nil # @@hair_colors = nil
|
40
|
+
end # end
|
41
|
+
#
|
42
|
+
def self.#{sym} # def self.hair_colors
|
43
|
+
@@#{sym} # @@hair_colors
|
44
|
+
end # end
|
45
|
+
#
|
46
|
+
def #{sym} # def hair_colors
|
47
|
+
@@#{sym} # @@hair_colors
|
48
|
+
end # end
|
49
|
+
EOS
|
54
50
|
end
|
55
51
|
end unless Class.respond_to?(:cattr_reader)
|
56
52
|
|
57
|
-
# Defines class-level (and optionally instance-level) attribute writer.
|
58
|
-
#
|
59
|
-
# @param <Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to define writer for.
|
60
|
-
# @option syms :instance_writer<Boolean> if true, instance-level attribute writer is defined.
|
61
|
-
# @return <Array[#to_s]> List of attributes that were made into cattr_writers
|
62
|
-
#
|
63
|
-
# @api public
|
64
53
|
def cattr_writer(*syms)
|
65
|
-
options = syms.
|
54
|
+
options = syms.extract_options!
|
66
55
|
syms.flatten.each do |sym|
|
67
|
-
class_eval(<<-
|
68
|
-
unless defined? @@#{sym}
|
69
|
-
@@#{sym} = nil
|
70
|
-
end
|
71
|
-
|
72
|
-
def self.#{sym}=(obj)
|
73
|
-
@@#{sym} = obj
|
74
|
-
end
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
RUBY
|
83
|
-
end
|
56
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
57
|
+
unless defined? @@#{sym} # unless defined? @@hair_colors
|
58
|
+
@@#{sym} = nil # @@hair_colors = nil
|
59
|
+
end # end
|
60
|
+
#
|
61
|
+
def self.#{sym}=(obj) # def self.hair_colors=(obj)
|
62
|
+
@@#{sym} = obj # @@hair_colors = obj
|
63
|
+
end # end
|
64
|
+
#
|
65
|
+
#{" #
|
66
|
+
def #{sym}=(obj) # def hair_colors=(obj)
|
67
|
+
@@#{sym} = obj # @@hair_colors = obj
|
68
|
+
end # end
|
69
|
+
" unless options[:instance_writer] == false } # # instance writer above is generated unless options[:instance_writer] == false
|
70
|
+
EOS
|
84
71
|
end
|
85
72
|
end unless Class.respond_to?(:cattr_writer)
|
86
73
|
|
87
|
-
# Defines class-level (and optionally instance-level) attribute accessor.
|
88
|
-
#
|
89
|
-
# @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to define accessor for.
|
90
|
-
# @option syms :instance_writer<Boolean> if true, instance-level attribute writer is defined.
|
91
|
-
# @return <Array[#to_s]> List of attributes that were made into accessors
|
92
|
-
#
|
93
|
-
# @api public
|
94
74
|
def cattr_accessor(*syms)
|
95
75
|
cattr_reader(*syms)
|
96
76
|
cattr_writer(*syms)
|
@@ -156,6 +136,8 @@ class Class
|
|
156
136
|
def #{ivar}=(obj) self.class.#{ivar} = obj end
|
157
137
|
RUBY
|
158
138
|
end
|
139
|
+
|
140
|
+
self.send("#{ivar}=", yield) if block_given?
|
159
141
|
end
|
160
142
|
end unless Class.respond_to?(:extlib_inheritable_writer)
|
161
143
|
|
@@ -168,9 +150,41 @@ class Class
|
|
168
150
|
# @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
|
169
151
|
#
|
170
152
|
# @api public
|
171
|
-
def extlib_inheritable_accessor(*syms)
|
153
|
+
def extlib_inheritable_accessor(*syms, &block)
|
172
154
|
extlib_inheritable_reader(*syms)
|
173
|
-
extlib_inheritable_writer(*syms)
|
155
|
+
extlib_inheritable_writer(*syms, &block)
|
174
156
|
end unless Class.respond_to?(:extlib_inheritable_accessor)
|
175
157
|
end
|
176
158
|
|
159
|
+
class Array
|
160
|
+
# Extracts options from a set of arguments. Removes and returns the last
|
161
|
+
# element in the array if it's a hash, otherwise returns a blank hash.
|
162
|
+
#
|
163
|
+
# def options(*args)
|
164
|
+
# args.extract_options!
|
165
|
+
# end
|
166
|
+
#
|
167
|
+
# options(1, 2) # => {}
|
168
|
+
# options(1, 2, :a => :b) # => {:a=>:b}
|
169
|
+
def extract_options!
|
170
|
+
last.is_a?(::Hash) ? pop : {}
|
171
|
+
end unless Array.new.respond_to?(:extract_options!)
|
172
|
+
|
173
|
+
# Wraps the object in an Array unless it's an Array. Converts the
|
174
|
+
# object to an Array using #to_ary if it implements that.
|
175
|
+
def self.wrap(object)
|
176
|
+
case object
|
177
|
+
when nil
|
178
|
+
[]
|
179
|
+
when self
|
180
|
+
object
|
181
|
+
else
|
182
|
+
if object.respond_to?(:to_ary)
|
183
|
+
object.to_ary
|
184
|
+
else
|
185
|
+
[object]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end unless Array.respond_to?(:wrap)
|
189
|
+
end
|
190
|
+
|
@@ -1,8 +1,4 @@
|
|
1
1
|
# This file contains various hacks for Rails compatibility.
|
2
|
-
# To use, just require in environment.rb, like so:
|
3
|
-
#
|
4
|
-
# require 'couchrest/support/rails'
|
5
|
-
|
6
2
|
class Hash
|
7
3
|
# Hack so that CouchRest::Document, which descends from Hash,
|
8
4
|
# doesn't appear to Rails routing as a Hash of options
|
@@ -12,8 +8,10 @@ class Hash
|
|
12
8
|
end
|
13
9
|
end
|
14
10
|
|
15
|
-
|
16
11
|
CouchRest::Document.class_eval do
|
12
|
+
# Need this when passing doc to a resourceful route
|
13
|
+
alias_method :to_param, :id
|
14
|
+
|
17
15
|
# Hack so that CouchRest::Document, which descends from Hash,
|
18
16
|
# doesn't appear to Rails routing as a Hash of options
|
19
17
|
def is_a?(o)
|
@@ -23,6 +21,15 @@ CouchRest::Document.class_eval do
|
|
23
21
|
alias_method :kind_of?, :is_a?
|
24
22
|
end
|
25
23
|
|
24
|
+
CouchRest::CastedModel.class_eval do
|
25
|
+
# The to_param method is needed for rails to generate resourceful routes.
|
26
|
+
# In your controller, remember that it's actually the id of the document.
|
27
|
+
def id
|
28
|
+
return nil if base_doc.nil?
|
29
|
+
base_doc.id
|
30
|
+
end
|
31
|
+
alias_method :to_param, :id
|
32
|
+
end
|
26
33
|
|
27
34
|
require Pathname.new(File.dirname(__FILE__)).join('..', 'validation', 'validation_errors')
|
28
35
|
|
@@ -253,7 +253,7 @@ describe CouchRest::Database do
|
|
253
253
|
describe "PUT attachment from file" do
|
254
254
|
before(:each) do
|
255
255
|
filename = FIXTURE_PATH + '/attachments/couchdb.png'
|
256
|
-
@file = File.open(filename)
|
256
|
+
@file = File.open(filename, "rb")
|
257
257
|
end
|
258
258
|
after(:each) do
|
259
259
|
@file.close
|
@@ -552,7 +552,7 @@ describe CouchRest::Database do
|
|
552
552
|
newdoc['artist'].should == 'Zappa'
|
553
553
|
end
|
554
554
|
it "should fail without an _id" do
|
555
|
-
lambda{@db.
|
555
|
+
lambda{@db.copy_doc({"not"=>"a real doc"})}.should raise_error(ArgumentError)
|
556
556
|
end
|
557
557
|
end
|
558
558
|
describe "to an existing location" do
|
@@ -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
|
@@ -171,7 +230,177 @@ describe CouchRest::CastedModel do
|
|
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
|