mongo_mapper-unstable 2009.10.16 → 2009.10.31

Sign up to get free protection for your applications and to get access to all the features.
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