mongo_mapper-unstable 2009.10.16 → 2009.10.31

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 (51) hide show
  1. data/.gitignore +3 -1
  2. data/README.rdoc +3 -0
  3. data/Rakefile +31 -65
  4. data/VERSION +1 -1
  5. data/lib/mongo_mapper/associations/base.rb +31 -4
  6. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +0 -2
  7. data/lib/mongo_mapper/associations/many_documents_proxy.rb +21 -15
  8. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +2 -2
  9. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +21 -36
  10. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +1 -1
  11. data/lib/mongo_mapper/associations/proxy.rb +1 -0
  12. data/lib/mongo_mapper/associations.rb +114 -17
  13. data/lib/mongo_mapper/callbacks.rb +18 -0
  14. data/lib/mongo_mapper/document.rb +230 -95
  15. data/lib/mongo_mapper/dynamic_finder.rb +1 -1
  16. data/lib/mongo_mapper/embedded_document.rb +7 -3
  17. data/lib/mongo_mapper/finder_options.rb +88 -56
  18. data/lib/mongo_mapper/pagination.rb +2 -0
  19. data/lib/mongo_mapper/serialization.rb +2 -3
  20. data/lib/mongo_mapper/serializers/json_serializer.rb +1 -1
  21. data/lib/mongo_mapper/support.rb +9 -0
  22. data/lib/mongo_mapper/validations.rb +14 -42
  23. data/lib/mongo_mapper.rb +15 -13
  24. data/mongo_mapper.gemspec +13 -13
  25. data/specs.watchr +2 -2
  26. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +5 -5
  27. data/test/functional/associations/test_belongs_to_proxy.rb +28 -30
  28. data/test/functional/associations/test_many_documents_as_proxy.rb +4 -4
  29. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +27 -3
  30. data/test/functional/associations/test_many_embedded_proxy.rb +58 -38
  31. data/test/functional/associations/test_many_polymorphic_proxy.rb +49 -7
  32. data/test/functional/associations/test_many_proxy.rb +65 -15
  33. data/test/functional/test_associations.rb +3 -3
  34. data/test/functional/test_binary.rb +1 -1
  35. data/test/functional/test_callbacks.rb +1 -1
  36. data/test/functional/test_dirty.rb +3 -3
  37. data/test/functional/test_document.rb +178 -57
  38. data/test/functional/test_embedded_document.rb +1 -1
  39. data/test/functional/test_pagination.rb +18 -18
  40. data/test/functional/test_rails_compatibility.rb +1 -1
  41. data/test/functional/test_validations.rb +80 -27
  42. data/test/models.rb +93 -17
  43. data/test/support/{test_timing.rb → timing.rb} +1 -1
  44. data/test/test_helper.rb +8 -11
  45. data/test/unit/test_association_base.rb +23 -1
  46. data/test/unit/test_document.rb +29 -12
  47. data/test/unit/test_embedded_document.rb +13 -4
  48. data/test/unit/test_finder_options.rb +74 -58
  49. data/test/unit/test_mongomapper.rb +2 -2
  50. data/test/unit/test_pagination.rb +4 -0
  51. metadata +7 -7
@@ -3,8 +3,8 @@ require 'models'
3
3
 
4
4
  class ManyEmbeddedProxyTest < Test::Unit::TestCase
5
5
  def setup
6
- Project.collection.clear
7
- RealPerson.collection.clear
6
+ Project.collection.remove
7
+ RealPerson.collection.remove
8
8
  end
9
9
 
10
10
  should "default reader to empty array" do
@@ -17,13 +17,6 @@ class ManyEmbeddedProxyTest < Test::Unit::TestCase
17
17
  project.addresses.push Address.new
18
18
  project.addresses.size.should == 2
19
19
  end
20
-
21
- should "allow finding :all embedded documents" do
22
- project = Project.new
23
- project.addresses << Address.new
24
- project.addresses << Address.new
25
- project.save
26
- end
27
20
 
28
21
  should "be embedded in document on save" do
29
22
  sb = Address.new(:city => 'South Bend', :state => 'IN')
@@ -45,7 +38,7 @@ class ManyEmbeddedProxyTest < Test::Unit::TestCase
45
38
  set_collection_name 'test'
46
39
  key :person, Person
47
40
  end
48
- @document.collection.clear
41
+ @document.collection.remove
49
42
 
50
43
  meg = Person.new(:name => "Meg")
51
44
  meg.child = Person.new(:name => "Steve")
