humanoid 1.2.7

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 (210) hide show
  1. data/.gitignore +6 -0
  2. data/.watchr +29 -0
  3. data/HISTORY +342 -0
  4. data/MIT_LICENSE +20 -0
  5. data/README.rdoc +56 -0
  6. data/Rakefile +53 -0
  7. data/VERSION +1 -0
  8. data/caliper.yml +4 -0
  9. data/humanoid.gemspec +374 -0
  10. data/lib/humanoid.rb +111 -0
  11. data/lib/humanoid/associations.rb +258 -0
  12. data/lib/humanoid/associations/belongs_to.rb +64 -0
  13. data/lib/humanoid/associations/belongs_to_related.rb +62 -0
  14. data/lib/humanoid/associations/has_many.rb +180 -0
  15. data/lib/humanoid/associations/has_many_related.rb +109 -0
  16. data/lib/humanoid/associations/has_one.rb +95 -0
  17. data/lib/humanoid/associations/has_one_related.rb +81 -0
  18. data/lib/humanoid/associations/options.rb +57 -0
  19. data/lib/humanoid/associations/proxy.rb +31 -0
  20. data/lib/humanoid/attributes.rb +184 -0
  21. data/lib/humanoid/callbacks.rb +23 -0
  22. data/lib/humanoid/collection.rb +118 -0
  23. data/lib/humanoid/collections/cyclic_iterator.rb +34 -0
  24. data/lib/humanoid/collections/master.rb +28 -0
  25. data/lib/humanoid/collections/mimic.rb +46 -0
  26. data/lib/humanoid/collections/operations.rb +41 -0
  27. data/lib/humanoid/collections/slaves.rb +44 -0
  28. data/lib/humanoid/commands.rb +182 -0
  29. data/lib/humanoid/commands/create.rb +21 -0
  30. data/lib/humanoid/commands/delete.rb +16 -0
  31. data/lib/humanoid/commands/delete_all.rb +23 -0
  32. data/lib/humanoid/commands/deletion.rb +18 -0
  33. data/lib/humanoid/commands/destroy.rb +19 -0
  34. data/lib/humanoid/commands/destroy_all.rb +23 -0
  35. data/lib/humanoid/commands/save.rb +27 -0
  36. data/lib/humanoid/components.rb +24 -0
  37. data/lib/humanoid/config.rb +84 -0
  38. data/lib/humanoid/contexts.rb +25 -0
  39. data/lib/humanoid/contexts/enumerable.rb +117 -0
  40. data/lib/humanoid/contexts/ids.rb +25 -0
  41. data/lib/humanoid/contexts/mongo.rb +224 -0
  42. data/lib/humanoid/contexts/paging.rb +42 -0
  43. data/lib/humanoid/criteria.rb +259 -0
  44. data/lib/humanoid/criterion/complex.rb +21 -0
  45. data/lib/humanoid/criterion/exclusion.rb +65 -0
  46. data/lib/humanoid/criterion/inclusion.rb +91 -0
  47. data/lib/humanoid/criterion/optional.rb +128 -0
  48. data/lib/humanoid/cursor.rb +82 -0
  49. data/lib/humanoid/document.rb +300 -0
  50. data/lib/humanoid/enslavement.rb +38 -0
  51. data/lib/humanoid/errors.rb +77 -0
  52. data/lib/humanoid/extensions.rb +84 -0
  53. data/lib/humanoid/extensions/array/accessors.rb +17 -0
  54. data/lib/humanoid/extensions/array/aliasing.rb +4 -0
  55. data/lib/humanoid/extensions/array/assimilation.rb +26 -0
  56. data/lib/humanoid/extensions/array/conversions.rb +29 -0
  57. data/lib/humanoid/extensions/array/parentization.rb +13 -0
  58. data/lib/humanoid/extensions/boolean/conversions.rb +16 -0
  59. data/lib/humanoid/extensions/date/conversions.rb +15 -0
  60. data/lib/humanoid/extensions/datetime/conversions.rb +17 -0
  61. data/lib/humanoid/extensions/float/conversions.rb +16 -0
  62. data/lib/humanoid/extensions/hash/accessors.rb +38 -0
  63. data/lib/humanoid/extensions/hash/assimilation.rb +30 -0
  64. data/lib/humanoid/extensions/hash/conversions.rb +15 -0
  65. data/lib/humanoid/extensions/hash/criteria_helpers.rb +20 -0
  66. data/lib/humanoid/extensions/hash/scoping.rb +12 -0
  67. data/lib/humanoid/extensions/integer/conversions.rb +16 -0
  68. data/lib/humanoid/extensions/nil/assimilation.rb +13 -0
  69. data/lib/humanoid/extensions/object/conversions.rb +33 -0
  70. data/lib/humanoid/extensions/proc/scoping.rb +12 -0
  71. data/lib/humanoid/extensions/string/conversions.rb +15 -0
  72. data/lib/humanoid/extensions/string/inflections.rb +97 -0
  73. data/lib/humanoid/extensions/symbol/inflections.rb +36 -0
  74. data/lib/humanoid/extensions/time/conversions.rb +18 -0
  75. data/lib/humanoid/factory.rb +19 -0
  76. data/lib/humanoid/field.rb +39 -0
  77. data/lib/humanoid/fields.rb +62 -0
  78. data/lib/humanoid/finders.rb +224 -0
  79. data/lib/humanoid/identity.rb +39 -0
  80. data/lib/humanoid/indexes.rb +30 -0
  81. data/lib/humanoid/matchers.rb +36 -0
  82. data/lib/humanoid/matchers/all.rb +11 -0
  83. data/lib/humanoid/matchers/default.rb +26 -0
  84. data/lib/humanoid/matchers/exists.rb +13 -0
  85. data/lib/humanoid/matchers/gt.rb +11 -0
  86. data/lib/humanoid/matchers/gte.rb +11 -0
  87. data/lib/humanoid/matchers/in.rb +11 -0
  88. data/lib/humanoid/matchers/lt.rb +11 -0
  89. data/lib/humanoid/matchers/lte.rb +11 -0
  90. data/lib/humanoid/matchers/ne.rb +11 -0
  91. data/lib/humanoid/matchers/nin.rb +11 -0
  92. data/lib/humanoid/matchers/size.rb +11 -0
  93. data/lib/humanoid/memoization.rb +27 -0
  94. data/lib/humanoid/named_scope.rb +40 -0
  95. data/lib/humanoid/scope.rb +75 -0
  96. data/lib/humanoid/timestamps.rb +30 -0
  97. data/lib/humanoid/versioning.rb +28 -0
  98. data/perf/benchmark.rb +77 -0
  99. data/spec/integration/humanoid/associations_spec.rb +301 -0
  100. data/spec/integration/humanoid/attributes_spec.rb +22 -0
  101. data/spec/integration/humanoid/commands_spec.rb +216 -0
  102. data/spec/integration/humanoid/contexts/enumerable_spec.rb +33 -0
  103. data/spec/integration/humanoid/criteria_spec.rb +224 -0
  104. data/spec/integration/humanoid/document_spec.rb +587 -0
  105. data/spec/integration/humanoid/extensions_spec.rb +26 -0
  106. data/spec/integration/humanoid/finders_spec.rb +119 -0
  107. data/spec/integration/humanoid/inheritance_spec.rb +137 -0
  108. data/spec/integration/humanoid/named_scope_spec.rb +46 -0
  109. data/spec/models/address.rb +39 -0
  110. data/spec/models/animal.rb +6 -0
  111. data/spec/models/comment.rb +8 -0
  112. data/spec/models/country_code.rb +6 -0
  113. data/spec/models/employer.rb +5 -0
  114. data/spec/models/game.rb +6 -0
  115. data/spec/models/inheritance.rb +56 -0
  116. data/spec/models/location.rb +5 -0
  117. data/spec/models/mixed_drink.rb +4 -0
  118. data/spec/models/name.rb +13 -0
  119. data/spec/models/namespacing.rb +11 -0
  120. data/spec/models/patient.rb +4 -0
  121. data/spec/models/person.rb +98 -0
  122. data/spec/models/pet.rb +7 -0
  123. data/spec/models/pet_owner.rb +6 -0
  124. data/spec/models/phone.rb +7 -0
  125. data/spec/models/post.rb +15 -0
  126. data/spec/models/translation.rb +5 -0
  127. data/spec/models/vet_visit.rb +5 -0
  128. data/spec/spec.opts +3 -0
  129. data/spec/spec_helper.rb +31 -0
  130. data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +141 -0
  131. data/spec/unit/mongoid/associations/belongs_to_spec.rb +193 -0
  132. data/spec/unit/mongoid/associations/has_many_related_spec.rb +387 -0
  133. data/spec/unit/mongoid/associations/has_many_spec.rb +471 -0
  134. data/spec/unit/mongoid/associations/has_one_related_spec.rb +179 -0
  135. data/spec/unit/mongoid/associations/has_one_spec.rb +282 -0
  136. data/spec/unit/mongoid/associations/options_spec.rb +191 -0
  137. data/spec/unit/mongoid/associations_spec.rb +545 -0
  138. data/spec/unit/mongoid/attributes_spec.rb +484 -0
  139. data/spec/unit/mongoid/callbacks_spec.rb +55 -0
  140. data/spec/unit/mongoid/collection_spec.rb +171 -0
  141. data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
  142. data/spec/unit/mongoid/collections/master_spec.rb +41 -0
  143. data/spec/unit/mongoid/collections/mimic_spec.rb +43 -0
  144. data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
  145. data/spec/unit/mongoid/commands/create_spec.rb +30 -0
  146. data/spec/unit/mongoid/commands/delete_all_spec.rb +58 -0
  147. data/spec/unit/mongoid/commands/delete_spec.rb +35 -0
  148. data/spec/unit/mongoid/commands/destroy_all_spec.rb +23 -0
  149. data/spec/unit/mongoid/commands/destroy_spec.rb +44 -0
  150. data/spec/unit/mongoid/commands/save_spec.rb +105 -0
  151. data/spec/unit/mongoid/commands_spec.rb +282 -0
  152. data/spec/unit/mongoid/config_spec.rb +165 -0
  153. data/spec/unit/mongoid/contexts/enumerable_spec.rb +374 -0
  154. data/spec/unit/mongoid/contexts/mongo_spec.rb +505 -0
  155. data/spec/unit/mongoid/contexts_spec.rb +25 -0
  156. data/spec/unit/mongoid/criteria_spec.rb +769 -0
  157. data/spec/unit/mongoid/criterion/complex_spec.rb +19 -0
  158. data/spec/unit/mongoid/criterion/exclusion_spec.rb +91 -0
  159. data/spec/unit/mongoid/criterion/inclusion_spec.rb +211 -0
  160. data/spec/unit/mongoid/criterion/optional_spec.rb +329 -0
  161. data/spec/unit/mongoid/cursor_spec.rb +74 -0
  162. data/spec/unit/mongoid/document_spec.rb +986 -0
  163. data/spec/unit/mongoid/enslavement_spec.rb +63 -0
  164. data/spec/unit/mongoid/errors_spec.rb +103 -0
  165. data/spec/unit/mongoid/extensions/array/accessors_spec.rb +50 -0
  166. data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +24 -0
  167. data/spec/unit/mongoid/extensions/array/conversions_spec.rb +35 -0
  168. data/spec/unit/mongoid/extensions/array/parentization_spec.rb +20 -0
  169. data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +49 -0
  170. data/spec/unit/mongoid/extensions/date/conversions_spec.rb +102 -0
  171. data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +70 -0
  172. data/spec/unit/mongoid/extensions/float/conversions_spec.rb +61 -0
  173. data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +184 -0
  174. data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +46 -0
  175. data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +21 -0
  176. data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +17 -0
  177. data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +14 -0
  178. data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +61 -0
  179. data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +24 -0
  180. data/spec/unit/mongoid/extensions/object/conversions_spec.rb +43 -0
  181. data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +34 -0
  182. data/spec/unit/mongoid/extensions/string/conversions_spec.rb +17 -0
  183. data/spec/unit/mongoid/extensions/string/inflections_spec.rb +208 -0
  184. data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +91 -0
  185. data/spec/unit/mongoid/extensions/time/conversions_spec.rb +70 -0
  186. data/spec/unit/mongoid/factory_spec.rb +31 -0
  187. data/spec/unit/mongoid/field_spec.rb +81 -0
  188. data/spec/unit/mongoid/fields_spec.rb +158 -0
  189. data/spec/unit/mongoid/finders_spec.rb +368 -0
  190. data/spec/unit/mongoid/identity_spec.rb +88 -0
  191. data/spec/unit/mongoid/indexes_spec.rb +93 -0
  192. data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
  193. data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
  194. data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
  195. data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
  196. data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
  197. data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
  198. data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
  199. data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
  200. data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
  201. data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
  202. data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
  203. data/spec/unit/mongoid/matchers_spec.rb +329 -0
  204. data/spec/unit/mongoid/memoization_spec.rb +75 -0
  205. data/spec/unit/mongoid/named_scope_spec.rb +123 -0
  206. data/spec/unit/mongoid/scope_spec.rb +240 -0
  207. data/spec/unit/mongoid/timestamps_spec.rb +25 -0
  208. data/spec/unit/mongoid/versioning_spec.rb +41 -0
  209. data/spec/unit/mongoid_spec.rb +37 -0
  210. metadata +431 -0
