djsun-mongo_mapper 0.5.6.6 → 0.5.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.gitignore +3 -1
  2. data/Rakefile +13 -8
  3. data/VERSION +1 -1
  4. data/djsun-mongo_mapper.gemspec +17 -21
  5. data/lib/mongo_mapper/associations/base.rb +32 -36
  6. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +0 -2
  7. data/lib/mongo_mapper/associations/many_documents_proxy.rb +19 -10
  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 +3 -2
  12. data/lib/mongo_mapper/associations.rb +114 -8
  13. data/lib/mongo_mapper/callbacks.rb +18 -0
  14. data/lib/mongo_mapper/document.rb +173 -37
  15. data/lib/mongo_mapper/dynamic_finder.rb +1 -1
  16. data/lib/mongo_mapper/embedded_document.rb +9 -13
  17. data/lib/mongo_mapper/finder_options.rb +67 -44
  18. data/lib/mongo_mapper/pagination.rb +2 -0
  19. data/lib/mongo_mapper/serialization.rb +1 -1
  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 +12 -42
  23. data/lib/mongo_mapper.rb +11 -5
  24. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +5 -5
  25. data/test/functional/associations/test_belongs_to_proxy.rb +29 -31
  26. data/test/functional/associations/test_many_documents_as_proxy.rb +5 -5
  27. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +27 -3
  28. data/test/functional/associations/test_many_embedded_proxy.rb +58 -38
  29. data/test/functional/associations/test_many_polymorphic_proxy.rb +45 -3
  30. data/test/functional/associations/test_many_proxy.rb +61 -11
  31. data/test/functional/test_associations.rb +3 -3
  32. data/test/functional/test_binary.rb +1 -1
  33. data/test/functional/test_callbacks.rb +1 -1
  34. data/test/functional/test_dirty.rb +3 -3
  35. data/test/functional/test_document.rb +62 -58
  36. data/test/functional/test_embedded_document.rb +1 -1
  37. data/test/functional/test_pagination.rb +1 -1
  38. data/test/functional/test_rails_compatibility.rb +1 -1
  39. data/test/functional/test_validations.rb +46 -14
  40. data/test/models.rb +87 -35
  41. data/test/support/{test_timing.rb → timing.rb} +1 -1
  42. data/test/test_helper.rb +8 -13
  43. data/test/unit/serializers/test_json_serializer.rb +0 -4
  44. data/test/unit/test_association_base.rb +24 -8
  45. data/test/unit/test_document.rb +40 -71
  46. data/test/unit/test_embedded_document.rb +27 -67
  47. data/test/unit/test_finder_options.rb +16 -0
  48. data/test/unit/test_key.rb +5 -17
  49. data/test/unit/test_mongomapper.rb +2 -2
  50. data/test/unit/test_pagination.rb +4 -0
  51. metadata +10 -12
  52. data/mongo_mapper.gemspec +0 -170
  53. data/test/functional/associations/test_namespace.rb +0 -27
@@ -4,67 +4,37 @@ module MongoMapper
4
4
  def validates_uniqueness_of(*args)
5
5
  add_validations(args, MongoMapper::Validations::ValidatesUniquenessOf)
6
6
  end
7
-
8
- def validates_exclusion_of(*args)
9
- add_validations(args, MongoMapper::Validations::ValidatesExclusionOf)
10
- end
11
-
12
- def validates_inclusion_of(*args)
13
- add_validations(args, MongoMapper::Validations::ValidatesInclusionOf)
14
- end
15
7
  end
16
8
 
17
9
  class ValidatesUniquenessOf < Validatable::ValidationBase
18
- option :scope
19
-
10
+ option :scope, :case_sensitive
11
+ default :case_sensitive => true
12
+
20
13
  def valid?(instance)
21
14
  value = instance[attribute]
22
15
  return true if allow_blank && value.blank?
23
- doc = instance.class.first({self.attribute => value}.merge(scope_conditions(instance)))
16
+ base_conditions = case_sensitive ? {self.attribute => value} : {}
17
+ doc = instance.class.first(base_conditions.merge(scope_conditions(instance)).merge(where_conditions(instance)))
24
18
  doc.nil? || instance.id == doc.id
