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,77 @@
1
+ require "rubygems"
2
+ require "ruby-prof"
3
+ require "benchmark"
4
+ require "humanoid"
5
+
6
+ Humanoid.configure do |config|
7
+ config.persist_in_safe_mode = false
8
+ config.master = Mongo::Connection.new.db("humanoid_perf_test")
9
+ end
10
+
11
+ Humanoid.master.collection("people").drop
12
+
13
+ class Person
14
+ include Humanoid::Document
15
+ include Humanoid::Timestamps
16
+ field :birth_date, :type => Date
17
+ has_one :name
18
+ has_one :address
19
+ has_many :phones
20
+ end
21
+
22
+ class Name
23
+ include Humanoid::Document
24
+ field :given
25
+ field :family
26
+ field :middle
27
+ belongs_to :person, :inverse_of => :name
28
+ end
29
+
30
+ class Address
31
+ include Humanoid::Document
32
+ field :street
33
+ field :city
34
+ field :state
35
+ field :post_code
36
+ field :address_type
37
+ belongs_to :person, :inverse_of => :address
38
+ end
39
+
40
+ class Phone
41
+ include Humanoid::Document
42
+ field :country_code, :type => Integer
43
+ field :number
44
+ field :phone_type
45
+ belongs_to :person, :inverse_of => :phones
46
+ end
47
+
48
+ # RubyProf.start
49
+
50
+ puts "Starting benchmark..."
51
+
52
+ Benchmark.bm do |bm|
53
+ bm.report("Humanoid") do
54
+ 10000.times do |n|
55
+ person = Person.new(:birth_date => Date.new(1970, 1, 1))
56
+ name = Name.new(:given => "James", :family => "Kirk", :middle => "Tiberius")
57
+ address = Address.new(
58
+ :street => "1 Starfleet Command Way",
59
+ :city => "San Francisco",
60
+ :state => "CA",
61
+ :post_code => "94133",
62
+ :type => "Work"
63
+ )
64
+ phone = Phone.new(:country_code => 1, :number => "415-555-1212", :type => "Mobile")
65
+ person.name = name
66
+ person.address = address
67
+ person.phones << phone
68
+ person.save
69
+ end
70
+ end
71
+ end
72
+
73
+ # result = RubyProf.stop
74
+ # printer = RubyProf::FlatPrinter.new(result)
75
+ # printer.print(STDOUT, 0)
76
+
77
+ # Humanoid.database.collection("people").drop
@@ -0,0 +1,301 @@
1
+ require "spec_helper"
2
+
3
+ describe Humanoid::Associations do
4
+
5
+ before do
6
+ Person.delete_all
7
+ Game.delete_all
8
+ Post.delete_all
9
+ end
10
+
11
+ context "anonymous extensions" do
12
+
13
+ before do
14
+ @person = Person.new(:title => "Dr")
15
+ @address_one = Address.new(:street => "Oxford")
16
+ @address_two = Address.new(:street => "Bond")
17
+ @name = Name.new(:first_name => "Richard", :last_name => "Dawkins")
18
+ @person.addresses << [ @address_one, @address_two ]
19
+ @person.name = @name
20
+ @person.save
21
+ end
22
+
23
+ context "when defined on a has_many" do
24
+
25
+ it "applies the extension" do
26
+ addresses = @person.addresses.find_by_street("Oxford")
27
+ addresses.size.should == 1
28
+ addresses.first.should == @address_one
29
+ end
30
+
31
+ end
32
+
33
+ context "when defined on a has_one" do
34
+
35
+ it "applies the extension" do
36
+ name = @person.name
37
+ name.dawkins?.should be_true
38
+ end
39
+
40
+ end
41
+
42
+ context "when defined on a belongs_to" do
43
+
44
+ it "applies the extension" do
45
+ @address_two.addressable.doctor?.should be_true
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ context "criteria on has many embedded associations" do
53
+
54
+ before do
55
+ @person = Person.new(:title => "Sir")
56
+ @sf_apartment = Address.new(:street => "Genoa Pl", :state => "CA", :address_type => "Apartment")
57
+ @la_home = Address.new(:street => "Rodeo Dr", :state => "CA", :address_type => "Home")
58
+ @sf_home = Address.new(:street => "Pacific", :state => "CA", :address_type => "Home")
59
+ @person.addresses << [ @sf_apartment, @la_home, @sf_home ]
60
+ end
61
+
62
+ it "handles a single criteria" do
63
+ cas = @person.addresses.california
64
+ cas.size.should == 3
65
+ cas.should == [ @sf_apartment, @la_home, @sf_home ]
66
+ end
67
+
68
+ it "handles chained criteria" do
69
+ ca_homes = @person.addresses.california.homes
70
+ ca_homes.size.should == 2
71
+ ca_homes.should == [ @la_home, @sf_home ]
72
+ end
73
+
74
+ it "handles chained criteria with named scopes" do
75
+ ca_homes = @person.addresses.california.homes.rodeo
76
+ ca_homes.size.should == 1
77
+ ca_homes.should == [ @la_home ]
78
+ end
79
+
80
+ end
81
+
82
+ context "one-to-one relational associations" do
83
+
84
+ before do
85
+ @person = Person.new(:title => "Sir")
86
+ @game = Game.new(:score => 1)
87
+ @person.game = @game
88
+ @person.save
89
+ end
90
+
91
+ it "sets the association on save" do
92
+ @from_db = Person.find(@person.id)
93
+ @from_db.game.should == @game
94
+ end
95
+
96
+ it "sets the reverse association before save" do
97
+ @game.person.should == @person
98
+ end
99
+
100
+ it "sets the reverse association after save" do
101
+ @from_db = Game.find(@game.id)
102
+ @game.person.should == @person
103
+ end
104
+
105
+ end
106
+
107
+ context "one-to-many relational associations" do
108
+
109
+ before do
110
+ @person = Person.new(:title => "Sir")
111
+ @post = Post.new(:title => "Testing")
112
+ @person.posts = [@post]
113
+ @person.save
114
+ end
115
+
116
+ it "sets the association on save" do
117
+ from_db = Person.find(@person.id)
118
+ from_db.posts.should == [@post]
119
+ end
120
+
121
+ context "when adding a new association" do
122
+
123
+ before do
124
+ @new_post = Post.new(:title => "New")
125
+ @person.posts << @new_post
126
+ end
127
+
128
+ it "rememoizes the new association" do
129
+ @person.posts.should == [ @post, @new_post ]
130
+ end
131
+ end
132
+
133
+ context "when building" do
134
+
135
+ before do
136
+ @person = Person.new(:title => "Mr")
137
+ @post = @person.posts.build(:title => "First")
138
+ end
139
+
140
+ it "sets the reverse association" do
141
+ @post.person.should == @person
142
+ end
143
+
144
+ end
145
+
146
+ context "finding associated objects" do
147
+
148
+ before do
149
+ @extra_post = Post.create(:title => "Orphan")
150
+ @from_db = Person.find(@person.id)
151
+ end
152
+
153
+ context "finding all" do
154
+
155
+ it "returns only those objects scoped to the parent" do
156
+ Post.all.size.should == 2
157
+ @from_db.posts.all.size.should == 1
158
+ end
159
+
160
+ end
161
+
162
+ context "finding with conditions" do
163
+
164
+ context "finding all" do
165
+
166
+ it "returns only those objects scoped to the parent" do
167
+ posts = @from_db.posts.find(:all, :conditions => { :title => "Testing" })
168
+ posts.size.should == 1
169
+ end
170
+
171
+ end
172
+
173
+ context "finding first" do
174
+
175
+ it "returns only those objects scoped to the parent" do
176
+ post = @from_db.posts.find(:first, :conditions => { :title => "Testing" })
177
+ post.should == @post
178
+ end
179
+
180
+ end
181
+
182
+ context "finding last" do
183
+
184
+ it "returns only those objects scoped to the parent" do
185
+ post = @from_db.posts.find(:last, :conditions => { :title => "Testing" })
186
+ post.should == @post
187
+ end
188
+
189
+ end
190
+
191
+ context "using a named scope" do
192
+
193
+ before do
194
+ @post.created_at = 15.days.ago
195
+ @post.save
196
+ end
197
+
198
+ it "returns only those scoped to the parent plus the named scope" do
199
+ posts = @from_db.posts.recent
200
+ posts.size.should == 1
201
+ end
202
+
203
+ end
204
+
205
+ context "using a criteria class method" do
206
+
207
+ before do
208
+ @post.created_at = 45.days.ago
209
+ @post.save
210
+ end
211
+
212
+ it "returns only those scoped to the parent plus the named scope" do
213
+ posts = @from_db.posts.old
214
+ posts.size.should == 1
215
+ end
216
+
217
+ end
218
+
219
+ end
220
+
221
+ end
222
+
223
+ end
224
+
225
+ context "nested embedded associations" do
226
+
227
+ before do
228
+ @person = Person.create(:title => "Mr")
229
+ end
230
+
231
+ context "one level nested" do
232
+
233
+ before do
234
+ @address = @person.addresses.create(:street => "Oxford St")
235
+ @name = @person.create_name(:first_name => "Gordon")
236
+ end
237
+
238
+ it "persists all the associations properly" do
239
+ @name.last_name = "Brown"
240
+ @person.name.last_name.should == "Brown"
241
+ end
242
+
243
+ end
244
+
245
+ context "multiple levels nested" do
246
+
247
+ context "when a has-many to has_one" do
248
+
249
+ before do
250
+ @person.phone_numbers.create(:number => "4155551212")
251
+ end
252
+
253
+ it "persists all the associations properly" do
254
+ from_db = Person.find(@person.id)
255
+ phone = from_db.phone_numbers.first
256
+ phone.create_country_code(:code => 1)
257
+ from_db.phone_numbers.first.country_code.code.should == 1
258
+ end
259
+
260
+ end
261
+
262
+ context "when a has-one to has-many" do
263
+
264
+ before do
265
+ @person = Person.new(:title => "Sir", :ssn => "1")
266
+ @name = Name.new(:first_name => "Syd")
267
+ @person.name = @name
268
+ @person.save
269
+ end
270
+
271
+ it "persists all the associations properly" do
272
+ from_db = Person.find(@person.id)
273
+ translation = Translation.new(:language => "fr")
274
+ from_db.name.translations << translation
275
+ from_db.attributes[:name][:translations].should_not be_nil
276
+ end
277
+
278
+ end
279
+
280
+ context "when a has-many to has-many" do
281
+
282
+ before do
283
+ @address = Address.new(:street => "Upper Street")
284
+ @person.addresses << @address
285
+ end
286
+
287
+ it "bubbles the child association up to root" do
288
+ location = Location.new(:name => "Home")
289
+ @address.locations << location
290
+ location.stubs(:valid?).returns(false)
291
+ @person.save
292
+ @person.addresses.first.locations.first.should == location
293
+ end
294
+
295
+ end
296
+
297
+ end
298
+
299
+ end
300
+
301
+ end
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+
3
+ describe Humanoid::Attributes do
4
+
5
+ context "when persisting nil attributes" do
6
+
7
+ before do
8
+ @person = Person.create(:score => nil, :ssn => "555-66-7777")
9
+ end
10
+
11
+ after do
12
+ Person.delete_all
13
+ end
14
+
15
+ it "the field should not exist" do
16
+ from_db = Person.find(@person.id)
17
+ from_db.attributes.has_key?(:score).should be_false
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,216 @@
1
+ require "spec_helper"
2
+
3
+ describe Humanoid::Commands do
4
+
5
+ before do
6
+ @person = Person.new(:title => "Sir", :ssn => "6969696")
7
+ end
8
+
9
+ after do
10
+ @person.delete
11
+ end
12
+
13
+ describe ".create" do
14
+
15
+ it "saves and returns the document" do
16
+ person = Person.create(:title => "Sensei", :ssn => "666-66-6666")
17
+ person.should be_a_kind_of(Person)
18
+ person.should_not be_a_new_record
19
+ end
20
+
21
+ end
22
+
23
+ describe ".create!" do
24
+
25
+ context "inserting with a field that is not unique" do
26
+
27
+ context "when a unique index exists" do
28
+
29
+ after do
30
+ Person.delete_all
31
+ end
32
+
33
+ it "raises an error" do
34
+ Person.create!(:ssn => "555-55-9999")
35
+ lambda { Person.create!(:ssn => "555-55-9999") }.should raise_error
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+
44
+ describe "#delete" do
45
+
46
+ context "deleting a root document" do
47
+
48
+ before do
49
+ @person.save
50
+ end
51
+
52
+ it "deletes the document" do
53
+ @person.delete
54
+ lambda { Person.find(@person.id) }.should raise_error
55
+ end
56
+
57
+ it "returns true" do
58
+ @person.delete.should be_true
59
+ end
60
+
61
+ end
62
+
63
+ context "deleting an embedded document" do
64
+
65
+ before do
66
+ @address = Address.new(:street => "Bond Street")
67
+ @person.addresses << @address
68
+ end
69
+
70
+ context "when the document is not yet saved" do
71
+
72
+ it "removes the document from the parent" do
73
+ @address.delete
74
+ @person.addresses.should be_empty
75
+ @person.attributes[:addresses].should be_empty
76
+ end
77
+
78
+ end
79
+
80
+ context "when the document has been saved" do
81
+
82
+ before do
83
+ @address.save
84
+ end
85
+
86
+ it "removes the object from the parent and database" do
87
+ @address.delete
88
+ from_db = Person.find(@person.id)
89
+ from_db.addresses.should be_empty
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+
98
+ describe "#destroy" do
99
+
100
+ context "destroying a root document" do
101
+
102
+ before do
103
+ @person.save
104
+ end
105
+
106
+ it "destroys the document" do
107
+ @person.destroy
108
+ lambda { Person.find(@person.id) }.should raise_error
109
+ end
110
+
111
+ it "returns true" do
112
+ @person.destroy.should be_true
113
+ end
114
+
115
+ end
116
+
117
+ context "deleting an embedded document" do
118
+
119
+ before do
120
+ @address = Address.new(:street => "Bond Street")
121
+ @person.addresses << @address
122
+ end
123
+
124
+ context "when the document is not yet saved" do
125
+
126
+ it "removes the document from the parent" do
127
+ @address.destroy
128
+ @person.addresses.should be_empty
129
+ @person.attributes[:addresses].should be_empty
130
+ end
131
+
132
+ end
133
+
134
+ context "when the document has been saved" do
135
+
136
+ before do
137
+ @address.save
138
+ end
139
+
140
+ it "removes the object from the parent and database" do
141
+ @address.destroy
142
+ from_db = Person.find(@person.id)
143
+ from_db.addresses.should be_empty
144
+ end
145
+
146
+ end
147
+
148
+ end
149
+
150
+ end
151
+
152
+ describe "#save" do
153
+
154
+ context "when validation passes" do
155
+
156
+ it "returns true" do
157
+ @person.save.should be_true
158
+ end
159
+
160
+ end
161
+
162
+ end
163
+
164
+ describe "save!" do
165
+
166
+ context "inserting with a field that is not unique" do
167
+
168
+ context "when a unique index exists" do
169
+
170
+ it "raises an error" do
171
+ Person.create!(:ssn => "555-55-9999")
172
+ person = Person.new(:ssn => "555-55-9999")
173
+ lambda { person.save!(:ssn => "555-55-9999") }.should raise_error
174
+ end
175
+
176
+ end
177
+
178
+ end
179
+
180
+ end
181
+
182
+ describe "#update_attributes" do
183
+
184
+ context "when validation passes" do
185
+
186
+ it "returns true" do
187
+ @person.update_attributes(:ssn => "555-55-1234").should be_true
188
+ end
189
+
190
+ it "saves the attributes" do
191
+ @person.update_attributes(:ssn => "555-55-1235", :title => "Blah")
192
+ @from_db = Person.find(@person.id)
193
+ @from_db.title.should == "Blah"
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+
200
+ describe ".delete_all" do
201
+
202
+ it "returns true" do
203
+ Person.delete_all.should == true
204
+ end
205
+
206
+ end
207
+
208
+ describe ".destroy_all" do
209
+
210
+ it "returns true" do
211
+ Person.destroy_all.should == true
212
+ end
213
+
214
+ end
215
+
216
+ end