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,74 @@
1
+ require "spec_helper"
2
+
3
+ describe Humanoid::Cursor do
4
+
5
+ let(:collection) do
6
+ stub.quacks_like(Humanoid::Collection.allocate)
7
+ end
8
+
9
+ let(:proxy) do
10
+ stub.quacks_like(Mongo::Cursor.allocate)
11
+ end
12
+
13
+ let(:cursor) do
14
+ Humanoid::Cursor.new(Person, collection, proxy)
15
+ end
16
+
17
+ (Humanoid::Cursor::OPERATIONS - [ :timeout ]).each do |name|
18
+
19
+ describe "##{name}" do
20
+
21
+ before do
22
+ proxy.expects(name)
23
+ end
24
+
25
+ it "delegates to the proxy" do
26
+ cursor.send(name)
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "#collection" do
32
+
33
+ it "returns the humanoid collection" do
34
+ cursor.collection.should == collection
35
+ end
36
+ end
37
+
38
+ describe "#each" do
39
+
40
+ before do
41
+ proxy.expects(:each).yields({ "_type" => "Person" })
42
+ end
43
+
44
+ it "yields to the next document" do
45
+ cursor.each do |doc|
46
+ doc.attributes.except(:_id).should == Person.new.attributes.except(:_id)
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#next_document" do
52
+
53
+ before do
54
+ proxy.expects(:next_document).returns({ "_type" => "Person" })
55
+ end
56
+
57
+ it "returns the next document from the proxied cursor" do
58
+ doc = cursor.next_document
59
+ doc.attributes.except(:_id).should == Person.new.attributes.except(:_id)
60
+ end
61
+ end
62
+
63
+ describe "#to_a" do
64
+
65
+ before do
66
+ proxy.expects(:to_a).returns([{ "_type" => "Person" }])
67
+ end
68
+
69
+ it "converts the proxy cursor to an array of documents" do
70
+ docs = cursor.to_a
71
+ docs[0].attributes.except(:_id).should == Person.new.attributes.except(:_id)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,986 @@
1
+ require "spec_helper"
2
+
3
+ describe Humanoid::Document do
4
+
5
+ before do
6
+ @database = mock
7
+ @collection = stub(:name => "people")
8
+ @canvas_collection = stub(:name => "canvases")
9
+ Humanoid::Collection.stubs(:new).with(Person, "people").returns(@collection)
10
+ Humanoid::Collection.stubs(:new).with(Canvas, "canvases").returns(@canvas_collection)
11
+ @collection.stubs(:create_index).with(:_type, false)
12
+ @canvas_collection.stubs(:create_index).with(:_type, false)
13
+ end
14
+
15
+ after do
16
+ Person._collection = nil
17
+ Canvas._collection = nil
18
+ end
19
+
20
+ describe "#==" do
21
+
22
+ context "when other object is a Document" do
23
+
24
+ context "when attributes are equal" do
25
+
26
+ before do
27
+ @document = Person.new(:_id => 1, :title => "Sir")
28
+ @other = Person.new(:_id => 1, :title => "Sir")
29
+ end
30
+
31
+ it "returns true" do
32
+ @document.should == @other
33
+ end
34
+
35
+ end
36
+
37
+ context "when attributes are not equal" do
38
+
39
+ before do
40
+ @document = Person.new(:title => "Sir")
41
+ @other = Person.new(:title => "Madam")
42
+ end
43
+
44
+ it "returns false" do
45
+ @document.should_not == @other
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ context "when other object is not a Document" do
53
+
54
+ it "returns false" do
55
+ Person.new.==("Test").should be_false
56
+ end
57
+
58
+ end
59
+
60
+ context "when comapring parent to its subclass" do
61
+
62
+ it "returns false" do
63
+ Canvas.new.should_not == Firefox.new
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ describe "#eql?" do
71
+
72
+ context "when other object is a Document" do
73
+
74
+ context "when attributes are equal" do
75
+
76
+ before do
77
+ @document = Person.new(:_id => 1, :title => "Sir")
78
+ @other = Person.new(:_id => 1, :title => "Sir")
79
+ end
80
+
81
+ it "returns true" do
82
+ @document.eql?(@other).should be_true
83
+ end
84
+
85
+ end
86
+
87
+ context "when attributes are not equal" do
88
+
89
+ before do
90
+ @document = Person.new(:title => "Sir")
91
+ @other = Person.new(:title => "Madam")
92
+ end
93
+
94
+ it "returns false" do
95
+ @document.eql?(@other).should_not be_true
96
+ end
97
+
98
+ end
99
+
100
+ end
101
+
102
+ context "when other object is not a Document" do
103
+
104
+ it "returns false" do
105
+ Person.new.eql?("Test").should be_false
106
+ end
107
+
108
+ end
109
+
110
+ context "when comapring parent to its subclass" do
111
+
112
+ it "returns false" do
113
+ Canvas.new.eql?(Firefox.new).should_not be_true
114
+ end
115
+
116
+ end
117
+
118
+ end
119
+
120
+ describe "#hash" do
121
+
122
+ before do
123
+ @document = Person.new(:_id => 1, :title => "Sir")
124
+ @other = Person.new(:_id => 2, :title => "Sir")
125
+ end
126
+
127
+ it "deligates to id" do
128
+ @document.hash.should == @document.id.hash
129
+ end
130
+
131
+ it "has unique hash per id" do
132
+ @document.hash.should_not == @other.hash
133
+ end
134
+
135
+ end
136
+
137
+ describe "#alias_method_chain" do
138
+
139
+ context "on a field setter" do
140
+
141
+ before do
142
+ @person = Person.new
143
+ end
144
+
145
+ it "chains the method properly" do
146
+ @person.score = 10
147
+ @person.rescored.should == 30
148
+ end
149
+
150
+ end
151
+
152
+ end
153
+
154
+ describe "#assimilate" do
155
+
156
+ before do
157
+ @child = Name.new(:first_name => "Hank", :last_name => "Moody")
158
+ @parent = Person.new(:title => "Mr.")
159
+ @options = Humanoid::Associations::Options.new(:name => :name)
160
+ end
161
+
162
+ it "sets up all associations in the object graph" do
163
+ @child.assimilate(@parent, @options)
164
+ @parent.name.should == @child
165
+ end
166
+
167
+ end
168
+
169
+ describe ".db" do
170
+
171
+ before do
172
+ @db = stub
173
+ @collection.expects(:db).returns(@db)
174
+ end
175
+
176
+ it "returns the database from the collection" do
177
+ Person.db.should == @db
178
+ end
179
+ end
180
+
181
+ describe "#clone" do
182
+
183
+ before do
184
+ @comment = Comment.new(:text => "Woooooo")
185
+ @clone = @comment.clone
186
+ end
187
+
188
+ it "returns a new document sans id and versions" do
189
+ @clone.id.should_not == @comment.id
190
+ @clone.versions.should be_empty
191
+ end
192
+
193
+ end
194
+
195
+ describe ".collection" do
196
+
197
+ before do
198
+ @person = Person.new
199
+ end
200
+
201
+ it "sets the collection name to the class pluralized" do
202
+ Person.collection.name.should == "people"
203
+ end
204
+
205
+ context "when document is embedded" do
206
+
207
+ before do
208
+ @address = Address.new
209
+ end
210
+
211
+ it "raises an error" do
212
+ lambda { Address.collection }.should raise_error
213
+ end
214
+
215
+ end
216
+
217
+ end
218
+
219
+ describe ".collection_name=" do
220
+
221
+ context "on a parent class" do
222
+
223
+ it "sets the collection name on the document class" do
224
+ Patient.collection_name = "pats"
225
+ Patient.collection_name.should == "pats"
226
+ end
227
+
228
+ end
229
+
230
+ context "on a subclass" do
231
+
232
+ after do
233
+ Canvas.collection_name = "canvases"
234
+ end
235
+
236
+ it "sets the collection name for the entire hierarchy" do
237
+ Firefox.collection_name = "browsers"
238
+ Canvas.collection_name.should == "browsers"
239
+ end
240
+
241
+ end
242
+
243
+ end
244
+
245
+ describe ".embedded" do
246
+
247
+ context "when the document is embedded" do
248
+
249
+ it "returns true" do
250
+ address = Address.new
251
+ address.embedded.should be_true
252
+ end
253
+
254
+ end
255
+
256
+ context "when the document is not embedded" do
257
+
258
+ it "returns false" do
259
+ person = Person.new
260
+ person.embedded.should be_false
261
+ end
262
+
263
+ end
264
+
265
+ context "when a subclass is embedded" do
266
+
267
+ it "returns true" do
268
+ circle = Circle.new
269
+ circle.embedded.should be_true
270
+ end
271
+
272
+ end
273
+
274
+ end
275
+
276
+ describe ".hereditary" do
277
+
278
+ context "when the class is part of a hierarchy" do
279
+
280
+ it "returns true" do
281
+ Canvas.hereditary.should be_true
282
+ end
283
+
284
+ end
285
+
286
+ context "when the class is not part of a hierarchy" do
287
+
288
+ it "returns false" do
289
+ Game.hereditary.should be_false
290
+ end
291
+
292
+ end
293
+
294
+ end
295
+
296
+ describe ".human_name" do
297
+
298
+ it "returns the class name underscored and humanized" do
299
+ MixedDrink.human_name.should == "Mixed drink"
300
+ end
301
+
302
+ end
303
+
304
+ describe ".initialize" do
305
+
306
+ context "when passed a block" do
307
+
308
+ it "yields self to the block" do
309
+ person = Person.new do |p|
310
+ p.title = "Sir"
311
+ p.age = 60
312
+ end
313
+ person.title.should == "Sir"
314
+ person.age.should == 60
315
+ end
316
+
317
+ end
318
+
319
+ context "with no attributes" do
320
+
321
+ it "sets default attributes" do
322
+ person = Person.new
323
+ person.attributes.empty?.should be_false
324
+ person.age.should == 100
325
+ person.blood_alcohol_content.should == 0.0
326
+ end
327
+
328
+ end
329
+
330
+ context "with nil attributes" do
331
+
332
+ before do
333
+ @person = Person.new(nil)
334
+ end
335
+
336
+ it "sets default attributes" do
337
+ @person.attributes.empty?.should be_false
338
+ @person.age.should == 100
339
+ @person.blood_alcohol_content.should == 0.0
340
+ end
341
+
342
+ end
343
+
344
+ context "with attributes" do
345
+
346
+ before do
347
+ @attributes = {
348
+ :_id => "1",
349
+ :title => "value",
350
+ :age => "30",
351
+ :terms => "true",
352
+ :name => {
353
+ :_id => "2", :first_name => "Test", :last_name => "User"
354
+ },
355
+ :addresses => [
356
+ { :_id => "3", :street => "First Street" },
357
+ { :_id => "4", :street => "Second Street" }
358
+ ]
359
+ }
360
+ end
361
+
362
+ it "sets the attributes hash on the object properly casted" do
363
+ person = Person.new(@attributes)
364
+ person.attributes[:age].should == 30
365
+ person.attributes[:terms].should be_true
366
+ end
367
+
368
+ end
369
+
370
+ context "with a primary key" do
371
+
372
+ context "when the value for the key exists" do
373
+
374
+ before do
375
+ Address.key :street
376
+ @address = Address.new(:street => "Test")
377
+ end
378
+
379
+ it "sets the primary key" do
380
+ @address.id.should == "test"
381
+ end
382
+
383
+ end
384
+
385
+ end
386
+
387
+ context "without a type specified" do
388
+
389
+ it "sets the type" do
390
+ Person.new._type.should == "Person"
391
+ end
392
+
393
+ end
394
+
395
+ end
396
+
397
+ describe ".instantiate" do
398
+
399
+ context "when attributes have an id" do
400
+
401
+ before do
402
+ @attributes = { "_id" => "1", "_type" => "Person", "title" => "Sir", "age" => 30 }
403
+ end
404
+
405
+ it "sets the attributes directly" do
406
+ person = Person.instantiate(@attributes)
407
+ person._id.should == "1"
408
+ person._type.should == "Person"
409
+ person.title.should == "Sir"
410
+ person.age.should == 30
411
+ end
412
+
413
+ end
414
+
415
+ context "with nil attributes" do
416
+
417
+ it "sets the attributes directly" do
418
+ person = Person.instantiate(nil)
419
+ person.id.should_not be_nil
420
+ end
421
+
422
+ end
423
+
424
+ end
425
+
426
+ describe ".key" do
427
+
428
+ context "when key is single field" do
429
+
430
+ before do
431
+ Address.key :street
432
+ @address = Address.new(:street => "Testing Street Name")
433
+ @address.expects(:collection).returns(@collection)
434
+ @collection.expects(:save)
435
+ end
436
+
437
+ it "adds the callback for primary key generation" do
438
+ @address.save
439
+ @address.id.should == "testing-street-name"
440
+ end
441
+
442
+ end
443
+
444
+ context "when key is composite" do
445
+
446
+ before do
447
+ Address.key :street, :post_code
448
+ @address = Address.new(:street => "Testing Street Name", :post_code => "94123")
449
+ @address.expects(:collection).returns(@collection)
450
+ @collection.expects(:save)
451
+ end
452
+
453
+ it "combines all fields" do
454
+ @address.save
455
+ @address.id.should == "testing-street-name-94123"
456
+ end
457
+
458
+ end
459
+
460
+ context "when key is on a subclass" do
461
+
462
+ before do
463
+ Firefox.key :name
464
+ end
465
+
466
+ it "sets the key for the entire hierarchy" do
467
+ Canvas.primary_key.should == [:name]
468
+ end
469
+
470
+ end
471
+
472
+ end
473
+
474
+ describe "#new_record?" do
475
+
476
+ context "when the object has been saved" do
477
+
478
+ before do
479
+ @person = Person.new(:_id => "1")
480
+ end
481
+
482
+ it "returns false" do
483
+ @person.new_record?.should be_false
484
+ end
485
+
486
+ end
487
+
488
+ context "when the object has not been saved" do
489
+
490
+ before do
491
+ @person = Person.new
492
+ end
493
+
494
+ it "returns true" do
495
+ @person.new_record?.should be_true
496
+ end
497
+
498
+ end
499
+
500
+ end
501
+
502
+ describe "#_parent" do
503
+
504
+ before do
505
+ @attributes = { :title => "Sir",
506
+ :addresses => [
507
+ { :street => "Street 1" },
508
+ { :street => "Street 2" } ] }
509
+ @person = Person.new(@attributes)
510
+ end
511
+
512
+ context "when document is embedded" do
513
+
514
+ it "returns the parent document" do
515
+ @person.addresses.first._parent.should == @person
516
+ end
517
+
518
+ end
519
+
520
+ context "when document is root" do
521
+
522
+ it "returns nil" do
523
+ @person._parent.should be_nil
524
+ end
525
+
526
+ end
527
+
528
+ end
529
+
530
+ describe "#parentize" do
531
+
532
+ before do
533
+ @parent = Person.new
534
+ @child = Name.new
535
+ end
536
+
537
+ it "sets the parent on each element" do
538
+ @child.parentize(@parent, :child)
539
+ @child._parent.should == @parent
540
+ end
541
+
542
+ end
543
+
544
+ describe "#reload" do
545
+
546
+ before do
547
+ @attributes = { "title" => "Herr" }
548
+ @person = Person.new(:_id => Mongo::ObjectID.new.to_s)
549
+ @collection.expects(:find_one).with(:_id => @person.id).returns(@attributes)
550
+ end
551
+
552
+ it "reloads the object attribtues from the database" do
553
+ @person.reload
554
+ @person.attributes.should == @attributes
555
+ end
556
+
557
+ it 'should return a person object' do
558
+ @person.reload.should be_kind_of(Person)
559
+ end
560
+
561
+ end
562
+
563
+ describe "#remove" do
564
+
565
+ context "when removing an element from a has many" do
566
+
567
+ before do
568
+ @person = Person.new
569
+ @address = Address.new(:street => "Testing")
570
+ @person.addresses << @address
571
+ end
572
+
573
+ it "removes the child document attributes" do
574
+ @person.remove(@address)
575
+ @person.addresses.size.should == 0
576
+ end
577
+
578
+ end
579
+
580
+ context "when removing a has one" do
581
+
582
+ before do
583
+ @person = Person.new
584
+ @name = Name.new(:first_name => "Neytiri")
585
+ @person.name = @name
586
+ end
587
+
588
+ it "removes the child document attributes" do
589
+ @person.remove(@name)
590
+ @person.name.should be_nil
591
+ end
592
+
593
+ end
594
+
595
+ end
596
+
597
+ describe "#_root" do
598
+
599
+ before do
600
+ @person = Person.new(:title => "Mr")
601
+ @phone_number = Phone.new(:number => "415-555-1212")
602
+ @country_code = CountryCode.new(:code => 1)
603
+ @phone_number.country_code = @country_code
604
+ @person.phone_numbers << @phone_number
605
+ end
606
+
607
+ context "when document is the root" do
608
+
609
+ it "returns self" do
610
+ @person._root.should == @person
611
+ end
612
+
613
+ end
614
+
615
+ context "when document is embedded one level" do
616
+
617
+ it "returns the parent" do
618
+ @phone_number._root.should == @person
619
+ end
620
+
621
+ end
622
+
623
+ context "when document is embedded multiple levels" do
624
+
625
+ it "returns the top level parent" do
626
+ @country_code._root.should == @person
627
+ end
628
+
629
+ end
630
+
631
+ end
632
+
633
+ describe ".store_in" do
634
+
635
+ context "on a parent class" do
636
+
637
+ it "sets the collection name and collection for the document" do
638
+ Humanoid::Collection.expects(:new).with(Patient, "population").returns(@collection)
639
+ Patient.store_in :population
640
+ Patient.collection_name.should == "population"
641
+ end
642
+
643
+ end
644
+
645
+ context "on a subclass" do
646
+
647
+ after do
648
+ Humanoid::Collection.expects(:new).with(Firefox, "canvases")
649
+ Firefox.store_in :canvases
650
+ end
651
+
652
+ it "changes the collection name for the entire hierarchy" do
653
+ Humanoid::Collection.expects(:new).with(Firefox, "browsers").returns(@collection)
654
+ Firefox.store_in :browsers
655
+ Canvas.collection_name.should == "browsers"
656
+ end
657
+
658
+ end
659
+
660
+ end
661
+
662
+ describe "._types" do
663
+
664
+ it "returns all subclasses for the class plus the class" do
665
+ types = Canvas._types
666
+ types.size.should == 3
667
+ types.should include("Firefox")
668
+ types.should include("Browser")
669
+ types.should include("Canvas")
670
+ end
671
+
672
+ it "does not return parent classes" do
673
+ types = Browser._types
674
+ types.size.should == 2
675
+ types.should include("Firefox")
676
+ types.should include("Browser")
677
+ end
678
+
679
+ end
680
+
681
+ describe "#to_a" do
682
+
683
+ it "returns an array with the document in it" do
684
+ person = Person.new
685
+ person.to_a.should == [ person ]
686
+ end
687
+
688
+ end
689
+
690
+ describe "#to_param" do
691
+
692
+ it "returns the id" do
693
+ id = Mongo::ObjectID.new.to_s
694
+ Person.new(:_id => id).to_param.should == id.to_s
695
+ end
696
+
697
+ end
698
+
699
+ context "validations" do
700
+
701
+ context "when defining using macros" do
702
+
703
+ after do
704
+ Person.validations.clear
705
+ end
706
+
707
+ describe "#validates_acceptance_of" do
708
+
709
+ it "adds the acceptance validation" do
710
+ Person.class_eval do
711
+ validates_acceptance_of :terms
712
+ end
713
+ Person.validations.first.should be_a_kind_of(Validatable::ValidatesAcceptanceOf)
714
+ end
715
+
716
+ end
717
+
718
+ describe "#validates_associated" do
719
+
720
+ it "adds the associated validation" do
721
+ Person.class_eval do
722
+ validates_associated :name
723
+ end
724
+ Person.validations.first.should be_a_kind_of(Validatable::ValidatesAssociated)
725
+ end
726
+
727
+ end
728
+
729
+ describe "#validates_format_of" do
730
+
731
+ it "adds the format validation" do
732
+ Person.class_eval do
733
+ validates_format_of :title, :with => /[A-Za-z]/
734
+ end
735
+ Person.validations.first.should be_a_kind_of(Validatable::ValidatesFormatOf)
736
+ end
737
+
738
+ end
739
+
740
+ describe "#validates_length_of" do
741
+
742
+ it "adds the length validation" do
743
+ Person.class_eval do
744
+ validates_length_of :title, :minimum => 10
745
+ end
746
+ Person.validations.first.should be_a_kind_of(Validatable::ValidatesLengthOf)
747
+ end
748
+
749
+ end
750
+
751
+ describe "#validates_numericality_of" do
752
+
753
+ it "adds the numericality validation" do
754
+ Person.class_eval do
755
+ validates_numericality_of :age
756
+ end
757
+ Person.validations.first.should be_a_kind_of(Validatable::ValidatesNumericalityOf)
758
+ end
759
+
760
+ end
761
+
762
+ describe "#validates_presence_of" do
763
+
764
+ it "adds the presence validation" do
765
+ Person.class_eval do
766
+ validates_presence_of :title
767
+ end
768
+ Person.validations.first.should be_a_kind_of(Validatable::ValidatesPresenceOf)
769
+ end
770
+
771
+ end
772
+
773
+ describe "#validates_uniqueness_of" do
774
+
775
+ it "adds the uniqueness validation" do
776
+ Person.class_eval do
777
+ validates_uniqueness_of :title
778
+ end
779
+ Person.validations.first.should be_a_kind_of(Validatable::ValidatesUniquenessOf)
780
+ end
781
+
782
+ end
783
+
784
+ describe "#validates_inclusion_of" do
785
+
786
+ it "adds the inclusion validation" do
787
+ Person.class_eval do
788
+ validates_inclusion_of :title, :within => ["test"]
789
+ end
790
+ Person.validations.first.should be_a_kind_of(Validatable::ValidatesInclusionOf)
791
+ end
792
+
793
+ end
794
+
795
+ describe "#validates_exclusion_of" do
796
+
797
+ it "adds the exclusion validation" do
798
+ Person.class_eval do
799
+ validates_exclusion_of :title, :within => ["test"]
800
+ end
801
+ Person.validations.first.should be_a_kind_of(Validatable::ValidatesExclusionOf)
802
+ end
803
+
804
+ end
805
+
806
+ describe "#validates_true_for" do
807
+
808
+ it "adds the true validation" do
809
+ Person.class_eval do
810
+ validates_true_for :title, :logic => lambda { title == "Esquire" }
811
+ end
812
+ Person.validations.first.should be_a_kind_of(Validatable::ValidatesTrueFor)
813
+ end
814
+
815
+ end
816
+
817
+ end
818
+
819
+ context "when running validations" do
820
+
821
+ before do
822
+ @person = Person.new
823
+ @canvas = Canvas.new
824
+ @firefox = Firefox.new
825
+ end
826
+
827
+ after do
828
+ Person.validations.clear
829
+ Canvas.validations.clear
830
+ Firefox.validations.clear
831
+ end
832
+
833
+ describe "#validates_acceptance_of" do
834
+
835
+ it "fails if field not accepted" do
836
+ Person.class_eval do
837
+ validates_acceptance_of :terms
838
+ end
839
+ @person.valid?.should be_false
840
+ @person.errors.on(:terms).should_not be_nil
841
+ end
842
+
843
+ end
844
+
845
+ describe "#validates_associated" do
846
+
847
+ context "when association is a has_many" do
848
+
849
+ it "fails when any association fails validation" do
850
+ Person.class_eval do
851
+ validates_associated :addresses
852
+ end
853
+ Address.class_eval do
854
+ validates_presence_of :street
855
+ end
856
+ @person.addresses << Address.new
857
+ @person.valid?.should be_false
858
+ @person.errors.on(:addresses).should_not be_nil
859
+ end
860
+
861
+ end
862
+
863
+ context "when association is a has_one" do
864
+
865
+ context "when the associated is not nil" do
866
+
867
+ it "fails when the association fails validation" do
868
+ Person.class_eval do
869
+ validates_associated :name
870
+ end
871
+ Name.class_eval do
872
+ validates_presence_of :first_name
873
+ end
874
+ @person.name = Name.new
875
+ @person.valid?.should be_false
876
+ @person.errors.on(:name).should_not be_nil
877
+ end
878
+
879
+ end
880
+
881
+ context "when the associated is nil" do
882
+
883
+ it "returns true" do
884
+ Person.class_eval do
885
+ validates_associated :name
886
+ end
887
+ @person.valid?.should be_true
888
+ end
889
+
890
+ end
891
+
892
+ end
893
+
894
+ end
895
+
896
+ describe "#validates_format_of" do
897
+
898
+ it "fails if the field is in the wrong format" do
899
+ Person.class_eval do
900
+ validates_format_of :title, :with => /[A-Za-z]/
901
+ end
902
+ @person.title = 10
903
+ @person.valid?.should be_false
904
+ @person.errors.on(:title).should_not be_nil
905
+ end
906
+
907
+ end
908
+
909
+ describe "#validates_length_of" do
910
+
911
+ it "fails if the field is the wrong length" do
912
+ Person.class_eval do
913
+ validates_length_of :title, :minimum => 10
914
+ end
915
+ @person.title = "Testing"
916
+ @person.valid?.should be_false
917
+ @person.errors.on(:title).should_not be_nil
918
+ end
919
+
920
+ end
921
+
922
+ describe "#validates_numericality_of" do
923
+
924
+ it "fails if the field is not a number" do
925
+ Person.class_eval do
926
+ validates_numericality_of :age
927
+ end
928
+ @person.age = "foo"
929
+ @person.valid?.should be_false
930
+ @person.errors.on(:age).should_not be_nil
931
+ end
932
+
933
+ end
934
+
935
+ describe "#validates_presence_of" do
936
+
937
+ context "on a parent class" do
938
+
939
+ it "fails if the field is nil on the parent" do
940
+ Person.class_eval do
941
+ validates_presence_of :title
942
+ end
943
+ @person.valid?.should be_false
944
+ @person.errors.on(:title).should_not be_nil
945
+ end
946
+
947
+ it "fails if the field is nil on a subclass" do
948
+ Canvas.class_eval do
949
+ validates_presence_of :name
950
+ end
951
+ @firefox.valid?.should be_false
952
+ @firefox.errors.on(:name).should_not be_nil
953
+ end
954
+
955
+ end
956
+
957
+ context "on a subclass" do
958
+
959
+ it "parent class does not get subclass validations" do
960
+ Firefox.class_eval do
961
+ validates_presence_of :name
962
+ end
963
+ @canvas.valid?.should be_true
964
+ end
965
+
966
+ end
967
+
968
+ end
969
+
970
+ describe "#validates_true_for" do
971
+
972
+ it "fails if the logic returns false" do
973
+ Person.class_eval do
974
+ validates_true_for :title, :logic => lambda { title == "Esquire" }
975
+ end
976
+ @person.valid?.should be_false
977
+ @person.errors.on(:title).should_not be_nil
978
+ end
979
+
980
+ end
981
+
982
+ end
983
+
984
+ end
985
+
986
+ end