mongo_mapper 0.6.10 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. data/README.rdoc +5 -14
  2. data/Rakefile +1 -1
  3. data/VERSION +1 -1
  4. data/lib/mongo_mapper.rb +48 -56
  5. data/lib/mongo_mapper/document.rb +136 -164
  6. data/lib/mongo_mapper/embedded_document.rb +29 -354
  7. data/lib/mongo_mapper/plugins.rb +31 -0
  8. data/lib/mongo_mapper/plugins/associations.rb +105 -0
  9. data/lib/mongo_mapper/plugins/associations/base.rb +123 -0
  10. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +30 -0
  11. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +25 -0
  12. data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
  13. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +50 -0
  14. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +139 -0
  15. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  16. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +117 -0
  17. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
  18. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
  19. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
  20. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  21. data/lib/mongo_mapper/plugins/associations/proxy.rb +118 -0
  22. data/lib/mongo_mapper/plugins/callbacks.rb +65 -0
  23. data/lib/mongo_mapper/plugins/clone.rb +13 -0
  24. data/lib/mongo_mapper/plugins/descendants.rb +16 -0
  25. data/lib/mongo_mapper/plugins/dirty.rb +119 -0
  26. data/lib/mongo_mapper/plugins/equality.rb +23 -0
  27. data/lib/mongo_mapper/plugins/identity_map.rb +122 -0
  28. data/lib/mongo_mapper/plugins/inspect.rb +14 -0
  29. data/lib/mongo_mapper/plugins/keys.rb +324 -0
  30. data/lib/mongo_mapper/plugins/logger.rb +17 -0
  31. data/lib/mongo_mapper/plugins/pagination.rb +24 -0
  32. data/lib/mongo_mapper/plugins/pagination/proxy.rb +68 -0
  33. data/lib/mongo_mapper/plugins/protected.rb +45 -0
  34. data/lib/mongo_mapper/plugins/rails.rb +45 -0
  35. data/lib/mongo_mapper/plugins/serialization.rb +105 -0
  36. data/lib/mongo_mapper/plugins/validations.rb +46 -0
  37. data/lib/mongo_mapper/query.rb +130 -0
  38. data/lib/mongo_mapper/support.rb +40 -17
  39. data/lib/mongo_mapper/support/descendant_appends.rb +46 -0
  40. data/lib/mongo_mapper/support/find.rb +77 -0
  41. data/mongo_mapper.gemspec +55 -38
  42. data/performance/read_write.rb +52 -0
  43. data/specs.watchr +23 -2
  44. data/test/functional/associations/test_belongs_to_proxy.rb +12 -10
  45. data/test/functional/associations/test_many_documents_as_proxy.rb +4 -21
  46. data/test/functional/associations/test_many_documents_proxy.rb +2 -8
  47. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +59 -39
  48. data/test/functional/associations/test_many_embedded_proxy.rb +145 -81
  49. data/test/functional/associations/test_many_polymorphic_proxy.rb +2 -40
  50. data/test/functional/associations/test_one_proxy.rb +25 -10
  51. data/test/functional/test_binary.rb +2 -8
  52. data/test/functional/test_callbacks.rb +1 -5
  53. data/test/functional/test_dirty.rb +27 -23
  54. data/test/functional/test_document.rb +224 -165
  55. data/test/functional/test_embedded_document.rb +72 -82
  56. data/test/functional/test_identity_map.rb +508 -0
  57. data/test/functional/test_modifiers.rb +15 -5
  58. data/test/functional/test_pagination.rb +1 -3
  59. data/test/functional/test_protected.rb +155 -0
  60. data/test/functional/test_string_id_compatibility.rb +7 -12
  61. data/test/functional/test_validations.rb +26 -58
  62. data/test/models.rb +0 -39
  63. data/test/test_helper.rb +37 -3
  64. data/test/unit/associations/test_base.rb +5 -5
  65. data/test/unit/associations/test_proxy.rb +8 -6
  66. data/test/unit/test_descendant_appends.rb +71 -0
  67. data/test/unit/test_document.rb +71 -76
  68. data/test/unit/test_dynamic_finder.rb +27 -29
  69. data/test/unit/test_embedded_document.rb +555 -601
  70. data/test/unit/{test_key.rb → test_keys.rb} +2 -5
  71. data/test/unit/test_mongo_mapper.rb +69 -9
  72. data/test/unit/test_pagination.rb +40 -32
  73. data/test/unit/test_plugins.rb +50 -0
  74. data/test/unit/{test_finder_options.rb → test_query.rb} +74 -65
  75. data/test/unit/test_rails.rb +123 -0
  76. data/test/unit/{test_serializations.rb → test_serialization.rb} +1 -2
  77. data/test/unit/test_support.rb +23 -7
  78. data/test/unit/test_time_zones.rb +3 -4
  79. data/test/unit/test_validations.rb +58 -17
  80. metadata +53 -36
  81. data/lib/mongo_mapper/associations.rb +0 -78
  82. data/lib/mongo_mapper/associations/base.rb +0 -119
  83. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +0 -26
  84. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +0 -21
  85. data/lib/mongo_mapper/associations/collection.rb +0 -19
  86. data/lib/mongo_mapper/associations/in_array_proxy.rb +0 -137
  87. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +0 -26
  88. data/lib/mongo_mapper/associations/many_documents_proxy.rb +0 -115
  89. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +0 -31
  90. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +0 -54
  91. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +0 -11
  92. data/lib/mongo_mapper/associations/one_proxy.rb +0 -64
  93. data/lib/mongo_mapper/associations/proxy.rb +0 -116
  94. data/lib/mongo_mapper/callbacks.rb +0 -61
  95. data/lib/mongo_mapper/dirty.rb +0 -117
  96. data/lib/mongo_mapper/dynamic_finder.rb +0 -74
  97. data/lib/mongo_mapper/finder_options.rb +0 -145
  98. data/lib/mongo_mapper/key.rb +0 -36
  99. data/lib/mongo_mapper/mongo_mapper.rb +0 -125
  100. data/lib/mongo_mapper/pagination.rb +0 -66
  101. data/lib/mongo_mapper/rails_compatibility/document.rb +0 -15
  102. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +0 -28
  103. data/lib/mongo_mapper/serialization.rb +0 -54
  104. data/lib/mongo_mapper/serializers/json_serializer.rb +0 -48
  105. data/lib/mongo_mapper/validations.rb +0 -39
  106. data/test/functional/test_rails_compatibility.rb +0 -25