@@ -92,7 +85,7 @@ class ManyEmbeddedProxyTest < Test::Unit::TestCase
92
85
  set_collection_name 'test'
93
86
  many :people
94
87
  end
95
- @document.collection.clear
88
+ @document.collection.remove
96
89
  end
97
90
 
98
91
  should "persist all embedded documents" do
@@ -130,45 +123,72 @@ class ManyEmbeddedProxyTest < Test::Unit::TestCase
130
123
  doc.people.first.pets.first._root_document.should == doc
131
124
  end
132
125
 
133
- should "create properly-named reference to parent document when building off association proxy" do
134
- person = RealPerson.new
135
- pet = person.pets.build
136
- person.should == pet.real_person
137
- end
138
-
139
-
140
126
  should "create a reference to the root document for all embedded documents" do
141
- meg = Person.new(:name => "Meg")
142
127
  sparky = Pet.new(:name => "Sparky", :species => "Dog")
143
-
128
+ meg = Person.new(:name => "Meg", :pets => [sparky])
144
129
  doc = @document.new
145
-
146
- meg.pets << sparky
147
-
148
130
  doc.people << meg
149
131
  doc.save
150
132
 
151
- from_db = @document.find(doc.id)
152
- from_db.people.first._root_document.should == doc
153
- from_db.people.first.pets.first._root_document.should == doc
133
+ doc = doc.reload
134
+ doc.people.first._root_document.should == doc
135
+ doc.people.first.pets.first._root_document.should == doc
154
136
  end
155
137
  end
156
138
 
157
- should "allow retrieval via find(:all)" do
158
- meg = Person.new(:name => "Meg")
139
+ should "allow finding by id" do
159
140
  sparky = Pet.new(:name => "Sparky", :species => "Dog")
160
-
161
- meg.pets << sparky
162
-
163
- meg.pets.find(:all).should include(sparky)
141
+ meg = Person.new(:name => "Meg", :pets => [sparky])
142
+ meg.pets.find(sparky.id).should == sparky
164
143
  end
165
144
 
166
- should "allow retrieval via find(id)" do
167
- meg = Person.new(:name => "Meg")
168
- sparky = Pet.new(:name => "Sparky", :species => "Dog")
169
-
170
- meg.pets << sparky
145
+ context "extending the association" do
146
+ setup do
147
+ @address_class = Class.new do
148
+ include MongoMapper::EmbeddedDocument
149
+ key :address, String
150
+ key :city, String
151
+ key :state, String
152
+ key :zip, Integer
153
+ end
154
+
155
+ @project_class = Class.new do
156
+ include MongoMapper::Document
157
+ key :name, String
158
+ end
159
+
160
+ @project_class.collection.remove
161
+ end
171
162
 
172
- meg.pets.find(sparky.id).should == sparky
163
+ should "work using a block passed to many" do
164
+ @project_class.many :addresses, :class => @address_class do
165
+ def find_all_by_state(state)
166
+ find_all { |a| a.state == state }
167
+ end
168
+ end
169
+
170
+ addr1 = @address_class.new(:address => "Gate-3 Lankershim Blvd.", :city => "Universal City", :state => "CA", :zip => "91608")
171
+ addr2 = @address_class.new(:address => "3000 W. Alameda Ave.", :city => "Burbank", :state => "CA", :zip => "91523")
172
+ addr3 = @address_class.new(:address => "111 Some Ln", :city => "Nashville", :state => "TN", :zip => "37211")
173
+ project = @project_class.create(:name => "Some Project", :addresses => [addr1, addr2, addr3])
174
+
175
+ project.addresses.find_all_by_state("CA").should == [addr1, addr2]
176
+ end
177
+
178
+ should "work using many's :extend option" do
179
+ module FindByCity
180
+ def find_by_city(city)
181
+ find_all { |a| a.city == city }
182
+ end
183
+ end
184
+ @project_class.many :addresses, :class => @address_class, :extend => FindByCity
185
+
186
+ addr1 = @address_class.new(:address => "Gate-3 Lankershim Blvd.", :city => "Universal City", :state => "CA", :zip => "91608")
187
+ addr2 = @address_class.new(:address => "3000 W. Alameda Ave.", :city => "Burbank", :state => "CA", :zip => "91523")
188
+ addr3 = @address_class.new(:address => "111 Some Ln", :city => "Nashville", :state => "TN", :zip => "37211")
189
+ project = @project_class.create(:name => "Some Project", :addresses => [addr1, addr2, addr3])
190
+
191
+ project.addresses.find_by_city('Burbank').should == [addr2]
192
+ end
173
193
  end
