lookout-mongo_mapper 0.11.3

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.
Files changed (176) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +33 -0
  3. data/UPGRADES +26 -0
  4. data/bin/mmconsole +59 -0
  5. data/examples/attr_accessible.rb +22 -0
  6. data/examples/attr_protected.rb +22 -0
  7. data/examples/cache_key.rb +24 -0
  8. data/examples/custom_types.rb +24 -0
  9. data/examples/identity_map.rb +33 -0
  10. data/examples/identity_map/automatic.rb +2 -0
  11. data/examples/keys.rb +40 -0
  12. data/examples/modifiers/set.rb +25 -0
  13. data/examples/plugins.rb +38 -0
  14. data/examples/querying.rb +35 -0
  15. data/examples/safe.rb +43 -0
  16. data/examples/scopes.rb +52 -0
  17. data/examples/validating/embedded_docs.rb +29 -0
  18. data/lib/mongo_mapper.rb +94 -0
  19. data/lib/mongo_mapper/connection.rb +96 -0
  20. data/lib/mongo_mapper/document.rb +42 -0
  21. data/lib/mongo_mapper/embedded_document.rb +32 -0
  22. data/lib/mongo_mapper/exceptions.rb +30 -0
  23. data/lib/mongo_mapper/extensions/array.rb +19 -0
  24. data/lib/mongo_mapper/extensions/binary.rb +22 -0
  25. data/lib/mongo_mapper/extensions/boolean.rb +44 -0
  26. data/lib/mongo_mapper/extensions/date.rb +25 -0
  27. data/lib/mongo_mapper/extensions/float.rb +14 -0
  28. data/lib/mongo_mapper/extensions/hash.rb +14 -0
  29. data/lib/mongo_mapper/extensions/integer.rb +19 -0
  30. data/lib/mongo_mapper/extensions/kernel.rb +9 -0
  31. data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
  32. data/lib/mongo_mapper/extensions/object.rb +26 -0
  33. data/lib/mongo_mapper/extensions/object_id.rb +32 -0
  34. data/lib/mongo_mapper/extensions/set.rb +20 -0
  35. data/lib/mongo_mapper/extensions/string.rb +18 -0
  36. data/lib/mongo_mapper/extensions/time.rb +28 -0
  37. data/lib/mongo_mapper/locale/en.yml +5 -0
  38. data/lib/mongo_mapper/middleware/identity_map.rb +16 -0
  39. data/lib/mongo_mapper/plugins.rb +22 -0
  40. data/lib/mongo_mapper/plugins/accessible.rb +52 -0
  41. data/lib/mongo_mapper/plugins/active_model.rb +18 -0
  42. data/lib/mongo_mapper/plugins/associations.rb +90 -0
  43. data/lib/mongo_mapper/plugins/associations/base.rb +92 -0
  44. data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +54 -0
  45. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +34 -0
  46. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +52 -0
  47. data/lib/mongo_mapper/plugins/associations/collection.rb +27 -0
  48. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +44 -0
  49. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +133 -0
  50. data/lib/mongo_mapper/plugins/associations/many_association.rb +63 -0
  51. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  52. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +118 -0
  53. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
  54. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
  55. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
  56. data/lib/mongo_mapper/plugins/associations/one_as_proxy.rb +22 -0
  57. data/lib/mongo_mapper/plugins/associations/one_association.rb +48 -0
  58. data/lib/mongo_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +30 -0
  59. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +44 -0
  60. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +95 -0
  61. data/lib/mongo_mapper/plugins/associations/proxy.rb +134 -0
  62. data/lib/mongo_mapper/plugins/associations/single_association.rb +46 -0
  63. data/lib/mongo_mapper/plugins/caching.rb +21 -0
  64. data/lib/mongo_mapper/plugins/callbacks.rb +29 -0
  65. data/lib/mongo_mapper/plugins/clone.rb +22 -0
  66. data/lib/mongo_mapper/plugins/dirty.rb +60 -0
  67. data/lib/mongo_mapper/plugins/document.rb +41 -0
  68. data/lib/mongo_mapper/plugins/dynamic_querying.rb +45 -0
  69. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
  70. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +56 -0
  71. data/lib/mongo_mapper/plugins/embedded_document.rb +53 -0
  72. data/lib/mongo_mapper/plugins/equality.rb +23 -0
  73. data/lib/mongo_mapper/plugins/identity_map.rb +128 -0
  74. data/lib/mongo_mapper/plugins/indexes.rb +13 -0
  75. data/lib/mongo_mapper/plugins/inspect.rb +16 -0
  76. data/lib/mongo_mapper/plugins/keys.rb +313 -0
  77. data/lib/mongo_mapper/plugins/keys/key.rb +61 -0
  78. data/lib/mongo_mapper/plugins/logger.rb +18 -0
  79. data/lib/mongo_mapper/plugins/modifiers.rb +134 -0
  80. data/lib/mongo_mapper/plugins/pagination.rb +16 -0
  81. data/lib/mongo_mapper/plugins/persistence.rb +69 -0
  82. data/lib/mongo_mapper/plugins/protected.rb +45 -0
  83. data/lib/mongo_mapper/plugins/querying.rb +165 -0
  84. data/lib/mongo_mapper/plugins/querying/decorator.rb +36 -0
  85. data/lib/mongo_mapper/plugins/rails.rb +58 -0
  86. data/lib/mongo_mapper/plugins/rails/active_record_association_adapter.rb +33 -0
  87. data/lib/mongo_mapper/plugins/safe.rb +28 -0
  88. data/lib/mongo_mapper/plugins/sci.rb +36 -0
  89. data/lib/mongo_mapper/plugins/scopes.rb +27 -0
  90. data/lib/mongo_mapper/plugins/serialization.rb +109 -0
  91. data/lib/mongo_mapper/plugins/timestamps.rb +22 -0
  92. data/lib/mongo_mapper/plugins/touch.rb +18 -0
  93. data/lib/mongo_mapper/plugins/userstamps.rb +18 -0
  94. data/lib/mongo_mapper/plugins/validations.rb +86 -0
  95. data/lib/mongo_mapper/railtie.rb +48 -0
  96. data/lib/mongo_mapper/railtie/database.rake +65 -0
  97. data/lib/mongo_mapper/translation.rb +10 -0
  98. data/lib/mongo_mapper/version.rb +4 -0
  99. data/lib/rails/generators/mongo_mapper/config/config_generator.rb +24 -0
  100. data/lib/rails/generators/mongo_mapper/config/templates/mongo.yml +18 -0
  101. data/lib/rails/generators/mongo_mapper/model/model_generator.rb +23 -0
  102. data/lib/rails/generators/mongo_mapper/model/templates/model.rb +13 -0
  103. data/test/_NOTE_ON_TESTING +1 -0
  104. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +64 -0
  105. data/test/functional/associations/test_belongs_to_proxy.rb +238 -0
  106. data/test/functional/associations/test_in_array_proxy.rb +349 -0
  107. data/test/functional/associations/test_many_documents_as_proxy.rb +231 -0
  108. data/test/functional/associations/test_many_documents_proxy.rb +866 -0
  109. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +239 -0
  110. data/test/functional/associations/test_many_embedded_proxy.rb +289 -0
  111. data/test/functional/associations/test_many_polymorphic_proxy.rb +303 -0
  112. data/test/functional/associations/test_one_as_proxy.rb +491 -0
  113. data/test/functional/associations/test_one_embedded_polymorphic_proxy.rb +208 -0
  114. data/test/functional/associations/test_one_embedded_proxy.rb +100 -0
  115. data/test/functional/associations/test_one_proxy.rb +383 -0
  116. data/test/functional/test_accessible.rb +198 -0
  117. data/test/functional/test_associations.rb +46 -0
  118. data/test/functional/test_binary.rb +27 -0
  119. data/test/functional/test_caching.rb +77 -0
  120. data/test/functional/test_callbacks.rb +232 -0
  121. data/test/functional/test_dirty.rb +301 -0
  122. data/test/functional/test_document.rb +282 -0
  123. data/test/functional/test_dynamic_querying.rb +75 -0
  124. data/test/functional/test_embedded_document.rb +288 -0
  125. data/test/functional/test_equality.rb +20 -0
  126. data/test/functional/test_identity_map.rb +513 -0
  127. data/test/functional/test_indexes.rb +50 -0
  128. data/test/functional/test_logger.rb +20 -0
  129. data/test/functional/test_modifiers.rb +537 -0
  130. data/test/functional/test_pagination.rb +91 -0
  131. data/test/functional/test_protected.rb +201 -0
  132. data/test/functional/test_querying.rb +935 -0
  133. data/test/functional/test_safe.rb +76 -0
  134. data/test/functional/test_sci.rb +240 -0
  135. data/test/functional/test_scopes.rb +171 -0
  136. data/test/functional/test_timestamps.rb +62 -0
  137. data/test/functional/test_touch.rb +125 -0
  138. data/test/functional/test_userstamps.rb +44 -0
  139. data/test/functional/test_validations.rb +414 -0
  140. data/test/models.rb +261 -0
  141. data/test/support/railtie.rb +4 -0
  142. data/test/support/railtie/autoloaded.rb +2 -0
  143. data/test/support/railtie/not_autoloaded.rb +3 -0
  144. data/test/support/railtie/parent.rb +3 -0
  145. data/test/test_active_model_lint.rb +18 -0
  146. data/test/test_helper.rb +93 -0
  147. data/test/unit/associations/test_base.rb +146 -0
  148. data/test/unit/associations/test_belongs_to_association.rb +29 -0
  149. data/test/unit/associations/test_many_association.rb +63 -0
  150. data/test/unit/associations/test_one_association.rb +47 -0
  151. data/test/unit/associations/test_proxy.rb +100 -0
  152. data/test/unit/serializers/test_json_serializer.rb +216 -0
  153. data/test/unit/serializers/test_xml_serializer.rb +196 -0
  154. data/test/unit/test_clone.rb +69 -0
  155. data/test/unit/test_document.rb +249 -0
  156. data/test/unit/test_dynamic_finder.rb +125 -0
  157. data/test/unit/test_embedded_document.rb +682 -0
  158. data/test/unit/test_equality.rb +38 -0
  159. data/test/unit/test_exceptions.rb +12 -0
  160. data/test/unit/test_extensions.rb +380 -0
  161. data/test/unit/test_identity_map_middleware.rb +34 -0
  162. data/test/unit/test_inspect.rb +47 -0
  163. data/test/unit/test_key.rb +205 -0
  164. data/test/unit/test_keys.rb +65 -0
  165. data/test/unit/test_mongo_mapper.rb +143 -0
  166. data/test/unit/test_pagination.rb +11 -0
  167. data/test/unit/test_plugins.rb +89 -0
  168. data/test/unit/test_rails.rb +183 -0
  169. data/test/unit/test_rails_compatibility.rb +38 -0
  170. data/test/unit/test_rails_reflect_on_association.rb +118 -0
  171. data/test/unit/test_railtie.rb +66 -0
  172. data/test/unit/test_serialization.rb +166 -0
  173. data/test/unit/test_time_zones.rb +44 -0
  174. data/test/unit/test_translation.rb +27 -0
  175. data/test/unit/test_validations.rb +562 -0
  176. metadata +285 -0
