seomoz-ripple 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/Gemfile +20 -0
  2. data/Guardfile +15 -0
  3. data/Rakefile +88 -0
  4. data/lib/rails/generators/ripple/configuration/configuration_generator.rb +13 -0
  5. data/lib/rails/generators/ripple/configuration/templates/ripple.yml +24 -0
  6. data/lib/rails/generators/ripple/js/js_generator.rb +13 -0
  7. data/lib/rails/generators/ripple/js/templates/js/contrib.js +63 -0
  8. data/lib/rails/generators/ripple/js/templates/js/iso8601.js +76 -0
  9. data/lib/rails/generators/ripple/js/templates/js/ripple.js +132 -0
  10. data/lib/rails/generators/ripple/model/model_generator.rb +20 -0
  11. data/lib/rails/generators/ripple/model/templates/model.rb +10 -0
  12. data/lib/rails/generators/ripple/observer/observer_generator.rb +16 -0
  13. data/lib/rails/generators/ripple/observer/templates/observer.rb +4 -0
  14. data/lib/rails/generators/ripple/test/templates/test_server.rb +46 -0
  15. data/lib/rails/generators/ripple/test/test_generator.rb +39 -0
  16. data/lib/rails/generators/ripple_generator.rb +78 -0
  17. data/lib/ripple.rb +79 -0
  18. data/lib/ripple/associations.rb +356 -0
  19. data/lib/ripple/associations/embedded.rb +35 -0
  20. data/lib/ripple/associations/instantiators.rb +26 -0
  21. data/lib/ripple/associations/linked.rb +65 -0
  22. data/lib/ripple/associations/many.rb +38 -0
  23. data/lib/ripple/associations/many_embedded_proxy.rb +38 -0
  24. data/lib/ripple/associations/many_linked_proxy.rb +66 -0
  25. data/lib/ripple/associations/many_reference_proxy.rb +93 -0
  26. data/lib/ripple/associations/many_stored_key_proxy.rb +76 -0
  27. data/lib/ripple/associations/one.rb +20 -0
  28. data/lib/ripple/associations/one_embedded_proxy.rb +35 -0
  29. data/lib/ripple/associations/one_key_proxy.rb +58 -0
  30. data/lib/ripple/associations/one_linked_proxy.rb +22 -0
  31. data/lib/ripple/associations/one_stored_key_proxy.rb +43 -0
  32. data/lib/ripple/associations/proxy.rb +118 -0
  33. data/lib/ripple/attribute_methods.rb +118 -0
  34. data/lib/ripple/attribute_methods/dirty.rb +50 -0
  35. data/lib/ripple/attribute_methods/query.rb +34 -0
  36. data/lib/ripple/attribute_methods/read.rb +26 -0
  37. data/lib/ripple/attribute_methods/write.rb +25 -0
  38. data/lib/ripple/callbacks.rb +74 -0
  39. data/lib/ripple/conflict/basic_resolver.rb +82 -0
  40. data/lib/ripple/conflict/document_hooks.rb +20 -0
  41. data/lib/ripple/conflict/resolver.rb +71 -0
  42. data/lib/ripple/conflict/test_helper.rb +33 -0
  43. data/lib/ripple/conversion.rb +28 -0
  44. data/lib/ripple/core_ext.rb +2 -0
  45. data/lib/ripple/core_ext/casting.rb +148 -0
  46. data/lib/ripple/core_ext/object.rb +8 -0
  47. data/lib/ripple/document.rb +104 -0
  48. data/lib/ripple/document/bucket_access.rb +25 -0
  49. data/lib/ripple/document/finders.rb +131 -0
  50. data/lib/ripple/document/key.rb +43 -0
  51. data/lib/ripple/document/link.rb +30 -0
  52. data/lib/ripple/document/persistence.rb +115 -0
  53. data/lib/ripple/embedded_document.rb +64 -0
  54. data/lib/ripple/embedded_document/around_callbacks.rb +18 -0
  55. data/lib/ripple/embedded_document/finders.rb +26 -0
  56. data/lib/ripple/embedded_document/persistence.rb +77 -0
  57. data/lib/ripple/i18n.rb +2 -0
  58. data/lib/ripple/inspection.rb +32 -0
  59. data/lib/ripple/locale/en.yml +21 -0
  60. data/lib/ripple/nested_attributes.rb +265 -0
  61. data/lib/ripple/observable.rb +28 -0
  62. data/lib/ripple/properties.rb +73 -0
  63. data/lib/ripple/property_type_mismatch.rb +12 -0
  64. data/lib/ripple/railtie.rb +13 -0
  65. data/lib/ripple/serialization.rb +84 -0
  66. data/lib/ripple/timestamps.rb +27 -0
  67. data/lib/ripple/translation.rb +14 -0
  68. data/lib/ripple/validations.rb +67 -0
  69. data/lib/ripple/validations/associated_validator.rb +43 -0
  70. data/ripple.gemspec +46 -0
  71. data/seomoz-ripple.gemspec +46 -0
  72. data/spec/fixtures/config.yml +8 -0
  73. data/spec/integration/ripple/associations_spec.rb +220 -0
  74. data/spec/integration/ripple/conflict_resolution_spec.rb +293 -0
  75. data/spec/integration/ripple/nested_attributes_spec.rb +264 -0
  76. data/spec/integration/ripple/persistence_spec.rb +57 -0
  77. data/spec/integration/ripple/search_associations_spec.rb +42 -0
  78. data/spec/ripple/associations/many_embedded_proxy_spec.rb +122 -0
  79. data/spec/ripple/associations/many_linked_proxy_spec.rb +191 -0
  80. data/spec/ripple/associations/many_reference_proxy_spec.rb +170 -0
  81. data/spec/ripple/associations/many_stored_key_proxy_spec.rb +158 -0
  82. data/spec/ripple/associations/one_embedded_proxy_spec.rb +125 -0
  83. data/spec/ripple/associations/one_key_proxy_spec.rb +82 -0
  84. data/spec/ripple/associations/one_linked_proxy_spec.rb +91 -0
  85. data/spec/ripple/associations/one_stored_key_proxy_spec.rb +72 -0
  86. data/spec/ripple/associations/proxy_spec.rb +84 -0
  87. data/spec/ripple/associations_spec.rb +129 -0
  88. data/spec/ripple/attribute_methods/dirty_spec.rb +80 -0
  89. data/spec/ripple/attribute_methods_spec.rb +230 -0
  90. data/spec/ripple/bucket_access_spec.rb +25 -0
  91. data/spec/ripple/callbacks_spec.rb +176 -0
  92. data/spec/ripple/conflict/resolver_spec.rb +42 -0
  93. data/spec/ripple/conversion_spec.rb +22 -0
  94. data/spec/ripple/core_ext_spec.rb +103 -0
  95. data/spec/ripple/document/link_spec.rb +67 -0
  96. data/spec/ripple/document_spec.rb +96 -0
  97. data/spec/ripple/embedded_document/finders_spec.rb +29 -0
  98. data/spec/ripple/embedded_document/persistence_spec.rb +80 -0
  99. data/spec/ripple/embedded_document_spec.rb +84 -0
  100. data/spec/ripple/finders_spec.rb +217 -0
  101. data/spec/ripple/inspection_spec.rb +51 -0
  102. data/spec/ripple/key_spec.rb +30 -0
  103. data/spec/ripple/observable_spec.rb +121 -0
  104. data/spec/ripple/persistence_spec.rb +326 -0
  105. data/spec/ripple/properties_spec.rb +262 -0
  106. data/spec/ripple/ripple_spec.rb +71 -0
  107. data/spec/ripple/serialization_spec.rb +51 -0
  108. data/spec/ripple/timestamps_spec.rb +76 -0
  109. data/spec/ripple/validations/associated_validator_spec.rb +77 -0
  110. data/spec/ripple/validations_spec.rb +104 -0
  111. data/spec/spec_helper.rb +26 -0
  112. data/spec/support/associations.rb +1 -0
  113. data/spec/support/associations/proxies.rb +17 -0
  114. data/spec/support/integration_setup.rb +11 -0
  115. data/spec/support/mocks.rb +4 -0
  116. data/spec/support/models.rb +4 -0
  117. data/spec/support/models/address.rb +12 -0
  118. data/spec/support/models/box.rb +13 -0
  119. data/spec/support/models/car.rb +16 -0
  120. data/spec/support/models/cardboard_box.rb +3 -0
  121. data/spec/support/models/clock.rb +12 -0
  122. data/spec/support/models/clock_observer.rb +3 -0
  123. data/spec/support/models/company.rb +23 -0
  124. data/spec/support/models/customer.rb +4 -0
  125. data/spec/support/models/driver.rb +6 -0
  126. data/spec/support/models/email.rb +4 -0
  127. data/spec/support/models/engine.rb +5 -0
  128. data/spec/support/models/family.rb +16 -0
  129. data/spec/support/models/favorite.rb +4 -0
  130. data/spec/support/models/invoice.rb +7 -0
  131. data/spec/support/models/late_invoice.rb +3 -0
  132. data/spec/support/models/ninja.rb +9 -0
  133. data/spec/support/models/note.rb +5 -0
  134. data/spec/support/models/page.rb +4 -0
  135. data/spec/support/models/paid_invoice.rb +4 -0
  136. data/spec/support/models/passenger.rb +6 -0
  137. data/spec/support/models/profile.rb +10 -0
  138. data/spec/support/models/seat.rb +5 -0
  139. data/spec/support/models/subscription.rb +27 -0
  140. data/spec/support/models/tasks.rb +14 -0
  141. data/spec/support/models/team.rb +11 -0
  142. data/spec/support/models/transactions.rb +17 -0
  143. data/spec/support/models/tree.rb +4 -0
  144. data/spec/support/models/user.rb +10 -0
  145. data/spec/support/models/wheel.rb +6 -0
  146. data/spec/support/models/widget.rb +22 -0
  147. data/spec/support/test_server.rb +18 -0
  148. data/spec/support/test_server.yml.example +2 -0
  149. metadata +362 -0
