mongoid 1.9.5 → 2.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (213) hide show
  1. data/.gitignore +6 -0
  2. data/.watchr +29 -0
  3. data/Rakefile +52 -0
  4. data/VERSION +1 -0
  5. data/caliper.yml +4 -0
  6. data/lib/mongoid.rb +20 -9
  7. data/lib/mongoid/associations.rb +100 -123
  8. data/lib/mongoid/associations/belongs_to_related.rb +3 -2
  9. data/lib/mongoid/associations/{embeds_many.rb → embed_many.rb} +29 -90
  10. data/lib/mongoid/associations/{embeds_one.rb → embed_one.rb} +7 -8
  11. data/lib/mongoid/associations/embedded_in.rb +4 -12
  12. data/lib/mongoid/associations/has_many_related.rb +4 -52
  13. data/lib/mongoid/associations/has_one_related.rb +4 -8
  14. data/lib/mongoid/associations/meta_data.rb +1 -2
  15. data/lib/mongoid/associations/options.rb +1 -6
  16. data/lib/mongoid/associations/proxy.rb +21 -14
  17. data/lib/mongoid/attributes.rb +13 -33
  18. data/lib/mongoid/callbacks.rb +11 -16
  19. data/lib/mongoid/collection.rb +3 -4
  20. data/lib/mongoid/collections/master.rb +2 -3
  21. data/lib/mongoid/collections/mimic.rb +46 -0
  22. data/lib/mongoid/collections/slaves.rb +2 -3
  23. data/lib/mongoid/commands.rb +161 -0
  24. data/lib/mongoid/commands/create.rb +19 -0
  25. data/lib/mongoid/commands/delete.rb +16 -0
  26. data/lib/mongoid/commands/delete_all.rb +23 -0
  27. data/lib/mongoid/commands/deletion.rb +18 -0
  28. data/lib/mongoid/commands/destroy.rb +17 -0
  29. data/lib/mongoid/commands/destroy_all.rb +23 -0
  30. data/lib/mongoid/commands/save.rb +29 -0
  31. data/lib/mongoid/components.rb +6 -4
  32. data/lib/mongoid/config.rb +15 -134
  33. data/lib/mongoid/contexts.rb +1 -1
  34. data/lib/mongoid/contexts/enumerable.rb +1 -1
  35. data/lib/mongoid/contexts/mongo.rb +1 -1
  36. data/lib/mongoid/contexts/paging.rb +2 -10
  37. data/lib/mongoid/criterion/inclusion.rb +0 -17
  38. data/lib/mongoid/criterion/optional.rb +1 -1
  39. data/lib/mongoid/cursor.rb +1 -0
  40. data/lib/mongoid/document.rb +45 -49
  41. data/lib/mongoid/errors.rb +1 -32
  42. data/lib/mongoid/extensions.rb +10 -12
  43. data/lib/mongoid/extensions/array/conversions.rb +6 -8
  44. data/lib/mongoid/extensions/big_decimal/conversions.rb +2 -2
  45. data/lib/mongoid/extensions/boolean/conversions.rb +2 -8
  46. data/lib/mongoid/extensions/date/conversions.rb +4 -13
  47. data/lib/mongoid/extensions/datetime/conversions.rb +6 -1
  48. data/lib/mongoid/extensions/float/conversions.rb +1 -5
  49. data/lib/mongoid/extensions/hash/assimilation.rb +3 -12
  50. data/lib/mongoid/extensions/hash/conversions.rb +4 -34
  51. data/lib/mongoid/extensions/integer/conversions.rb +1 -5
  52. data/lib/mongoid/extensions/nil/assimilation.rb +0 -4
  53. data/lib/mongoid/extensions/object/conversions.rb +2 -8
  54. data/lib/mongoid/extensions/objectid/conversions.rb +1 -1
  55. data/lib/mongoid/extensions/string/conversions.rb +1 -1
  56. data/lib/mongoid/extensions/symbol/inflections.rb +1 -1
  57. data/lib/mongoid/extensions/time/conversions.rb +18 -0
  58. data/lib/mongoid/factory.rb +1 -2
  59. data/lib/mongoid/field.rb +2 -9
  60. data/lib/mongoid/fields.rb +7 -11
  61. data/lib/mongoid/finders.rb +2 -2
  62. data/lib/mongoid/identity.rb +4 -4
  63. data/lib/mongoid/indexes.rb +7 -10
  64. data/lib/mongoid/memoization.rb +2 -8
  65. data/lib/mongoid/named_scope.rb +5 -0
  66. data/lib/mongoid/observable.rb +1 -1
  67. data/lib/mongoid/paths.rb +22 -30
  68. data/lib/mongoid/state.rb +21 -28
  69. data/lib/mongoid/timestamps.rb +1 -1
  70. data/lib/mongoid/validations.rb +51 -0
  71. data/lib/mongoid/validations/associated.rb +32 -0
  72. data/lib/mongoid/validations/locale/en.yml +4 -0
  73. data/lib/mongoid/validations/uniqueness.rb +22 -0
  74. data/lib/mongoid/versioning.rb +1 -2
  75. data/mongoid.gemspec +408 -0
  76. data/perf/benchmark.rb +77 -0
  77. data/spec/integration/mongoid/associations_spec.rb +340 -0
  78. data/spec/integration/mongoid/attributes_spec.rb +22 -0
  79. data/spec/integration/mongoid/commands_spec.rb +227 -0
  80. data/spec/integration/mongoid/contexts/enumerable_spec.rb +33 -0
  81. data/spec/integration/mongoid/criteria_spec.rb +272 -0
  82. data/spec/integration/mongoid/document_spec.rb +650 -0
  83. data/spec/integration/mongoid/extensions_spec.rb +22 -0
  84. data/spec/integration/mongoid/finders_spec.rb +119 -0
  85. data/spec/integration/mongoid/inheritance_spec.rb +137 -0
  86. data/spec/integration/mongoid/named_scope_spec.rb +46 -0
  87. data/spec/models/address.rb +39 -0
  88. data/spec/models/animal.rb +6 -0
  89. data/spec/models/callbacks.rb +18 -0
  90. data/spec/models/comment.rb +8 -0
  91. data/spec/models/country_code.rb +6 -0
  92. data/spec/models/employer.rb +5 -0
  93. data/spec/models/game.rb +7 -0
  94. data/spec/models/inheritance.rb +56 -0
  95. data/spec/models/location.rb +5 -0
  96. data/spec/models/mixed_drink.rb +4 -0
  97. data/spec/models/name.rb +13 -0
  98. data/spec/models/namespacing.rb +11 -0
  99. data/spec/models/patient.rb +4 -0
  100. data/spec/models/person.rb +99 -0
  101. data/spec/models/pet.rb +7 -0
  102. data/spec/models/pet_owner.rb +6 -0
  103. data/spec/models/phone.rb +7 -0
  104. data/spec/models/post.rb +15 -0
  105. data/spec/models/translation.rb +5 -0
  106. data/spec/models/vet_visit.rb +5 -0
  107. data/spec/spec.opts +3 -0
  108. data/spec/spec_helper.rb +31 -0
  109. data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +145 -0
  110. data/spec/unit/mongoid/associations/embed_many_spec.rb +516 -0
  111. data/spec/unit/mongoid/associations/embed_one_spec.rb +282 -0
  112. data/spec/unit/mongoid/associations/embedded_in_spec.rb +193 -0
  113. data/spec/unit/mongoid/associations/has_many_related_spec.rb +418 -0
  114. data/spec/unit/mongoid/associations/has_one_related_spec.rb +179 -0
  115. data/spec/unit/mongoid/associations/meta_data_spec.rb +88 -0
  116. data/spec/unit/mongoid/associations/options_spec.rb +192 -0
  117. data/spec/unit/mongoid/associations_spec.rb +595 -0
  118. data/spec/unit/mongoid/attributes_spec.rb +507 -0
  119. data/spec/unit/mongoid/callbacks_spec.rb +55 -0
  120. data/spec/unit/mongoid/collection_spec.rb +187 -0
  121. data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
  122. data/spec/unit/mongoid/collections/master_spec.rb +41 -0
  123. data/spec/unit/mongoid/collections/mimic_spec.rb +43 -0
  124. data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
  125. data/spec/unit/mongoid/commands/create_spec.rb +31 -0
  126. data/spec/unit/mongoid/commands/delete_all_spec.rb +58 -0
  127. data/spec/unit/mongoid/commands/delete_spec.rb +38 -0
  128. data/spec/unit/mongoid/commands/destroy_all_spec.rb +21 -0
  129. data/spec/unit/mongoid/commands/destroy_spec.rb +51 -0
  130. data/spec/unit/mongoid/commands/save_spec.rb +107 -0
  131. data/spec/unit/mongoid/commands_spec.rb +270 -0
  132. data/spec/unit/mongoid/config_spec.rb +172 -0
  133. data/spec/unit/mongoid/contexts/enumerable_spec.rb +421 -0
  134. data/spec/unit/mongoid/contexts/mongo_spec.rb +682 -0
  135. data/spec/unit/mongoid/contexts_spec.rb +25 -0
  136. data/spec/unit/mongoid/criteria_spec.rb +824 -0
  137. data/spec/unit/mongoid/criterion/complex_spec.rb +19 -0
  138. data/spec/unit/mongoid/criterion/exclusion_spec.rb +91 -0
  139. data/spec/unit/mongoid/criterion/inclusion_spec.rb +219 -0
  140. data/spec/unit/mongoid/criterion/optional_spec.rb +319 -0
  141. data/spec/unit/mongoid/cursor_spec.rb +74 -0
  142. data/spec/unit/mongoid/deprecation_spec.rb +24 -0
  143. data/spec/unit/mongoid/document_spec.rb +818 -0
  144. data/spec/unit/mongoid/errors_spec.rb +103 -0
  145. data/spec/unit/mongoid/extensions/array/accessors_spec.rb +50 -0
  146. data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +24 -0
  147. data/spec/unit/mongoid/extensions/array/conversions_spec.rb +35 -0
  148. data/spec/unit/mongoid/extensions/array/parentization_spec.rb +20 -0
  149. data/spec/unit/mongoid/extensions/big_decimal/conversions_spec.rb +22 -0
  150. data/spec/unit/mongoid/extensions/binary/conversions_spec.rb +22 -0
  151. data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +49 -0
  152. data/spec/unit/mongoid/extensions/date/conversions_spec.rb +102 -0
  153. data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +67 -0
  154. data/spec/unit/mongoid/extensions/float/conversions_spec.rb +61 -0
  155. data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +184 -0
  156. data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +46 -0
  157. data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +21 -0
  158. data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +17 -0
  159. data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +14 -0
  160. data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +61 -0
  161. data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +24 -0
  162. data/spec/unit/mongoid/extensions/object/conversions_spec.rb +57 -0
  163. data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +34 -0
  164. data/spec/unit/mongoid/extensions/string/conversions_spec.rb +17 -0
  165. data/spec/unit/mongoid/extensions/string/inflections_spec.rb +208 -0
  166. data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +91 -0
  167. data/spec/unit/mongoid/extensions/time/conversions_spec.rb +70 -0
  168. data/spec/unit/mongoid/extras_spec.rb +102 -0
  169. data/spec/unit/mongoid/factory_spec.rb +31 -0
  170. data/spec/unit/mongoid/field_spec.rb +143 -0
  171. data/spec/unit/mongoid/fields_spec.rb +181 -0
  172. data/spec/unit/mongoid/finders_spec.rb +404 -0
  173. data/spec/unit/mongoid/identity_spec.rb +109 -0
  174. data/spec/unit/mongoid/indexes_spec.rb +93 -0
  175. data/spec/unit/mongoid/javascript_spec.rb +48 -0
  176. data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
  177. data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
  178. data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
  179. data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
  180. data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
  181. data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
  182. data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
  183. data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
  184. data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
  185. data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
  186. data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
  187. data/spec/unit/mongoid/matchers_spec.rb +329 -0
  188. data/spec/unit/mongoid/memoization_spec.rb +75 -0
  189. data/spec/unit/mongoid/named_scope_spec.rb +123 -0
  190. data/spec/unit/mongoid/observable_spec.rb +46 -0
  191. data/spec/unit/mongoid/paths_spec.rb +124 -0
  192. data/spec/unit/mongoid/scope_spec.rb +240 -0
  193. data/spec/unit/mongoid/state_spec.rb +83 -0
  194. data/spec/unit/mongoid/timestamps_spec.rb +25 -0
  195. data/spec/unit/mongoid/validations/associated_spec.rb +103 -0
  196. data/spec/unit/mongoid/validations/uniqueness_spec.rb +47 -0
  197. data/spec/unit/mongoid/validations_spec.rb +190 -0
  198. data/spec/unit/mongoid/versioning_spec.rb +41 -0
  199. data/spec/unit/mongoid_spec.rb +46 -0
  200. metadata +316 -110
  201. data/lib/mongoid/collections.rb +0 -41
  202. data/lib/mongoid/concern.rb +0 -31
  203. data/lib/mongoid/dirty.rb +0 -253
  204. data/lib/mongoid/extensions/time_conversions.rb +0 -35
  205. data/lib/mongoid/persistence.rb +0 -222
  206. data/lib/mongoid/persistence/command.rb +0 -39
  207. data/lib/mongoid/persistence/insert.rb +0 -50
  208. data/lib/mongoid/persistence/insert_embedded.rb +0 -38
  209. data/lib/mongoid/persistence/remove.rb +0 -39
  210. data/lib/mongoid/persistence/remove_all.rb +0 -37
  211. data/lib/mongoid/persistence/remove_embedded.rb +0 -50
  212. data/lib/mongoid/persistence/update.rb +0 -63
  213. data/lib/mongoid/version.rb +0 -4