25
19
  end
26
20
 
27
21
  def message(instance)
28
22
  super || "has already been taken"
29
23
  end
30
-
24
+
31
25
  def scope_conditions(instance)
32
26
  return {} unless scope
33
27
  Array(scope).inject({}) do |conditions, key|
34
28
  conditions.merge(key => instance[key])
35
29
  end
36
30
  end
37
- end
38
-
39
- class ValidatesExclusionOf < Validatable::ValidationBase
40
- required_option :within
41
-
42
- def valid?(instance)
43
- value = instance[attribute]
44
- return true if allow_nil && value.nil?
45
- return true if allow_blank && value.blank?
46
-
47
- !within.include?(instance[attribute])
48
- end
49
-
50
- def message(instance)
51
- super || "is reserved"
52
- end
53
- end
54
31
 
55
- class ValidatesInclusionOf < Validatable::ValidationBase
56
- required_option :within
57
-
58
- def valid?(instance)
59
- value = instance[attribute]
60
- return true if allow_nil && value.nil?
61
- return true if allow_blank && value.blank?
62
-
63
- within.include?(value)
64
- end
65
-
66
- def message(instance)
67
- super || "is not in the list"
32
+ def where_conditions(instance)
33
+ conditions = {}
34
+ unless case_sensitive
35
+ conditions.merge!({'$where' => "this.#{attribute}.toLowerCase() == '#{instance[attribute].downcase}'"})
36
+ end
37
+ conditions
68
38
  end
69
39
  end
70
40
  end
data/lib/mongo_mapper.rb CHANGED
@@ -1,13 +1,19 @@
1
- require 'rubygems'
2
- gem 'activesupport', '>= 2.3'
3
1
  require 'active_support'
4
2
  require 'mongo'
5
3
  require 'validatable'
6
4
 
7
5
  module MongoMapper
8
- class KeyNotFound < RuntimeError; end
9
- DocumentNotFound = Class.new(StandardError)
10
- DocumentNotValid = Class.new(StandardError) do
6
+ # generic MM error
7
+ class MongoMapperError < StandardError; end
8
+
9
+ # raised when key expected to exist but not found
10
+ class KeyNotFound < MongoMapperError; end
11
+
12
+ # raised when document expected but not found
13
+ class DocumentNotFound < MongoMapperError; end
14
+
15
+ # raised when document not valid and using !
16
+ class DocumentNotValid < MongoMapperError
11
17
  def initialize(document)
12
18
  @document = document
13
19
  super("Validation failed: #{@document.errors.full_messages.join(", ")}")
@@ -3,8 +3,8 @@ require 'models'
3
3
 
4
4
  class BelongsToPolymorphicProxyTest < Test::Unit::TestCase
5
5
  def setup
6
- Status.collection.clear
7
- Project.collection.clear
6
+ Status.collection.remove
7
+ Project.collection.remove
8
8
  end
9
9
 
10
10
  should "default to nil" do
@@ -14,7 +14,7 @@ class BelongsToPolymorphicProxyTest < Test::Unit::TestCase
14
14
  end
15
15
 
16
16
  should "be able to replace the association" do
17
- status = Status.new
17
+ status = Status.new(:name => 'Foo!')
18
18
  project = Project.new(:name => "mongomapper")
19
19
  status.target = project
20
20
  status.save.should be_true
@@ -27,7 +27,7 @@ class BelongsToPolymorphicProxyTest < Test::Unit::TestCase
27
27
  end
28
28
 
29
29
  should "unset the association" do
30
- status = Status.new
30
+ status = Status.new(:name => 'Foo!')
31
31
  project = Project.new(:name => "mongomapper")
32
32
  status.target = project
33
33
  status.save.should be_true
@@ -41,7 +41,7 @@ class BelongsToPolymorphicProxyTest < Test::Unit::TestCase
41
41
 
42
42
  context "association id set but document not found" do
43
43
  setup do