@@ -3,24 +3,29 @@ require 'models'
3
3
 
4
4
  class EmbeddedDocumentTest < Test::Unit::TestCase
5
5
  def setup
6
- @document = Class.new do
7
- include MongoMapper::Document
8
- set_collection_name 'users'
9
-
6
+ @klass = Doc do
10
7
  key :first_name, String
11
8
  key :last_name, String
12
9
  end
13
- @document.collection.remove
10
+
11
+ @pet_klass = EDoc do
12
+ key :name, String
13
+ end
14
+
15
+ @klass.many :pets, :class => @pet_klass
16
+
17
+ @address_class = EDoc do
18
+ key :city, String
19
+ key :state, String
20
+ end
14
21
  end
15
22
 
16
23
  context "Saving a document with an embedded document" do
17
24
  setup do
18
- @document.class_eval do
19
- key :foo, Address
20
- end
25
+ @klass.key :foo, @address_class
21
26
 
22
- @address = Address.new(:city => 'South Bend', :state => 'IN')
23
- @doc = @document.new(:foo => @address)
27
+ @address = @address_class.new(:city => 'South Bend', :state => 'IN')
28
+ @doc = @klass.new(:foo => @address)
24
29
  end
25
30
 
26
31
  should "embed embedded document" do
@@ -34,48 +39,36 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
34
39
  end
35
40
  end
36
41
 
37
- context "Instantiating single collection inherited embedded documents" do
38
- setup do
39
- @document = Class.new do
40
- include MongoMapper::Document
41
- key :message, Message
42
- end
43
- end
44
-
45
- should "work" do
46
- doc1 = @document.create(:message => Enter.new)
47
- doc2 = @document.create(:message => Exit.new)
48
- doc3 = @document.create(:message => Chat.new)
49
-
50
- doc1.reload.message.class.should be(Enter)
51
- doc2.reload.message.class.should be(Exit)
52
- doc3.reload.message.class.should be(Chat)
42
+ should "correctly instantiate single collection inherited embedded documents" do
43
+ document = Doc('Foo') do
44
+ key :message, Message
53
45
  end
46
+
47
+ doc1 = document.create(:message => Enter.new)
48
+ doc1.reload.message.class.should be(Enter)
54
49
  end
55
50
 