174
194
  end
@@ -3,7 +3,8 @@ require 'models'
3
3
 
4
4
  class ManyPolymorphicProxyTest < Test::Unit::TestCase
5
5
  def setup
6
- Room.collection.clear
6
+ Room.collection.remove
7
+ Message.collection.remove
7
8
  end
8
9
 
9
10
  should "default reader to empty array" do
@@ -181,7 +182,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
181
182
  end
182
183
 
183
184
  should "work with conditions" do
184
- messages = @lounge.messages.find(:all, :conditions => {:body => 'Loungin!'}, :order => "position")
185
+ messages = @lounge.messages.find(:all, :body => 'Loungin!', :order => "position")
185
186
  messages.should == [@lm1]
186
187
  end
187
188
 
@@ -197,7 +198,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
197
198
  end
198
199
 
199
200
  should "work with conditions" do
200
- messages = @lounge.messages.all(:conditions => {:body => 'Loungin!'}, :order => "position")
201
+ messages = @lounge.messages.all(:body => 'Loungin!', :order => "position")
201
202
  messages.should == [@lm1]
202
203
  end
203
204
 
@@ -213,7 +214,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
213
214
  end
214
215
 
215
216
  should "work with conditions" do
216
- message = @lounge.messages.find(:first, :conditions => {:body => 'I love loungin!'}, :order => "position asc")
217
+ message = @lounge.messages.find(:first, :body => 'I love loungin!', :order => "position asc")
217
218
  message.should == @lm2
218
219
  end
219
220
  end
@@ -224,7 +225,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
224
225
  end
225
226
 
226
227
  should "work with conditions" do
227
- message = @lounge.messages.first(:conditions => {:body => 'I love loungin!'}, :order => "position asc")
228
+ message = @lounge.messages.first(:body => 'I love loungin!', :order => "position asc")
228
229
  message.should == @lm2
229
230
  end
230
231
  end
@@ -235,7 +236,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
235
236
  end
236
237
 
237
238
  should "work with conditions" do
238
- message = @lounge.messages.find(:last, :conditions => {:body => 'Loungin!'}, :order => "position asc")
239
+ message = @lounge.messages.find(:last, :body => 'Loungin!', :order => "position asc")
239
240
  message.should == @lm1
240
241
  end
241
242
  end
@@ -246,7 +247,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
246
247
  end
247
248
 
248
249
  should "work with conditions" do
249
- message = @lounge.messages.last(:conditions => {:body => 'Loungin!'}, :order => "position asc")
250
+ message = @lounge.messages.last(:body => 'Loungin!', :order => "position asc")
250
251
  message.should == @lm1
251
252
  end
252
253
  end
@@ -263,6 +264,20 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
263
264
  end
264
265
  end
265
266
 
267
+ context "with query options/criteria" do
268
+ should "work with order on association" do
269
+ @lounge.messages.should == [@lm1, @lm2]
270
+ end
271
+
272
+ should "allow overriding the order provided to the association" do
273
+ @lounge.messages.all(:order => 'position desc').should == [@lm2, @lm1]
274
+ end
275
+
276
+ should "allow using conditions on association" do
277
+ @hall.latest_messages.should == [@hm3, @hm2]
278
+ end
279
+ end
280
+
266
281
  context "with multiple ids" do
267
282
  should "work for ids in association" do
268
283
  messages = @lounge.messages.find(@lm1.id, @lm2.id)
@@ -294,4 +309,31 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
294
309
  end
295
310
  end
296
311
  end
312
+
313
+ context "extending the association" do
314
+ should "work using a block passed to many" do
315
+ room = Room.new(:name => "Amazing Room")
316
+ messages = room.messages = [
317
+ Enter.new(:body => 'John entered room', :position => 3),
318
+ Chat.new(:body => 'Heyyyoooo!', :position => 4),
319
+ Exit.new(:body => 'John exited room', :position => 5),
320
+ Enter.new(:body => 'Steve entered room', :position => 6),
321
+ Chat.new(:body => 'Anyone there?', :position => 7),
322
+ Exit.new(:body => 'Steve exited room', :position => 8)
323
+ ]
324
+ room.save
325
+ room.messages.older.should == messages[3..5]
326
+ end
327
+
328
+ should "work using many's :extend option" do
329
+ room = Room.new(:name => "Amazing Room")
330
+ accounts = room.accounts = [
331
+ Bot.new(:last_logged_in => 3.weeks.ago),
332
+ User.new(:last_logged_in => nil),
333
+ Bot.new(:last_logged_in => 1.week.ago)
334
+ ]
335
+ room.save
336
+ room.accounts.inactive.should == [accounts[1]]
337
+ end
338
+ end
297
339
  end