@@ -0,0 +1,264 @@
1
+ require 'spec_helper'
2
+ require 'active_support/core_ext/array'
3
+
4
+ describe Ripple::NestedAttributes do
5
+ require 'support/models/car'
6
+ require 'support/models/driver'
7
+ require 'support/models/passenger'
8
+ require 'support/models/engine'
9
+ require 'support/models/seat'
10
+ require 'support/models/wheel'
11
+ require 'support/test_server'
12
+
13
+ context "one :driver (link)" do
14
+ subject { Car.new }
15
+
16
+ it { should respond_to(:driver_attributes=) }
17
+
18
+ it "should not have a driver" do
19
+ subject.driver.should be_nil
20
+ end
21
+
22
+ describe "creation" do
23
+ subject { Car.new(:make => 'VW', :model => 'Rabbit', :driver_attributes => { :name => 'Speed Racer' }) }
24
+
25
+ it "should have a driver of class Driver" do
26
+ subject.driver.should be_a(Driver)
27
+ end
28
+
29
+ it "should have a driver with name 'Speed Racer'" do
30
+ subject.driver.name.should == 'Speed Racer'
31
+ end
32
+
33
+ it "should save the child when saving the parent" do
34
+ subject.driver.should_receive(:save)
35
+ subject.save
36
+ end
37
+ end
38
+
39
+ describe "update" do
40
+ let(:driver) { Driver.create(:name => 'Slow Racer') }
41
+
42
+ before do
43
+ subject.driver = driver
44
+ subject.save
45
+ end
46
+
47
+ it "should have a driver" do
48
+ subject.driver.should == driver
49
+ end
50
+
51
+ it "should update attributes" do
52
+ subject.driver_attributes = { :name => 'Speed Racer' }
53
+ subject.driver.name.should == 'Speed Racer'
54
+ end
55
+
56
+ it "should not save the child if attributes haven't been updated" do
57
+ subject.driver.should_not_receive(:save)
58
+ subject.save
59
+ end
60
+
61
+ it "should save the child when saving the parent" do
62
+ subject.driver_attributes = { :name => 'Speed Racer' }
63
+ subject.driver.should_receive(:save)
64
+ subject.save
65
+ end
66
+ end
67
+ end
68
+
69
+ context "many :passengers (link)" do
70
+ subject { Car.new }
71
+
72
+ it { should respond_to(:passengers_attributes=) }
73
+
74
+ it "should not have passengers" do
75
+ subject.passengers.should == []
76
+ end
77
+
78
+ describe "creation" do
79
+ subject { Car.new(:make => 'VW',
80
+ :model => 'Rabbit',
81
+ :passengers_attributes => [ { :name => 'Joe' },
82
+ { :name => 'Sue' },
83
+ { :name => 'Pat' } ] ) }
84
+
85
+ it "should have 3 passengers" do
86
+ subject.passengers.size.should == 3
87
+ end
88
+
89
+ it "should have 3 passengers with specified names" do
90
+ subject.passengers.first.name.should == 'Joe'
91
+ subject.passengers.second.name.should == 'Sue'
92
+ subject.passengers.third.name.should == 'Pat'
93
+ end
94
+
95
+ it "should save the children when saving the parent" do
96
+ subject.save
97
+ found_subject = Car.find(subject.key)
98
+ found_subject.passengers.map(&:name).should =~ %w[ Joe Sue Pat ]
99
+ end
100
+ end
101
+
102
+ describe "update" do
103
+ let(:passenger1) { Passenger.create(:name => 'One') }
104
+ let(:passenger2) { Passenger.create(:name => 'Two') }
105
+ let(:passenger3) { Passenger.create(:name => 'Three') }
106
+
107
+ before do
108
+ subject.passengers << passenger1
109
+ subject.passengers << passenger2
110
+ subject.passengers << passenger3
111
+ subject.save
112
+ end
113
+
114
+ it "should have 3 passengers" do
115
+ subject.passengers.size.should == 3
116
+ end
117
+
118
+ it "should update attributes" do
119
+ subject.passengers_attributes = [ { :key => passenger1.key, :name => 'UPDATED One' },
120
+ { :key => passenger2.key, :name => 'UPDATED Two' },
121
+ { :key => passenger3.key, :name => 'UPDATED Three' } ]
122
+ subject.passengers.first.name.should == 'UPDATED One'
123
+ subject.passengers.second.name.should == 'UPDATED Two'
124
+ subject.passengers.third.name.should == 'UPDATED Three'
125
+ end
126
+
127
+ it "should not save the child if attributes haven't been updated" do
128
+ subject.passengers.each do |passenger|
129
+ passenger.should_not_receive(:save)
130
+ end
131
+ subject.save
132
+ end
133
+
134
+ it "should save the child when saving the parent" do
135
+ subject.passengers_attributes = [ { :key => passenger1.key, :name => 'UPDATED One' },
136
+ { :key => passenger2.key, :name => 'UPDATED Two' },
137
+ { :key => passenger3.key, :name => 'UPDATED Three' } ]
138
+ subject.save
139
+
140
+ found_subject = Car.find(subject.key)
141
+ found_subject.passengers.map(&:name).should =~ [
142
+ 'UPDATED One',
143
+ 'UPDATED Two',
144
+ 'UPDATED Three'
145
+ ]
146
+ end
147
+ end
148
+ end
149
+
150
+ context "one :engine (embedded)" do
151
+ subject { Car.new }
152
+
153
+ it { should respond_to(:engine_attributes=) }
154
+
155
+ it "should not have an engine" do
156
+ subject.engine.should be_nil
157
+ end
158
+
159
+ describe "creation" do
160
+ subject { Car.new(:make => 'VW', :model => 'Rabbit', :engine_attributes => { :displacement => '2.5L' }) }
161
+
162
+ it "should have an engine of class Engine" do
163
+ subject.engine.should be_a(Engine)
164
+ end
165
+
166
+ it "should have a engine with displacement '2.5L'" do
167
+ subject.engine.displacement.should == '2.5L'
168
+ end
169
+
170
+ it "should save the child when saving the parent" do
171
+ subject.engine.should_not_receive(:save)
172
+ subject.save
173
+ end
174
+ end
175
+
176
+ describe "update" do
177
+ before do
178
+ subject.engine.build(:displacement => '3.6L')
179
+ subject.save
180
+ end
181
+
182
+ it "should have a specified engine" do
183
+ subject.engine.displacement.should == '3.6L'
184
+ end
185
+
186
+ it "should update attributes" do
187
+ subject.engine_attributes = { :displacement => 'UPDATED 3.6L' }
188
+ subject.engine.displacement.should == 'UPDATED 3.6L'
189
+ end
190
+
191
+ it "should not save the child if attributes haven't been updated" do
192
+ subject.engine.should_not_receive(:save)
193
+ subject.save
194
+ end
195
+
196
+ it "should not save the child when saving the parent" do
197
+ subject.engine_attributes = { :displacement => 'UPDATED 3.6L' }
198
+ subject.engine.should_not_receive(:save)
199
+ subject.save
200
+ end
201
+ end
202
+ end
203
+
204
+ context "many :seats (embedded)" do
205
+ subject { Car.new }
206
+
207
+ it { should respond_to(:seats_attributes=) }
208
+
209
+ it "should not have passengers" do
210
+ subject.seats.should == []
211
+ end
212
+
213
+ describe "creation" do
214
+ subject { Car.new(:make => 'VW',
215
+ :model => 'Rabbit',
216
+ :seats_attributes => [ { :color => 'red' },
217
+ { :color => 'blue' },
218
+ { :color => 'brown' } ] ) }
219
+
220
+ it "should have 3 seats" do
221
+ subject.seats.size.should == 3
222
+ end
223
+
224
+ it "should have 3 passengers with specified names" do
225
+ subject.seats.first.color.should == 'red'
226
+ subject.seats.second.color.should == 'blue'
227
+ subject.seats.third.color.should == 'brown'
228
+ end
229
+
230
+ specify "replace/clobber" do
231
+ subject.seats_attributes = [ { :color => 'orange' } ]
232
+ subject.seats.size.should == 1
233
+ subject.seats.first.color.should == 'orange'
234
+ end
235
+
236
+ end
237
+ end
238
+
239
+ context ":reject_if" do
240
+ it "should not create a wheel" do
241
+ car = Car.new(:wheels_attributes => [ { :diameter => 10 } ])
242
+ car.wheels.should == []
243
+ end
244
+
245
+ it "should create a wheel" do
246
+ car = Car.new(:wheels_attributes => [ { :diameter => 16 } ])
247
+ car.wheels.size.should == 1
248
+ car.wheels.first.diameter.should == 16
249
+ end
250
+ end
251
+
252
+ context ":allow_delete" do
253
+ let(:wheel) { Wheel.create(:diameter => 17) }
254
+ subject { Car.create(:wheels => [ wheel ] ) }
255
+
256
+ it "should allow us to delete the wheel" do
257
+ subject.wheels_attributes = [ { :key => wheel.key, :_destroy => "1" } ]
258
+ subject.save
259
+ subject.wheels.should == []
260
+ end
261
+
262
+ end
263
+
264
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Ripple Persistence" do
4
+ require 'support/test_server'
5
+
6
+ before :all do
7
+ Object.module_eval do
8
+ class Widget
9
+ include Ripple::Document
10
+ property :name, String
11
+ property :size, Integer
12
+ end
13
+ end
14
+ end
15
+
16
+ before :each do
17
+ @widget = Widget.new
18
+ end
19
+
20
+ it "should save an object to the riak database" do
21
+ @widget.save
22
+ @found = Widget.find(@widget.key)
23
+ @found.should be_a(Widget)
24
+ end
25
+
26
+ it "should save attributes properly to riak" do
27
+ @widget.attributes = {:name => 'Sprocket', :size => 10}
28
+ @widget.save
29
+ @found = Widget.find(@widget.key)
30
+ @found.name.should == 'Sprocket'
31
+ @found.size.should == 10
32
+ end
33
+
34
+ after :each do
35
+ Widget.destroy_all
36
+ end
37
+
38
+ after :all do
39
+ Object.send(:remove_const, :Widget)
40
+ end
41
+
42
+ end
43
+
44
+ describe Ripple::Document do
45
+ let(:custom_data) { Subscription::MyCustomType.new('bar') }
46
+ let(:days_of_month) { Set.new([1, 7, 15, 23]) }
47
+ let(:subscription) { Subscription.create!(:custom_data => custom_data, :days_of_month => days_of_month) }
48
+ let(:found_subscription) { Subscription.find(subscription.key) }
49
+
50
+ it 'allows properties with custom types to be saved and restored from riak' do
51
+ found_subscription.custom_data.should == custom_data
52
+ end
53
+
54
+ it 'allows Set properties to be saved and restored from riak' do
55
+ found_subscription.days_of_month.should == days_of_month
56
+ end
57
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Ripple Search Associations" do
4
+ require 'support/test_server'
5
+
6
+ before :all do
7
+ Object.module_eval do
8
+ class Transaction
9
+ include Ripple::Document
10
+ property :account_key, String
11
+ property :name, String
12
+ end
13
+ class Account
14
+ include Ripple::Document
15
+ many :transactions, :using => :reference
16
+ property :email, String
17
+ end
18
+ end
19
+ end
20
+
21
+ before :each do
22
+ pending("depends upon riak search, which is not available") unless $test_server.riak_search?
23
+ @account = Account.new(:email => 'riak@ripple.com')
24
+ @transaction1 = Transaction.new(:name => 'One')
25
+ @transaction2 = Transaction.new(:name => 'Two')
26
+ end
27
+
28
+ it "should save a many referenced association" do
29
+ @account.save!
30
+ @account.transactions << @transaction1 << @transaction2
31
+ @transaction1.save!
32
+ @transaction2.save!
33
+ @found = Account.find(@account.key)
34
+ @found.transactions.map(&:key).should include(@transaction1.key)
35
+ @found.transactions.map(&:key).should include(@transaction2.key)
36
+ end
37
+
38
+ after :all do
39
+ Object.send(:remove_const, :Account)
40
+ Object.send(:remove_const, :Transaction)
41
+ end
42
+ end
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ripple::Associations::ManyEmbeddedProxy do
4
+ require 'support/models/user'
5
+ require 'support/models/address'
6
+ require 'support/models/note'
7
+
8
+ before :each do
9
+ @user = User.new
10
+ @address = Address.new
11
+ @addr = Address.new(:street => '123 Somewhere')
12
+ @note = Note.new
13
+ end
14
+
15
+ it "should not have children before any are set" do
16
+ @user.addresses.should == []
17
+ end
18
+
19
+ it "should be able to set and get its children" do
20
+ Address.stub!(:instantiate).and_return(@address)
21
+ @user.addresses = [@address]
22
+ @user.addresses.should == [@address]
23
+ end
24
+
25
+ it "should set the parent document on the children when assigning" do
26
+ @user.addresses = [@address]
27
+ @address._parent_document.should == @user
28
+ end
29
+
30
+ it "should return the assignment when assigning" do
31
+ rtn = @user.addresses = [@address]
32
+ rtn.should == [@address]
33
+ end
34
+
35
+ it "should set the parent document on the children when accessing" do
36
+ @user.addresses = [@address]
37
+ @user.addresses.first._parent_document.should == @user
38
+ end
39
+
40
+ it "should be able to replace its children with different children" do
41
+ @user.addresses = [@address]
42
+ @user.addresses.first.street.should be_blank
43
+ @user.addresses = [@addr]
44
+ @user.addresses.first.street.should == '123 Somewhere'
45
+ end
46
+
47
+ it "should be able to add to its children" do
48
+ Address.stub!(:instantiate).and_return(@address)
49
+ @user.addresses = [@address]
50
+ @user.addresses << @address
51
+ @user.addresses.should == [@address, @address]
52
+ end
53
+
54
+ it "should be able to chain calls to adding children" do
55
+ Address.stub!(:instantiate).and_return(@address)
56
+ @user.addresses = [@address]
57
+ @user.addresses << @address << @address << @address
58
+ @user.addresses.should == [@address, @address, @address, @address]
59
+ end
60
+
61
+ it "should set the parent document when adding to its children" do
62
+ @user.addresses << @address
63
+ @user.addresses.first._parent_document.should == @user
64
+ end
65
+
66
+ it "should be able to count its children" do
67
+ @user.addresses = [@address, @address]
68
+ @user.addresses.count.should == 2
69
+ end
70
+
71
+ it "should be able to build a new child" do
72
+ Address.stub!(:new).and_return(@address)
73
+ @user.addresses.build.should == @address
74
+ end
75
+
76
+ it "should assign a parent to the children created with instantiate_target" do
77
+ Address.stub!(:new).and_return(@address)
78
+ @address._parent_document.should be_nil
79
+ @user.addresses.build._parent_document.should == @user
80
+ end
81
+
82
+ it "should validate the children when saving the parent" do
83
+ @user.valid?.should be_true
84
+ @user.addresses << @address
85
+ @address.valid?.should be_false
86
+ @user.valid?.should be_false
87
+ end
88
+
89
+ it "should not save the root document when a child is invalid" do
90
+ @user.addresses << @address
91
+ @user.save.should be_false
92
+ end
93
+
94
+ it "should allow embedding documents in embedded documents" do
95
+ @user.addresses << @address
96
+ @address.notes << @note
97
+ @note._root_document.should == @user
98
+ @note._parent_document.should == @address
99
+ end
100
+
101
+ it "should allow assiging child documents as an array of hashes" do
102
+ @user.attributes = {'addresses' => [{'street' => '123 Somewhere'}]}
103
+ @user.addresses.first.street.should == '123 Somewhere'
104
+ end
105
+
106
+ it "should return an array from to_ary" do
107
+ Address.stub!(:instantiate).and_return(@address)
108
+ @user.addresses << @address
109
+ @user.addresses.to_ary.should == [@address]
110
+ end
111
+
112
+ it "should refuse assigning documents of the wrong type" do
113
+ lambda { @user.addresses = nil }.should raise_error
114
+ lambda { @user.addresses = @address }.should raise_error
115
+ lambda { @user.addresses = [@note] }.should raise_error
116
+ end
117
+
118
+ it "should not add the associated validator multiple times" do
119
+ #$stderr.puts User.validators.collect(&:inspect)
120
+ User.validators_on(:addresses).count.should eq(1)
121
+ end
122
+ end