56
51
  context "new?" do
57
52
  setup do
58
- @document.class_eval do
59
- key :foo, Address
60
- end
53
+ @klass.key :foo, @address_class
61
54
  end
62
55
 
63
56
  should "be new until document is saved" do
64
- address = Address.new(:city => 'South Bend', :state => 'IN')
65
- doc = @document.new(:foo => address)
57
+ address = @address_class.new(:city => 'South Bend', :state => 'IN')
58
+ doc = @klass.new(:foo => address)
66
59
  address.new?.should == true
67
60
  end
68
61
 
69
62
  should "not be new after document is saved" do
70
- address = Address.new(:city => 'South Bend', :state => 'IN')
71
- doc = @document.new(:foo => address)
63
+ address = @address_class.new(:city => 'South Bend', :state => 'IN')
64
+ doc = @klass.new(:foo => address)
72
65
  doc.save
73
66
  doc.foo.new?.should == false
74
67
  end
75
68
 
76
69
  should "not be new when document is read back" do
77
- address = Address.new(:city => 'South Bend', :state => 'IN')
78
- doc = @document.new(:foo => address)
70
+ address = @address_class.new(:city => 'South Bend', :state => 'IN')
71
+ doc = @klass.new(:foo => address)
79
72
  doc.save
80
73
 
81
74
  doc = doc.reload
@@ -83,53 +76,50 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
83
76
  end
84
77
  end
85
78
 
86
- context "save" do
87
- should "save the root document" do
88
- person = RealPerson.create
89
-
90
- pet = Pet.new :name => 'sparky'
91
- person.pets << pet
92
- pet.save
93
-
94
- person = person.reload
95
- person.pets.first.should == pet
96
- end
79
+ should "be able to save" do
80
+ person = @klass.create
97
81
 
98
- should "save new keys" do
99
- person = RealPerson.new
100
- person[:new_attribute] = 'foobar'
101
- person.save
102
-
103
- person = person.reload
104
- person.new_attribute.should == 'foobar'
105
- end
82
+ pet = @pet_klass.new(:name => 'sparky')
83
+ person.pets << pet
84
+ pet.should be_new
85
+ pet.save
86
+ pet.should_not be_new
87
+
88
+ person.reload
89
+ person.pets.first.should == pet
106
90
  end
107
91
 