@@ -3,8 +3,8 @@ require 'models'
3
3
 
4
4
  class ManyProxyTest < Test::Unit::TestCase
5
5
  def setup
6
- Project.collection.clear
7
- Status.collection.clear
6
+ Project.collection.remove
7
+ Status.collection.remove
8
8
  end
9
9
 
10
10
  should "default reader to empty array" do
@@ -14,9 +14,9 @@ class ManyProxyTest < Test::Unit::TestCase
14
14
 
15
15
  should "allow adding to association like it was an array" do
16
16
  project = Project.new
17
- project.statuses << Status.new
18
- project.statuses.push Status.new
19
- project.statuses.concat Status.new
17
+ project.statuses << Status.new(:name => 'Foo1!')
18
+ project.statuses.push Status.new(:name => 'Foo2!')
19
+ project.statuses.concat Status.new(:name => 'Foo3!')
20
20
  project.statuses.size.should == 3
21
21
  end
22
22
 
@@ -59,14 +59,14 @@ class ManyProxyTest < Test::Unit::TestCase
59
59
  context "create" do
60
60
  should "assign foreign key" do
61
61
  project = Project.create
62
- status = project.statuses.create
62
+ status = project.statuses.create(:name => 'Foo!')
63
63
  status.project_id.should == project.id
64
64
  end
65
65
 
66
66
  should "save record" do
67
67
  project = Project.create
68
68
  lambda {
69
- project.statuses.create
69
+ project.statuses.create(:name => 'Foo!')
70
70
  }.should change { Status.count }
71
71
  end
72
72
 
@@ -77,13 +77,42 @@ class ManyProxyTest < Test::Unit::TestCase
77
77
  end
78
78
  end
79
79
 
80
+ context "create!" do
81
+ should "assign foreign key" do
82
+ project = Project.create
83
+ status = project.statuses.create!(:name => 'Foo!')
84
+ status.project_id.should == project.id
85
+ end
86
+
87
+ should "save record" do
88
+ project = Project.create
89
+ lambda {
90
+ project.statuses.create!(:name => 'Foo!')
91
+ }.should change { Status.count }
92
+ end
93
+
94
+ should "allow passing attributes" do
95
+ project = Project.create
96
+ status = project.statuses.create!(:name => 'Foo!')
97
+ status.name.should == 'Foo!'
98
+ end
99
+
100
+ should "raise exception if not valid" do
101
+ project = Project.create
102
+ lambda {
103
+ project.statuses.create!(:name => nil)
104
+ }.should raise_error(MongoMapper::DocumentNotValid)
105
+ end
106
+ end
107
+
108
+
80
109
  context "count" do
81
110
  should "work scoped to association" do
82
111
  project = Project.create
83
- 3.times { project.statuses.create }
112
+ 3.times { project.statuses.create(:name => 'Foo!') }
84
113
 
85
114
  other_project = Project.create
86
- 2.times { other_project.statuses.create }
115
+ 2.times { other_project.statuses.create(:name => 'Foo!') }
87
116
 
88
117
  project.statuses.count.should == 3
89
118
  other_project.statuses.count.should == 2
@@ -215,7 +244,7 @@ class ManyProxyTest < Test::Unit::TestCase
215
244
  end
216
245
 
217
246
  should "work with conditions" do
218
- statuses = @project1.statuses.find(:all, :conditions => {'name' => 'Complete'})
247
+ statuses = @project1.statuses.find(:all, :name => 'Complete')
219
248
  statuses.should == [@complete]
220
249
  end
221
250
 
@@ -231,7 +260,7 @@ class ManyProxyTest < Test::Unit::TestCase
231
260
  end
232
261
 
233
262
  should "work with conditions" do
234
- statuses = @project1.statuses.all(:conditions => {'name' => 'Complete'})
263
+ statuses = @project1.statuses.all(:name => 'Complete')
235
264
  statuses.should == [@complete]
236
265
  end
237
266
 
@@ -247,7 +276,7 @@ class ManyProxyTest < Test::Unit::TestCase
247
276
  end
248
277
 
249
278
  should "work with conditions" do