@@ -0,0 +1,387 @@
1
+ require "spec_helper"
2
+
3
+ describe Humanoid::Associations::HasManyRelated do
4
+
5
+ let(:block) do
6
+ Proc.new do
7
+ def extension
8
+ "Testing"
9
+ end
10
+ end
11
+ end
12
+
13
+ let(:options) do
14
+ Humanoid::Associations::Options.new(:name => :posts, :extend => block)
15
+ end
16
+
17
+ describe "#<<" do
18
+
19
+ before do
20
+ @child = stub
21
+ @second = stub
22
+ @children = [@child, @second]
23
+ end
24
+
25
+ context "when parent document has been saved" do
26
+
27
+ before do
28
+ @parent = stub(:id => "1", :new_record? => false, :class => Person)
29
+ Post.expects(:all).returns([])
30
+ @association = Humanoid::Associations::HasManyRelated.new(@parent, options)
31
+ end
32
+
33
+ it "saves and appends the child document" do
34
+ @child.expects(:person_id=).with(@parent.id)
35
+ @child.expects(:save).returns(true)
36
+ @association << @child
37
+ @association.size.should == 1
38
+ end
39
+
40
+ end
41
+
42
+ context "when parent document has not been saved" do
43
+
44
+ before do
45
+ @parent = stub(:id => "1", :new_record? => true, :class => Person)
46
+ Post.expects(:all).returns([])
47
+ @association = Humanoid::Associations::HasManyRelated.new(@parent, options)
48
+ end
49
+
50
+ it "appends the child document" do
51
+ @child.expects(:person_id=).with(@parent.id)
52
+ @association << @child
53
+ @association.size.should == 1
54
+ end
55
+
56
+ end
57
+
58
+ context "with multiple objects" do
59
+
60
+ before do
61
+ @parent = stub(:id => "1", :new_record? => true, :class => Person)
62
+ Post.expects(:all).returns([])
63
+ @association = Humanoid::Associations::HasManyRelated.new(@parent, options)
64
+ end
65
+
66
+ it "appends the child documents" do
67
+ @child.expects(:person_id=).with(@parent.id)
68
+ @second.expects(:person_id=).with(@parent.id)
69
+ @association << [@child, @second]
70
+ @association.size.should == 2
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+
77
+ describe "#build" do
78
+
79
+ before do
80
+ @parent = stub(:id => "5", :class => Person)
81
+ Post.expects(:all).returns([])
82
+ @association = Humanoid::Associations::HasManyRelated.new(@parent, options)
83
+ end
84
+
85
+ it "adds a new object to the association" do
86
+ @association.build(:title => "Sassy")
87
+ @association.size.should == 1
88
+ end
89
+
90
+ it "sets the parent object id on the child" do
91
+ @association.build(:title => "Sassy")
92
+ @association.first.person_id.should == @parent.id
93
+ end
94
+
95
+ it "returns the new object" do
96
+ @association.build(:title => "Sassy").title.should == "Sassy"
97
+ end
98
+
99
+ it "sets the parent object reference on the child" do
100
+ @association.build(:title => "Sassy")
101
+ @association.first.person.should == @parent
102
+ end
103
+
104
+ end
105
+
106
+ describe "#concat" do
107
+
108
+ before do
109
+ @child = stub
110
+ @second = stub
111
+ end
112
+
113
+ context "when parent document has been saved" do
114
+
115
+ before do
116
+ @parent = stub(:id => "1", :new_record? => false, :class => Person)
117
+ Post.expects(:all).returns([])
118
+ @association = Humanoid::Associations::HasManyRelated.new(@parent, options)
119
+ end
120
+
121
+ it "saves and appends the child document" do
122
+ @child.expects(:person_id=).with(@parent.id)
123
+ @child.expects(:save).returns(true)
124
+ @association.concat(@child)
125
+ @association.size.should == 1
126
+ end
127
+
128
+ end
129
+
130
+ context "when parent document has not been saved" do
131
+
132
+ before do
133
+ @parent = stub(:id => "1", :new_record? => true, :class => Person)
134
+ Post.expects(:all).returns([])
135
+ @association = Humanoid::Associations::HasManyRelated.new(@parent, options)
136
+ end
137
+
138
+ it "appends the child document" do
139
+ @child.expects(:person_id=).with(@parent.id)
140
+ @association.concat(@child)
141
+ @association.size.should == 1
142
+ end
143
+
144
+ end
145
+
146
+ context "with multiple objects" do
147
+
148
+ before do
149
+ @parent = stub(:id => "1", :new_record? => true, :class => Person)
150
+ Post.expects(:all).returns([])
151
+ @association = Humanoid::Associations::HasManyRelated.new(@parent, options)
152
+ end
153
+
154
+ it "appends the child documents" do
155
+ @child.expects(:person_id=).with(@parent.id)
156
+ @second.expects(:person_id=).with(@parent.id)
157
+ @association.concat([@child, @second])
158
+ @association.size.should == 2
159
+ end
160
+
161
+ end
162
+
163
+ end
164
+
165
+ describe "#create" do
166
+
167
+ before do
168
+ @parent = stub(:id => "5", :class => Person)
169
+ Post.expects(:all).returns([])
170
+ Humanoid::Commands::Save.expects(:execute)
171
+ @association = Humanoid::Associations::HasManyRelated.new(@parent, options)
172
+ end
173
+
174
+ it "builds and saves the new object" do
175
+ @association.create(:title => "Sassy")
176
+ end
177
+
178
+ it "returns the new object" do
179
+ @association.create(:title => "Sassy").should be_a_kind_of(Post)
180
+ end
181
+
182
+ end
183
+
184
+ describe "#find" do
185
+
186
+ before do
187
+ @parent = stub(:id => "5", :class => Person)
188
+ Post.expects(:all).returns([])
189
+ @association = Humanoid::Associations::HasManyRelated.new(@parent, options)
190
+ end
191
+
192
+ context "when finding by id" do
193
+
194
+ before do
195
+ @post = stub
196
+ end
197
+
198
+ it "returns the document in the array with that id" do
199
+ Post.expects(:find).with("5").returns(@post)
200
+ post = @association.find("5")
201
+ post.should == @post
202
+ end
203
+
204
+ end
205
+
206
+ context "when finding all with conditions" do
207
+
208
+ before do
209
+ @post = stub
210
+ end
211
+
212
+ it "passes the conditions to the association class" do
213
+ Post.expects(:find).with(:all, :conditions => { :title => "Testing", :person_id => @parent.id }).returns([@post])
214
+ posts = @association.find(:all, :conditions => { :title => "Testing" })
215
+ posts.should == [@post]
216
+ end
217
+
218
+ end
219
+
220
+ context "when finding first with conditions" do
221
+
222
+ before do
223
+ @post = stub
224
+ end
225
+
226
+ it "passes the conditions to the association class" do
227
+ Post.expects(:find).with(:first, :conditions => { :title => "Testing", :person_id => @parent.id }).returns(@post)
228
+ post = @association.find(:first, :conditions => { :title => "Testing" })
229
+ post.should == @post
230
+ end
231
+
232
+ end
233
+
234
+ context "when finding last with conditions" do
235
+
236
+ before do
237
+ @post = stub
238
+ end
239
+
240
+ it "passes the conditions to the association class" do
241
+ Post.expects(:find).with(:last, :conditions => { :title => "Testing", :person_id => @parent.id }).returns(@post)
242
+ post = @association.find(:last, :conditions => { :title => "Testing" })
243
+ post.should == @post
244
+ end
245
+
246
+ end
247
+
248
+ end
249
+
250
+ describe ".initialize" do
251
+
252
+ before do
253
+ @document = Person.new
254
+ @criteria = stub
255
+ @first = stub(:person_id => @document.id)
256
+ @second = stub(:person_id => @document.id)
257
+ @related = [@first, @second]
258
+ Post.expects(:all).with(:conditions => { "person_id" => @document.id }).returns(@related)
259
+ end
260
+
261
+ context "when related id has been set" do
262
+
263
+ it "finds the object by id" do
264
+ association = Humanoid::Associations::HasManyRelated.new(@document, options)
265
+ association.should == @related
266
+ end
267
+
268
+ end
269
+
270
+ context "when the options have an extension" do
271
+
272
+ it "adds the extension module" do
273
+ association = Humanoid::Associations::HasManyRelated.new(@document, options)
274
+ association.extension.should == "Testing"
275
+ end
276
+
277
+ end
278
+
279
+ end
280
+
281
+ describe ".instantiate" do
282
+
283
+ context "when related id has been set" do
284
+
285
+ before do
286
+ @document = Person.new
287
+ end
288
+
289
+ it "delegates to new" do
290
+ Humanoid::Associations::HasManyRelated.expects(:new).with(@document, options, nil)
291
+ association = Humanoid::Associations::HasManyRelated.instantiate(@document, options)
292
+ end
293
+
294
+ end
295
+
296
+ end
297
+
298
+ describe ".macro" do
299
+
300
+ it "returns :has_many_related" do
301
+ Humanoid::Associations::HasManyRelated.macro.should == :has_many_related
302
+ end
303
+
304
+ end
305
+
306
+ describe "#push" do
307
+
308
+ before do
309
+ @child = stub
310
+ @second = stub
311
+ end
312
+
313
+ context "when parent document has been saved" do
314
+
315
+ before do
316
+ @parent = stub(:id => "1", :new_record? => false, :class => Person)
317
+ Post.expects(:all).returns([])
318
+ @association = Humanoid::Associations::HasManyRelated.new(@parent, options)
319
+ end
320
+
321
+ it "saves and appends the child document" do
322
+ @child.expects(:person_id=).with(@parent.id)
323
+ @child.expects(:save).returns(true)
324
+ @association.push(@child)
325
+ @association.size.should == 1
326
+ end
327
+
328
+ end
329
+
330
+ context "when parent document has not been saved" do
331
+
332
+ before do
333
+ @parent = stub(:id => "1", :new_record? => true, :class => Person)
334
+ Post.expects(:all).returns([])
335
+ @association = Humanoid::Associations::HasManyRelated.new(@parent, options)
336
+ end
337
+
338
+ it "appends the child document" do
339
+ @child.expects(:person_id=).with(@parent.id)
340
+ @association.push(@child)
341
+ @association.size.should == 1
342
+ end
343
+
344
+ end
345
+
346
+ context "with multiple objects" do
347
+
348
+ before do
349
+ @parent = stub(:id => "1", :new_record? => true, :class => Person)
350
+ Post.expects(:all).returns([])
351
+ @association = Humanoid::Associations::HasManyRelated.new(@parent, options)
352
+ end
353
+
354
+ it "appends the child documents" do
355
+ @child.expects(:person_id=).with(@parent.id)
356
+ @second.expects(:person_id=).with(@parent.id)
357
+ @association.push(@child, @second)
358
+ @association.size.should == 2
359
+ end
360
+
361
+ end
362
+
363
+ end
364
+
365
+ describe ".update" do
366
+
367
+ before do
368
+ @first = Post.new
369
+ @second = Post.new
370
+ @related = [@first, @second]
371
+ @parent = Person.new
372
+ end
373
+
374
+ it "sets the related object id on the parent" do
375
+ Humanoid::Associations::HasManyRelated.update(@related, @parent, options)
376
+ @first.person_id.should == @parent.id
377
+ @second.person_id.should == @parent.id
378
+ end
379
+
380
+ it "returns the related objects" do
381
+ @proxy = Humanoid::Associations::HasManyRelated.update(@related, @parent, options)
382
+ @proxy.target.should == @related
383
+ end
384
+
385
+ end
386
+
387
+ end
@@ -0,0 +1,471 @@
1
+ require "spec_helper"
2
+
3
+ describe Humanoid::Associations::HasMany do
4
+
5
+ before do
6
+ @attributes = { "addresses" => [
7
+ { "_id" => "street-1", "street" => "Street 1", "state" => "CA" },
8
+ { "_id" => "street-2", "street" => "Street 2" } ] }
9
+ @document = stub(:raw_attributes => @attributes, :add_observer => true, :update => true)
10
+ end
11
+
12
+ describe "#[]" do
13
+
14
+ before do
15
+ @association = Humanoid::Associations::HasMany.new(
16
+ @document,
17
+ Humanoid::Associations::Options.new(:name => :addresses)
18
+ )
19
+ end
20
+
21
+ context "when the index is present in the association" do
22
+
23
+ it "returns the document at the index" do
24
+ @association[0].should be_a_kind_of(Address)
25
+ @association[0].street.should == "Street 1"
26
+ end
27
+
28
+ end
29
+
30
+ context "when the index is not present in the association" do
31
+
32
+ it "returns nil" do
33
+ @association[3].should be_nil
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+
40
+ describe "#<<" do
41
+
42
+ before do
43
+ @association = Humanoid::Associations::HasMany.new(
44
+ @document,
45
+ Humanoid::Associations::Options.new(:name => :addresses)
46
+ )
47
+ @address = Address.new
48
+ end
49
+
50
+ it "adds the parent document before appending to the array" do
51
+ @association << @address
52
+ @association.length.should == 3
53
+ @address._parent.should == @document
54
+ end
55
+
56
+ it "allows multiple additions" do
57
+ @association << @address
58
+ @association << @address
59
+ @association.length.should == 4
60
+ end
61
+
62
+ end
63
+
64
+ describe "#build" do
65
+
66
+ context "setting the parent relationship" do
67
+
68
+ before do
69
+ @person = Person.new
70
+ end
71
+
72
+ it "happens before any other operation" do
73
+ address = @person.addresses.build(:set_parent => true, :street => "Madison Ave")
74
+ address._parent.should == @person
75
+ @person.addresses.first.should == address
76
+ end
77
+
78
+ end
79
+
80
+ context "when a type is not provided" do
81
+
82
+ before do
83
+ @association = Humanoid::Associations::HasMany.new(
84
+ @document,
85
+ Humanoid::Associations::Options.new(:name => :addresses)
86
+ )
87
+ end
88
+
89
+ it "adds a new document to the array with the suppied parameters" do
90
+ @association.build({ :street => "Street 1" })
91
+ @association.length.should == 3
92
+ @association[2].should be_a_kind_of(Address)
93
+ @association[2].street.should == "Street 1"
94
+ end
95
+
96
+ it "returns the newly built object in the association" do
97
+ address = @association.build({ :street => "Yet Another" })
98
+ address.should be_a_kind_of(Address)
99
+ address.street.should == "Yet Another"
100
+ end
101
+
102
+ end
103
+
104
+ context "when a type is provided" do
105
+
106
+ before do
107
+ @association = Humanoid::Associations::HasMany.new(
108
+ @document,
109
+ Humanoid::Associations::Options.new(:name => :shapes)
110
+ )
111
+ end
112
+
113
+ it "instantiates a class of the type" do
114
+ circle = @association.build({ :radius => 100 }, Circle)
115
+ circle.should be_a_kind_of(Circle)
116
+ circle.radius.should == 100
117
+ end
118
+
119
+ end
120
+
121
+ end
122
+
123
+ describe "#create" do
124
+
125
+ context "when a type is not provided" do
126
+
127
+ before do
128
+ @association = Humanoid::Associations::HasMany.new(
129
+ @document,
130
+ Humanoid::Associations::Options.new(:name => :addresses)
131
+ )
132
+ @address = Address.new(:street => "Yet Another")
133
+ end
134
+
135
+ it "builds and saves a new object" do
136
+ Humanoid::Commands::Save.expects(:execute).returns(true)
137
+ address = @association.create({ :street => "Yet Another" })
138
+ address.should be_a_kind_of(Address)
139
+ address.street.should == "Yet Another"
140
+ end
141
+
142
+ end
143
+
144
+ context "when a type is provided" do
145
+
146
+ before do
147
+ @association = Humanoid::Associations::HasMany.new(
148
+ @document,
149
+ Humanoid::Associations::Options.new(:name => :shapes)
150
+ )
151
+ end
152
+
153
+ it "instantiates a class of that type" do
154
+ Humanoid::Commands::Save.expects(:execute).returns(true)
155
+ circle = @association.create({ :radius => 100 }, Circle)
156
+ circle.should be_a_kind_of(Circle)
157
+ circle.radius.should == 100
158
+ end
159
+
160
+ end
161
+
162
+ end
163
+
164
+ describe "#concat" do
165
+
166
+ before do
167
+ @association = Humanoid::Associations::HasMany.new(
168
+ @document,
169
+ Humanoid::Associations::Options.new(:name => :addresses)
170
+ )
171
+ @address = Address.new
172
+ end
173
+
174
+ it "adds the parent document before appending to the array" do
175
+ @association.concat [@address]
176
+ @association.length.should == 3
177
+ @address._parent.should == @document
178
+ end
179
+
180
+ end
181
+
182
+ describe "#clear" do
183
+
184
+ before do
185
+ @association = Humanoid::Associations::HasMany.new(
186
+ @document,
187
+ Humanoid::Associations::Options.new(:name => :addresses)
188
+ )
189
+ @address = Address.new
190
+ @association << @address
191
+ end
192
+
193
+ it "clears out the association" do
194
+ @association.clear
195
+ @association.size.should == 0
196
+ end
197
+
198
+ end
199
+
200
+ describe "#find" do
201
+
202
+ before do
203
+ @association = Humanoid::Associations::HasMany.new(
204
+ @document,
205
+ Humanoid::Associations::Options.new(:name => :addresses)
206
+ )
207
+ end
208
+
209
+ context "when finding all" do
210
+
211
+ it "returns all the documents" do
212
+ @association.find(:all).should == @association
213
+ end
214
+
215
+ end
216
+
217
+ context "when finding by id" do
218
+
219
+ it "returns the document in the array with that id" do
220
+ address = @association.find("street-2")
221
+ address.should_not be_nil
222
+ end
223
+
224
+ end
225
+
226
+ end
227
+
228
+ describe "#first" do
229
+
230
+ context "when there are elements in the array" do
231
+
232
+ before do
233
+ @association = Humanoid::Associations::HasMany.new(
234
+ @document,
235
+ Humanoid::Associations::Options.new(:name => :addresses)
236
+ )
237
+ end
238
+
239
+ it "returns the first element" do
240
+ @association.first.should be_a_kind_of(Address)
241
+ @association.first.street.should == "Street 1"
242
+ end
243
+
244
+ end
245
+
246
+ context "when the array is empty" do
247
+
248
+ before do
249
+ @association = Humanoid::Associations::HasMany.new(
250
+ Person.new,
251
+ Humanoid::Associations::Options.new(:name => :addresses)
252
+ )
253
+ end
254
+
255
+ it "returns nil" do
256
+ @association.first.should be_nil
257
+ end
258
+
259
+ end
260
+
261
+ end
262
+
263
+ describe "#initialize" do
264
+
265
+ context "when no extension exists" do
266
+
267
+ before do
268
+ @canvas = stub(:raw_attributes => { "shapes" => [{ "_type" => "Circle", "radius" => 5 }] }, :update => true)
269
+ @association = Humanoid::Associations::HasMany.new(
270
+ @canvas,
271
+ Humanoid::Associations::Options.new(:name => :shapes)
272
+ )
273
+ end
274
+
275
+ it "creates the classes based on their types" do
276
+ circle = @association.first
277
+ circle.should be_a_kind_of(Circle)
278
+ circle.radius.should == 5
279
+ end
280
+
281
+ end
282
+
283
+ context "when an extension is in the options" do
284
+
285
+ before do
286
+ @person = Person.new
287
+ @block = Proc.new do
288
+ def extension
289
+ "Testing"
290
+ end
291
+ end
292
+ @association = Humanoid::Associations::HasMany.new(
293
+ @person,
294
+ Humanoid::Associations::Options.new(:name => :addresses, :extend => @block)
295
+ )
296
+ end
297
+
298
+ it "adds the extension module" do
299
+ @association.extension.should == "Testing"
300
+ end
301
+
302
+ end
303
+
304
+ end
305
+
306
+ describe ".instantiate" do
307
+
308
+ it "delegates to new" do
309
+ Humanoid::Associations::HasMany.expects(:new).with(@document, @options)
310
+ Humanoid::Associations::HasMany.instantiate(@document, @options)
311
+ end
312
+
313
+ end
314
+
315
+ describe "#length" do
316
+
317
+ context "#length" do
318
+
319
+ it "returns the length of the delegated array" do
320
+ @association = Humanoid::Associations::HasMany.new(
321
+ @document,
322
+ Humanoid::Associations::Options.new(:name => :addresses)
323
+ )
324
+ @association.length.should == 2
325
+ end
326
+
327
+ end
328
+
329
+ end
330
+
331
+ describe ".macro" do
332
+
333
+ it "returns :has_many" do
334
+ Humanoid::Associations::HasMany.macro.should == :has_many
335
+ end
336
+
337
+ end
338
+
339
+ describe "#nested_build" do
340
+
341
+ before do
342
+ @association = Humanoid::Associations::HasMany.new(
343
+ @document,
344
+ Humanoid::Associations::Options.new(:name => :addresses)
345
+ )
346
+ end
347
+
348
+ it "returns the newly built object in the association" do
349
+ @association.nested_build({ "0" => { :street => "Yet Another" } })
350
+ @association.size.should == 3
351
+ @association.last.street.should == "Yet Another"
352
+ end
353
+
354
+ end
355
+
356
+ describe "#method_missing" do
357
+
358
+ context "when the association class has a criteria class method" do
359
+
360
+ before do
361
+ @association = Humanoid::Associations::HasMany.new(
362
+ @document,
363
+ Humanoid::Associations::Options.new(:name => :addresses)
364
+ )
365
+ end
366
+
367
+ it "returns the criteria" do
368
+ @association.california.should be_a_kind_of(Humanoid::Criteria)
369
+ end
370
+
371
+ it "sets the documents on the criteria" do
372
+ criteria = @association.california
373
+ criteria.documents.should == @association.entries
374
+ end
375
+
376
+ it "returns the scoped documents" do
377
+ addresses = @association.california
378
+ addresses.size.should == 1
379
+ addresses.first.should be_a_kind_of(Address)
380
+ addresses.first.state.should == "CA"
381
+ end
382
+
383
+ end
384
+
385
+ context "when no class method exists" do
386
+
387
+ before do
388
+ @association = Humanoid::Associations::HasMany.new(
389
+ @document,
390
+ Humanoid::Associations::Options.new(:name => :addresses)
391
+ )
392
+ end
393
+
394
+ it "delegates to the array" do
395
+ @association.entries.size.should == 2
396
+ end
397
+
398
+ end
399
+
400
+ end
401
+
402
+ describe "#paginate" do
403
+
404
+ before do
405
+ @association = Humanoid::Associations::HasMany.new(
406
+ @document,
407
+ Humanoid::Associations::Options.new(:name => :addresses)
408
+ )
409
+ @options = { :page => 1, :per_page => 10 }
410
+ @criteria = mock
411
+ end
412
+
413
+ it "creates a criteria and paginates it" do
414
+ Humanoid::Criteria.expects(:translate).with(Address, @options).returns(@criteria)
415
+ @criteria.expects(:documents=).with(@association.target)
416
+ @criteria.expects(:paginate).returns([])
417
+ @association.paginate(@options).should == []
418
+ end
419
+ end
420
+
421
+ describe "#push" do
422
+
423
+ before do
424
+ @association = Humanoid::Associations::HasMany.new(
425
+ @document,
426
+ Humanoid::Associations::Options.new(:name => :addresses)
427
+ )
428
+ @address = Address.new
429
+ end
430
+
431
+ it "adds the parent document before appending to the array" do
432
+ @association.push @address
433
+ @association.length.should == 3
434
+ @address._parent.should == @document
435
+ end
436
+
437
+ it "appends the document to the end of the array" do
438
+ @association.push(Address.new)
439
+ @association.length.should == 3
440
+ end
441
+
442
+ end
443
+
444
+ describe ".update" do
445
+
446
+ before do
447
+ @address = Address.new(:street => "Madison Ave")
448
+ @person = Person.new(:title => "Sir")
449
+ @association = Humanoid::Associations::HasMany.update(
450
+ [@address],
451
+ @person,
452
+ Humanoid::Associations::Options.new(:name => :addresses)
453
+ )
454
+ end
455
+
456
+ it "parentizes the child document" do
457
+ @address._parent.should == @person
458
+ end
459
+
460
+ it "sets the attributes of the child on the parent" do
461
+ @person.attributes[:addresses].should ==
462
+ [{ "_id" => "madison-ave", "street" => "Madison Ave", "_type" => "Address" }]
463
+ end
464
+
465
+ it "returns the association proxy" do
466
+ @association.target.size.should == 1
467
+ end
468
+
469
+ end
470
+
471
+ end