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,239 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class ManyEmbeddedPolymorphicProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ Catalog.collection.remove
7
+ TrModels::Fleet.collection.remove
8
+ end
9
+
10
+ should "default reader to empty array" do
11
+ catalog = Catalog.new
12
+ catalog.medias.should == []
13
+ end
14
+
15
+ should "allow adding to association like it was an array" do
16
+ catalog = Catalog.new
17
+ catalog.medias << Video.new
18
+ catalog.medias.push Video.new
19
+ catalog.medias.size.should == 2
20
+ end
21
+
22
+ should "be able to replace the association" do
23
+ catalog = Catalog.new
24
+ catalog.medias = [Video.new('file' => 'video.mpg', 'length' => 3600)]
25
+ catalog.save.should be_true
26
+
27
+ catalog = catalog.reload
28
+ catalog.medias.size.should == 1
29
+ catalog.medias[0].file.should == 'video.mpg'
30
+ catalog.medias[0].new?.should == false
31
+ end
32
+
33
+ context "associating objects of non-SCI class" do
34
+ should "work on replacement" do
35
+ catalog = Catalog.new
36
+ catalog.medias = [Human.new(:name => 'Frank'), Robot.new(:serial_number => '1B')]
37
+
38
+ catalog.medias.size.should == 2
39
+ catalog.medias[0].name.should == 'Frank'
40
+ catalog.medias[0].class.should == Human
41
+ catalog.medias[1].serial_number.should == '1B'
42
+ catalog.medias[1].class.should == Robot
43
+
44
+ catalog.save.should be_true
45
+ catalog.reload
46
+
47
+ catalog.medias.size.should == 2
48
+ catalog.medias[0].name.should == 'Frank'
49
+ catalog.medias[0].class.should == Human
50
+ catalog.medias[1].serial_number.should == '1B'
51
+ catalog.medias[1].class.should == Robot
52
+ end
53
+
54
+ should "work on replacement with hashes" do
55
+ catalog = Catalog.new
56
+ catalog.medias = [{:name => 'Frank', '_type' => 'Human'}, {:serial_number => '1B', '_type' => 'Robot'}]
57
+
58
+ catalog.medias.size.should == 2
59
+ catalog.medias[0].name.should == 'Frank'
60
+ catalog.medias[0].class.should == Human
61
+ catalog.medias[1].serial_number.should == '1B'
62
+ catalog.medias[1].class.should == Robot
63
+
64
+ catalog.save.should be_true
65
+ catalog.reload
66
+
67
+ catalog.medias.size.should == 2
68
+ catalog.medias[0].name.should == 'Frank'
69
+ catalog.medias[0].class.should == Human
70
+ catalog.medias[1].serial_number.should == '1B'
71
+ catalog.medias[1].class.should == Robot
72
+ end
73
+
74
+ should "work with concatination" do
75
+ catalog = Catalog.new
76
+ catalog.medias << Human.new(:name => 'Frank')
77
+ catalog.medias << Robot.new(:serial_number => '1B')
78
+
79
+ catalog.medias.size.should == 2
80
+ catalog.medias[0].name.should == 'Frank'
81
+ catalog.medias[0].class.should == Human
82
+ catalog.medias[1].serial_number.should == '1B'
83
+ catalog.medias[1].class.should == Robot
84
+
85
+ catalog.save.should be_true
86
+ catalog.reload
87
+
88
+ catalog.medias.size.should == 2
89
+ catalog.medias[0].name.should == 'Frank'
90
+ catalog.medias[0].class.should == Human
91
+ catalog.medias[1].serial_number.should == '1B'
92
+ catalog.medias[1].class.should == Robot
93
+ end
94
+ end
95
+
96
+ context "count" do
97
+ should "default to 0" do
98
+ Catalog.new.medias.count.should == 0
99
+ end
100
+
101
+ should 'return correct count if any are embedded' do
102
+ catalog = Catalog.new
103
+ catalog.medias = [
104
+ Video.new('file' => 'video.mpg', 'length' => 3600),
105
+ Music.new('file' => 'music.mp3', 'bitrate' => '128kbps'),
106
+ Image.new('file' => 'image.png', 'width' => 800, 'height' => 600)
107
+ ]
108
+ catalog.medias.count.should == 3
109
+ catalog.save.should be_true
110
+ catalog.reload
111
+ catalog.medias.count.should == 3
112
+ end
113
+ end
114
+
115
+ should "store different associations" do
116
+ catalog = Catalog.new
117
+ catalog.medias = [
118
+ Video.new('file' => 'video.mpg', 'length' => 3600),
119
+ Music.new('file' => 'music.mp3', 'bitrate' => '128kbps'),
120
+ Image.new('file' => 'image.png', 'width' => 800, 'height' => 600)
121
+ ]
122
+ catalog.save.should be_true
123
+
124
+ catalog = catalog.reload
125
+ catalog.medias.size.should == 3
126
+ catalog.medias[0].file.should == 'video.mpg'
127
+ catalog.medias[0].length.should == 3600
128
+ catalog.medias[1].file.should == 'music.mp3'
129
+ catalog.medias[1].bitrate.should == "128kbps"
130
+ catalog.medias[2].file.should == 'image.png'
131
+ catalog.medias[2].width.should == 800
132
+ catalog.medias[2].height.should == 600
133
+ end
134
+
135
+ context "With modularized models" do
136
+ should "set associations correctly" do
137
+ fleet_attributes = {
138
+ 'name' => 'My Fleet',
139
+ 'transports' => [
140
+ {'_type' => 'TrModels::Ambulance', 'license_plate' => 'GGG123', 'icu' => true},
141
+ {'_type' => 'TrModels::Car', 'license_plate' => 'ABC123', 'model' => 'VW Golf', 'year' => 2001},
142
+ {'_type' => 'TrModels::Car', 'license_plate' => 'DEF123', 'model' => 'Honda Accord', 'year' => 2008},
143
+ ]
144
+ }
145
+
146
+ fleet = TrModels::Fleet.new(fleet_attributes)
147
+ fleet.transports.size.should == 3
148
+ fleet.transports[0].class.should == TrModels::Ambulance
149
+ fleet.transports[0].license_plate.should == 'GGG123'
150
+ fleet.transports[0].icu.should be_true
151
+ fleet.transports[1].class.should == TrModels::Car
152
+ fleet.transports[1].license_plate.should == 'ABC123'
153
+ fleet.transports[1].model.should == 'VW Golf'
154
+ fleet.transports[1].year.should == 2001
155
+ fleet.transports[2].class.should == TrModels::Car
156
+ fleet.transports[2].license_plate.should == 'DEF123'
157
+ fleet.transports[2].model.should == 'Honda Accord'
158
+ fleet.transports[2].year.should == 2008
159
+ fleet.save.should be_true
160
+
161
+ fleet = fleet.reload
162
+ fleet.transports.size.should == 3
163
+ fleet.transports[0].license_plate.should == 'GGG123'
164
+ fleet.transports[0].icu.should be_true
165
+ fleet.transports[1].license_plate.should == 'ABC123'
166
+ fleet.transports[1].model.should == 'VW Golf'
167
+ fleet.transports[1].year.should == 2001
168
+ fleet.transports[2].license_plate.should == 'DEF123'
169
+ fleet.transports[2].model.should == 'Honda Accord'
170
+ fleet.transports[2].year.should == 2008
171
+ end
172
+
173
+ should "default reader to empty array" do
174
+ fleet = TrModels::Fleet.new
175
+ fleet.transports.should == []
176
+ end
177
+
178
+ should "allow adding to association like it was an array" do
179
+ fleet = TrModels::Fleet.new
180
+ fleet.transports << TrModels::Car.new
181
+ fleet.transports.push TrModels::Bus.new
182
+ fleet.transports.size.should == 2
183
+ end
184
+
185
+ should "be able to replace the association" do
186
+ fleet = TrModels::Fleet.new
187
+ fleet.transports = [TrModels::Car.new('license_plate' => 'DCU2013', 'model' => 'Honda Civic')]
188
+ fleet.save.should be_true
189
+
190
+ fleet = fleet.reload
191
+ fleet.transports.size.should == 1
192
+ fleet.transports[0].license_plate.should == 'DCU2013'
193
+ end
194
+
195
+ should "store different associations" do
196
+ fleet = TrModels::Fleet.new
197
+ fleet.transports = [
198
+ TrModels::Car.new('license_plate' => 'ABC1223', 'model' => 'Honda Civic', 'year' => 2003),
199
+ TrModels::Bus.new('license_plate' => 'XYZ9090', 'max_passengers' => 51),
200
+ TrModels::Ambulance.new('license_plate' => 'HDD3030', 'icu' => true)
201
+ ]
202
+ fleet.save.should be_true
203
+
204
+ fleet = fleet.reload
205
+ fleet.transports.size.should == 3
206
+ fleet.transports[0].license_plate.should == 'ABC1223'
207
+ fleet.transports[0].model.should == 'Honda Civic'
208
+ fleet.transports[0].year.should == 2003
209
+ fleet.transports[1].license_plate.should == 'XYZ9090'
210
+ fleet.transports[1].max_passengers.should == 51
211
+ fleet.transports[2].license_plate.should == 'HDD3030'
212
+ fleet.transports[2].icu.should == true
213
+ end
214
+ end
215
+
216
+ context "extending the association" do
217
+ should "work using a block passed to many" do
218
+ catalog = Catalog.new
219
+ medias = catalog.medias = [
220
+ Video.new('file' => 'video.mpg', 'length' => 3600, :visible => true),
221
+ Music.new('file' => 'music.mp3', 'bitrate' => '128kbps', :visible => true),
222
+ Image.new('file' => 'image.png', 'width' => 800, 'height' => 600, :visible => false)
223
+ ]
224
+ catalog.save
225
+ catalog.medias.visible.should == [medias[0], medias[1]]
226
+ end
227
+
228
+ should "work using many's :extend option" do
229
+ fleet = TrModels::Fleet.new
230
+ transports = fleet.transports = [
231
+ TrModels::Car.new('license_plate' => 'ABC1223', 'model' => 'Honda Civic', 'year' => 2003, :purchased_on => 2.years.ago.to_date),
232
+ TrModels::Bus.new('license_plate' => 'XYZ9090', 'max_passengers' => 51, :purchased_on => 3.years.ago.to_date),
233
+ TrModels::Ambulance.new('license_plate' => 'HDD3030', 'icu' => true, :purchased_on => 1.year.ago.to_date)
234
+ ]
235
+ fleet.save
236
+ fleet.transports.to_be_replaced.should == [transports[1]]
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,289 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class ManyEmbeddedProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ @comment_class = EDoc do
7
+ key :name, String
8
+ key :body, String
9
+ end
10
+ @post_class = Doc do
11
+ key :title, String
12
+ end
13
+ @post_class.many :comments, :class => @comment_class
14
+
15
+ @pet_class = EDoc do
16
+ key :name, String
17
+ end
18
+ @pet_class.embedded_in :person
19
+ @person_class = EDoc do
20
+ key :name, String
21
+ end
22
+ @person_class.key :child, @person_class
23
+ @person_class.many :pets, :class => @pet_class
24
+
25
+ @owner_class = Doc do
26
+ key :name, String
27
+ end
28
+ @owner_class.many :pets, :class => @pet_class
29
+ end
30
+
31
+ should "default reader to empty array" do
32
+ @post_class.new.comments.should == []
33
+ end
34
+
35
+ should "allow adding to association like it was an array" do
36
+ post = @post_class.new
37
+ post.comments << @comment_class.new
38
+ post.comments.push @comment_class.new
39
+ post.comments.size.should == 2
40
+ end
41
+
42
+ should "be embedded in document on save" do
43
+ frank = @comment_class.new(:name => 'Frank', :body => 'Hi!')
44
+ bill = @comment_class.new(:name => 'Bill', :body => 'Hi!')
45
+ post = @post_class.new
46
+ post.comments << frank
47
+ post.comments << bill
48
+ post.save
49
+
50
+ post.reload
51
+ post.comments.size.should == 2
52
+ post.comments[0].should == frank
53
+ post.comments[0].new?.should == false
54
+ post.comments[1].should == bill
55
+ post.comments[1].new?.should == false
56
+ end
57
+
58
+ should "allow embedding arbitrarily deep" do
59
+ @klass = Doc()
60
+ @klass.key :person, @person_class
61
+
62
+ meg = @person_class.new(:name => 'Meg')
63
+ meg.child = @person_class.new(:name => 'Steve')
64
+ meg.child.child = @person_class.new(:name => 'Linda')
65
+
66
+ doc = @klass.new(:person => meg)
67
+ doc.save
68
+ doc.reload
69
+
70
+ doc.person.name.should == 'Meg'
71
+ doc.person.child.name.should == 'Steve'
72
+ doc.person.child.child.name.should == 'Linda'
73
+ end
74
+
75
+ should "allow assignment of many embedded documents using a hash" do
76
+ person_attributes = {
77
+ 'name' => 'Mr. Pet Lover',
78
+ 'pets' => [
79
+ {'name' => 'Jimmy', 'species' => 'Cocker Spainel'},
80
+ {'name' => 'Sasha', 'species' => 'Siberian Husky'},
81
+ ]
82
+ }
83
+
84
+ owner = @owner_class.new(person_attributes)
85
+ owner.name.should == 'Mr. Pet Lover'
86
+ owner.pets[0].id.class.should == BSON::ObjectId
87
+ owner.pets[0].name.should == 'Jimmy'
88
+ owner.pets[0].species.should == 'Cocker Spainel'
89
+ owner.pets[1].id.class.should == BSON::ObjectId
90
+ owner.pets[1].name.should == 'Sasha'
91
+ owner.pets[1].species.should == 'Siberian Husky'
92
+
93
+ owner.save.should be_true
94
+ owner.reload
95
+
96
+ owner.name.should == 'Mr. Pet Lover'
97
+ owner.pets[0].id.class.should == BSON::ObjectId
98
+ owner.pets[0].name.should == 'Jimmy'
99
+ owner.pets[0].species.should == 'Cocker Spainel'
100
+ owner.pets[1].id.class.should == BSON::ObjectId
101
+ owner.pets[1].name.should == 'Sasha'
102
+ owner.pets[1].species.should == 'Siberian Husky'
103
+ end
104
+
105
+ context "passing documents between versions of code" do
106
+ setup do
107
+ @old_klass = Doc do
108
+ set_collection_name 'generic_parents'
109
+ key :name, String
110
+ end
111
+
112
+ @updated_klass = Doc do
113
+ set_collection_name 'generic_parents'
114
+ key :name, String
115
+ end
116
+ @updated_klass.many :pets, :class => @pet_class
117
+ end
118
+
119
+ should "not break many embedded proxy" do
120
+ @old_klass.collection.drop
121
+ created_by_new_code = @updated_klass.create!
122
+ created_by_new_code.pets.should == []
123
+
124
+ @old_klass.first # ensure_key_exists calls @old_klass.key(:embedded_docs) (not @old_klass.many(:embedded_docs))
125
+ @old_klass.create!(:name => 'created in old code') # creates doc with {embedded_docs : null}
126
+
127
+ lambda {
128
+ loaded_in_new_code = @updated_klass.find_by_name('created in old code')
129
+ loaded_in_new_code.pets.should == []
130
+ }.should_not raise_error
131
+ end
132
+ end
133
+
134
+ context "embedding many embedded documents" do
135
+ setup do
136
+ @klass = Doc()
137
+ @klass.many :people, :class => @person_class
138
+ end
139
+
140
+ should "persist all embedded documents" do
141
+ meg = @person_class.new(:name => 'Meg', :pets => [
142
+ @pet_class.new(:name => 'Sparky', :species => 'Dog'),
143
+ @pet_class.new(:name => 'Koda', :species => 'Dog')
144
+ ])
145
+
146
+ doc = @klass.new
147
+ doc.people << meg
148
+ doc.save
149
+ doc.reload
150
+
151
+ doc.people.first.name.should == 'Meg'
152
+ doc.people.first.pets.should_not == []
153
+ doc.people.first.pets.first.name.should == 'Sparky'
154
+ doc.people.first.pets.first.species.should == 'Dog'
155
+ doc.people.first.pets[1].name.should == 'Koda'
156
+ doc.people.first.pets[1].species.should == 'Dog'
157
+ end
158
+
159
+ should "create a reference to the root document for all embedded documents before save" do
160
+ doc = @klass.new
161
+ meg = @person_class.new(:name => 'Meg')
162
+ pet = @pet_class.new(:name => 'Sparky', :species => 'Dog')
163
+
164
+ doc.people << meg
165
+ meg.pets << pet
166
+
167
+ doc.people.first._root_document.should == doc
168
+ doc.people.first.pets.first._root_document.should == doc
169
+ end
170
+ should "create a reference to the owning document for all embedded documents before save" do
171
+ doc = @klass.new
172
+ meg = @person_class.new(:name => 'Meg')
173
+ pet = @pet_class.new(:name => 'Sparky', :species => 'Dog')
174
+
175
+ doc.people << meg
176
+ meg.pets << pet
177
+
178
+ doc.people.first._parent_document.should == doc
179
+ doc.people.first.pets.first._parent_document.should == doc.people.first
180
+ end
181
+
182
+ should "create a reference to the root document for all embedded documents" do
183
+ sparky = @pet_class.new(:name => 'Sparky', :species => 'Dog')
184
+ meg = @person_class.new(:name => 'Meg', :pets => [sparky])
185
+ doc = @klass.new
186
+ doc.people << meg
187
+ doc.save
188
+
189
+ doc.reload
190
+ doc.people.first._root_document.should == doc
191
+ doc.people.first.pets.first._root_document.should == doc
192
+ end
193
+ should "create a reference to the owning document for all embedded documents" do
194
+ doc = @klass.new
195
+ meg = @person_class.new(:name => 'Meg')
196
+ pet = @pet_class.new(:name => 'Sparky', :species => 'Dog')
197
+
198
+ doc.people << meg
199
+ meg.pets << pet
200
+ doc.save
201
+
202
+ doc.reload
203
+ doc.people.first._parent_document.should == doc
204
+ doc.people.first.pets.first._parent_document.should == doc.people.first
205
+ end
206
+
207
+ should "create embedded_in relationship for embedded docs" do
208
+ doc = @klass.new
209
+ meg = @person_class.new(:name => 'Meg')
210
+ pet = @pet_class.new(:name => 'Sparky', :species => 'Dog')
211
+
212
+ doc.people << meg
213
+ meg.pets << pet
214
+ doc.save
215
+
216
+ doc.reload
217
+ doc.people.first.pets.first.person.should == doc.people.first
218
+ end
219
+ end
220
+
221
+ should "allow finding by id" do
222
+ sparky = @pet_class.new(:name => 'Sparky', :species => 'Dog')
223
+ meg = @owner_class.create(:name => 'Meg', :pets => [sparky])
224
+
225
+ meg.pets.find(sparky._id).should == sparky # oid
226
+ meg.pets.find(sparky.id.to_s).should == sparky # string
227
+ end
228
+
229
+ context "count" do
230
+ should "default to 0" do
231
+ @owner_class.new.pets.count.should == 0
232
+ end
233
+
234
+ should "return correct count if any are embedded" do
235
+ owner = @owner_class.new(:name => 'Meg')
236
+ owner.pets = [@pet_class.new, @pet_class.new]
237
+ owner.pets.count.should == 2
238
+ owner.save
239
+ owner.reload
240
+ owner.pets.count.should == 2
241
+ end
242
+ end
243
+
244
+ context "extending the association" do
245
+ setup do
246
+ @address_class = EDoc do
247
+ key :address, String
248
+ key :city, String
249
+ key :state, String
250
+ key :zip, Integer
251
+ end
252
+
253
+ @project_class = Doc do
254
+ key :name, String
255
+ end
256
+ end
257
+
258
+ should "work using a block passed to many" do
259
+ @project_class.many :addresses, :class => @address_class do
260
+ def find_all_by_state(state)
261
+ find_all { |a| a.state == state }
262
+ end
263
+ end
264
+
265
+ addr1 = @address_class.new(:address => "Gate-3 Lankershim Blvd.", :city => "Universal City", :state => "CA", :zip => "91608")
266
+ addr2 = @address_class.new(:address => "3000 W. Alameda Ave.", :city => "Burbank", :state => "CA", :zip => "91523")
267
+ addr3 = @address_class.new(:address => "111 Some Ln", :city => "Nashville", :state => "TN", :zip => "37211")
268
+ project = @project_class.create(:name => "Some Project", :addresses => [addr1, addr2, addr3])
269
+
270
+ project.addresses.find_all_by_state("CA").should == [addr1, addr2]
271
+ end
272
+
273
+ should "work using many's :extend option" do
274
+ module FindByCity
275
+ def find_by_city(city)
276
+ find_all { |a| a.city == city }
277
+ end
278
+ end
279
+ @project_class.many :addresses, :class => @address_class, :extend => FindByCity
280
+
281
+ addr1 = @address_class.new(:address => "Gate-3 Lankershim Blvd.", :city => "Universal City", :state => "CA", :zip => "91608")
282
+ addr2 = @address_class.new(:address => "3000 W. Alameda Ave.", :city => "Burbank", :state => "CA", :zip => "91523")
283
+ addr3 = @address_class.new(:address => "111 Some Ln", :city => "Nashville", :state => "TN", :zip => "37211")
284
+ project = @project_class.create(:name => "Some Project", :addresses => [addr1, addr2, addr3])
285
+
286
+ project.addresses.find_by_city('Burbank').should == [addr2]
287
+ end
288
+ end
289
+ end