@@ -0,0 +1,303 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class ManyPolymorphicProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ Room.collection.remove
7
+ Message.collection.remove
8
+ end
9
+
10
+ should "default reader to empty array" do
11
+ Room.new.messages.should == []
12
+ end
13
+
14
+ should "add type key to polymorphic class base" do
15
+ Message.keys.keys.should include('_type')
16
+ end
17
+
18
+ should "allow adding to assiciation like it was an array" do
19
+ room = Room.new
20
+ room.messages << Enter.new
21
+ room.messages.push Exit.new
22
+ room.messages.concat Exit.new
23
+ room.messages.size.should == 3
24
+ end
25
+
26
+ should "be able to replace the association" do
27
+ room = Room.create(:name => 'Lounge')
28
+
29
+ lambda {
30
+ room.messages = [
31
+ Enter.new(:body => 'John entered room', :position => 1),
32
+ Chat.new(:body => 'Heyyyoooo!', :position => 2),
33
+ Exit.new(:body => 'John exited room', :position => 3)
34
+ ]
35
+ }.should change { Message.count }.by(3)
36
+
37
+ room = room.reload
38
+ messages = room.messages.all :order => "position"
39
+ messages.size.should == 3
40
+ messages[0].body.should == 'John entered room'
41
+ messages[1].body.should == 'Heyyyoooo!'
42
+ messages[2].body.should == 'John exited room'
43
+ end
44
+
45
+ should "correctly store type when using <<, push and concat" do
46
+ room = Room.new
47
+ room.messages << Enter.new(:body => 'John entered the room', :position => 1)
48
+ room.messages.push Exit.new(:body => 'John entered the room', :position => 2)
49
+ room.messages.concat Chat.new(:body => 'Holla!' , :position => 3)
50
+
51
+ room = room.reload
52
+ messages = room.messages.all :order => "position"
53
+ messages[0]._type.should == 'Enter'
54
+ messages[1]._type.should == 'Exit'
55
+ messages[2]._type.should == 'Chat'
56
+ end
57
+
58
+ context "build" do
59
+ should "assign foreign key" do
60
+ room = Room.create
61
+ message = room.messages.build
62
+ message.room_id.should == room._id
63
+ end
64
+
65
+ should "assign _type" do
66
+ room = Room.create
67
+ message = room.messages.build
68
+ message._type.should == 'Message'
69
+ end
70
+
71
+ should "allow assigning attributes" do
72
+ room = Room.create
73
+ message = room.messages.build(:body => 'Foo!')
74
+ message.body.should == 'Foo!'
75
+ end
76
+ end
77
+
78
+ context "create" do
79
+ should "assign foreign key" do
80
+ room = Room.create
81
+ message = room.messages.create
82
+ message.room_id.should == room._id
83
+ end
84
+
85
+ should "assign _type" do
86
+ room = Room.create
87
+ message = room.messages.create
88
+ message._type.should == 'Message'
89
+ end
90
+
91
+ should "save record" do
92
+ room = Room.create
93
+ lambda {
94
+ room.messages.create
95
+ }.should change { Message.count }
96
+ end
97
+
98
+ should "allow passing attributes" do
99
+ room = Room.create
100
+ message = room.messages.create(:body => 'Foo!')
101
+ message.body.should == 'Foo!'
102
+ end
103
+ end
104
+
105
+ context "count" do
106
+ should "work scoped to association" do
107
+ room = Room.create
108
+ 3.times { room.messages.create }
109
+
110
+ other_room = Room.create
111
+ 2.times { other_room.messages.create }
112
+
113
+ room.messages.count.should == 3
114
+ other_room.messages.count.should == 2
115
+ end
116
+
117
+ should "work with conditions" do
118
+ room = Room.create
119
+ room.messages.create(:body => 'Foo')
120
+ room.messages.create(:body => 'Other 1')
121
+ room.messages.create(:body => 'Other 2')
122
+
123
+ room.messages.count(:body => 'Foo').should == 1
124
+ end
125
+ end
126
+
127
+ context "Finding scoped to association" do
128
+ setup do
129
+ @lounge = Room.create(:name => 'Lounge')
130
+ @lm1 = Message.create(:body => 'Loungin!', :position => 1)
131
+ @lm2 = Message.create(:body => 'I love loungin!', :position => 2)
132
+ @lounge.messages = [@lm1, @lm2]
133
+ @lounge.save
134
+
135
+ @hall = Room.create(:name => 'Hall')
136
+ @hm1 = Message.create(:body => 'Do not fall in the hall', :position => 1)
137
+ @hm3 = Message.create(:body => 'Loungin!', :position => 3)
138
+ @hm2 = Message.create(:body => 'Hall the king!', :position => 2)
139
+ @hall.messages = [@hm1, @hm2, @hm3]
140
+ @hall.save
141
+ @hall.reload
142
+ end
143
+
144
+ context "dynamic finders" do
145
+ should "work with single key" do
146
+ @lounge.messages.find_by_position(1).should == @lm1
147
+ @hall.messages.find_by_position(2).should == @hm2
148
+ end
149
+
150
+ should "work with multiple keys" do
151
+ @lounge.messages.find_by_body_and_position('Loungin!', 1).should == @lm1
152
+ @lounge.messages.find_by_body_and_position('Loungin!', 2).should be_nil
153
+ end
154
+
155
+ should "raise error when using !" do
156
+ lambda {
157
+ @lounge.messages.find_by_position!(222)
158
+ }.should raise_error(MongoMapper::DocumentNotFound)
159
+ end
160
+
161
+ context "find_or_create_by" do
162
+ should "not create document if found" do
163
+ lambda {
164
+ message = @lounge.messages.find_or_create_by_body('Loungin!')
165
+ message.room.should == @lounge
166
+ message.should == @lm1
167
+ }.should_not change { Message.count }
168
+ end
169
+
170
+ should "create document if not found" do
171
+ lambda {
172
+ message = @lounge.messages.find_or_create_by_body('Yo dawg!')
173
+ message.room.should == @lounge
174
+ message._type.should == 'Message'
175
+ }.should change { Message.count }.by(1)
176
+ end
177
+ end
178
+ end
179
+
180
+ context "with #all" do
181
+ should "work" do
182
+ @lounge.messages.all(:order => "position").should == [@lm1, @lm2]
183
+ end
184
+
185
+ should "work with conditions" do
186
+ messages = @lounge.messages.all(:body => 'Loungin!', :order => "position")
187
+ messages.should == [@lm1]
188
+ end
189
+
190
+ should "work with order" do
191
+ messages = @lounge.messages.all(:order => 'position desc')
192
+ messages.should == [@lm2, @lm1]
193
+ end
194
+ end
195
+
196
+ context "with #first" do
197
+ should "work" do
198
+ @lounge.messages.first(:order => "position asc").should == @lm1
199
+ end
200
+
201
+ should "work with conditions" do
202
+ message = @lounge.messages.first(:body => 'I love loungin!', :order => "position asc")
203
+ message.should == @lm2
204
+ end
205
+ end
206
+
207
+ context "with #last" do
208
+ should "work" do
209
+ @lounge.messages.last(:order => "position asc").should == @lm2
210
+ end
211
+
212
+ should "work with conditions" do
213
+ message = @lounge.messages.last(:body => 'Loungin!', :order => "position asc")
214
+ message.should == @lm1
215
+ end
216
+ end
217
+
218
+ context "with one id" do
219
+ should "work for id in association" do
220
+ @lounge.messages.find(@lm2._id).should == @lm2
221
+ end
222
+
223
+ should "not work for id not in association" do
224
+ lambda {
225
+ @lounge.messages.find!(@hm2._id)
226
+ }.should raise_error(MongoMapper::DocumentNotFound)
227
+ end
228
+ end
229
+
230
+ context "with query options/criteria" do
231
+ should "work with order on association" do
232
+ @lounge.messages.should == [@lm1, @lm2]
233
+ end
234
+
235
+ should "allow overriding the order provided to the association" do
236
+ @lounge.messages.all(:order => 'position').should == [@lm1, @lm2]
237
+ end
238
+
239
+ should "allow using conditions on association" do
240
+ @hall.latest_messages.should == [@hm3, @hm2]
241
+ end
242
+ end
243
+
244
+ context "with multiple ids" do
245
+ should "work for ids in association" do
246
+ messages = @lounge.messages.find(@lm1._id, @lm2._id)
247
+ messages.should == [@lm1, @lm2]
248
+ end
249
+
250
+ should "not work for ids not in association" do
251
+ assert_raises(MongoMapper::DocumentNotFound) do
252
+ @lounge.messages.find!(@lm1._id, @lm2._id, @hm2._id)
253
+ end
254
+ end
255
+ end
256
+
257
+ context "with #paginate" do
258
+ setup do
259
+ @messages = @hall.messages.paginate(:per_page => 2, :page => 1, :order => 'position asc')
260
+ end
261
+
262
+ should "return total pages" do
263
+ @messages.total_pages.should == 2
264
+ end
265
+
266
+ should "return total entries" do
267
+ @messages.total_entries.should == 3
268
+ end
269
+
270
+ should "return the subject" do
271
+ @messages.should == [@hm1, @hm2]
272
+ end
273
+ end
274
+ end
275
+
276
+ context "extending the association" do
277
+ should "work using a block passed to many" do
278
+ room = Room.new(:name => "Amazing Room")
279
+ messages = room.messages = [
280
+ Enter.new(:body => 'John entered room', :position => 3),
281
+ Chat.new(:body => 'Heyyyoooo!', :position => 4),
282
+ Exit.new(:body => 'John exited room', :position => 5),
283
+ Enter.new(:body => 'Steve entered room', :position => 6),
284
+ Chat.new(:body => 'Anyone there?', :position => 7),
285
+ Exit.new(:body => 'Steve exited room', :position => 8)
286
+ ]
287
+ room.save
288
+ room.messages.older.should == messages[3..5]
289
+ end
290
+
291
+ should "work using many's :extend option" do
292
+
293
+ room = Room.new(:name => "Amazing Room")
294
+ accounts = room.accounts = [
295
+ Bot.new(:last_logged_in => 3.weeks.ago),
296
+ AccountUser.new(:last_logged_in => nil),
297
+ Bot.new(:last_logged_in => 1.week.ago)
298
+ ]
299
+ room.save
300
+ room.accounts.inactive.should == [accounts[1]]
301
+ end
302
+ end
303
+ end
@@ -0,0 +1,491 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class OneAsProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ @post_class = Doc('Post')
7
+ @author_class = Doc('Author')
8
+ end
9
+
10
+ should "default to nil" do
11
+ @post_class.one :author, :as => :authorable, :class => @author_class
12
+ @post_class.new.author.nil?.should be_true
13
+ end
14
+
15
+ should "return nil instead of a proxy" do
16
+ @post_class.one :author, :as => :authorable, :class => @author_class
17
+ nil.should === @post_class.new.author
18
+ end
19
+
20
+ should "not define any keys" do
21
+ count = @post_class.keys.length
22
+ @post_class.one :author, :class => @author_class
23
+ @post_class.keys.length.should == count
24
+ end
25
+
26
+ should "allow assignment of associated document using a hash" do
27
+ @post_class.one :author, :as => :authorable, :class => @author_class
28
+
29
+ post = @post_class.new('author' => { 'name' => 'Frank' })
30
+ post.author.name.should == 'Frank'
31
+
32
+ post.save.should be_true
33
+ post.reload
34
+
35
+ post.author.name.should == 'Frank'
36
+ end
37
+
38
+ should "only return a document with the correct type" do
39
+ @post_class.one :author, :as => :authorable, :class => @author_class
40
+ post = @post_class.create
41
+
42
+ author = @author_class.create(:authorable_id => post.id, :authorable_type => "Nada")
43
+ post.reload.author.should be_nil
44
+
45
+ author = @author_class.create(:authorable_id => post.id, :authorable_type => "Post")
46
+ post.reload.author.should == author
47
+ end
48
+
49
+ context "replacing the association" do
50
+ context "with an object of the class" do
51
+ setup do
52
+ @post_class.one :author, :as => :authorable, :class => @author_class
53
+ @post = @post_class.new
54
+ @author = @author_class.new(:name => 'Frank')
55
+ end
56
+
57
+ should "work" do
58
+ @post.author = @author
59
+ @post.reload
60
+
61
+ @post.author.should == @author
62
+ @post.author.nil?.should be_false
63
+
64
+ new_author = @author_class.new(:name => 'Emily')
65
+ @post.author = new_author
66
+ @post.author.should == new_author
67
+ end
68
+
69
+ should "generate a new proxy instead of modifying the existing one" do
70
+ @post.author = @author
71
+ @post.reload
72
+
73
+ @post.author.should == @author
74
+ @post.author.nil?.should be_false
75
+
76
+ original_author = @post.author
77
+ original_author.name.should == 'Frank'
78
+ new_author = @author_class.new(:name => 'Emily')
79
+ @post.author = new_author
80
+ @post.author.should == new_author
81
+
82
+ original_author.name.should == 'Frank'
83
+ end
84
+
85
+ should "assign foreign key" do
86
+ @post.author = @author
87
+ @post.author.authorable_id.should == @post.id
88
+ end
89
+
90
+ should "assign _type" do
91
+ @post.author = @author
92
+ @post.author.authorable_type.should == "Post"
93
+ end
94
+ end
95
+
96
+ context "with a Hash" do
97
+ setup do
98
+ @post_class.one :author, :as => :authorable, :class => @author_class
99
+ @post = @post_class.new
100
+ end
101
+
102
+ should "convert to an object of the class and work" do
103
+ @post.author = {'name' => 'Frank'}
104
+ @post.reload
105
+
106
+ @post.author.name.should == 'Frank'
107
+ @post.author.nil?.should be_false
108
+
109
+ @post.author = {'name' => 'Emily'}
110
+ @post.author.name.should == 'Emily'
111
+ end
112
+
113
+ should "assign foreign key" do
114
+ @post.author = {'name' => 'Frank'}
115
+ @post.author.authorable_id.should == @post.id
116
+ end
117
+
118
+ should "assign _type" do
119
+ @post.author = {'name' => 'Frank'}
120
+ @post.author.authorable_type.should == "Post"
121
+ end
122
+ end
123
+
124
+ context "with :dependent" do
125
+ context "=> delete" do
126
+ setup do
127
+ @post_class.one :author, :as => :authorable, :class => @author_class, :dependent => :delete
128
+
129
+ @post = @post_class.create
130
+ @author = @author_class.new
131
+ @post.author = @author
132
+ end
133
+
134
+ should "call delete on the existing document" do
135
+ @author_class.any_instance.expects(:delete).once
136
+ @post.author = @author_class.new
137
+ end
138
+
139
+ should "remove the existing document from the database" do
140
+ @post.author = @author_class.new
141
+ lambda { @author.reload }.should raise_error(MongoMapper::DocumentNotFound)
142
+ end
143
+
144
+ should "do nothing if it's the same document" do
145
+ @author_class.any_instance.expects(:delete).never
146
+ @post.author = @author
147
+ end
148
+ end
149
+
150
+ context "=> destory" do
151
+ setup do
152
+ @post_class.one :author, :as => :authorable, :class => @author_class, :dependent => :destroy
153
+
154
+ @post = @post_class.create
155
+ @author = @author_class.new
156
+ @post.author = @author
157
+ end
158
+
159
+ should "call destroy the existing document" do
160
+ @author_class.any_instance.expects(:destroy).once
161
+ @post.author = @author_class.new
162
+ end
163
+
164
+ should "remove the existing document from the database" do
165
+ @post.author = @author_class.new
166
+ lambda { @author.reload }.should raise_error(MongoMapper::DocumentNotFound)
167
+ end
168
+
169
+ should "do nothing if it's the same document" do
170
+ @author_class.any_instance.expects(:destroy).never
171
+ @post.author = @author
172
+ end
173
+ end
174
+
175
+ context "=> nullify" do
176
+ setup do
177
+ @post_class.one :author, :as => :authorable, :class => @author_class, :dependent => :nullify
178
+
179
+ @post = @post_class.create
180
+ @author = @author_class.new
181
+ @post.author = @author
182
+ end
183
+
184
+ should "nullify the existing document" do
185
+ @author.reload
186
+ @author.authorable_id.should == @post.id
187
+
188
+ @post.author = @author_class.new
189
+
190
+ @author.reload
191
+ @author.authorable_id.should be_nil
192
+ end
193
+
194
+ should "work when it's the same document" do
195
+ old_author = @post.author
196
+ @post.author = @author
197
+ old_author.should == @post.author
198
+ end
199
+
200
+ should "nullify _type" do
201
+ @post.author = @author_class.new
202
+ @author.reload
203
+ @author.authorable_type.should be_nil
204
+ end
205
+ end
206
+
207
+ context "unspecified" do
208
+ setup do
209
+ @post_class.one :author, :as => :authorable, :class => @author_class
210
+
211
+ @post = @post_class.create
212
+ @author = @author_class.new
213
+ @post.author = @author
214
+ end
215
+
216
+ should "nullify the existing document" do
217
+ @author.reload
218
+ @author.authorable_id.should == @post.id
219
+
220
+ @post.author = @author_class.new
221
+
222
+ @author.reload
223
+ @author.authorable_id.should be_nil
224
+ end
225
+
226
+ should "nullify _type" do
227
+ @post.author = @author_class.new
228
+ @author.reload
229
+ @author.authorable_type.should be_nil
230
+ end
231
+ end
232
+ end
233
+
234
+ context "with nil" do
235
+ setup do
236
+ @post_class.one :author, :as => :authorable, :class => @author_class
237
+
238
+ @post = @post_class.new
239
+ @author = @author_class.new(:name => 'Frank')
240
+ @post.author = @author
241
+ end
242
+
243
+ should "nullify the existing document" do
244
+ @post.author = nil
245
+ @author.reload
246
+ @author.authorable_id.should be_nil
247
+ end
248
+
249
+ should "set the target to nil" do
250
+ @post.author = nil
251
+ @post.author.should == nil
252
+ end
253
+
254
+ should "nullify _type" do
255
+ @post.author = nil
256
+ @author.reload
257
+ @author.authorable_type.should be_nil
258
+ end
259
+ end
260
+ end
261
+
262
+ should "have boolean method for testing presence" do
263
+ @post_class.one :author, :as => :authorable, :class => @author_class
264
+
265
+ post = @post_class.new
266
+ post.author?.should be_false
267
+
268
+ post.author = @author_class.new(:name => 'Frank')
269
+ post.author?.should be_true
270
+ end
271
+
272
+ should "work with criteria" do
273
+ @post_class.one :primary_author, :as => :authorable, :class => @author_class, :primary => true
274
+ @post_class.one :author, :as => :authorable, :class => @author_class, :primary => false
275
+
276
+ post = @post_class.create
277
+ author = @author_class.create(:name => 'Frank', :primary => false, :authorable_id => post.id, :authorable_type => 'Post')
278
+ primary = @author_class.create(:name => 'Bill', :primary => true, :authorable_id => post.id, :authorable_type => 'Post')
279
+ post.reload
280
+ post.author.should == author
281
+ post.primary_author.should == primary
282
+ end
283
+
284
+ should "unset the association" do
285
+ @post_class.one :author, :as => :authorable, :class => @author_class
286
+ post = @post_class.create
287
+ author = @author_class.create
288
+ post.update_attributes!(:author => author)
289
+ post.reload
290
+ post.author = nil
291
+ post.author.nil?.should be_true
292
+ end
293
+
294
+ context "destroying parent with :dependent" do
295
+ context "=> destroy" do
296
+ setup do
297
+ @post_class.one :author, :as => :authorable, :class => @author_class, :dependent => :destroy
298
+
299
+ @post = @post_class.create
300
+ @author = @author_class.new
301
+ @post.author = @author
302
+ end
303
+
304
+ should "should call destroy on the associated documents" do
305
+ @author_class.any_instance.expects(:destroy).once
306
+ @post.destroy
307
+ end
308
+
309
+ should "should remove the associated documents" do
310
+ @author_class.count.should == 1
311
+ @post.destroy
312
+ @post.author.should == nil
313
+ @author_class.count.should == 0
314
+ end
315
+ end
316
+
317
+ context "=> delete" do
318
+ setup do
319
+ @post_class.one :author, :as => :authorable, :class => @author_class, :dependent => :delete
320
+
321
+ @post = @post_class.create
322
+ @author = @author_class.new
323
+ @post.author = @author
324
+ end
325
+
326
+ should "should call delete the associated documents" do
327
+ @author_class.any_instance.expects(:delete).once
328
+ @post.destroy
329
+ end
330
+
331
+ should "remove the associated documents" do
332
+ @author_class.count.should == 1
333
+ @post.destroy
334
+ @post.author.should == nil
335
+ @author_class.count.should == 0
336
+ end
337
+ end
338
+
339
+ context "=> nullify" do
340
+ setup do
341
+ @post_class.one :author, :as => :authorable, :class => @author_class, :dependent => :nullify
342
+
343
+ @post = @post_class.create
344
+ @author = @author_class.new
345
+ @post.author = @author
346
+ end
347
+
348
+ should "should nullify the relationship but not destroy the associated document" do
349
+ @author_class.count.should == 1
350
+ @post.destroy
351
+ @post.author.should == nil
352
+ @author_class.count.should == 1
353
+
354
+ @author_class.first.should == @author
355
+ @author.authorable_id.should == nil
356
+ end
357
+
358
+ should "nullify _type" do
359
+ @post.destroy
360
+ @author.authorable_type.should be_nil
361
+ end
362
+ end
363
+
364
+ context "unspecified" do
365
+ setup do
366
+ @post_class.one :author, :as => :authorable, :class => @author_class
367
+
368
+ @post = @post_class.create
369
+ @author = @author_class.new
370
+ @post.author = @author
371
+ end
372
+
373
+ should "should nullify the relationship but not destroy the associated document" do
374
+ @author_class.count.should == 1
375
+ @post.destroy
376
+ @post.author.should == nil
377
+ @author_class.count.should == 1
378
+
379
+ @author_class.first.should == @author
380
+ @author.authorable_id.should == nil
381
+ end
382
+
383
+ should "nullify _type" do
384
+ @post.destroy
385
+ @author.authorable_type.should be_nil
386
+ end
387
+ end
388
+ end
389
+
390
+ context "#build" do
391
+ setup do
392
+ @post_class.one :author, :as => :authorable, :class => @author_class
393
+ @post = @post_class.create
394
+ end
395
+
396
+ should "be able to build with attributes" do
397
+ author = @post.build_author(:name => 'John')
398
+ @post.author.should be_instance_of(@author_class)
399
+ @post.author.should be_new
400
+ @post.author.name.should == 'John'
401
+ @post.author.should == author
402
+ end
403
+
404
+ should "assign foreign key" do
405
+ @post.build_author
406
+ @post.author.authorable_id.should == @post.id
407
+ end
408
+
409
+ should "assign _type" do
410
+ @post.build_author
411
+ @post.author.authorable_type.should == 'Post'
412
+ end
413
+ end
414
+
415
+ context "#create" do
416
+ setup do
417
+ @post_class.one :author, :as => :authorable, :class => @author_class
418
+ @post = @post_class.create
419
+ end
420
+
421
+ should "be able to create" do
422
+ author = @post.create_author(:name => 'John')
423
+ @post.author.should be_instance_of(@author_class)
424
+ @post.author.should_not be_new
425
+ @post.author.name.should == 'John'
426
+ @post.author.should == author
427
+ end
428
+
429
+ should "assign foreign key" do
430
+ @post.create_author
431
+ @post.author.authorable_id.should == @post.id
432
+ end
433
+
434
+ should "assign _type" do
435
+ @post.create_author
436
+ @post.author.authorable_type.should == 'Post'
437
+ end
438
+ end
439
+
440
+ context "#create!" do
441
+ setup do
442
+ @author_class.key :name, String, :required => true
443
+ @post_class.one :author, :as => :authorable, :class => @author_class
444
+ @post = @post_class.create
445
+ end
446
+
447
+ should "raise exception if invalid" do
448
+ assert_raises(MongoMapper::DocumentNotValid) do
449
+ @post.create_author!
450
+ end
451
+ end
452
+
453
+ should "work if valid" do
454
+ author = @post.create_author!(:name => 'John')
455
+ @post.author.should be_instance_of(@author_class)
456
+ @post.author.should_not be_new
457
+ @post.author.name.should == 'John'
458
+ @post.author.should == author
459
+ end
460
+
461
+ should "assign foreign key if valid" do
462
+ @post.create_author!(:name => 'John')
463
+ @post.author.authorable_id.should == @post.id
464
+ end
465
+
466
+ should "assign _type if valid" do
467
+ @post.create_author!(:name => 'John')
468
+ @post.author.authorable_type.should == 'Post'
469
+ end
470
+ end
471
+
472
+ context "namespaced foreign keys" do
473
+ setup do
474
+ News::Paper.one :article, :as => 'articleable', :class_name => 'News::Article'
475
+ News::Article.belongs_to :articleable, :polymorphic => true
476
+
477
+ @paper = News::Paper.create
478
+ end
479
+
480
+ should "work" do
481
+ @paper.create_article
482
+ @paper.article.class.should == News::Article
483
+ end
484
+
485
+ should "properly infer the foreign key" do
486
+ article = @paper.create_article
487
+ article.should respond_to(:articleable_id)
488
+ article.articleable_id.should == @paper.id
489
+ end
490
+ end
491
+ end