mongoid-locomotive 2.0.0.beta9

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 (274) hide show
  1. data/MIT_LICENSE +20 -0
  2. data/README.rdoc +47 -0
  3. data/lib/mongoid.rb +141 -0
  4. data/lib/mongoid/associations.rb +306 -0
  5. data/lib/mongoid/associations/embedded_in.rb +74 -0
  6. data/lib/mongoid/associations/embeds_many.rb +280 -0
  7. data/lib/mongoid/associations/embeds_one.rb +97 -0
  8. data/lib/mongoid/associations/foreign_key.rb +35 -0
  9. data/lib/mongoid/associations/meta_data.rb +38 -0
  10. data/lib/mongoid/associations/options.rb +62 -0
  11. data/lib/mongoid/associations/proxy.rb +33 -0
  12. data/lib/mongoid/associations/referenced_in.rb +59 -0
  13. data/lib/mongoid/associations/references_many.rb +245 -0
  14. data/lib/mongoid/associations/references_many_as_array.rb +78 -0
  15. data/lib/mongoid/associations/references_one.rb +99 -0
  16. data/lib/mongoid/atomicity.rb +55 -0
  17. data/lib/mongoid/attributes.rb +242 -0
  18. data/lib/mongoid/callbacks.rb +21 -0
  19. data/lib/mongoid/collection.rb +120 -0
  20. data/lib/mongoid/collections.rb +71 -0
  21. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  22. data/lib/mongoid/collections/master.rb +29 -0
  23. data/lib/mongoid/collections/operations.rb +41 -0
  24. data/lib/mongoid/collections/slaves.rb +45 -0
  25. data/lib/mongoid/components.rb +34 -0
  26. data/lib/mongoid/config.rb +263 -0
  27. data/lib/mongoid/contexts.rb +24 -0
  28. data/lib/mongoid/contexts/enumerable.rb +156 -0
  29. data/lib/mongoid/contexts/ids.rb +25 -0
  30. data/lib/mongoid/contexts/mongo.rb +285 -0
  31. data/lib/mongoid/contexts/paging.rb +50 -0
  32. data/lib/mongoid/criteria.rb +248 -0
  33. data/lib/mongoid/criterion/complex.rb +21 -0
  34. data/lib/mongoid/criterion/exclusion.rb +65 -0
  35. data/lib/mongoid/criterion/inclusion.rb +110 -0
  36. data/lib/mongoid/criterion/optional.rb +189 -0
  37. data/lib/mongoid/cursor.rb +81 -0
  38. data/lib/mongoid/deprecation.rb +21 -0
  39. data/lib/mongoid/dirty.rb +252 -0
  40. data/lib/mongoid/document.rb +210 -0
  41. data/lib/mongoid/errors.rb +131 -0
  42. data/lib/mongoid/extensions.rb +115 -0
  43. data/lib/mongoid/extensions/array/accessors.rb +17 -0
  44. data/lib/mongoid/extensions/array/assimilation.rb +26 -0
  45. data/lib/mongoid/extensions/array/conversions.rb +23 -0
  46. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  47. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  48. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  49. data/lib/mongoid/extensions/boolean/conversions.rb +27 -0
  50. data/lib/mongoid/extensions/date/conversions.rb +24 -0
  51. data/lib/mongoid/extensions/datetime/conversions.rb +12 -0
  52. data/lib/mongoid/extensions/false_class/equality.rb +13 -0
  53. data/lib/mongoid/extensions/float/conversions.rb +20 -0
  54. data/lib/mongoid/extensions/hash/accessors.rb +42 -0
  55. data/lib/mongoid/extensions/hash/assimilation.rb +40 -0
  56. data/lib/mongoid/extensions/hash/conversions.rb +42 -0
  57. data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
  58. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  59. data/lib/mongoid/extensions/integer/conversions.rb +20 -0
  60. data/lib/mongoid/extensions/nil/assimilation.rb +17 -0
  61. data/lib/mongoid/extensions/object/conversions.rb +21 -0
  62. data/lib/mongoid/extensions/objectid/conversions.rb +15 -0
  63. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  64. data/lib/mongoid/extensions/set/conversions.rb +20 -0
  65. data/lib/mongoid/extensions/string/conversions.rb +15 -0
  66. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  67. data/lib/mongoid/extensions/symbol/inflections.rb +40 -0
  68. data/lib/mongoid/extensions/time_conversions.rb +35 -0
  69. data/lib/mongoid/extensions/true_class/equality.rb +13 -0
  70. data/lib/mongoid/extras.rb +61 -0
  71. data/lib/mongoid/factory.rb +20 -0
  72. data/lib/mongoid/field.rb +83 -0
  73. data/lib/mongoid/fields.rb +62 -0
  74. data/lib/mongoid/finders.rb +145 -0
  75. data/lib/mongoid/hierarchy.rb +74 -0
  76. data/lib/mongoid/identity.rb +47 -0
  77. data/lib/mongoid/indexes.rb +27 -0
  78. data/lib/mongoid/javascript.rb +21 -0
  79. data/lib/mongoid/javascript/functions.yml +37 -0
  80. data/lib/mongoid/logger.rb +19 -0
  81. data/lib/mongoid/matchers.rb +35 -0
  82. data/lib/mongoid/matchers/all.rb +11 -0
  83. data/lib/mongoid/matchers/default.rb +26 -0
  84. data/lib/mongoid/matchers/exists.rb +13 -0
  85. data/lib/mongoid/matchers/gt.rb +11 -0
  86. data/lib/mongoid/matchers/gte.rb +11 -0
  87. data/lib/mongoid/matchers/in.rb +11 -0
  88. data/lib/mongoid/matchers/lt.rb +11 -0
  89. data/lib/mongoid/matchers/lte.rb +11 -0
  90. data/lib/mongoid/matchers/ne.rb +11 -0
  91. data/lib/mongoid/matchers/nin.rb +11 -0
  92. data/lib/mongoid/matchers/size.rb +11 -0
  93. data/lib/mongoid/memoization.rb +33 -0
  94. data/lib/mongoid/named_scope.rb +37 -0
  95. data/lib/mongoid/paranoia.rb +106 -0
  96. data/lib/mongoid/paths.rb +61 -0
  97. data/lib/mongoid/persistence.rb +216 -0
  98. data/lib/mongoid/persistence/command.rb +39 -0
  99. data/lib/mongoid/persistence/insert.rb +48 -0
  100. data/lib/mongoid/persistence/insert_embedded.rb +44 -0
  101. data/lib/mongoid/persistence/remove.rb +39 -0
  102. data/lib/mongoid/persistence/remove_all.rb +38 -0
  103. data/lib/mongoid/persistence/remove_embedded.rb +50 -0
  104. data/lib/mongoid/persistence/update.rb +71 -0
  105. data/lib/mongoid/railtie.rb +67 -0
  106. data/lib/mongoid/railties/database.rake +60 -0
  107. data/lib/mongoid/scope.rb +75 -0
  108. data/lib/mongoid/state.rb +32 -0
  109. data/lib/mongoid/timestamps.rb +27 -0
  110. data/lib/mongoid/validations.rb +51 -0
  111. data/lib/mongoid/validations/associated.rb +32 -0
  112. data/lib/mongoid/validations/locale/en.yml +5 -0
  113. data/lib/mongoid/validations/uniqueness.rb +56 -0
  114. data/lib/mongoid/version.rb +4 -0
  115. data/lib/mongoid/versioning.rb +26 -0
  116. data/lib/rails/generators/mongoid/config/config_generator.rb +25 -0
  117. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +24 -0
  118. data/lib/rails/generators/mongoid/model/model_generator.rb +24 -0
  119. data/lib/rails/generators/mongoid/model/templates/model.rb +15 -0
  120. data/lib/rails/generators/mongoid_generator.rb +61 -0
  121. data/spec/integration/mongoid/association_attributes_spec.rb +71 -0
  122. data/spec/integration/mongoid/associations_spec.rb +768 -0
  123. data/spec/integration/mongoid/attributes_spec.rb +59 -0
  124. data/spec/integration/mongoid/callback_spec.rb +33 -0
  125. data/spec/integration/mongoid/contexts/enumerable_spec.rb +33 -0
  126. data/spec/integration/mongoid/criteria_spec.rb +281 -0
  127. data/spec/integration/mongoid/dirty_spec.rb +85 -0
  128. data/spec/integration/mongoid/document_spec.rb +741 -0
  129. data/spec/integration/mongoid/extensions_spec.rb +22 -0
  130. data/spec/integration/mongoid/finders_spec.rb +119 -0
  131. data/spec/integration/mongoid/inheritance_spec.rb +171 -0
  132. data/spec/integration/mongoid/named_scope_spec.rb +58 -0
  133. data/spec/integration/mongoid/paranoia_spec.rb +44 -0
  134. data/spec/integration/mongoid/persistence/update_spec.rb +46 -0
  135. data/spec/integration/mongoid/persistence_spec.rb +311 -0
  136. data/spec/integration/mongoid/validations/uniqueness_spec.rb +206 -0
  137. data/spec/models/account.rb +5 -0
  138. data/spec/models/address.rb +40 -0
  139. data/spec/models/agent.rb +7 -0
  140. data/spec/models/animal.rb +15 -0
  141. data/spec/models/answer.rb +4 -0
  142. data/spec/models/callbacks.rb +47 -0
  143. data/spec/models/category.rb +13 -0
  144. data/spec/models/comment.rb +10 -0
  145. data/spec/models/country_code.rb +6 -0
  146. data/spec/models/employer.rb +5 -0
  147. data/spec/models/favorite.rb +8 -0
  148. data/spec/models/game.rb +9 -0
  149. data/spec/models/inheritance.rb +72 -0
  150. data/spec/models/location.rb +5 -0
  151. data/spec/models/login.rb +6 -0
  152. data/spec/models/mixed_drink.rb +4 -0
  153. data/spec/models/name.rb +13 -0
  154. data/spec/models/namespacing.rb +11 -0
  155. data/spec/models/paranoid_post.rb +18 -0
  156. data/spec/models/parents.rb +32 -0
  157. data/spec/models/patient.rb +15 -0
  158. data/spec/models/person.rb +106 -0
  159. data/spec/models/pet.rb +7 -0
  160. data/spec/models/pet_owner.rb +6 -0
  161. data/spec/models/phone.rb +7 -0
  162. data/spec/models/post.rb +25 -0
  163. data/spec/models/preference.rb +7 -0
  164. data/spec/models/question.rb +8 -0
  165. data/spec/models/survey.rb +6 -0
  166. data/spec/models/translation.rb +5 -0
  167. data/spec/models/user.rb +6 -0
  168. data/spec/models/user_accout.rb +5 -0
  169. data/spec/models/vet_visit.rb +5 -0
  170. data/spec/models/video.rb +5 -0
  171. data/spec/spec_helper.rb +33 -0
  172. data/spec/unit/mongoid/associations/embedded_in_spec.rb +193 -0
  173. data/spec/unit/mongoid/associations/embeds_many_spec.rb +626 -0
  174. data/spec/unit/mongoid/associations/embeds_one_spec.rb +287 -0
  175. data/spec/unit/mongoid/associations/foreign_key_spec.rb +90 -0
  176. data/spec/unit/mongoid/associations/meta_data_spec.rb +110 -0
  177. data/spec/unit/mongoid/associations/options_spec.rb +215 -0
  178. data/spec/unit/mongoid/associations/referenced_in_spec.rb +145 -0
  179. data/spec/unit/mongoid/associations/references_many_as_array_spec.rb +424 -0
  180. data/spec/unit/mongoid/associations/references_many_spec.rb +502 -0
  181. data/spec/unit/mongoid/associations/references_one_spec.rb +204 -0
  182. data/spec/unit/mongoid/associations_spec.rb +688 -0
  183. data/spec/unit/mongoid/atomicity_spec.rb +164 -0
  184. data/spec/unit/mongoid/attributes_spec.rb +646 -0
  185. data/spec/unit/mongoid/callbacks_spec.rb +85 -0
  186. data/spec/unit/mongoid/collection_spec.rb +187 -0
  187. data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
  188. data/spec/unit/mongoid/collections/master_spec.rb +41 -0
  189. data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
  190. data/spec/unit/mongoid/collections_spec.rb +98 -0
  191. data/spec/unit/mongoid/config_spec.rb +298 -0
  192. data/spec/unit/mongoid/contexts/enumerable_spec.rb +447 -0
  193. data/spec/unit/mongoid/contexts/mongo_spec.rb +703 -0
  194. data/spec/unit/mongoid/contexts_spec.rb +25 -0
  195. data/spec/unit/mongoid/criteria_spec.rb +873 -0
  196. data/spec/unit/mongoid/criterion/complex_spec.rb +17 -0
  197. data/spec/unit/mongoid/criterion/exclusion_spec.rb +121 -0
  198. data/spec/unit/mongoid/criterion/inclusion_spec.rb +274 -0
  199. data/spec/unit/mongoid/criterion/optional_spec.rb +483 -0
  200. data/spec/unit/mongoid/cursor_spec.rb +80 -0
  201. data/spec/unit/mongoid/deprecation_spec.rb +24 -0
  202. data/spec/unit/mongoid/dirty_spec.rb +430 -0
  203. data/spec/unit/mongoid/document_spec.rb +623 -0
  204. data/spec/unit/mongoid/errors_spec.rb +154 -0
  205. data/spec/unit/mongoid/extensions/array/accessors_spec.rb +50 -0
  206. data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +24 -0
  207. data/spec/unit/mongoid/extensions/array/conversions_spec.rb +52 -0
  208. data/spec/unit/mongoid/extensions/array/parentization_spec.rb +20 -0
  209. data/spec/unit/mongoid/extensions/big_decimal/conversions_spec.rb +36 -0
  210. data/spec/unit/mongoid/extensions/binary/conversions_spec.rb +22 -0
  211. data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +49 -0
  212. data/spec/unit/mongoid/extensions/date/conversions_spec.rb +145 -0
  213. data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +14 -0
  214. data/spec/unit/mongoid/extensions/false_class/equality_spec.rb +35 -0
  215. data/spec/unit/mongoid/extensions/float/conversions_spec.rb +61 -0
  216. data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +184 -0
  217. data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +59 -0
  218. data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +35 -0
  219. data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +17 -0
  220. data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +14 -0
  221. data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +61 -0
  222. data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +29 -0
  223. data/spec/unit/mongoid/extensions/object/conversions_spec.rb +44 -0
  224. data/spec/unit/mongoid/extensions/objectid/conversions_spec.rb +22 -0
  225. data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +34 -0
  226. data/spec/unit/mongoid/extensions/set/conversions_spec.rb +21 -0
  227. data/spec/unit/mongoid/extensions/string/conversions_spec.rb +28 -0
  228. data/spec/unit/mongoid/extensions/string/inflections_spec.rb +208 -0
  229. data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +107 -0
  230. data/spec/unit/mongoid/extensions/time_conversions_spec.rb +186 -0
  231. data/spec/unit/mongoid/extensions/true_class/equality_spec.rb +35 -0
  232. data/spec/unit/mongoid/extras_spec.rb +102 -0
  233. data/spec/unit/mongoid/factory_spec.rb +31 -0
  234. data/spec/unit/mongoid/field_spec.rb +169 -0
  235. data/spec/unit/mongoid/fields_spec.rb +181 -0
  236. data/spec/unit/mongoid/finders_spec.rb +439 -0
  237. data/spec/unit/mongoid/hierarchy_spec.rb +68 -0
  238. data/spec/unit/mongoid/identity_spec.rb +109 -0
  239. data/spec/unit/mongoid/indexes_spec.rb +99 -0
  240. data/spec/unit/mongoid/javascript_spec.rb +48 -0
  241. data/spec/unit/mongoid/logger_spec.rb +38 -0
  242. data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
  243. data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
  244. data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
  245. data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
  246. data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
  247. data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
  248. data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
  249. data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
  250. data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
  251. data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
  252. data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
  253. data/spec/unit/mongoid/matchers_spec.rb +329 -0
  254. data/spec/unit/mongoid/memoization_spec.rb +75 -0
  255. data/spec/unit/mongoid/named_scope_spec.rb +123 -0
  256. data/spec/unit/mongoid/paranoia_spec.rb +108 -0
  257. data/spec/unit/mongoid/paths_spec.rb +272 -0
  258. data/spec/unit/mongoid/persistence/insert_embedded_spec.rb +154 -0
  259. data/spec/unit/mongoid/persistence/insert_spec.rb +144 -0
  260. data/spec/unit/mongoid/persistence/remove_all_spec.rb +82 -0
  261. data/spec/unit/mongoid/persistence/remove_embedded_spec.rb +152 -0
  262. data/spec/unit/mongoid/persistence/remove_spec.rb +89 -0
  263. data/spec/unit/mongoid/persistence/update_spec.rb +177 -0
  264. data/spec/unit/mongoid/persistence_spec.rb +452 -0
  265. data/spec/unit/mongoid/scope_spec.rb +240 -0
  266. data/spec/unit/mongoid/serialization_spec.rb +43 -0
  267. data/spec/unit/mongoid/state_spec.rb +94 -0
  268. data/spec/unit/mongoid/timestamps_spec.rb +30 -0
  269. data/spec/unit/mongoid/validations/associated_spec.rb +103 -0
  270. data/spec/unit/mongoid/validations/uniqueness_spec.rb +201 -0
  271. data/spec/unit/mongoid/validations_spec.rb +43 -0
  272. data/spec/unit/mongoid/versioning_spec.rb +41 -0
  273. data/spec/unit/mongoid_spec.rb +46 -0
  274. metadata +433 -0