108
- context "update_attributes" do
109
- should "save the root document" do
110
- person = RealPerson.create
111
-
112
- pet = Pet.new(:name => 'sparky')
113
- person.pets << pet
114
- pet.save
115
-
116
- person = person.reload
117
- pet = person.pets.first
118
- pet.update_attributes :name => 'koda'
119
-
120
- person = person.reload
121
- person.pets.first._id.should == pet._id
122
- person.pets.first.name.should == 'koda'
123
- end
124
- end
125
-
126
- context "update_attributes!" do
127
- should "pass the attributes to self.attributes" do
128
- person = RealPerson.create
129
- attributes = { :foo => 'bar' }
130
- person.expects(:attributes=).with(attributes)
131
- person.expects(:save!)
132
- person.update_attributes!(attributes)
133
- end
134
- end
135
- end
92
+ should "be able to dynamically add new keys and save" do
93
+ person = @klass.create
94
+
95
+ pet = @pet_klass.new(:name => 'sparky', :crazy_key => 'crazy')
96
+ person.pets << pet
97
+ pet.save
98
+
99
+ person.reload
100
+ person.pets.first.crazy_key.should == 'crazy'
101
+ end
102
+
103
+ should "be able to update_attributes" do
104
+ pet = @pet_klass.new(:name => 'sparky')
105
+ person = @klass.create(:pets => [pet])
106
+ person.reload
107
+ pet = person.pets.first
108
+
109
+ pet.update_attributes(:name => 'koda').should be_true
110
+ person.reload
111
+ person.pets.first._id.should == pet._id
112
+ person.pets.first.name.should == 'koda'
113
+ end
114
+
115
+ should "be able to update_attributes!" do
116
+ person = @klass.create(:pets => [@pet_klass.new(:name => 'sparky')])
117
+ person.reload
118
+ pet = person.pets.first
119
+
120
+ attributes = {:name => 'koda'}
121
+ pet.expects(:attributes=).with(attributes)
122
+ pet.expects(:save!)
123
+ pet.update_attributes!(attributes)
124
+ end
125
+ end
@@ -0,0 +1,508 @@
1
+ require 'test_helper'
2
+
3
+ class IdentityMapTest < Test::Unit::TestCase
4
+ def assert_in_map(*resources)
5
+ [resources].flatten.each do |resource|
6
+ resource.identity_map.keys.should include(resource._id)
7
+ mapped_resource = resource.identity_map[resource._id]
8
+ resource.should equal(mapped_resource)
9
+ end
10
+ end
11
+
12
+ def assert_not_in_map(*resources)
13
+ [resources].flatten.each do |resource|
14
+ resource.identity_map.keys.should_not include(resource._id)
15
+ end
16
+ end
17
+
18
+ def expect_no_queries
19
+ Mongo::Collection.any_instance.expects(:find_one).never
20
+ Mongo::Collection.any_instance.expects(:find).never
21
+ end
22
+
23
+ def expects_one_query
24
+ Mongo::Collection.any_instance.expects(:find_one).once.returns({})
25
+ end
26
+
27
+ context "Document" do
28
+ setup do
29
+ MongoMapper::Plugins::IdentityMap.models.clear
30
+
31
+ @person_class = Doc('Person') do
32
+ set_collection_name 'people'
33
+ plugin MongoMapper::Plugins::IdentityMap
34
+
35
+ key :name, String
36
+ end
37
+
38
+ @post_class = Doc('Post') do
39
+ set_collection_name 'posts'
40
+ plugin MongoMapper::Plugins::IdentityMap
41
+
42
+ key :title, String
43
+ key :person_id, ObjectId
44
+ end
45
+
46
+ @post_class.belongs_to :person, :class => @person_class
47
+ @person_class.many :posts, :class => @post_class
48
+
49
+ @post_class.identity_map_on
50
+ @person_class.identity_map_on
51
+ MongoMapper::Plugins::IdentityMap.clear
52
+ end
53
+
54
+ should "track identity mapped models" do
55
+ MongoMapper::Plugins::IdentityMap.models.should == [@person_class, @post_class].to_set
56
+ end
57
+
58
+ should "be able to clear the map of all models" do
59
+ person = @person_class.create(:name => 'John')
60
+ post = @post_class.create(:title => 'IM 4eva')
61
+ assert_in_map(person, post)
62
+
63
+ MongoMapper::Plugins::IdentityMap.clear
64
+
65
+ assert_not_in_map(person, post)
66
+
67
+ [@person_class, @post_class].each { |klass| klass.identity_map.should == {} }
68
+ end
69
+
70
+ context "IM on off status" do
71
+ teardown do
72
+ @post_class.identity_map_on
73
+ @person_class.identity_map_on
74
+ end
75
+
76
+ should "default identity map status to on" do
77
+ Doc { plugin MongoMapper::Plugins::IdentityMap }.identity_map_status.should be_true
78
+ end
79
+
80
+ should "be true if on" do
81
+ @post_class.identity_map_on
82
+ @post_class.should be_identity_map_on
83
+ @post_class.should_not be_identity_map_off
84
+ end
85
+
86
+ should "be false if off" do
87
+ @post_class.identity_map_off
88
+ @post_class.should be_identity_map_off
89
+ @post_class.should_not be_identity_map_on
90
+ end
91
+
92
+ should "not share with other classes" do
93
+ @post_class.identity_map_off
94
+ @person_class.identity_map_on
95
+ @post_class.identity_map_status.should_not == @person_class.identity_map_status
96
+ end
97
+ end
98
+
99
+ should "default identity map to hash" do
100
+ Doc { plugin MongoMapper::Plugins::IdentityMap }.identity_map.should == {}
101
+ end
102
+
103
+ should "add key to map when saved" do
104
+ person = @person_class.new
105
+ assert_not_in_map(person)
106
+ person.save.should be_true
107
+ assert_in_map(person)
108
+ end
109
+
110
+ should "allow saving with options" do
111
+ person = @person_class.new
112
+ assert_nothing_raised do
113
+ person.save(:validate => false).should be_true
114
+ end
115
+ end
116
+
117
+ should "remove key from map when deleted" do
118
+ person = @person_class.create(:name => 'Fred')
119
+ assert_in_map(person)
120
+ person.destroy
121
+ assert_not_in_map(person)
122
+ end
123
+
124
+ context "reload" do
125
+ setup do
126
+ @person = @person_class.create(:name => 'Fred')
127
+ end
128
+
129
+ should "remove object from identity and re-query" do
130
+ assert_in_map(@person)
131
+ expects_one_query
132
+ @person.reload
133
+ end
134
+
135
+ should "add object back into map" do
136
+ assert_in_map(@person)
137
+ before_reload = @person
138
+ @person.reload.should equal(before_reload)
139
+ assert_in_map(@person)
140
+ end
141
+ end
142
+
143
+ context "#load" do
144
+ setup do
145
+ @id = Mongo::ObjectID.new
146
+ end
147
+
148
+ should "add document to map" do
149
+ loaded = @person_class.load({'_id' => @id, 'name' => 'Frank'})
150
+ assert_in_map(loaded)
151
+ end
152
+
153
+ should "return document if already in map" do
154
+ first_load = @person_class.load({'_id' => @id, 'name' => 'Frank'})
155
+ @person_class.identity_map.expects(:[]=).never
156
+ second_load = @person_class.load({'_id' => @id, 'name' => 'Frank'})
157
+ first_load.should equal(second_load)
158
+ end
159
+ end
160
+
161
+ context "#find (with one id)" do
162
+ context "for object not in map" do
163
+ setup do
164
+ @person = @person_class.create(:name => 'Fred')
165
+ @person_class.identity_map.clear
166
+ end
167
+
168
+ should "query the database" do
169
+ expects_one_query
170
+ @person_class.find(@person.id)
171
+ end
172
+
173
+ should "add object to map" do
174
+ assert_not_in_map(@person)
175
+ found_person = @person_class.find(@person.id)
176
+ assert_in_map(found_person)
177
+ end
178
+
179
+ should "return nil if not found " do
180
+ @person_class.find(1234).should be_nil
181
+ end
182
+ end
183
+
184
+ context "for object in map" do
185
+ setup do
186
+ @person = @person_class.create(:name => 'Fred')
187
+ end
188
+
189
+ should "not query database" do
190
+ expect_no_queries
191
+ @person_class.find(@person.id)
192
+ end
193
+
194
+ should "return exact object" do
195
+ assert_in_map(@person)
196
+ found_person = @person_class.find(@person.id)
197
+ found_person.should equal(@person)
198
+ end
199
+ end
200
+ end
201
+
202
+ context "#find (with one id and options)" do
203
+ setup do
204
+ @person = @person_class.create(:name => 'Fred')
205
+ @post1 = @person.posts.create(:title => 'I Love Mongo')
206
+ @post2 = @person.posts.create(:title => 'Migrations Suck!')
207
+ end
208
+
209
+ # There are times when even though the id matches, other criteria doesn't
210
+ # so we need to do the query to ensure that when criteria doesn't match
211
+ # the document is in fact not found.
212
+ #
213
+ # I'm open to not making this query if someone can figure out reliable
214
+ # way to check if document matches criteria without querying.
215
+ should "query the database" do
216
+ assert_in_map(@post1)
217
+ expects_one_query
218
+ @person.posts.find(@post1.id)
219
+ end
220
+
221
+ should "return exact object" do
222
+ assert_in_map(@post1)
223
+ @person.posts.find(@post1.id)
224
+ assert_in_map(@post1)
225
+ end
226
+
227
+ should "return nil if not found " do
228
+ @person.posts.find(1234).should be_nil
229
+ end
230
+ end
231
+
232
+ context "#find (with multiple ids)" do
233
+ should "add all documents to map" do
234
+ person1 = @person_class.create(:name => 'Fred')
235
+ person2 = @person_class.create(:name => 'Bill')
236
+ person3 = @person_class.create(:name => 'Jesse')
237
+ @person_class.identity_map.clear
238
+
239
+ people = @person_class.find(person1.id, person2.id, person3.id)
240
+ assert_in_map(people)
241
+ end
242
+
243
+ should "add missing documents to map and return existing ones" do
244
+ person1 = @person_class.create(:name => 'Fred')
245
+ @person_class.identity_map.clear
246
+ person2 = @person_class.create(:name => 'Bill')
247
+ person3 = @person_class.create(:name => 'Jesse')
248
+
249
+ assert_not_in_map(person1)
250
+ assert_in_map(person2, person3)
251
+
252
+ people = @person_class.find(person1.id, person2.id, person3.id)
253
+ assert_in_map(people.first) # making sure one that wasn't mapped now is
254
+ assert_in_map(person2, person3)
255
+ end
256
+ end
257
+
258
+ context "#first" do
259
+ context "for object not in map" do
260
+ setup do
261
+ @person = @person_class.create(:name => 'Fred')
262
+ @person_class.identity_map.clear
263
+ end
264
+
265
+ should "query the database" do
266
+ expects_one_query
267
+ @person_class.first(:_id => @person.id)
268
+ end
269
+
270
+ should "add object to map" do
271
+ assert_not_in_map(@person)
272
+ found_person = @person_class.first(:_id => @person.id)
273
+ assert_in_map(found_person)
274
+ end
275
+
276
+ should "return nil if not found" do
277
+ @person_class.first(:name => 'Bill').should be_nil
278
+ end
279
+ end
280
+
281
+ context "for object in map" do
282
+ setup do
283
+ @person = @person_class.create(:name => 'Fred')
284
+ end
285
+
286
+ should "not query database" do
287
+ expect_no_queries
288
+ @person_class.first(:_id => @person.id)
289
+ end
290
+
291
+ should "return exact object" do
292
+ assert_in_map(@person)
293
+ found_person = @person_class.first(:_id => @person.id)
294
+ found_person.should equal(@person)
295
+ end
296
+ end
297
+ end
298
+
299
+ context "#all" do
300
+ should "add all documents to map" do
301
+ person1 = @person_class.create(:name => 'Fred')
302
+ person2 = @person_class.create(:name => 'Bill')
303
+ person3 = @person_class.create(:name => 'Jesse')
304
+ @person_class.identity_map.clear
305
+
306
+ people = @person_class.all(:_id => [person1.id, person2.id, person3.id])
307
+ assert_in_map(people)
308
+ end
309
+
310
+ should "add missing documents to map and return existing ones" do
311
+ person1 = @person_class.create(:name => 'Fred')
312
+ @person_class.identity_map.clear
313
+ person2 = @person_class.create(:name => 'Bill')
314
+ person3 = @person_class.create(:name => 'Jesse')
315
+
316
+ assert_not_in_map(person1)
317
+ assert_in_map(person2, person3)
318
+
319
+ people = @person_class.all(:_id => [person1.id, person2.id, person3.id])
320
+ # people.first is making sure one that wasn't mapped now is
321
+ assert_in_map(people.first, person2, person3)
322
+ end
323
+ end
324
+
325
+ context "#find_by_id" do
326
+ setup do
327
+ @person = @person_class.create(:name => 'Bill')
328
+ end
329
+
330
+ should "return nil for document id not found in collection" do
331
+ assert_in_map(@person)
332
+ @person_class.find_by_id(1234).should be_nil
333
+ end
334
+ end
335
+
336
+ context "querying and selecting certain fields" do
337
+ setup do
338
+ @person = @person_class.create(:name => 'Bill')
339
+ @person_class.identity_map.clear
340
+ end
341
+
342
+ should "not add to map" do
343
+ assert_not_in_map(@person)
344
+ @person_class.first(:_id => @person.id, :select => 'name').should == @person
345
+ @person_class.first(:_id => @person.id, 'fields' => ['name']).should == @person
346
+ @person_class.last(:_id => @person.id, :select => 'name', :order => 'name').should == @person
347
+ @person_class.find(@person.id, :select => 'name').should == @person
348
+ @person_class.all(:_id => @person.id, :select => 'name').should == [@person]
349
+ assert_not_in_map(@person)
350
+ end
351
+
352
+ should "return nil if not found" do
353
+ @person_class.find(1234, :select => 'name').should be_nil
354
+ end
355
+ end
356
+
357
+ context "single collection inherited models" do
358
+ setup do
359
+ class ::Item
360
+ include MongoMapper::Document
361
+ plugin MongoMapper::Plugins::IdentityMap
362
+
363
+ key :_type, String
364
+ key :title, String
365
+ key :parent_id, ObjectId
366
+
367
+ belongs_to :parent, :class_name => 'Item'
368
+ one :blog, :class_name => 'Blog', :foreign_key => 'parent_id'
369
+ end
370
+ Item.collection.remove
371
+
372
+ class ::Blog < ::Item; end
373
+
374
+ class ::BlogPost < ::Item
375
+ key :blog_id, ObjectId
376
+ belongs_to :blog
377
+ end
378
+ end
379
+
380
+ teardown do
381
+ Object.send :remove_const, 'Item' if defined?(::Item)
382
+ Object.send :remove_const, 'Blog' if defined?(::Blog)
383
+ Object.send :remove_const, 'BlogPost' if defined?(::BlogPost)
384
+ end
385
+
386
+ should "share the same identity map" do
387
+ blog = Blog.create(:title => 'Jill')
388
+ assert_in_map(blog)
389
+ Item.identity_map.should equal(Blog.identity_map)
390
+ end
391
+
392
+ should "not query when finding by _id and _type" do
393
+ blog = Blog.create(:title => 'Blog')
394
+ post = BlogPost.create(:title => 'Mongo Rocks', :blog => blog)
395
+ Item.identity_map.clear
396
+
397
+ blog = Item.find(blog.id)
398
+ post = Item.find(post.id)
399
+ assert_in_map(blog, post)
400
+
401
+ expect_no_queries
402
+ post.blog
403
+ Blog.find(blog.id)
404
+ end
405
+
406
+ should "load from map when using parent collection inherited class" do
407
+ blog = Blog.create(:title => 'Jill')
408
+ Item.find(blog.id).should equal(blog)
409
+ end
410
+
411
+ should "work correctly with belongs to proxy" do
412
+ root = Item.create(:title => 'Root')
413
+ assert_in_map(root)
414
+
415
+ blog = Blog.create(:title => 'Jill', :parent => root)
416
+ assert_in_map(blog)
417
+ root.should equal(blog.parent)
418
+ end
419
+
420
+ should "work correctly with one proxy" do
421
+ blog = Blog.create(:title => 'Jill')
422
+ assert_in_map(blog)
423
+
424
+ root = Item.create(:title => 'Root', :blog => blog)
425
+ assert_in_map(root)
426
+ root.blog.should equal(blog)
427
+ end
428
+
429
+ should "work correctly with one proxy create" do
430
+ root = Item.create(:title => 'Root')
431
+ blog = root.blog.create(:title => 'Blog')
432
+ blog.parent.should equal(root)
433
+ end
434
+ end
435
+
436
+ context "without identity map" do
437
+ should "not add to map on save" do
438
+ @post_class.without_identity_map do
439
+ post = @post_class.create(:title => 'Bill')
440
+ assert_not_in_map(post)
441
+ end
442
+ end
443
+
444
+ should "not remove from map on delete" do
445
+ post = @post_class.create(:title => 'Bill')
446
+ assert_in_map(post)
447
+
448
+ @post_class.without_identity_map do
449
+ post.destroy
450
+ end
451
+
452
+ assert_in_map(post)
453
+ end
454
+
455
+ should "not add to map when loading" do
456
+ @post_class.without_identity_map do
457
+ post = @post_class.load({'_id' => Mongo::ObjectID.new, 'title' => 'Awesome!'})
458
+ assert_not_in_map(post)
459
+ end
460
+ end
461
+
462
+ should "not load from map when loading" do
463
+ post = @post_class.create(:title => 'Awesome!')
464
+
465
+ @post_class.without_identity_map do
466
+ loaded = @post_class.load('_id' => post._id, 'title' => 'Awesome!')
467
+ loaded.should_not equal(post)
468
+ end
469
+ end
470
+
471
+ context "all" do
472
+ should "not add to map" do
473
+ @post_class.without_identity_map do
474
+ post1 = @post_class.create(:title => 'Foo')
475
+ post2 = @post_class.create(:title => 'Bar')
476
+ @post_class.identity_map.clear
477
+
478
+ assert_not_in_map(@post_class.all)
479
+ end
480
+ end
481
+ end
482
+
483
+ context "first" do
484
+ should "not add to map" do
485
+ @post_class.without_identity_map do
486
+ post1 = @post_class.create(:title => 'Foo')
487
+ post2 = @post_class.create(:title => 'Bar')
488
+ @post_class.identity_map.clear
489
+
490
+ assert_not_in_map(@post_class.first)
491
+ end
492
+ end
493
+ end
494
+
495
+ context "last" do
496
+ should "not add to map" do
497
+ @post_class.without_identity_map do
498
+ post1 = @post_class.create(:title => 'Foo')
499
+ post2 = @post_class.create(:title => 'Bar')
500
+ @post_class.identity_map.clear
501
+
502
+ assert_not_in_map(@post_class.last(:order => 'title'))
503
+ end
504
+ end
505
+ end
506
+ end
507
+ end
508
+ end