44
- @status = Status.new
44
+ @status = Status.new(:name => 'Foo!')
45
45
  project = Project.new(:name => "mongomapper")
46
46
  @status.target = project
47
47
  @status.save.should be_true
@@ -2,48 +2,46 @@ require 'test_helper'
2
2
  require 'models'
3
3
 
4
4
  class BelongsToProxyTest < Test::Unit::TestCase
5
- def setup
6
- Status.collection.clear
7
- Project.collection.clear
5
+ def setup
6
+ @post_class = Class.new do
7
+ include MongoMapper::Document
8
+ end
9
+
10
+ @comment_class = Class.new do
11
+ include MongoMapper::Document
12
+ key :post_id, String
13
+ end
14
+ @comment_class.belongs_to :post, :class => @post_class
15
+
16
+ @post_class.collection.remove
17
+ @comment_class.collection.remove
8
18
  end
9
19
 
10
20
  should "default to nil" do
11
- status = Status.new
12
- status.project.nil?.should == true
13
- status.project.inspect.should == 'nil'
21
+ @comment_class.new.post.nil?.should be_true
14
22
  end
15
23
 
16
24
  should "be able to replace the association" do
17
- status = Status.new
18
- project = Project.new(:name => "mongomapper")
19
- status.project = project
20
- status.save.should be_true
25
+ post = @post_class.new(:name => 'mongomapper')
26
+ comment = @comment_class.new(:name => 'Foo!', :post => post)
27
+ comment.save.should be_true
21
28
 
22
- from_db = Status.find(status.id)
23
- from_db.project.nil?.should be_false
24
- from_db.project.name.should == "mongomapper"
29
+ comment = comment.reload
30
+ comment.post.should == post
31
+ comment.post.nil?.should be_false
25
32
  end
26
33
 
27
34
  should "unset the association" do
28
- status = Status.new
29
- project = Project.new(:name => "mongomapper")
30
- status.project = project
31
- status.save.should be_true
35
+ post = @post_class.new(:name => 'mongomapper')
36
+ comment = @comment_class.new(:name => 'Foo!', :post => post)
37
+ comment.save.should be_true
32
38
 
33
- from_db = Status.find(status.id)
34
- from_db.project = nil
35
- from_db.project.nil?.should be_true
36
- from_db.project.inspect.should == 'nil'
39
+ comment = comment.reload
40
+ comment.post = nil
41
+ comment.post.nil?.should be_true
37
42
  end
38
43
 
39
- context "association id set but document not found" do
40
- setup do
41
- @status = Status.new(:name => 'Foo', :project_id => '1234')
42
- end
43
-
44
- should "return nil instead of raising error" do
45
- @status.project.nil?.should be_true
46
- @status.project.inspect.should == 'nil'
47
- end
44
+ should "return nil if id set but document not found" do
45
+ @comment_class.new(:name => 'Foo', :post_id => '1234').post.nil?.should be_true
48
46
  end
49
- end
47
+ end
@@ -3,8 +3,8 @@ require 'models'
3
3
 
4
4
  class ManyDocumentsAsProxyTest < Test::Unit::TestCase
5
5
  def setup
6
- Post.collection.clear
7
- PostComment.collection.clear
6
+ Post.collection.remove
7
+ PostComment.collection.remove
8
8
  end
9
9
 
10
10
  should "default reader to empty array" do
@@ -171,20 +171,20 @@ class ManyDocumentsAsProxyTest < Test::Unit::TestCase
171
171
 
172
172
  should "not work for id not in association" do
173
173
  lambda {
174
- @post.comments.find(@comment5.id)
174
+ @post.comments.find!(@comment5.id)
175
175
  }.should raise_error(MongoMapper::DocumentNotFound)
176
176
  end
177
177
  end
178
178
 
179
179
  context "with multiple ids" do
180
180
  should "work for ids in association" do
181
- posts = @post.comments.find(@comment1.id, @comment2.id)
181
+ posts = @post.comments.find!(@comment1.id, @comment2.id)
182
182
  posts.should == [@comment1, @comment2]
183
183
  end
184
184
 