@@ -0,0 +1,77 @@
1
+ require "rubygems"
2
+ require "ruby-prof"
3
+ require "benchmark"
4
+ require "mongoid"
5
+
6
+ Mongoid.configure do |config|
7
+ config.persist_in_safe_mode = false
8
+ config.master = Mongo::Connection.new.db("mongoid_perf_test")
9
+ end
10
+
11
+ Mongoid.master.collection("people").drop
12
+
13
+ class Person
14
+ include Mongoid::Document
15
+ include Mongoid::Timestamps
16
+ field :birth_date, :type => Date
17
+ has_one :name
18
+ has_one :address
19
+ has_many :phones
20
+ end
21
+
22
+ class Name
23
+ include Mongoid::Document
24
+ field :given
25
+ field :family
26
+ field :middle
27
+ belongs_to :person, :inverse_of => :name
28
+ end
29
+
30
+ class Address
31
+ include Mongoid::Document
32
+ field :street
33
+ field :city
34
+ field :state
35
+ field :post_code
36
+ field :address_type
37
+ belongs_to :person, :inverse_of => :address
38
+ end
39
+
40
+ class Phone
41
+ include Mongoid::Document
42
+ field :country_code, :type => Integer
43
+ field :number
44
+ field :phone_type
45
+ belongs_to :person, :inverse_of => :phones
46
+ end
47
+
48
+ # RubyProf.start
49
+
50
+ puts "Starting benchmark..."
51
+
52
+ Benchmark.bm do |bm|
53
+ bm.report("Mongoid") do
54
+ 10000.times do |n|
55
+ person = Person.new(:birth_date => Date.new(1970, 1, 1))
56
+ name = Name.new(:given => "James", :family => "Kirk", :middle => "Tiberius")
57
+ address = Address.new(
58
+ :street => "1 Starfleet Command Way",
59
+ :city => "San Francisco",
60
+ :state => "CA",
61
+ :post_code => "94133",
62
+ :type => "Work"
63
+ )
64
+ phone = Phone.new(:country_code => 1, :number => "415-555-1212", :type => "Mobile")
65
+ person.name = name
66
+ person.address = address
67
+ person.phones << phone
68
+ person.save
69
+ end
70
+ end
71
+ end
72
+
73
+ # result = RubyProf.stop
74
+ # printer = RubyProf::FlatPrinter.new(result)
75
+ # printer.print(STDOUT, 0)
76
+
77
+ # Mongoid.database.collection("people").drop
@@ -0,0 +1,340 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Associations do
4
+
5
+ before do
6
+ Person.delete_all
7
+ Game.delete_all
8
+ Post.delete_all
9
+ end
10
+
11
+ context "anonymous extensions" do
12
+
13
+ before do
14
+ @person = Person.new(:title => "Dr")
15
+ @address_one = Address.new(:street => "Oxford")
16
+ @address_two = Address.new(:street => "Bond")
17
+ @name = Name.new(:first_name => "Richard", :last_name => "Dawkins")
18
+ @person.addresses << [ @address_one, @address_two ]
19
+ @person.name = @name
20
+ @person.save
21
+ end
22
+
23
+ context "when defined on an embed_many" do
24
+
25
+ it "applies the extension" do
26
+ addresses = @person.addresses.find_by_street("Oxford")
27
+ addresses.size.should == 1
28
+ addresses.first.should == @address_one
29
+ end
30
+
31
+ end
32
+
33
+ context "when defined on a has_one" do
34
+
35
+ it "applies the extension" do
36
+ name = @person.name
37
+ name.dawkins?.should be_true
38
+ end
39
+
40
+ end
41
+
42
+ context "when defined on a belongs_to" do
43
+
44
+ it "applies the extension" do
45
+ @address_two.addressable.doctor?.should be_true
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ context "creation of an embedded association on a callback" do
53
+
54
+ it "allows the use of create!" do
55
+ artist = Artist.create!(:name => "Depeche Mode")
56
+ artist.songs.size.should == 2
57
+ artist.songs.first.title.should == "0"
58
+ artist.songs.last.title.should == "1"
59
+ end
60
+ end
61
+
62
+ context "criteria on has many embedded associations" do
63
+
64
+ before do
65
+ @person = Person.new(:title => "Sir")
66
+ @sf_apartment = Address.new(:street => "Genoa Pl", :state => "CA", :address_type => "Apartment")
67
+ @la_home = Address.new(:street => "Rodeo Dr", :state => "CA", :address_type => "Home")
68
+ @sf_home = Address.new(:street => "Pacific", :state => "CA", :address_type => "Home")
69
+ @person.addresses << [ @sf_apartment, @la_home, @sf_home ]
70
+ end
71
+
72
+ it "handles a single criteria" do
73
+ cas = @person.addresses.california
74
+ cas.size.should == 3
75
+ cas.should == [ @sf_apartment, @la_home, @sf_home ]
76
+ end
77
+
78
+ it "handles chained criteria" do
79
+ ca_homes = @person.addresses.california.homes
80
+ ca_homes.size.should == 2
81
+ ca_homes.should == [ @la_home, @sf_home ]
82
+ end
83
+
84
+ it "handles chained criteria with named scopes" do
85
+ ca_homes = @person.addresses.california.homes.rodeo
86
+ ca_homes.size.should == 1
87
+ ca_homes.should == [ @la_home ]
88
+ end
89
+
90
+ end
91
+
92
+ context "one-to-one relational associations" do
93
+
94
+ before do
95
+ @person = Person.new(:title => "Sir")
96
+ @game = Game.new(:score => 1)
97
+ @person.game = @game
98
+ @person.save
99
+ end
100
+
101
+ it "sets the association on save" do
102
+ @from_db = Person.find(@person.id)
103
+ @from_db.game.should == @game
104
+ end
105
+
106
+ it "sets the reverse association before save" do
107
+ @game.person.should == @person
108
+ end
109
+
110
+ it "sets the reverse association after save" do
111
+ @from_db = Game.find(@game.id)
112
+ @game.person.should == @person
113
+ end
114
+
115
+ end
116
+
117
+ context "one-to-many relational associations" do
118
+
119
+ before do
120
+ @person = Person.new(:title => "Sir")
121
+ @post = Post.new(:title => "Testing")
122
+ @person.posts = [@post]
123
+ @person.save
124
+ end
125
+
126
+ it "sets the association on save" do
127
+ from_db = Person.find(@person.id)
128
+ from_db.posts.should == [@post]
129
+ end
130
+
131
+ context "when adding a new association" do
132
+
133
+ before do
134
+ @new_post = Post.new(:title => "New")
135
+ @person.posts << @new_post
136
+ end
137
+
138
+ it "rememoizes the new association" do
139
+ @person.posts.should == [ @post, @new_post ]
140
+ end
141
+ end
142
+
143
+ context "when building" do
144
+
145
+ before do
146
+ @person = Person.new(:title => "Mr")
147
+ @post = @person.posts.build(:title => "First")
148
+ end
149
+
150
+ it "sets the reverse association" do
151
+ @post.person.should == @person
152
+ end
153
+
154
+ end
155
+
156
+ context "finding associated objects" do
157
+
158
+ before do
159
+ @extra_post = Post.create(:title => "Orphan")
160
+ @from_db = Person.find(@person.id)
161
+ end
162
+
163
+ context "finding all" do
164
+
165
+ it "returns only those objects scoped to the parent" do
166
+ Post.all.size.should == 2
167
+ @from_db.posts.all.size.should == 1
168
+ end
169
+
170
+ end
171
+
172
+ context "finding with conditions" do
173
+
174
+ context "finding all" do
175
+
176
+ it "returns only those objects scoped to the parent" do
177
+ posts = @from_db.posts.find(:all, :conditions => { :title => "Testing" })
178
+ posts.size.should == 1
179
+ end
180
+
181
+ end
182
+
183
+ context "finding first" do
184
+
185
+ it "returns only those objects scoped to the parent" do
186
+ post = @from_db.posts.find(:first, :conditions => { :title => "Testing" })
187
+ post.should == @post
188
+ end
189
+
190
+ end
191
+
192
+ context "finding last" do
193
+
194
+ it "returns only those objects scoped to the parent" do
195
+ post = @from_db.posts.find(:last, :conditions => { :title => "Testing" })
196
+ post.should == @post
197
+ end
198
+
199
+ end
200
+
201
+ context "using a named scope" do
202
+
203
+ before do
204
+ @post.created_at = 15.days.ago
205
+ @post.save
206
+ end
207
+
208
+ it "returns only those scoped to the parent plus the named scope" do
209
+ posts = @from_db.posts.recent
210
+ posts.size.should == 1
211
+ end
212
+
213
+ end
214
+
215
+ context "using a criteria class method" do
216
+
217
+ before do
218
+ @post.created_at = 45.days.ago
219
+ @post.save
220
+ end
221
+
222
+ it "returns only those scoped to the parent plus the named scope" do
223
+ posts = @from_db.posts.old
224
+ posts.size.should == 1
225
+ end
226
+
227
+ end
228
+
229
+ context "calling criteria methods" do
230
+
231
+ before do
232
+ @post.title = "New Title"
233
+ @post.save
234
+ end
235
+
236
+ it "returns the proper object for the criteria" do
237
+ posts = @from_db.posts.where(:title => "New Title")
238
+ posts.size.should == 1
239
+ end
240
+
241
+ context "when calling with a new criteria" do
242
+
243
+ before do
244
+ @from_db.posts.create(:title => "Other Title")
245
+ end
246
+
247
+ it "does not retain the old criteria" do
248
+ @from_db.posts.where(:title => "New Title").size.should == 1
249
+ @from_db.posts.size.should == 2
250
+ @from_db.posts.where(:title => "Other Title").size.should == 1
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end
257
+
258
+ context "nested embedded associations" do
259
+
260
+ before do
261
+ @person = Person.create(:title => "Mr")
262
+ end
263
+
264
+ context "one level nested" do
265
+
266
+ before do
267
+ @address = @person.addresses.create(:street => "Oxford St")
268
+ @name = @person.create_name(:first_name => "Gordon")
269
+ end
270
+
271
+ it "persists all the associations properly" do
272
+ @name.last_name = "Brown"
273
+ @person.name.last_name.should == "Brown"
274
+ end
275
+
276
+ it "sets the new_record values properly" do
277
+ from_db = Person.find(@person.id)
278
+ new_name = from_db.create_name(:first_name => "Flash")
279
+ new_name.new_record?.should be_false
280
+ end
281
+
282
+ end
283
+
284
+ context "multiple levels nested" do
285
+
286
+ context "when a has-many to has_one" do
287
+
288
+ before do
289
+ @person.phone_numbers.create(:number => "4155551212")
290
+ end
291
+
292
+ it "persists all the associations properly" do
293
+ from_db = Person.find(@person.id)
294
+ phone = from_db.phone_numbers.first
295
+ phone.create_country_code(:code => 1)
296
+ from_db.phone_numbers.first.country_code.code.should == 1
297
+ end
298
+
299
+ end
300
+
301
+ context "when a has-one to has-many" do
302
+
303
+ before do
304
+ @person = Person.new(:title => "Sir", :ssn => "1")
305
+ @name = Name.new(:first_name => "Syd")
306
+ @person.name = @name
307
+ @person.save
308
+ end
309
+
310
+ it "persists all the associations properly" do
311
+ from_db = Person.find(@person.id)
312
+ translation = Translation.new(:language => "fr")
313
+ from_db.name.translations << translation
314
+ from_db.attributes[:name][:translations].should_not be_nil
315
+ end
316
+
317
+ end
318
+
319
+ context "when a has-many to has-many" do
320
+
321
+ before do
322
+ @address = Address.new(:street => "Upper Street")
323
+ @person.addresses << @address
324
+ end
325
+
326
+ it "bubbles the child association up to root" do
327
+ location = Location.new(:name => "Home")
328
+ @address.locations << location
329
+ location.stubs(:valid?).returns(false)
330
+ @person.save
331
+ @person.addresses.first.locations.first.should == location
332
+ end
333
+
334
+ end
335
+
336
+ end
337
+
338
+ end
339
+
340
+ end
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Attributes do
4
+
5
+ context "when persisting nil attributes" do
6
+
7
+ before do
8
+ @person = Person.create(:score => nil, :ssn => "555-66-7777")
9
+ end
10
+
11
+ after do
12
+ Person.delete_all
13
+ end
14
+
15
+ it "the field should exist with a nil value" do
16
+ from_db = Person.find(@person.id)
17
+ from_db.attributes.has_key?(:score).should be_true
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,227 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Commands do
4
+
5
+ before do
6
+ @person = Person.new(:title => "Sir", :ssn => "6969696", :pets => true)
7
+ end
8
+
9
+ after do
10
+ @person.delete
11
+ end
12
+
13
+ describe ".create" do
14
+
15
+ it "saves and returns the document" do
16
+ person = Person.create(:title => "Sensei", :ssn => "666-66-6666")
17
+ person.should be_a_kind_of(Person)
18
+ person.should_not be_a_new_record
19
+ end
20
+
21
+ end
22
+
23
+ describe ".create!" do
24
+
25
+ context "inserting with a field that is not unique" do
26
+
27
+ context "when a unique index exists" do
28
+
29
+ after do
30
+ Person.delete_all
31
+ end
32
+
33
+ it "raises an error" do
34
+ Person.create!(:ssn => "555-55-9999")
35
+ lambda { Person.create!(:ssn => "555-55-9999") }.should raise_error
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+
44
+ describe "#delete" do
45
+
46
+ context "deleting a root document" do
47
+
48
+ before do
49
+ @person.save
50
+ end
51
+
52
+ it "deletes the document" do
53
+ @person.delete
54
+ lambda { Person.find(@person.id) }.should raise_error
55
+ end
56
+
57
+ it "returns true" do
58
+ @person.delete.should be_true
59
+ end
60
+
61
+ end
62
+
63
+ context "deleting an embedded document" do
64
+
65
+ before do
66
+ @address = Address.new(:street => "Bond Street")
67
+ @person.addresses << @address
68
+ end
69
+
70
+ context "when the document is not yet saved" do
71
+
72
+ it "removes the document from the parent" do
73
+ @address.delete
74
+ @person.addresses.should be_empty
75
+ @person.attributes[:addresses].should be_empty
76
+ end
77
+
78
+ end
79
+
80
+ context "when the document has been saved" do
81
+
82
+ before do
83
+ @address.save
84
+ end
85
+
86
+ it "removes the object from the parent and database" do
87
+ @address.delete
88
+ from_db = Person.find(@person.id)
89
+ from_db.addresses.should be_empty
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+
98
+ describe "#destroy" do
99
+
100
+ context "destroying a root document" do
101
+
102
+ before do
103
+ @person.save
104
+ end
105
+
106
+ it "destroys the document" do
107
+ @person.destroy
108
+ lambda { Person.find(@person.id) }.should raise_error
109
+ end
110
+
111
+ it "returns true" do
112
+ @person.destroy.should be_true
113
+ end
114
+
115
+ end
116
+
117
+ context "deleting an embedded document" do
118
+
119
+ before do
120
+ @address = Address.new(:street => "Bond Street")
121
+ @person.addresses << @address
122
+ end
123
+
124
+ context "when the document is not yet saved" do
125
+
126
+ it "removes the document from the parent" do
127
+ @address.destroy
128
+ @person.addresses.should be_empty
129
+ @person.attributes[:addresses].should be_empty
130
+ end
131
+
132
+ end
133
+
134
+ context "when the document has been saved" do
135
+
136
+ before do
137
+ @address.save
138
+ end
139
+
140
+ it "removes the object from the parent and database" do
141
+ @address.destroy
142
+ from_db = Person.find(@person.id)
143
+ from_db.addresses.should be_empty
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ describe "#destroy_all" do
150
+
151
+ before do
152
+ @person.save
153
+ end
154
+
155
+ it "destroys all the documents" do
156
+ Person.destroy_all
157
+ Person.count.should == 0
158
+ end
159
+ end
160
+
161
+ describe "#save" do
162
+
163
+ context "when validation passes" do
164
+
165
+ it "returns true" do
166
+ @person.save.should be_true
167
+ end
168
+
169
+ end
170
+
171
+ end
172
+
173
+ describe "save!" do
174
+
175
+ context "inserting with a field that is not unique" do
176
+
177
+ context "when a unique index exists" do
178
+
179
+ it "raises an error" do
180
+ Person.create!(:ssn => "555-55-9999")
181
+ person = Person.new(:ssn => "555-55-9999")
182
+ lambda { person.save!(:ssn => "555-55-9999") }.should raise_error
183
+ end
184
+
185
+ end
186
+
187
+ end
188
+
189
+ end
190
+
191
+ describe "#update_attributes" do
192
+
193
+ context "when validation passes" do
194
+
195
+ it "returns true" do
196
+ @person.update_attributes(:ssn => "555-55-1234").should be_true
197
+ end
198
+
199
+ it "saves the attributes" do
200
+ @person.update_attributes(:ssn => "555-55-1235", :pets => false, :title => nil)
201
+ @from_db = Person.find(@person.id)
202
+ @from_db.ssn.should == "555-55-1235"
203
+ @from_db.pets.should == false
204
+ @from_db.title.should be_nil
205
+ end
206
+
207
+ end
208
+
209
+ end
210
+
211
+ describe ".delete_all" do
212
+
213
+ it "returns true" do
214
+ Person.delete_all.should == true
215
+ end
216
+
217
+ end
218
+
219
+ describe ".destroy_all" do
220
+
221
+ it "returns true" do
222
+ Person.destroy_all.should == true
223
+ end
224
+
225
+ end
226
+
227
+ end