@@ -0,0 +1,71 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Associations do
4
+
5
+ before do
6
+ Person.collection.remove
7
+ end
8
+
9
+ context "embeds_one" do
10
+
11
+ context "with attributes hash" do
12
+ describe "creating associated document" do
13
+ let(:person) { Person.create!( :ssn => "1234", :pet_attributes => { :name => 'odie' } ) }
14
+ specify { person.reload.pet.name.should == 'odie' }
15
+ end
16
+
17
+ describe "updating associated document" do
18
+ let(:person) { Person.create!( :ssn => "1234", :pet_attributes => { :name => 'garfield' } ) }
19
+ before { person.update_attributes(:pet_attributes => { :name => 'odie' } ) }
20
+ specify { person.reload.pet.name.should == 'odie' }
21
+ end
22
+ end
23
+
24
+ context "with a normal hash" do
25
+ describe "creating associated document" do
26
+ let(:person) { Person.create!( :ssn => "1234", :pet => { :name => 'odie', :tag_list => "dog, beagle" } ) }
27
+ specify { person.reload.pet.name.should == 'odie' }
28
+ specify { person.reload.pet.tags.should == ["dog", "beagle"] }
29
+ end
30
+
31
+ describe "updating associated document" do
32
+ let(:person) { Person.create!( :ssn => "1234", :pet => { :name => 'garfield', :tag_list => "cat" } ) }
33
+ before { person.update_attributes!(:pet => { :name => 'odie', :tag_list => "dog, beagle" } ) }
34
+ specify { person.reload.pet.name.should == 'odie' }
35
+ specify { person.reload.pet.tags.should == ["dog", "beagle"] }
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ context "embeds_many" do
42
+
43
+ context "with attributes hash" do
44
+ describe "creating associated document" do
45
+ let(:person) { Person.create!( :ssn => "1234", :favorites_attributes => { '0' => { :title => 'something' } } ) }
46
+ specify { person.reload.favorites.first.title.should == 'something' }
47
+ end
48
+
49
+ describe "updating associated document" do
50
+ let(:person) { Person.create!( :ssn => "1234", :favorites => { '0' => { :title => 'nothing' } } ) }
51
+ before do
52
+ person.update_attributes(:favorites_attributes => { '0' => { :title => 'something' } } )
53
+ end
54
+ specify { person.reload.favorites.first.title.should == 'something' }
55
+ end
56
+ end
57
+
58
+ context "with a normal hash" do
59
+ describe "creating associated document" do
60
+ let(:person) { Person.create!( :ssn => "1234", :favorites => { '0' => { :title => 'something' } } ) }
61
+ specify { person.reload.favorites.first.title.should == 'something' }
62
+ end
63
+
64
+ describe "updating associated document" do
65
+ let(:person) { Person.create!( :ssn => "1234", :favorites => { '0' => { :title => 'nothing' } } ) }
66
+ before { person.update_attributes(:favorites => { '0' => { :title => 'something' } } ) }
67
+ specify { person.reload.favorites.first.title.should == 'something' }
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,768 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Associations do
4
+
5
+ before do
6
+ [ Artist, Person, Game, Post, Preference ].each { |klass| klass.collection.remove }
7
+ end
8
+
9
+ context "anonymous extensions" do
10
+
11
+ before do
12
+ @person = Person.new(:title => "Dr")
13
+ @address_one = Address.new(:street => "Oxford")
14
+ @address_two = Address.new(:street => "Bond")
15
+ @name = Name.new(:first_name => "Richard", :last_name => "Dawkins")
16
+ @person.addresses << [ @address_one, @address_two ]
17
+ @person.name = @name
18
+ @person.save
19
+ end
20
+
21
+ context "when defined on an embeds_many" do
22
+
23
+ it "applies the extension" do
24
+ addresses = @person.addresses.find_by_street("Oxford")
25
+ addresses.size.should == 1
26
+ addresses.first.should == @address_one
27
+ end
28
+ end
29
+
30
+ context "when defined on a has_one" do
31
+
32
+ it "applies the extension" do
33
+ name = @person.name
34
+ name.dawkins?.should be_true
35
+ end
36
+ end
37
+
38
+ context "when defined on an embedded_in" do
39
+
40
+ it "applies the extension" do
41
+ @address_two.addressable.doctor?.should be_true
42
+ end
43
+ end
44
+ end
45
+
46
+ context "creation of an embedded association on a callback" do
47
+
48
+ it "allows the use of create!" do
49
+ artist = Artist.create!(:name => "Depeche Mode")
50
+ artist.songs.size.should == 2
51
+ artist.songs.first.title.should == "0"
52
+ artist.songs.last.title.should == "1"
53
+ end
54
+ end
55
+
56
+ context "passing a relational child to the parent constructor" do
57
+
58
+ before do
59
+ @game = Game.new(:score => 1)
60
+ @person = Person.new(:title => "Sir", :game => @game)
61
+ @person.save
62
+ end
63
+
64
+ it "sets the association on save" do
65
+ @from_db = Person.find(@person.id)
66
+ @from_db.game.should == @game
67
+ end
68
+
69
+ it "sets the reverse association before save" do
70
+ @game.person.should == @person
71
+ end
72
+
73
+ it "sets the reverse association after save" do
74
+ @from_db = Game.find(@game.id)
75
+ @game.person.should == @person
76
+ end
77
+ end
78
+
79
+ context "creation of an embedded association via create" do
80
+
81
+ context "when passed a parent" do
82
+
83
+ before do
84
+ @artist = Artist.new(:name => "Placebo")
85
+ @label = Label.create(:artist => @artist, :name => "Island")
86
+ end
87
+
88
+ it "saves the parent and the child" do
89
+ from_db = Artist.find(@artist.id)
90
+ from_db.labels.first.should == @label
91
+ end
92
+
93
+ it "does not save the child more than once" do
94
+ from_db = Artist.find(@artist.id)
95
+ from_db.labels.size.should == 1
96
+ end
97
+ end
98
+ end
99
+
100
+ context "criteria on has many embedded associations" do
101
+
102
+ before do
103
+ @person = Person.new(:title => "Sir")
104
+ @sf_apartment = Address.new(:street => "Genoa Pl", :state => "CA", :address_type => "Apartment")
105
+ @la_home = Address.new(:street => "Rodeo Dr", :state => "CA", :address_type => "Home")
106
+ @sf_home = Address.new(:street => "Pacific", :state => "CA", :address_type => "Home")
107
+ @person.addresses << [ @sf_apartment, @la_home, @sf_home ]
108
+ end
109
+
110
+ it "handles a single criteria" do
111
+ cas = @person.addresses.california
112
+ cas.size.should == 3
113
+ cas.should == [ @sf_apartment, @la_home, @sf_home ]
114
+ end
115
+
116
+ it "handles chained criteria" do
117
+ ca_homes = @person.addresses.california.homes
118
+ ca_homes.size.should == 2
119
+ ca_homes.should == [ @la_home, @sf_home ]
120
+ end
121
+
122
+ it "handles chained criteria with named scopes" do
123
+ ca_homes = @person.addresses.california.homes.rodeo
124
+ ca_homes.size.should == 1
125
+ ca_homes.should == [ @la_home ]
126
+ end
127
+ end
128
+
129
+ context "one-to-one relational associations" do
130
+
131
+ before do
132
+ @person = Person.new(:title => "Sir")
133
+ @game = Game.new(:score => 1)
134
+ @person.game = @game
135
+ @person.save
136
+ end
137
+
138
+ it "sets the association on save" do
139
+ @from_db = Person.find(@person.id)
140
+ @from_db.game.should == @game
141
+ end
142
+
143
+ it "sets the reverse association before save" do
144
+ @game.person.should == @person
145
+ end
146
+
147
+ it "sets the reverse association after save" do
148
+ @from_db = Game.find(@game.id)
149
+ @game.person.should == @person
150
+ end
151
+
152
+ context "when defining a class name and foreign key" do
153
+
154
+ before do
155
+ @user = User.new(:name => "Don Julio")
156
+ @account = @user.account.build(:number => "1234567890")
157
+ end
158
+
159
+ it "sets the name of the association properly" do
160
+ @account.creator.should == @user
161
+ end
162
+ end
163
+
164
+ end
165
+
166
+ context "one-to-many relational associations" do
167
+ [
168
+ ["with default naming scheme", Person, :person],
169
+ ["with custom naming scheme and :inverse_of", User, :author],
170
+ ["with custom naming scheme and no :inverse_of", Agent, :poster]
171
+ ].each do |description, parent_class, association_name|
172
+ context description do
173
+ before do
174
+ @person = parent_class.new(:title => "Sir")
175
+ @post = Post.new(:title => "Testing")
176
+ @person.posts = [@post]
177
+ @person.save
178
+ end
179
+
180
+ it "sets the association on save" do
181
+ from_db = parent_class.find(@person.id)
182
+ from_db.posts.should == [@post]
183
+ end
184
+
185
+ describe "#build" do
186
+
187
+ before do
188
+ @another = @person.posts.build(:title => "Another")
189
+ end
190
+
191
+ it "sets new_record to true" do
192
+ @another.new_record?.should == true
193
+ end
194
+ end
195
+
196
+ describe "#delete_all" do
197
+
198
+ context "without conditions" do
199
+
200
+ before do
201
+ @person.posts.delete_all
202
+ end
203
+
204
+ it "deletes all the related objects" do
205
+ Post.count.should == 0
206
+ @person.posts.size.should == 0
207
+ end
208
+ end
209
+
210
+ context "with conditions" do
211
+
212
+ before do
213
+ @person.posts.delete_all(:conditions => { :title => "Testing" })
214
+ end
215
+
216
+ it "deletes the appropriate objects" do
217
+ Post.count.should == 0
218
+ @person.posts.size.should == 0
219
+ end
220
+ end
221
+ end
222
+
223
+ describe "#destroy_all" do
224
+
225
+ context "without conditions" do
226
+
227
+ before do
228
+ @person.posts.destroy_all
229
+ end
230
+
231
+ it "deletes all the related objects" do
232
+ Post.count.should == 0
233
+ @person.posts.size.should == 0
234
+ end
235
+ end
236
+
237
+ context "with conditions" do
238
+
239
+ before do
240
+ @person.posts.destroy_all(:conditions => { :title => "Testing" })
241
+ end
242
+
243
+ it "deletes the appropriate objects" do
244
+ Post.count.should == 0
245
+ @person.posts.size.should == 0
246
+ end
247
+ end
248
+ end
249
+
250
+ context "when adding a new association" do
251
+
252
+ before do
253
+ @new_post = Post.new(:title => "New")
254
+ @person.posts << @new_post
255
+ end
256
+
257
+ it "rememoizes the new association" do
258
+ @person.posts.should == [ @post, @new_post ]
259
+ end
260
+ end
261
+
262
+ context "when building" do
263
+
264
+ before do
265
+ @person = parent_class.new(:title => "Mr")
266
+ @post = @person.posts.build(:title => "First")
267
+ end
268
+
269
+ it "sets the reverse association" do
270
+ @post.send(association_name).should == @person
271
+ end
272
+
273
+ end
274
+
275
+ context "finding associated objects" do
276
+
277
+ before do
278
+ @extra_post = Post.create(:title => "Orphan")
279
+ @from_db = parent_class.find(@person.id)
280
+ end
281
+
282
+ context "finding all" do
283
+
284
+ it "returns only those objects scoped to the parent" do
285
+ Post.all.size.should == 2
286
+ @from_db.posts.all.size.should == 1
287
+ end
288
+
289
+ end
290
+
291
+ context "finding with conditions" do
292
+
293
+ context "finding all" do
294
+
295
+ it "returns only those objects scoped to the parent" do
296
+ posts = @from_db.posts.find(:all, :conditions => { :title => "Testing" })
297
+ posts.size.should == 1
298
+ end
299
+
300
+ end
301
+
302
+ context "finding first" do
303
+
304
+ it "returns only those objects scoped to the parent" do
305
+ post = @from_db.posts.find(:first, :conditions => { :title => "Testing" })
306
+ post.should == @post
307
+ end
308
+
309
+ end
310
+
311
+ context "finding last" do
312
+
313
+ it "returns only those objects scoped to the parent" do
314
+ post = @from_db.posts.find(:last, :conditions => { :title => "Testing" })
315
+ post.should == @post
316
+ end
317
+
318
+ end
319
+
320
+ context "using a named scope" do
321
+
322
+ before do
323
+ @post.created_at = 15.days.ago
324
+ @post.save
325
+ end
326
+
327
+ it "returns only those scoped to the parent plus the named scope" do
328
+ posts = @from_db.posts.recent
329
+ posts.size.should == 1
330
+ end
331
+
332
+ end
333
+
334
+ context "using a criteria class method" do
335
+
336
+ before do
337
+ @post.created_at = 45.days.ago
338
+ @post.save
339
+ end
340
+
341
+ it "returns only those scoped to the parent plus the named scope" do
342
+ posts = @from_db.posts.old
343
+ posts.size.should == 1
344
+ end
345
+
346
+ end
347
+
348
+ context "calling criteria methods" do
349
+
350
+ before do
351
+ @post.title = "New Title"
352
+ @post.save
353
+ end
354
+
355
+ it "returns the proper object for the criteria" do
356
+ posts = @from_db.posts.where(:title => "New Title")
357
+ posts.size.should == 1
358
+ end
359
+
360
+ context "when calling with a new criteria" do
361
+
362
+ before do
363
+ @from_db.posts.create(:title => "Other Title")
364
+ end
365
+
366
+ it "does not retain the old criteria" do
367
+ @from_db.posts.where(:title => "New Title").size.should == 1
368
+ @from_db.posts.size.should == 2
369
+ @from_db.posts.where(:title => "Other Title").size.should == 1
370
+ end
371
+ end
372
+ end
373
+ end
374
+ end
375
+ end
376
+ end
377
+ end
378
+
379
+ context "nested embedded associations" do
380
+
381
+ before do
382
+ @person = Person.create(:title => "Mr")
383
+ end
384
+
385
+ context "having an embedded document as both an embeds_one and many" do
386
+
387
+ before do
388
+ @agent = Agent.new(:number => "007")
389
+ @person = Person.new(:title => "Dr", :ssn => "123-12-6666")
390
+ @agent_name = Name.new(:first_name => "James")
391
+ @person_name = Name.new(:first_name => "Jack")
392
+ @agent.names << @agent_name
393
+ @person.name = @person_name
394
+ @agent.save
395
+ @person.save
396
+ end
397
+
398
+ context "when the document is an embeds_one" do
399
+
400
+ it "sets the association_name" do
401
+ @agent_name.namable = @agent
402
+ @agent_name.namable.should == @agent
403
+ @agent_name.association_name.should == "names"
404
+ end
405
+ end
406
+
407
+ context "when the document is an embeds_many" do
408
+
409
+ it "sets the association_name" do
410
+ @person_name.namable = @person
411
+ @person_name.namable.should == @person
412
+ @person_name.association_name.should == "name"
413
+ end
414
+ end
415
+ end
416
+
417
+ context "saving an existing parent document with existing children" do
418
+
419
+ before do
420
+ @address = @person.addresses.create(:street => "Oxford St")
421
+ @address.city = "London"
422
+ @person.save
423
+ end
424
+
425
+ it "saves all dirty children" do
426
+ from_db = Person.find(@person.id)
427
+ from_db.addresses.first.city.should == "London"
428
+ end
429
+ end
430
+
431
+ context "saving an existing parent document with new children" do
432
+
433
+ context "when building" do
434
+
435
+ before do
436
+ @address = @person.addresses.build(:street => "Oxford St")
437
+ @person.save
438
+ end
439
+
440
+ it "saves all new children" do
441
+ from_db = Person.find(@person.id)
442
+ from_db.addresses.first.should == @address
443
+ end
444
+ end
445
+
446
+ context "when appending one elements" do
447
+
448
+ before do
449
+ @address = Address.new(:street => "Oxford St")
450
+ @person.addresses << @address
451
+ @person.save
452
+ end
453
+
454
+ it "saves all new children" do
455
+ from_db = Person.find(@person.id)
456
+ from_db.addresses.first.should == @address
457
+ end
458
+ end
459
+
460
+ context "when appending several elements" do
461
+
462
+ before do
463
+ @address = Address.new(:street => "Oxford St")
464
+ @address2 = Address.new(:street => "Cambridge St")
465
+ @person.addresses << @address
466
+ @person.addresses << @address2
467
+ @person.save
468
+ end
469
+
470
+ it "saves all new children" do
471
+ from_db = Person.find(@person.id)
472
+ from_db.addresses.count.should == 2
473
+ from_db.addresses.first.should == @address
474
+ from_db.addresses.second.should == @address2
475
+ end
476
+ end
477
+
478
+ context "when creating" do
479
+
480
+ before do
481
+ @address = @person.addresses.create(:street => "Oxford St")
482
+ end
483
+
484
+ it "saves all new children" do
485
+ @person.reload.addresses.first.should == @address
486
+ end
487
+ end
488
+
489
+ context "when overwriting" do
490
+ before do
491
+ @person.addresses.build(:street => "Oxford St")
492
+ @person.addresses = @person.addresses
493
+ end
494
+
495
+ it "still recognizes the embedded document as a new record" do
496
+ @person.addresses.first.should be_new_record
497
+ end
498
+ end
499
+
500
+ context "when overwriting with nil" do
501
+ before do
502
+ @person.addresses = nil
503
+ end
504
+
505
+ it "sets the association to an empty array" do
506
+ @person.addresses.should == []
507
+ end
508
+ end
509
+
510
+ end
511
+
512
+ context "one level nested" do
513
+
514
+ before do
515
+ @address = @person.addresses.create(:street => "Oxford St")
516
+ @name = @person.create_name(:first_name => "Gordon")
517
+ end
518
+
519
+ it "persists all the associations properly" do
520
+ @name.last_name = "Brown"
521
+ @person.name.last_name.should == "Brown"
522
+ end
523
+
524
+ it "sets the new_record values properly" do
525
+ from_db = Person.find(@person.id)
526
+ new_name = from_db.create_name(:first_name => "Flash")
527
+ new_name.new_record?.should be_false
528
+ end
529
+
530
+ context "updating embedded arrays" do
531
+
532
+ before do
533
+ @person.addresses.create(:street => "Picadilly Circus")
534
+ @from_db = Person.find(@person.id)
535
+ @first = @from_db.addresses[0]
536
+ @second = @from_db.addresses[1]
537
+ end
538
+
539
+ it "does not change the internal order of the array" do
540
+ @from_db.addresses.first.update_attributes(:city => "London")
541
+ @from_db.addresses.should == [ @first, @second ]
542
+ end
543
+
544
+ it "does not change the internal order of the attributes in the parent" do
545
+ @from_db.addresses.first.update_attributes(:city => "London")
546
+ @from_db.attributes["addresses"].should == [@first.attributes, @second.attributes]
547
+ end
548
+
549
+ context "updating an element that is a new record" do
550
+ before do
551
+ @third = Address.new(:street => "Foo")
552
+ @fourth = Address.new(:street => "Bar")
553
+ @from_db.addresses << @third
554
+ @from_db.addresses << @fourth
555
+ @from_db.save!
556
+ end
557
+
558
+ it "does not change the internal order of the array" do
559
+ @third.update_attributes(:city => "London")
560
+ @from_db.addresses.should == [ @first, @second, @third, @fourth ]
561
+ end
562
+
563
+ it "does not change the internal order of the attributes in the parent" do
564
+ @third.update_attributes(:city => "London")
565
+ @from_db.attributes["addresses"].should == [@first.attributes, @second.attributes, @third.attributes, @fourth.attributes]
566
+ end
567
+ end
568
+ end
569
+
570
+ describe "#first" do
571
+
572
+ let(:person) do
573
+ Person.create(:ssn => "444-33-7777")
574
+ end
575
+
576
+ before do
577
+ @video_id = person.videos.create(:title => "Oldboy").id
578
+ end
579
+
580
+ it "does not generate a new id each time" do
581
+ 5.times { person.videos.first.id.should == @video_id }
582
+ end
583
+ end
584
+
585
+ describe "#delete_all" do
586
+
587
+ it "removes the appropriate documents" do
588
+ @person.addresses.delete_all(:conditions => { :street => "Oxford St" }).should == 1
589
+ @person.addresses.size.should == 0
590
+ end
591
+ end
592
+
593
+ describe "#destroy_all" do
594
+
595
+ it "removes the appropriate documents" do
596
+ @person.addresses.destroy_all(:conditions => { :street => "Oxford St" }).should == 1
597
+ @person.addresses.size.should == 0
598
+ end
599
+ end
600
+ end
601
+
602
+ context "embedded_in instantiated and added later to parent" do
603
+ before do
604
+ @address = Address.new
605
+ @person = Person.new
606
+ end
607
+
608
+ it "doesn't memoize a nil parent" do
609
+ @address.addressable
610
+ @person.addresses << @address
611
+ @address.addressable.should == @person
612
+ end
613
+ end
614
+
615
+ context "multiple levels nested" do
616
+
617
+ context "when a has-many to has_one" do
618
+
619
+ before do
620
+ @person.phone_numbers.create(:number => "4155551212")
621
+ end
622
+
623
+ it "persists all the associations properly" do
624
+ from_db = Person.find(@person.id)
625
+ phone = from_db.phone_numbers.first
626
+ phone.create_country_code(:code => 1)
627
+ from_db.phone_numbers.first.country_code.code.should == 1
628
+ end
629
+
630
+ end
631
+
632
+ context "when a has-one to has-many" do
633
+
634
+ before do
635
+ @person = Person.new(:title => "Sir", :ssn => "1")
636
+ @name = Name.new(:first_name => "Syd")
637
+ @person.name = @name
638
+ @person.save
639
+ end
640
+
641
+ it "persists all the associations properly" do
642
+ from_db = Person.find(@person.id)
643
+ translation = Translation.new(:language => "fr")
644
+ from_db.name.translations << translation
645
+ from_db.attributes[:name][:translations].should_not be_nil
646
+ end
647
+
648
+ end
649
+
650
+ context "when a has-many to has-many" do
651
+
652
+ before do
653
+ @address = Address.new(:street => "Upper Street")
654
+ @person.addresses << @address
655
+ end
656
+
657
+ it "bubbles the child association up to root" do
658
+ location = Location.new(:name => "Home")
659
+ @address.locations << location
660
+ location.stubs(:valid?).returns(false)
661
+ @person.save
662
+ @person.addresses.first.locations.first.should == location
663
+ end
664
+ end
665
+ end
666
+ end
667
+
668
+ context "references many as array" do
669
+
670
+ context "with a saved parent" do
671
+
672
+ before do
673
+ @person = Person.create!(:ssn => "992-33-1010")
674
+ end
675
+
676
+ context "appending a new document" do
677
+
678
+ before do
679
+ @preference = Preference.new(:name => "test")
680
+ @person.preferences << @preference
681
+ end
682
+
683
+ it "adds the document to the array" do
684
+ @person.preferences.first.should == @preference
685
+ end
686
+
687
+ it "adds the parent document to the reverse association" do
688
+ @preference.people.first.should == @person
689
+ end
690
+ end
691
+
692
+ context "building a document" do
693
+
694
+ before do
695
+ @preference = @person.preferences.build(:name => "test")
696
+ end
697
+
698
+ it "adds the document to the array" do
699
+ @person.preferences.first.should == @preference
700
+ end
701
+
702
+ it "adds the parent document to the reverse association" do
703
+ @preference.people.first.should == @person
704
+ end
705
+ end
706
+
707
+ context "creating a document" do
708
+
709
+ before do
710
+ @preference = @person.preferences.create(:name => "test")
711
+ end
712
+
713
+ it "adds the document to the array" do
714
+ @person.preferences.first.should == @preference
715
+ end
716
+
717
+ it "adds the parent document to the reverse association" do
718
+ @preference.people.first.should == @person
719
+ end
720
+ end
721
+ end
722
+
723
+ context "with a new parent" do
724
+
725
+ let(:person) do
726
+ Person.new(:ssn => "992-33-1010")
727
+ end
728
+
729
+ context "appending a new document" do
730
+
731
+ before do
732
+ @preference = Preference.new(:name => "test")
733
+ person.preferences << @preference
734
+ end
735
+
736
+ it "adds the document to the array" do
737
+ person.preferences.first.should == @preference
738
+ end
739
+ end
740
+
741
+ context "building a document" do
742
+
743
+ before do
744
+ @preference = person.preferences.build(:name => "test")
745
+ end
746
+
747
+ it "adds the document to the array" do
748
+ person.preferences.first.should == @preference
749
+ end
750
+
751
+ it "adds the parent document to the reverse association"
752
+ end
753
+
754
+ context "creating a document" do
755
+
756
+ before do
757
+ @preference = person.preferences.create(:name => "test")
758
+ end
759
+
760
+ it "adds the document to the array" do
761
+ person.preferences.first.should == @preference
762
+ end
763
+
764
+ it "adds the parent document to the reverse association"
765
+ end
766
+ end
767
+ end
768
+ end