185
185
  should "not work for ids not in association" do
186
186
  lambda {
187
- @post.comments.find(@comment1.id, @comment2.id, @comment4.id)
187
+ @post.comments.find!(@comment1.id, @comment2.id, @comment4.id)
188
188
  }.should raise_error(MongoMapper::DocumentNotFound)
189
189
  end
190
190
  end
@@ -3,8 +3,8 @@ require 'models'
3
3
 
4
4
  class ManyEmbeddedPolymorphicProxyTest < Test::Unit::TestCase
5
5
  def setup
6
- Catalog.collection.clear
7
- TrModels::Fleet.collection.clear
6
+ Catalog.collection.remove
7
+ TrModels::Fleet.collection.remove
8
8
  end
9
9
 
10
10
  should "default reader to empty array" do
@@ -129,4 +129,28 @@ class ManyEmbeddedPolymorphicProxyTest < Test::Unit::TestCase
129
129
  from_db.transports[2].icu.should == true
130
130
  end
131
131
  end
132
- end
132
+
133
+ context "extending the association" do
134
+ should "work using a block passed to many" do
135
+ catalog = Catalog.new
136
+ medias = catalog.medias = [
137
+ Video.new("file" => "video.mpg", "length" => 3600, :visible => true),
138
+ Music.new("file" => "music.mp3", "bitrate" => "128kbps", :visible => true),
139
+ Image.new("file" => "image.png", "width" => 800, "height" => 600, :visible => false)
140
+ ]
141
+ catalog.save
142
+ catalog.medias.visible.should == [medias[0], medias[1]]
143
+ end
144
+
145
+ should "work using many's :extend option" do
146
+ fleet = TrModels::Fleet.new
147
+ transports = fleet.transports = [
148
+ TrModels::Car.new("license_plate" => "ABC1223", "model" => "Honda Civic", "year" => 2003, :purchased_on => 2.years.ago.to_date),
149
+ TrModels::Bus.new("license_plate" => "XYZ9090", "max_passengers" => 51, :purchased_on => 3.years.ago.to_date),
150
+ TrModels::Ambulance.new("license_plate" => "HDD3030", "icu" => true, :purchased_on => 1.year.ago.to_date)
151
+ ]
152
+ fleet.save
153
+ fleet.transports.to_be_replaced.should == [transports[1]]
154
+ end
155
+ end
156
+ end
@@ -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
@@ -258,11 +259,25 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
258
259
 
259
260
  should "not work for id not in association" do
260
261
  lambda {
261
- @lounge.messages.find(@hm2.id)
262
+ @lounge.messages.find!(@hm2.id)
262
263
  }.should raise_error(MongoMapper::DocumentNotFound)
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)
@@ -271,7 +286,7 @@ class ManyPolymorphicProxyTest < Test::Unit::TestCase
271
286
 
272
287
  should "not work for ids not in association" do
273
288
  lambda {
274
- @lounge.messages.find(@lm1.id, @lm2.id, @hm2.id)
289
+ @lounge.messages.find!(@lm1.id, @lm2.id, @hm2.id)
275
290
  }.should raise_error(MongoMapper::DocumentNotFound)
276
291
  end
277
292
  end
@@ -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
@@ -292,7 +321,7 @@ class ManyProxyTest < Test::Unit::TestCase
292
321
 
293
322
  should "not work for id not in association" do
294
323
  lambda {
295
- @project1.statuses.find(@archived.id)
324
+ @project1.statuses.find!(@archived.id)
296
325
  }.should raise_error(MongoMapper::DocumentNotFound)
297
326
  end
298
327
  end
@@ -305,7 +334,7 @@ class ManyProxyTest < Test::Unit::TestCase
305
334
 
306
335
  should "not work for ids not in association" do
307
336
  lambda {
308
- @project1.statuses.find(@brand_new.id, @complete.id, @archived.id)
337
+ @project1.statuses.find!(@brand_new.id, @complete.id, @archived.id)
309
338
  }.should raise_error(MongoMapper::DocumentNotFound)
310
339
  end
311
340
  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