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,88 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Associations::MetaData do
4
+
5
+ before do
6
+ @extension = lambda { "Test" }
7
+ end
8
+
9
+ let(:association) do
10
+ Mongoid::Associations::HasManyRelated
11
+ end
12
+
13
+ let(:options) do
14
+ Mongoid::Associations::Options.new(
15
+ :name => :games,
16
+ :extend => @extension,
17
+ :foreign_key => "person_id",
18
+ :inverse_of => :people
19
+ )
20
+ end
21
+
22
+ let(:metadata) do
23
+ Mongoid::Associations::MetaData.new(association, options)
24
+ end
25
+
26
+ describe "#extension" do
27
+
28
+ it "delegates to the options" do
29
+ metadata.extension.should == @extension
30
+ end
31
+ end
32
+
33
+ describe "#foreign_key" do
34
+
35
+ it "delegates to the options" do
36
+ metadata.foreign_key.should == "person_id"
37
+ end
38
+ end
39
+
40
+ describe "#inverse_of" do
41
+
42
+ it "delegates to the options" do
43
+ metadata.inverse_of.should == :people
44
+ end
45
+ end
46
+
47
+ describe "#klass" do
48
+
49
+ it "returns the associated klass" do
50
+ metadata.klass.should == Game
51
+ end
52
+ end
53
+
54
+ describe "#macro" do
55
+
56
+ it "returns the association macro" do
57
+ metadata.macro.should == :has_many_related
58
+ end
59
+ end
60
+
61
+ describe "#name" do
62
+
63
+ it "delegates to the options" do
64
+ metadata.name.should == "games"
65
+ end
66
+ end
67
+
68
+ describe "#options" do
69
+
70
+ it "returns the association options" do
71
+ metadata.options.should == options
72
+ end
73
+ end
74
+
75
+ describe "#polymorphic" do
76
+
77
+ it "delegates to the options" do
78
+ metadata.polymorphic.should be_false
79
+ end
80
+ end
81
+
82
+ describe "#association" do
83
+
84
+ it "returns the association type" do
85
+ metadata.association.should == Mongoid::Associations::HasManyRelated
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,192 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Associations::Options do
4
+
5
+ describe "#name" do
6
+
7
+ before do
8
+ @attributes = { :name => :addresses }
9
+ @options = Mongoid::Associations::Options.new(@attributes)
10
+ end
11
+
12
+ it "returns the association name" do
13
+ @options.name.should == "addresses"
14
+ end
15
+
16
+ end
17
+
18
+ describe "#extend" do
19
+
20
+ context "when extension exists" do
21
+
22
+ before do
23
+ @attributes = { :extend => lambda { "Test" } }
24
+ @options = Mongoid::Associations::Options.new(@attributes)
25
+ end
26
+
27
+ it "returns the proc" do
28
+ @options.extension.should == @attributes[:extend]
29
+ end
30
+
31
+ end
32
+
33
+ context "when extension doesnt exist" do
34
+
35
+ before do
36
+ @options = Mongoid::Associations::Options.new({})
37
+ end
38
+
39
+ it "returns nil" do
40
+ @options.extension.should be_nil
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ describe "extension?" do
48
+
49
+ context "when extension exists" do
50
+
51
+ before do
52
+ @attributes = { :extend => lambda { "Test" } }
53
+ @options = Mongoid::Associations::Options.new(@attributes)
54
+ end
55
+
56
+ it "returns true" do
57
+ @options.extension?.should be_true
58
+ end
59
+
60
+ end
61
+
62
+ context "when extension doesnt exist" do
63
+
64
+ before do
65
+ @options = Mongoid::Associations::Options.new({})
66
+ end
67
+
68
+ it "returns false" do
69
+ @options.extension?.should be_false
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+
76
+ describe "#foreign_key" do
77
+
78
+ context "when no custom key defined" do
79
+
80
+ before do
81
+ @attributes = { :name => :posts }
82
+ @options = Mongoid::Associations::Options.new(@attributes)
83
+ end
84
+
85
+ it "returns the association foreign_key" do
86
+ @options.foreign_key.should == "post_id"
87
+ end
88
+ end
89
+
90
+ context "when a custom key is defined" do
91
+
92
+ before do
93
+ @attributes = { :name => :posts, :foreign_key => "blog_post_id" }
94
+ @options = Mongoid::Associations::Options.new(@attributes)
95
+ end
96
+
97
+ it "returns the custom foreign_key" do
98
+ @options.foreign_key.should == "blog_post_id"
99
+ end
100
+ end
101
+ end
102
+
103
+ describe "#inverse_of" do
104
+
105
+ before do
106
+ @attributes = { :inverse_of => :addresses }
107
+ @options = Mongoid::Associations::Options.new(@attributes)
108
+ end
109
+
110
+ it "returns the inverse_of value" do
111
+ @options.inverse_of.should == :addresses
112
+ end
113
+
114
+ end
115
+
116
+ describe "#klass" do
117
+
118
+ context "when class_name provided" do
119
+
120
+ before do
121
+ @attributes = { :class_name => "Person" }
122
+ @options = Mongoid::Associations::Options.new(@attributes)
123
+ end
124
+
125
+ it "constantizes the class name" do
126
+ @options.klass.should == Person
127
+ end
128
+
129
+ end
130
+
131
+ context "when no class_name provided" do
132
+
133
+ context "when association name is singular" do
134
+
135
+ before do
136
+ @attributes = { :name => :person }
137
+ @options = Mongoid::Associations::Options.new(@attributes)
138
+ end
139
+
140
+ it "classifies and constantizes the association name" do
141
+ @options.klass.should == Person
142
+ end
143
+
144
+ end
145
+
146
+ context "when association name is plural" do
147
+
148
+ before do
149
+ @attributes = { :name => :people }
150
+ @options = Mongoid::Associations::Options.new(@attributes)
151
+ end
152
+
153
+ it "classifies and constantizes the association name" do
154
+ @options.klass.should == Person
155
+ end
156
+
157
+ end
158
+
159
+ end
160
+
161
+ end
162
+
163
+ describe "#polymorphic" do
164
+
165
+ context "when attribute provided" do
166
+
167
+ before do
168
+ @attributes = { :polymorphic => true }
169
+ @options = Mongoid::Associations::Options.new(@attributes)
170
+ end
171
+
172
+ it "returns the attribute" do
173
+ @options.polymorphic.should be_true
174
+ end
175
+
176
+ end
177
+
178
+ context "when attribute not provided" do
179
+
180
+ before do
181
+ @options = Mongoid::Associations::Options.new
182
+ end
183
+
184
+ it "returns false" do
185
+ @options.polymorphic.should be_false
186
+ end
187
+
188
+ end
189
+
190
+ end
191
+
192
+ end
@@ -0,0 +1,595 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Associations do
4
+
5
+ before do
6
+ @collection = stub(:name => "people")
7
+ @database = stub(:collection => @collection)
8
+ Mongoid.stubs(:database).returns(@database)
9
+ end
10
+
11
+ after do
12
+ Person.instance_variable_set(:@collection, nil)
13
+ @database = nil
14
+ @collection = nil
15
+ end
16
+
17
+ describe "#association=" do
18
+
19
+ context "when child is a has one" do
20
+
21
+ before do
22
+ @person = Person.new(:title => "Sir", :age => 30)
23
+ @name = Name.new(:first_name => "Test", :last_name => "User")
24
+ @person.name = @name
25
+ end
26
+
27
+ it "parentizes the association" do
28
+ @name._parent.should == @person
29
+ end
30
+
31
+ it "sets the child attributes on the parent" do
32
+ @person.attributes[:name].should ==
33
+ { "_id" => "test-user", "first_name" => "Test", "last_name" => "User", "_type" => "Name" }
34
+ end
35
+
36
+ end
37
+
38
+ context "when child and parent are namespaced" do
39
+
40
+ before do
41
+ @patient = Medical::Patient.new(:name => "Ridley")
42
+ @prescription = Medical::Prescription.new(:name => "Zoloft")
43
+ @patient.prescriptions << @prescription
44
+ @second = @patient.prescriptions.build(:name => "Codeine")
45
+ end
46
+
47
+ it "sets the correct association classes" do
48
+ @patient.prescriptions.first.should == @prescription
49
+ @patient.prescriptions.last.should == @second
50
+ end
51
+
52
+ end
53
+
54
+ context "when setting a parent" do
55
+
56
+ context "when the child is one level deep" do
57
+
58
+ before do
59
+ @person = Person.new(:title => "Mr")
60
+ @address = Address.new(:street => "Picadilly Circus")
61
+ @address.addressable = @person
62
+ end
63
+
64
+ it "re-parentizes the association" do
65
+ @address._parent.should == @person
66
+ end
67
+
68
+ it "adds the child attributes to the parent" do
69
+ @person.attributes[:addresses].should ==
70
+ [{ "_id" => "picadilly-circus", "street" => "Picadilly Circus", "_type" => "Address" }]
71
+ end
72
+
73
+ end
74
+
75
+ context "when the child is multiple levels deep" do
76
+
77
+ before do
78
+ @person = Person.new(:title => "Mr")
79
+ @phone = Phone.new(:number => "415-555-1212")
80
+ @person.phone_numbers = [@phone]
81
+ @country_code = CountryCode.new(:code => 1)
82
+ @phone.country_code = @country_code
83
+ end
84
+
85
+ it "properly decorates all parent references" do
86
+ @country_code.phone_number.should == @phone
87
+ @phone.person.should == @person
88
+ @country_code.phone_number.person.should == @person
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+
97
+ describe "#associations" do
98
+
99
+ before do
100
+ @person = Person.new
101
+ end
102
+
103
+ it "is a hash with name keys and meta data values" do
104
+ @person.associations["addresses"].should be_a_kind_of(Mongoid::Associations::MetaData)
105
+ end
106
+ end
107
+
108
+ describe ".embedded_in" do
109
+
110
+ it "creates a reader for the association" do
111
+ address = Address.new
112
+ address.should respond_to(:addressable)
113
+ end
114
+
115
+ it "creates a writer for the association" do
116
+ address = Address.new
117
+ address.should respond_to(:addressable=)
118
+ end
119
+
120
+ it "allows the parent to be any type of class" do
121
+ phone_number = Phone.new(:number => "415-555-1212")
122
+ code = CountryCode.new(:code => 1)
123
+ phone_number.country_code = code
124
+ code.phone_number.should == phone_number
125
+ end
126
+
127
+ context "when adding an anonymous extension" do
128
+
129
+ before do
130
+ @person = Person.new(:title => "Dr")
131
+ @address = Address.new(:street => "Clarkenwell Road")
132
+ @person.addresses << @address
133
+ end
134
+
135
+ it "defines the method on the association" do
136
+ @address.addressable.extension.should == "Testing"
137
+ end
138
+
139
+ end
140
+
141
+ context "when inverse_of not supplied" do
142
+
143
+ it "raises an error" do
144
+ lambda { Person.class_eval { embedded_in :nothing } }.should raise_error
145
+ end
146
+
147
+ end
148
+
149
+ context "when navigating the graph" do
150
+
151
+ before do
152
+ @person = Person.new(:title => "Mr")
153
+ @name = Name.new(:first_name => "Mason")
154
+ @address = Address.new(:street => "King St.")
155
+ @person.name = @name
156
+ @person.addresses << @address
157
+ end
158
+
159
+ it "allows referencing another child through the parent" do
160
+ @name.person.addresses.first.should == @address
161
+ end
162
+
163
+ end
164
+
165
+ end
166
+
167
+ describe "#build_*" do
168
+
169
+ before do
170
+ @canvas = Canvas.new
171
+ end
172
+
173
+ context "when type is passed in" do
174
+
175
+ before do
176
+ @writer = @canvas.build_writer(:speed => 250, :_type => "HtmlWriter")
177
+ end
178
+
179
+ it "returns a new document" do
180
+ @writer.should_not be_nil
181
+ end
182
+
183
+ it "returns the properly typed document" do
184
+ @writer.should be_a_kind_of(HtmlWriter)
185
+ end
186
+
187
+ it "sets the appropriate attributes" do
188
+ @writer.speed.should == 250
189
+ end
190
+
191
+ end
192
+
193
+ context "when type is not passed in" do
194
+
195
+ before do
196
+ @writer = @canvas.build_writer(:speed => 250)
197
+ end
198
+
199
+ it "returns a new document" do
200
+ @writer.should_not be_nil
201
+ end
202
+
203
+ it "returns the properly typed document" do
204
+ @writer.should be_a_kind_of(Writer)
205
+ end
206
+
207
+ it "sets the appropriate attributes" do
208
+ @writer.speed.should == 250
209
+ end
210
+
211
+ end
212
+
213
+ context "when attributes are nil" do
214
+
215
+ before do
216
+ @writer = @canvas.build_writer(nil)
217
+ end
218
+
219
+ it "defaults them to empty" do
220
+ @writer.should be_a_kind_of(Writer)
221
+ end
222
+
223
+ end
224
+
225
+ end
226
+
227
+ describe "#create_*" do
228
+
229
+ before do
230
+ @canvas = Canvas.new
231
+ end
232
+
233
+ context "when type is passed in" do
234
+
235
+ before do
236
+ Mongoid::Commands::Save.expects(:execute)
237
+ @writer = @canvas.create_writer(:speed => 250, :_type => "HtmlWriter")
238
+ end
239
+
240
+ it "returns a new document" do
241
+ @writer.should_not be_nil
242
+ end
243
+
244
+ it "returns the properly typed document" do
245
+ @writer.should be_a_kind_of(HtmlWriter)
246
+ end
247
+
248
+ it "sets the appropriate attributes" do
249
+ @writer.speed.should == 250
250
+ end
251
+
252
+ end
253
+
254
+ context "when type is not passed in" do
255
+
256
+ before do
257
+ Mongoid::Commands::Save.expects(:execute)
258
+ @writer = @canvas.create_writer(:speed => 250, :_type => "HtmlWriter")
259
+ end
260
+
261
+ it "returns a new document" do
262
+ @writer.should_not be_nil
263
+ end
264
+
265
+ it "returns the properly typed document" do
266
+ @writer.should be_a_kind_of(Writer)
267
+ end
268
+
269
+ it "sets the appropriate attributes" do
270
+ @writer.speed.should == 250
271
+ end
272
+
273
+ end
274
+
275
+ end
276
+
277
+ describe ".embed_many" do
278
+
279
+ it "adds a new Association to the collection" do
280
+ person = Person.new
281
+ person.addresses.should_not be_nil
282
+ end
283
+
284
+ it "creates a reader for the association" do
285
+ person = Person.new
286
+ person.should respond_to(:addresses)
287
+ end
288
+
289
+ it "creates a writer for the association" do
290
+ person = Person.new
291
+ person.should respond_to(:addresses=)
292
+ end
293
+
294
+ context "when adding an anonymous extension" do
295
+
296
+ it "defines the method on the association" do
297
+ person = Person.new
298
+ person.addresses.extension.should == "Testing"
299
+ end
300
+
301
+ end
302
+
303
+ context "when setting the association directly" do
304
+
305
+ before do
306
+ @attributes = { :title => "Sir",
307
+ :addresses => [
308
+ { :street => "Street 1" },
309
+ { :street => "Street 2" } ] }
310
+ @person = Person.new(@attributes)
311
+ end
312
+
313
+ it "sets the attributes for the association" do
314
+ address = Address.new(:street => "New Street")
315
+ @person.addresses = [address]
316
+ @person.addresses.first.street.should == "New Street"
317
+ end
318
+
319
+ end
320
+
321
+ context "when a class_name is supplied" do
322
+
323
+ before do
324
+ @attributes = { :title => "Sir",
325
+ :phone_numbers => [ { :number => "404-555-1212" } ]
326
+ }
327
+ @person = Person.new(@attributes)
328
+ end
329
+
330
+ it "sets the association name" do
331
+ @person.phone_numbers.first.should == Phone.new(:number => "404-555-1212")
332
+ end
333
+
334
+ end
335
+
336
+ context "when updating objects internally" do
337
+
338
+ before do
339
+ @address = Address.new(:street => "Bourke Street")
340
+ @person = Person.new(:title => "Sir")
341
+ @person.addresses << @address
342
+ @person.update_addresses
343
+ end
344
+
345
+ it "retains its references to the original objects" do
346
+ @address.street.should == "Updated Address"
347
+ end
348
+
349
+ end
350
+
351
+ end
352
+
353
+ describe ".embed_one" do
354
+
355
+ before do
356
+ @person = Person.new
357
+ end
358
+
359
+ it "adds a new Association to the document" do
360
+ @person.name.should be_nil
361
+ end
362
+
363
+ it "creates a reader for the association" do
364
+ @person.should respond_to(:name)
365
+ end
366
+
367
+ it "creates a writer for the association" do
368
+ @person.should respond_to(:name=)
369
+ end
370
+
371
+ it "creates a builder for the association" do
372
+ @person.should respond_to(:build_name)
373
+ end
374
+
375
+ it "creates a creator for the association" do
376
+ @person.should respond_to(:create_name)
377
+ end
378
+
379
+ context "when adding an anonymous extension" do
380
+
381
+ it "defines the method on the association" do
382
+ @person.name = Name.new(:first_name => "Richard")
383
+ @person.name.extension.should == "Testing"
384
+ end
385
+
386
+ end
387
+
388
+ context "when setting the association directly" do
389
+
390
+ before do
391
+ @attributes = { :title => "Sir",
392
+ :name => { :first_name => "Test" } }
393
+ @person = Person.new(@attributes)
394
+ end
395
+
396
+ it "sets the attributes for the association" do
397
+ name = Name.new(:first_name => "New Name")
398
+ @person.name = name
399
+ @person.name.first_name.should == "New Name"
400
+ end
401
+
402
+ end
403
+
404
+ context "when a class_name is supplied" do
405
+
406
+ before do
407
+ @attributes = { :title => "Sir",
408
+ :pet => { :name => "Fido" }
409
+ }
410
+ @person = Person.new(@attributes)
411
+ end
412
+
413
+ it "sets the association name" do
414
+ @person.pet.should == Animal.new(:name => "Fido")
415
+ end
416
+
417
+ end
418
+
419
+ end
420
+
421
+ describe ".reflect_on_association" do
422
+
423
+ it "returns the association class for the name" do
424
+ Person.reflect_on_association(:addresses).should == :embed_many
425
+ end
426
+
427
+ end
428
+
429
+ describe ".belongs_to_related" do
430
+
431
+ before do
432
+ @game = Game.new
433
+ end
434
+
435
+ it "creates an id field for the relationship" do
436
+ @game.should respond_to(:person_id)
437
+ end
438
+
439
+ it "creates a getter for the parent" do
440
+ @game.should respond_to(:person)
441
+ end
442
+
443
+ it "defaults the foreign_key option to the name_id" do
444
+ @game.associations["person"].foreign_key.should == "person_id"
445
+ end
446
+
447
+ context "when document is root level" do
448
+
449
+ it "puts an index on the foreign key" do
450
+ Game.expects(:index).with("person_id")
451
+ Game.belongs_to_related :person
452
+ end
453
+
454
+ end
455
+
456
+ context "when using object ids" do
457
+
458
+ before do
459
+ Mongoid.use_object_ids = true
460
+ end
461
+
462
+ after do
463
+ Mongoid.use_object_ids = false
464
+ end
465
+
466
+ it "sets the foreign key as an object id" do
467
+ Game.expects(:field).with("person_id", :type => Mongo::ObjectID)
468
+ Game.belongs_to_related :person
469
+ end
470
+ end
471
+
472
+ end
473
+
474
+ describe ".has_one_related" do
475
+
476
+ before do
477
+ @person = Person.new
478
+ @game = Game.new
479
+ @person.game = @game
480
+ end
481
+
482
+ it "creates a getter for the relationship" do
483
+ @person.should respond_to(:game)
484
+ end
485
+
486
+ it "creates a setter for the relationship" do
487
+ @person.should respond_to(:game=)
488
+ end
489
+
490
+ context "when adding an anonymous extension" do
491
+
492
+ it "defines the method on the association" do
493
+ @person.game.extension.should == "Testing"
494
+ end
495
+
496
+ end
497
+
498
+ end
499
+
500
+ describe ".has_many_related" do
501
+
502
+ it "creates a getter for the association" do
503
+ Person.new.should respond_to(:posts)
504
+ end
505
+
506
+ it "creates a setter for the association" do
507
+ Person.new.should respond_to(:posts=)
508
+ end
509
+
510
+ context "when adding an anonymous extension" do
511
+
512
+ before do
513
+ @person = Person.new
514
+ end
515
+
516
+ it "defines the method on the association" do
517
+ @person.posts.extension.should == "Testing"
518
+ end
519
+
520
+ end
521
+
522
+ end
523
+
524
+ describe "#update_associations" do
525
+
526
+ context "when associations exist" do
527
+
528
+ context "when the document is a new record" do
529
+
530
+ before do
531
+ @related = stub(:id => "100", :person= => true)
532
+ @person = Person.new
533
+ @person.posts = [@related]
534
+ end
535
+
536
+ it "saves each association" do
537
+ @related.expects(:save).returns(@related)
538
+ @person.update_associations(:posts)
539
+ end
540
+
541
+ end
542
+
543
+ context "when the document is not new" do
544
+
545
+ before do
546
+ @related = stub(:id => "100", :person= => true)
547
+ @person = Person.new
548
+ @person.instance_variable_set(:@new_record, false)
549
+ @person.posts = [@related]
550
+ end
551
+
552
+ it "does not save each association" do
553
+ @person.update_associations(:posts)
554
+ end
555
+
556
+ end
557
+
558
+ end
559
+
560
+ context "when no associations exist" do
561
+
562
+ before do
563
+ @person = Person.new
564
+ end
565
+
566
+ it "does nothing" do
567
+ Post.expects(:find).returns([])
568
+ @person.update_associations(:posts)
569
+ @person.posts.first.should be_nil
570
+ end
571
+
572
+ end
573
+
574
+ end
575
+
576
+ describe "#update_association" do
577
+
578
+ context "when the association exists" do
579
+
580
+ before do
581
+ @related = stub(:id => "100", :person= => true)
582
+ @person = Person.new
583
+ @person.game = @related
584
+ end
585
+
586
+ it "saves each association" do
587
+ @related.expects(:save).returns(@related)
588
+ @person.update_association(:game)
589
+ end
590
+
591
+ end
592
+
593
+ end
594
+
595
+ end