simply_stored 0.3.6 → 0.3.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.
@@ -0,0 +1,470 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../fixtures/couch')
3
+
4
+ class CouchHasManyTest < Test::Unit::TestCase
5
+ context "has_many" do
6
+ setup do
7
+ CouchPotato::Config.database_name = 'simply_stored_test'
8
+ recreate_db
9
+ end
10
+
11
+ context "with has_many" do
12
+ should "create a fetch method for the associated objects" do
13
+ user = User.new
14
+ assert user.respond_to?(:posts)
15
+ end
16
+
17
+ should "fetch the associated objects" do
18
+ user = User.create(:title => "Mr.")
19
+ 3.times {
20
+ post = Post.new
21
+ post.user = user
22
+ post.save!
23
+ }
24
+ assert_equal 3, user.posts.size
25
+ end
26
+
27
+ should "set the parent object on the clients cache" do
28
+ User.expects(:find).never
29
+ user = User.create(:title => "Mr.")
30
+ 3.times {
31
+ post = Post.new
32
+ post.user = user
33
+ post.save!
34
+ }
35
+ post = user.posts.first
36
+ assert_equal user, user.posts.first.user
37
+ end
38
+
39
+ context "limit" do
40
+
41
+ should "be able to limit the result set" do
42
+ user = User.create(:title => "Mr.")
43
+ 3.times {
44
+ post = Post.new
45
+ post.user = user
46
+ post.save!
47
+ }
48
+ assert_equal 2, user.posts(:limit => 2).size
49
+ end
50
+
51
+ should "use the given options in the cache-key" do
52
+ user = User.create(:title => "Mr.")
53
+ 3.times {
54
+ post = Post.new
55
+ post.user = user
56
+ post.save!
57
+ }
58
+ assert_equal 2, user.posts(:limit => 2).size
59
+ assert_equal 3, user.posts(:limit => 3).size
60
+ end
61
+
62
+ should "be able to limit the result set - also for through objects" do
63
+ @user = User.create(:title => "Mr.")
64
+ first_pain = Pain.create
65
+ frist_hemorrhoid = Hemorrhoid.create(:user => @user, :pain => first_pain)
66
+ assert_equal [first_pain], @user.pains
67
+ second_pain = Pain.create
68
+ second_hemorrhoid = Hemorrhoid.create(:user => @user, :pain => second_pain)
69
+ @user.reload
70
+ assert_equal 2, @user.pains.size
71
+ assert_equal 1, @user.pains(:limit => 1).size
72
+ end
73
+ end
74
+
75
+ context "order" do
76
+ setup do
77
+ @user = User.create(:title => "Mr.")
78
+ 3.times {
79
+ post = Post.new
80
+ post.user = @user
81
+ post.save!
82
+ }
83
+ end
84
+
85
+ should "support different order" do
86
+ assert_nothing_raised do
87
+ @user.posts(:order => :asc)
88
+ end
89
+
90
+ assert_nothing_raised do
91
+ @user.posts(:order => :desc)
92
+ end
93
+ end
94
+
95
+ should "reverse the order if :desc" do
96
+ assert_equal @user.posts(:order => :asc).map(&:id).reverse, @user.posts(:order => :desc).map(&:id)
97
+ end
98
+
99
+ should "work with the limit option" do
100
+ last_post = Post.create(:user => @user)
101
+ assert_not_equal @user.posts(:order => :asc, :limit => 3).map(&:id).reverse, @user.posts(:order => :desc, :limit => 3).map(&:id)
102
+ end
103
+ end
104
+
105
+ should "verify the given options for the accessor method" do
106
+ user = User.create(:title => "Mr.")
107
+ assert_raise(ArgumentError) do
108
+ user.posts(:foo => false)
109
+ end
110
+ end
111
+
112
+ should "verify the given options for the association defintion" do
113
+ assert_raise(ArgumentError) do
114
+ User.instance_eval do
115
+ has_many :foo, :bar => :do
116
+ end
117
+ end
118
+ end
119
+
120
+ should "only fetch objects of the correct type" do
121
+ user = User.create(:title => "Mr.")
122
+ post = Post.new
123
+ post.user = user
124
+ post.save!
125
+
126
+ comment = Comment.new
127
+ comment.user = user
128
+ comment.save!
129
+
130
+ assert_equal 1, user.posts.size
131
+ end
132
+
133
+ should "getter should user cache" do
134
+ user = User.create(:title => "Mr.")
135
+ post = Post.new
136
+ post.user = user
137
+ post.save!
138
+ user.posts
139
+ assert_equal [post], user.instance_variable_get("@posts")[:all]
140
+ end
141
+
142
+ should "add methods to handle associated objects" do
143
+ user = User.new(:title => "Mr.")
144
+ assert user.respond_to?(:add_post)
145
+ assert user.respond_to?(:remove_post)
146
+ assert user.respond_to?(:remove_all_posts)
147
+ end
148
+
149
+ should 'ignore the cache when requesting explicit reload' do
150
+ user = User.create(:title => "Mr.")
151
+ assert_equal [], user.posts
152
+ post = Post.new
153
+ post.user = user
154
+ post.save!
155
+ assert_equal [post], user.posts(:force_reload => true)
156
+ end
157
+
158
+ context "when adding items" do
159
+ should "add the item to the internal cache" do
160
+ daddy = User.new(:title => "Mr.")
161
+ item = Post.new
162
+ assert_equal [], daddy.posts
163
+ daddy.add_post(item)
164
+ assert_equal [item], daddy.posts
165
+ assert_equal [item], daddy.instance_variable_get("@posts")[:all]
166
+ end
167
+
168
+ should "raise an error when the added item is not an object of the expected class" do
169
+ user = User.new
170
+ assert_raise(ArgumentError, 'excepted Post got String') do
171
+ user.add_post('foo')
172
+ end
173
+ end
174
+
175
+ should "save the added item" do
176
+ post = Post.new
177
+ user = User.create(:title => "Mr.")
178
+ user.add_post(post)
179
+ assert !post.new_record?
180
+ end
181
+
182
+ should 'set the forein key on the added object' do
183
+ post = Post.new
184
+ user = User.create(:title => "Mr.")
185
+ user.add_post(post)
186
+ assert_equal user.id, post.user_id
187
+ end
188
+ end
189
+
190
+ context "when removing items" do
191
+ should "should unset the foreign key" do
192
+ user = User.create(:title => "Mr.")
193
+ post = Post.create(:user => user)
194
+
195
+ user.remove_post(post)
196
+ assert_nil post.user_id
197
+ end
198
+
199
+ should "remove the item from the cache" do
200
+ user = User.create(:title => "Mr.")
201
+ post = Post.create(:user => user)
202
+ assert user.posts.include?(post)
203
+ user.remove_post(post)
204
+ assert !user.posts.any?{|p| post.id == p.id}
205
+ assert_equal [], user.instance_variable_get("@posts")[:all]
206
+ end
207
+
208
+ should "save the removed item with the nullified foreign key" do
209
+ user = User.create(:title => "Mr.")
210
+ post = Post.create(:user => user)
211
+
212
+ user.remove_post(post)
213
+ post = Post.find(post.id)
214
+ assert_nil post.user_id
215
+ end
216
+
217
+ should 'raise an error when another object is the owner of the object to be removed' do
218
+ user = User.create(:title => "Mr.")
219
+ mrs = User.create(:title => "Mrs.")
220
+ post = Post.create(:user => user)
221
+ assert_raise(ArgumentError) do
222
+ mrs.remove_post(post)
223
+ end
224
+ end
225
+
226
+ should 'raise an error when the object is the wrong type' do
227
+ user = User.new
228
+ assert_raise(ArgumentError, 'excepted Post got String') do
229
+ user.remove_post('foo')
230
+ end
231
+ end
232
+
233
+ should "delete the object when dependent:destroy" do
234
+ Category.instance_eval do
235
+ has_many :tags, :dependent => :destroy
236
+ end
237
+
238
+ category = Category.create(:name => "food")
239
+ tag = Tag.create(:name => "food", :category => category)
240
+ assert !tag.new?
241
+ category.remove_tag(tag)
242
+
243
+ assert_equal [], Tag.find(:all)
244
+ end
245
+
246
+ should "not nullify or delete dependents if the options is set to :ignore when removing" do
247
+ master = Master.create
248
+ master_id = master.id
249
+ servant = Servant.create(:master => master)
250
+ master.remove_servant(servant)
251
+ assert_equal master_id, servant.reload.master_id
252
+ end
253
+
254
+ should "not nullify or delete dependents if the options is set to :ignore when deleting" do
255
+ master = Master.create
256
+ master_id = master.id
257
+ servant = Servant.create(:master => master)
258
+ master.destroy
259
+ assert_equal master_id, servant.reload.master_id
260
+ end
261
+
262
+ end
263
+
264
+ context "when removing all items" do
265
+ should 'nullify the foreign keys on all referenced items' do
266
+ user = User.create(:title => "Mr.")
267
+ post = Post.create(:user => user)
268
+ post2 = Post.create(:user => user)
269
+ user.remove_all_posts
270
+ post = Post.find(post.id)
271
+ post2 = Post.find(post2.id)
272
+ assert_nil post.user_id
273
+ assert_nil post2.user_id
274
+ end
275
+
276
+ should 'empty the cache' do
277
+ user = User.create(:title => "Mr.")
278
+ post = Post.create(:user => user)
279
+ post2 = Post.create(:user => user)
280
+ user.remove_all_posts
281
+ assert_equal [], user.posts
282
+ assert_equal [], user.instance_variable_get("@posts")[:all]
283
+ end
284
+
285
+ context "when counting" do
286
+ setup do
287
+ @user = User.create(:title => "Mr.")
288
+ end
289
+
290
+ should "define a count method" do
291
+ assert @user.respond_to?(:post_count)
292
+ end
293
+
294
+ should "cache the result" do
295
+ assert_equal 0, @user.post_count
296
+ Post.create(:user => @user)
297
+ assert_equal 0, @user.post_count
298
+ assert_equal 0, @user.instance_variable_get("@post_count")
299
+ @user.instance_variable_set("@post_count", nil)
300
+ assert_equal 1, @user.post_count
301
+ end
302
+
303
+ should "force reload even if cached" do
304
+ assert_equal 0, @user.post_count
305
+ Post.create(:user => @user)
306
+ assert_equal 0, @user.post_count
307
+ assert_equal 1, @user.post_count(:force_reload => true)
308
+ end
309
+
310
+ should "count the number of belongs_to objects" do
311
+ assert_equal 0, @user.post_count(:force_reload => true)
312
+ Post.create(:user => @user)
313
+ assert_equal 1, @user.post_count(:force_reload => true)
314
+ Post.create(:user => @user)
315
+ assert_equal 2, @user.post_count(:force_reload => true)
316
+ end
317
+
318
+ should "not count foreign objects" do
319
+ assert_equal 0, @user.post_count
320
+ Post.create(:user => nil)
321
+ Post.create(:user => User.create(:title => 'Doc'))
322
+ assert_equal 0, @user.post_count
323
+ assert_equal 2, Post.count
324
+ end
325
+
326
+ should "not count delete objects" do
327
+ hemorrhoid = Hemorrhoid.create(:user => @user)
328
+ assert_equal 1, @user.hemorrhoid_count
329
+ hemorrhoid.delete
330
+ assert_equal 0, @user.hemorrhoid_count(:force_reload => true)
331
+ assert_equal 1, @user.hemorrhoid_count(:force_reload => true, :with_deleted => true)
332
+ end
333
+
334
+ should "work with has_many :through" do
335
+ assert_equal 0, @user.pain_count
336
+ first_pain = Pain.create
337
+ frist_hemorrhoid = Hemorrhoid.create(:user => @user, :pain => first_pain)
338
+ assert_equal [first_pain], @user.pains
339
+ assert_equal 1, @user.pain_count(:force_reload => true)
340
+
341
+ second_pain = Pain.create
342
+ second_hemorrhoid = Hemorrhoid.create(:user => @user, :pain => second_pain)
343
+ assert_equal 2, @user.pain_count(:force_reload => true)
344
+ end
345
+
346
+ end
347
+ end
348
+
349
+ context 'when destroying the parent objects' do
350
+ should "delete relations when dependent is destroy" do
351
+ Category.instance_eval do
352
+ has_many :tags, :dependent => :destroy
353
+ end
354
+
355
+ category = Category.create(:name => "food")
356
+ tag = Tag.create(:name => "food", :category => category)
357
+
358
+ assert_equal [tag], Tag.find(:all)
359
+ category.destroy
360
+ assert_equal [], Tag.find(:all)
361
+ end
362
+
363
+ should "nullify relations when dependent is nullify" do
364
+
365
+ user = User.create(:title => "Mr.")
366
+ post = Post.create(:user => user)
367
+
368
+ user.destroy
369
+ post = Post.find(post.id)
370
+ assert_nil post.user_id
371
+ end
372
+
373
+ should "nullify the foreign key even if validation forbids" do
374
+ user = User.create(:title => "Mr.")
375
+ post = StrictPost.create(:user => user)
376
+
377
+ user.destroy
378
+ post = StrictPost.find(post.id)
379
+ assert_nil post.user_id
380
+ end
381
+ end
382
+ end
383
+
384
+ context "with has_many :trough" do
385
+ setup do
386
+ @journal_1 = Journal.create
387
+ @journal_2 = Journal.create
388
+ @reader_1 = Reader.create
389
+ @reader_2 = Reader.create
390
+ end
391
+
392
+ should "raise an exception if there is no :through relation" do
393
+
394
+ assert_raise(ArgumentError) do
395
+ class FooHasManyThroughBar
396
+ include SimplyStored::Couch
397
+ has_many :foos, :through => :bars
398
+ end
399
+ end
400
+ end
401
+
402
+ should "define a getter" do
403
+ assert @journal_1.respond_to?(:readers)
404
+ assert @reader_1.respond_to?(:journals)
405
+ end
406
+
407
+ should "load the objects through" do
408
+ membership = Membership.new
409
+ membership.journal = @journal_1
410
+ membership.reader = @reader_1
411
+ assert membership.save
412
+
413
+ assert_equal @journal_1, membership.journal
414
+ assert_equal @reader_1, membership.reader
415
+ assert_equal [membership], @journal_1.reload.memberships
416
+ assert_equal [membership], @journal_1.reload.memberships
417
+
418
+ assert_equal [@reader_1], @journal_1.readers
419
+ assert_equal [@journal_1], @reader_1.journals
420
+
421
+ membership_2 = Membership.new
422
+ membership_2.journal = @journal_1
423
+ membership_2.reader = @reader_2
424
+ assert membership_2.save
425
+
426
+ assert_equal [@reader_1.id, @reader_2.id].sort, @journal_1.reload.readers.map(&:id).sort
427
+ assert_equal [@journal_1.id], @reader_1.reload.journals.map(&:id).sort
428
+ assert_equal [@journal_1.id], @reader_2.reload.journals.map(&:id).sort
429
+
430
+ membership_3 = Membership.new
431
+ membership_3.journal = @journal_2
432
+ membership_3.reader = @reader_2
433
+ assert membership_3.save
434
+
435
+ assert_equal [@reader_1.id, @reader_2.id].sort, @journal_1.reload.readers.map(&:id).sort
436
+ assert_equal [@reader_2.id].sort, @journal_2.reload.readers.map(&:id).sort
437
+ assert_equal [@journal_1.id], @reader_1.reload.journals.map(&:id).sort
438
+ assert_equal [@journal_1.id, @journal_2.id].sort, @reader_2.reload.journals.map(&:id).sort
439
+
440
+ membership_3.destroy
441
+
442
+ assert_equal [@reader_1.id, @reader_2.id].sort, @journal_1.reload.readers.map(&:id).sort
443
+ assert_equal [], @journal_2.reload.readers
444
+ assert_equal [@journal_1.id], @reader_1.reload.journals.map(&:id).sort
445
+ assert_equal [@journal_1.id], @reader_2.reload.journals.map(&:id).sort
446
+ end
447
+
448
+ should "verify the given options" do
449
+ assert_raise(ArgumentError) do
450
+ @journal_1.readers(:foo => true)
451
+ end
452
+ end
453
+
454
+ should "not try to destroy/nullify through-objects on parent object delete" do
455
+ membership = Membership.new
456
+ membership.journal = @journal_1
457
+ membership.reader = @reader_1
458
+ assert membership.save
459
+
460
+ @reader_1.reload
461
+ @journal_1.reload
462
+
463
+ Reader.any_instance.expects("journal=").never
464
+ Journal.any_instance.expects(:readers).never
465
+
466
+ @journal_1.delete
467
+ end
468
+ end
469
+ end
470
+ end
@@ -0,0 +1,135 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/../fixtures/couch')
3
+
4
+ class CouchHasOneTest < Test::Unit::TestCase
5
+ context "with has_one" do
6
+ setup do
7
+ CouchPotato::Config.database_name = 'simply_stored_test'
8
+ recreate_db
9
+ end
10
+
11
+ should "add a getter method" do
12
+ assert Instance.new.respond_to?(:identity)
13
+ end
14
+
15
+ should "fetch the object when invoking the getter" do
16
+ instance = Instance.create
17
+ identity = Identity.create(:instance => instance)
18
+ assert_equal identity, instance.identity
19
+ end
20
+
21
+ should "set the parent object on the clients cache" do
22
+ Instance.expects(:find).never
23
+ instance = Instance.create
24
+ identity = Identity.create(:instance => instance)
25
+
26
+ assert_equal instance, instance.identity.instance
27
+ end
28
+
29
+ should "verify the given options for the accessor method" do
30
+ instance = Instance.create
31
+ assert_raise(ArgumentError) do
32
+ instance.identity(:foo => :var)
33
+ end
34
+ end
35
+
36
+ should "verify the given options for the association defintion" do
37
+ assert_raise(ArgumentError) do
38
+ User.instance_eval do
39
+ has_one :foo, :bar => :do
40
+ end
41
+ end
42
+ end
43
+
44
+ should "store the fetched object into the cache" do
45
+ instance = Instance.create
46
+ identity = Identity.create(:instance => instance)
47
+ instance.identity
48
+ assert_equal identity, instance.instance_variable_get("@identity")
49
+ end
50
+
51
+ should "not fetch from the database when object is in cache" do
52
+ instance = Instance.create
53
+ identity = Identity.create(:instance => instance)
54
+ instance.identity
55
+ CouchPotato.database.expects(:view).never
56
+ instance.identity
57
+ end
58
+
59
+ should "update the foreign object to have the owner's id in the forein key" do
60
+ instance = Instance.create
61
+ identity = Identity.create
62
+ instance.identity = identity
63
+ identity.reload
64
+ assert_equal instance.id, identity.instance_id
65
+ end
66
+
67
+ should "update the cache when setting" do
68
+ instance = Instance.create
69
+ identity = Identity.create
70
+ instance.identity = identity
71
+ CouchPotato.expects(:database).never
72
+ assert_equal identity, instance.identity
73
+ end
74
+
75
+ should "set the foreign key value to nil when assigning nil" do
76
+ instance = Instance.create
77
+ identity = Identity.create(:instance => instance)
78
+ instance.identity = nil
79
+ identity = Identity.find(identity.id)
80
+ assert_nil identity.instance_id
81
+ end
82
+
83
+ should 'check the class' do
84
+ instance = Instance.create
85
+ assert_raise(ArgumentError, 'expected Item got String') do
86
+ instance.identity = 'foo'
87
+ end
88
+ end
89
+
90
+ should 'delete the dependent objects when dependent is set to destroy' do
91
+ identity = Identity.create
92
+ mag = Magazine.create
93
+ mag.identity = identity
94
+ mag.identity = nil
95
+ assert_nil Identity.find_by_id(identity.id)
96
+ end
97
+
98
+ should 'unset the id on the foreign object when a new object is set' do
99
+ instance = Instance.create
100
+ identity = Identity.create(:instance => instance)
101
+ identity2 = Identity.create
102
+
103
+ instance.identity = identity2
104
+ identity = Identity.find(identity.id)
105
+ assert_nil identity.instance_id
106
+ end
107
+
108
+ should 'delete the foreign object when a new object is set and dependent is set to destroy' do
109
+ identity = Identity.create
110
+ identity2 = Identity.create
111
+ mag = Magazine.create
112
+ mag.identity = identity
113
+ mag.identity = identity2
114
+ assert_nil Identity.find_by_id(identity.id)
115
+ end
116
+
117
+ should 'delete the foreign object when parent is destroyed and dependent is set to destroy' do
118
+ identity = Identity.create
119
+ mag = Magazine.create
120
+ mag.identity = identity
121
+
122
+ mag.destroy
123
+ assert_nil Identity.find_by_id(identity.id)
124
+ end
125
+
126
+ should 'nullify the foreign objects foreign key when parent is destroyed' do
127
+ identity = Identity.create
128
+ instance = Instance.create
129
+ instance.identity = identity
130
+ instance.destroy
131
+ identity = Identity.find(identity.id)
132
+ assert_nil identity.instance_id
133
+ end
134
+ end
135
+ end