250
- status = @project1.statuses.find(:first, :conditions => {:name => 'Complete'})
279
+ status = @project1.statuses.find(:first, :name => 'Complete')
251
280
  status.should == @complete
252
281
  end
253
282
  end
@@ -258,7 +287,7 @@ class ManyProxyTest < Test::Unit::TestCase
258
287
  end
259
288
 
260
289
  should "work with conditions" do
261
- status = @project1.statuses.first(:conditions => {:name => 'Complete'})
290
+ status = @project1.statuses.first(:name => 'Complete')
262
291
  status.should == @complete
263
292
  end
264
293
  end
@@ -269,7 +298,7 @@ class ManyProxyTest < Test::Unit::TestCase
269
298
  end
270
299
 
271
300
  should "work with conditions" do
272
- status = @project1.statuses.find(:last, :order => 'position', :conditions => {:name => 'New'})
301
+ status = @project1.statuses.find(:last, :order => 'position', :name => 'New')
273
302
  status.should == @brand_new
274
303
  end
275
304
  end
@@ -280,7 +309,7 @@ class ManyProxyTest < Test::Unit::TestCase
280
309
  end
281
310
 
282
311
  should "work with conditions" do
283
- status = @project1.statuses.last(:order => 'position', :conditions => {:name => 'New'})
312
+ status = @project1.statuses.last(:order => 'position', :name => 'New')
284
313
  status.should == @brand_new
285
314
  end
286
315
  end
@@ -328,4 +357,25 @@ class ManyProxyTest < Test::Unit::TestCase
328
357
  end
329
358
  end
330
359
  end
360
+
361
+ context "extending the association" do
362
+ should "work using a block passed to many" do
363
+ project = Project.new(:name => "Some Project")
364
+ status1 = Status.new(:name => "New")
365
+ status2 = Status.new(:name => "Assigned")
366
+ status3 = Status.new(:name => "Closed")
367
+ project.statuses = [status1, status2, status3]
368
+ project.save
369
+ project.statuses.open.should == [status1, status2]
370
+ end
371
+
372
+ should "work using many's :extend option" do
373
+ project = Project.new(:name => "Some Project")
374
+ collaborator1 = Collaborator.new(:name => "zing")
375
+ collaborator2 = Collaborator.new(:name => "zang")
376
+ project.collaborators = [collaborator1, collaborator2]
377
+ project.save
378
+ project.collaborators.top.should == collaborator1
379
+ end
380
+ end
331
381
  end
@@ -8,7 +8,7 @@ class AssociationsTest < Test::Unit::TestCase
8
8
 
9
9
  many :posts, :class_name => 'AssociationsTest::AwesomePost', :foreign_key => :creator_id
10
10
  end
11
- AwesomeUser.collection.clear
11
+ AwesomeUser.collection.remove
12
12
 
13
13
  class AwesomeTag
14
14
  include MongoMapper::EmbeddedDocument
@@ -28,8 +28,8 @@ class AssociationsTest < Test::Unit::TestCase
28
28
  many :tags, :class_name => 'AssociationsTest::AwesomeTag', :foreign_key => :post_id
29
29
  end
30
30
 
31
- AwesomeUser.collection.clear
32
- AwesomePost.collection.clear
31
+ AwesomeUser.collection.remove
32
+ AwesomePost.collection.remove
33
33
 
34
34
  user = AwesomeUser.create
35
35
  tag1 = AwesomeTag.new(:name => 'awesome')
@@ -7,7 +7,7 @@ class BinaryTest < Test::Unit::TestCase
7
7
  set_collection_name 'test'
8
8
  key :contents, Binary
9
9
  end
10
- klass.collection.clear
10
+ klass.collection.remove
11
11
 
12
12
  doc = klass.new(:contents => '010101')
13
13
  doc.save
@@ -30,7 +30,7 @@ class CallbacksTest < Test::Unit::TestCase
30
30
  @history = nil
31
31
  end
32
32
  end
33
- @document.collection.clear
33
+ @document.collection.remove
34
34
  end
35
35
 
36
36
  should "get the order right for creating documents" do
@@ -8,10 +8,10 @@ class DirtyTest < Test::Unit::TestCase
8
8
  set_collection_name 'test'
9
9
  key :phrase, String
10
10
  end
11
- @document.collection.clear
11
+ @document.collection.remove
12
12
 
13
- Status.collection.clear
14
- Project.collection.clear
13
+ Status.collection.remove
14
+ Project.collection.remove
15
15
  end
16
16
 
17
17
  context "marking changes" do