forked from rails/rails
-
Notifications
You must be signed in to change notification settings - Fork 0
/
inverse_associations_test.rb
1029 lines (845 loc) · 46.3 KB
/
inverse_associations_test.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# frozen_string_literal: true
require "cases/helper"
require "models/human"
require "models/face"
require "models/interest"
require "models/zine"
require "models/club"
require "models/sponsor"
require "models/rating"
require "models/post"
require "models/comment"
require "models/car"
require "models/bulb"
require "models/mixed_case_monkey"
require "models/admin"
require "models/admin/account"
require "models/admin/user"
require "models/developer"
require "models/company"
require "models/project"
require "models/author"
require "models/user"
require "models/room"
require "models/contract"
require "models/subscription"
require "models/book"
require "models/branch"
class AutomaticInverseFindingTests < ActiveRecord::TestCase
fixtures :ratings, :comments, :cars, :books
def test_has_one_and_belongs_to_should_find_inverse_automatically_on_multiple_word_name
monkey_reflection = MixedCaseMonkey.reflect_on_association(:human)
human_reflection = Human.reflect_on_association(:mixed_case_monkey)
assert monkey_reflection.has_inverse?, "The monkey reflection should have an inverse"
assert_equal human_reflection, monkey_reflection.inverse_of, "The monkey reflection's inverse should be the human reflection"
assert human_reflection.has_inverse?, "The human reflection should have an inverse"
assert_equal monkey_reflection, human_reflection.inverse_of, "The human reflection's inverse should be the monkey reflection"
end
def test_has_many_and_belongs_to_should_find_inverse_automatically_for_model_in_module
account_reflection = Admin::Account.reflect_on_association(:users)
user_reflection = Admin::User.reflect_on_association(:account)
assert account_reflection.has_inverse?, "The Admin::Account reflection should have an inverse"
assert_equal user_reflection, account_reflection.inverse_of, "The Admin::Account reflection's inverse should be the Admin::User reflection"
end
def test_has_one_and_belongs_to_should_find_inverse_automatically
car_reflection = Car.reflect_on_association(:bulb)
bulb_reflection = Bulb.reflect_on_association(:car)
assert car_reflection.has_inverse?, "The Car reflection should have an inverse"
assert_equal bulb_reflection, car_reflection.inverse_of, "The Car reflection's inverse should be the Bulb reflection"
assert bulb_reflection.has_inverse?, "The Bulb reflection should have an inverse"
assert_equal car_reflection, bulb_reflection.inverse_of, "The Bulb reflection's inverse should be the Car reflection"
end
def test_has_many_and_belongs_to_should_find_inverse_automatically
comment_reflection = Comment.reflect_on_association(:ratings)
rating_reflection = Rating.reflect_on_association(:comment)
assert comment_reflection.has_inverse?, "The Comment reflection should have an inverse"
assert_equal rating_reflection, comment_reflection.inverse_of, "The Comment reflection's inverse should be the Rating reflection"
end
def test_has_many_and_belongs_to_should_find_inverse_automatically_for_extension_block
comment_reflection = Comment.reflect_on_association(:post)
post_reflection = Post.reflect_on_association(:comments)
assert_predicate post_reflection, :has_inverse?
assert_equal comment_reflection, post_reflection.inverse_of
end
def test_has_many_and_belongs_to_should_find_inverse_automatically_for_sti
author_reflection = Author.reflect_on_association(:posts)
author_child_reflection = Author.reflect_on_association(:special_posts)
post_reflection = Post.reflect_on_association(:author)
assert_respond_to author_reflection, :has_inverse?
assert author_reflection.has_inverse?, "The Author reflection should have an inverse"
assert_equal post_reflection, author_reflection.inverse_of, "The Author reflection's inverse should be the Post reflection"
assert_respond_to author_child_reflection, :has_inverse?
assert author_child_reflection.has_inverse?, "The Author reflection should have an inverse"
assert_equal post_reflection, author_child_reflection.inverse_of, "The Author reflection's inverse should be the Post reflection"
end
def test_has_one_and_belongs_to_with_non_default_foreign_key_should_not_find_inverse_automatically
user = User.create!
owned_room = Room.create!(owner: user)
assert_nil user.room
assert_nil owned_room.user
assert_equal user, owned_room.owner
assert_equal owned_room, user.owned_room
end
def test_has_one_and_belongs_to_with_custom_association_name_should_not_find_wrong_inverse_automatically
user_reflection = Room.reflect_on_association(:user)
owner_reflection = Room.reflect_on_association(:owner)
room_reflection = User.reflect_on_association(:room)
assert_predicate user_reflection, :has_inverse?
assert_equal room_reflection, user_reflection.inverse_of
assert_not_predicate owner_reflection, :has_inverse?
assert_not_equal room_reflection, owner_reflection.inverse_of
end
def test_has_many_and_belongs_to_with_a_scope_and_automatic_scope_inversing_should_find_inverse_automatically
contacts_reflection = Company.reflect_on_association(:special_contracts)
company_reflection = SpecialContract.reflect_on_association(:company)
assert contacts_reflection.scope
assert_not company_reflection.scope
with_automatic_scope_inversing(contacts_reflection, company_reflection) do
assert_predicate contacts_reflection, :has_inverse?
assert_equal company_reflection, contacts_reflection.inverse_of
assert_not_equal contacts_reflection, company_reflection.inverse_of
end
end
def test_has_one_and_belongs_to_with_a_scope_and_automatic_scope_inversing_should_find_inverse_automatically
post_reflection = Author.reflect_on_association(:recent_post)
author_reflection = Post.reflect_on_association(:author)
assert post_reflection.scope
assert_not author_reflection.scope
with_automatic_scope_inversing(post_reflection, author_reflection) do
assert_predicate post_reflection, :has_inverse?
assert_equal author_reflection, post_reflection.inverse_of
assert_not_equal post_reflection, author_reflection.inverse_of
end
end
def test_has_many_with_scoped_belongs_to_does_not_find_inverse_automatically
book = books(:tlg)
book.update_attribute(:author_visibility, :invisible)
assert_nil book.subscriptions.new.book
subscription_reflection = Book.reflect_on_association(:subscriptions)
book_reflection = Subscription.reflect_on_association(:book)
assert_not subscription_reflection.scope
assert book_reflection.scope
with_automatic_scope_inversing(book_reflection, subscription_reflection) do
assert_nil book.subscriptions.new.book
end
end
def test_has_one_and_belongs_to_automatic_inverse_shares_objects
car = Car.first
bulb = Bulb.create!(car: car)
assert_equal car.bulb, bulb, "The Car's bulb should be the original bulb"
car.bulb.color = "Blue"
assert_equal car.bulb.color, bulb.color, "Changing the bulb's color on the car association should change the bulb's color"
bulb.color = "Red"
assert_equal bulb.color, car.bulb.color, "Changing the bulb's color should change the bulb's color on the car association"
end
def test_has_many_and_belongs_to_automatic_inverse_shares_objects_on_rating
comment = Comment.first
rating = Rating.create!(comment: comment)
assert_equal rating.comment, comment, "The Rating's comment should be the original Comment"
rating.comment.body = "Fennec foxes are the smallest of the foxes."
assert_equal rating.comment.body, comment.body, "Changing the Comment's body on the association should change the original Comment's body"
comment.body = "Kittens are adorable."
assert_equal comment.body, rating.comment.body, "Changing the original Comment's body should change the Comment's body on the association"
end
def test_has_many_and_belongs_to_automatic_inverse_shares_objects_on_comment
rating = Rating.create!
comment = Comment.first
rating.comment = comment
assert_equal rating.comment, comment, "The Rating's comment should be the original Comment"
rating.comment.body = "Fennec foxes are the smallest of the foxes."
assert_equal rating.comment.body, comment.body, "Changing the Comment's body on the association should change the original Comment's body"
comment.body = "Kittens are adorable."
assert_equal comment.body, rating.comment.body, "Changing the original Comment's body should change the Comment's body on the association"
end
def test_polymorphic_and_has_many_through_relationships_should_not_have_inverses
sponsor_reflection = Sponsor.reflect_on_association(:sponsorable)
assert_not sponsor_reflection.has_inverse?, "A polymorphic association should not find an inverse automatically"
club_reflection = Club.reflect_on_association(:members)
assert_not club_reflection.has_inverse?, "A has_many_through association should not find an inverse automatically"
end
def test_polymorphic_has_one_should_find_inverse_automatically
human_reflection = Human.reflect_on_association(:polymorphic_face_without_inverse)
assert_predicate human_reflection, :has_inverse?
end
end
class InverseAssociationTests < ActiveRecord::TestCase
def test_should_allow_for_inverse_of_options_in_associations
assert_nothing_raised do
Class.new(ActiveRecord::Base).has_many(:wheels, inverse_of: :car)
end
assert_nothing_raised do
Class.new(ActiveRecord::Base).has_one(:engine, inverse_of: :car)
end
assert_nothing_raised do
Class.new(ActiveRecord::Base).belongs_to(:car, inverse_of: :driver)
end
end
def test_should_be_able_to_ask_a_reflection_if_it_has_an_inverse
has_one_with_inverse_ref = Human.reflect_on_association(:face)
assert_predicate has_one_with_inverse_ref, :has_inverse?
has_many_with_inverse_ref = Human.reflect_on_association(:interests)
assert_predicate has_many_with_inverse_ref, :has_inverse?
belongs_to_with_inverse_ref = Face.reflect_on_association(:human)
assert_predicate belongs_to_with_inverse_ref, :has_inverse?
has_one_without_inverse_ref = Club.reflect_on_association(:sponsor)
assert_not_predicate has_one_without_inverse_ref, :has_inverse?
has_many_without_inverse_ref = Club.reflect_on_association(:memberships)
assert_not_predicate has_many_without_inverse_ref, :has_inverse?
belongs_to_without_inverse_ref = Sponsor.reflect_on_association(:sponsor_club)
assert_not_predicate belongs_to_without_inverse_ref, :has_inverse?
end
def test_inverse_of_method_should_supply_the_actual_reflection_instance_it_is_the_inverse_of
has_one_ref = Human.reflect_on_association(:face)
assert_equal Face.reflect_on_association(:human), has_one_ref.inverse_of
has_many_ref = Human.reflect_on_association(:interests)
assert_equal Interest.reflect_on_association(:human), has_many_ref.inverse_of
belongs_to_ref = Face.reflect_on_association(:human)
assert_equal Human.reflect_on_association(:face), belongs_to_ref.inverse_of
end
def test_associations_with_no_inverse_of_should_return_nil
has_one_ref = Club.reflect_on_association(:sponsor)
assert_nil has_one_ref.inverse_of
has_many_ref = Club.reflect_on_association(:memberships)
assert_nil has_many_ref.inverse_of
belongs_to_ref = Sponsor.reflect_on_association(:sponsor_club)
assert_nil belongs_to_ref.inverse_of
end
def test_polymorphic_associations_dont_attempt_to_find_inverse_of
belongs_to_ref = Sponsor.reflect_on_association(:sponsor)
assert_raise(ArgumentError) { belongs_to_ref.klass }
assert_nil belongs_to_ref.inverse_of
belongs_to_ref = Face.reflect_on_association(:super_human)
assert_raise(ArgumentError) { belongs_to_ref.klass }
assert_nil belongs_to_ref.inverse_of
end
def test_this_inverse_stuff
firm = Firm.create!(name: "Adequate Holdings")
Project.create!(name: "Project 1", firm: firm)
Developer.create!(name: "Gorbypuff", firm: firm)
new_project = Project.last
assert Project.reflect_on_association(:lead_developer).inverse_of.present?, "Expected inverse of to be present"
assert new_project.lead_developer.present?, "Expected lead developer to be present on the project"
end
end
class InverseHasOneTests < ActiveRecord::TestCase
fixtures :humans, :faces
def test_parent_instance_should_be_shared_with_child_on_find
human = humans(:gordon)
face = human.face
assert_equal human.name, face.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, face.human.name, "Name of human should be the same after changes to parent instance"
face.human.name = "Mungo"
assert_equal human.name, face.human.name, "Name of human should be the same after changes to child-owned instance"
end
def test_parent_instance_should_be_shared_with_eager_loaded_child_on_find
human = Human.all.merge!(where: { name: "Gordon" }, includes: :face).first
face = human.face
assert_equal human.name, face.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, face.human.name, "Name of human should be the same after changes to parent instance"
face.human.name = "Mungo"
assert_equal human.name, face.human.name, "Name of human should be the same after changes to child-owned instance"
human = Human.all.merge!(where: { name: "Gordon" }, includes: :face, order: "faces.id").first
face = human.face
assert_equal human.name, face.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, face.human.name, "Name of human should be the same after changes to parent instance"
face.human.name = "Mungo"
assert_equal human.name, face.human.name, "Name of human should be the same after changes to child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_built_child
human = Human.first
face = human.build_face(description: "haunted")
assert_not_nil face.human
assert_equal human.name, face.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, face.human.name, "Name of human should be the same after changes to parent instance"
face.human.name = "Mungo"
assert_equal human.name, face.human.name, "Name of human should be the same after changes to just-built-child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_created_child
human = Human.first
face = human.create_face(description: "haunted")
assert_not_nil face.human
assert_equal human.name, face.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, face.human.name, "Name of human should be the same after changes to parent instance"
face.human.name = "Mungo"
assert_equal human.name, face.human.name, "Name of human should be the same after changes to newly-created-child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_created_child_via_bang_method
human = Human.first
face = human.create_face!(description: "haunted")
assert_not_nil face.human
assert_equal human.name, face.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, face.human.name, "Name of human should be the same after changes to parent instance"
face.human.name = "Mungo"
assert_equal human.name, face.human.name, "Name of human should be the same after changes to newly-created-child-owned instance"
end
def test_parent_instance_should_be_shared_with_replaced_via_accessor_child
human = Human.first
face = Face.new(description: "haunted")
human.face = face
assert_not_nil face.human
assert_equal human.name, face.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, face.human.name, "Name of human should be the same after changes to parent instance"
face.human.name = "Mungo"
assert_equal human.name, face.human.name, "Name of human should be the same after changes to replaced-child-owned instance"
end
def test_child_instance_should_be_shared_with_replaced_via_accessor_parent
human = Human.first
face = Face.create!(description: "haunted", human: Human.last)
face.human = human
assert_equal face, human.face
assert_equal face.description, human.face.description, "Description of the face should be the same before changes to child instance"
face.description = "Bongo"
assert_equal face.description, human.face.description, "Description of the face should be the same after changes to child instance"
human.face.description = "Mungo"
assert_equal face.description, human.face.description, "Description of the face should be the same after changes to replaced-parent-owned instance"
end
def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Human.first.confused_face }
end
def test_trying_to_use_inverses_that_dont_exist_should_have_suggestions_for_fix
error = assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) {
Human.first.confused_face
}
if error.respond_to?(:detailed_message)
assert_match "Did you mean?", error.detailed_message
else
assert_match "Did you mean?", error.message
end
assert_equal "confused_human", error.corrections.first
end
end
class InverseHasManyTests < ActiveRecord::TestCase
fixtures :humans, :interests, :posts, :authors, :author_addresses, :comments
def test_parent_instance_should_be_shared_with_every_child_on_find
human = humans(:gordon)
interests = human.interests
interests.each do |interest|
assert_equal human.name, interest.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to parent instance"
interest.human.name = "Mungo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to child-owned instance"
end
end
def test_parent_instance_should_be_shared_with_every_child_on_find_for_sti
author = authors(:david)
posts = author.posts
posts.each do |post|
assert_equal author.name, post.author.name, "Name of human should be the same before changes to parent instance"
author.name = "Bongo"
assert_equal author.name, post.author.name, "Name of human should be the same after changes to parent instance"
post.author.name = "Mungo"
assert_equal author.name, post.author.name, "Name of human should be the same after changes to child-owned instance"
end
special_posts = author.special_posts
special_posts.each do |post|
assert_equal author.name, post.author.name, "Name of human should be the same before changes to parent instance"
author.name = "Bongo"
assert_equal author.name, post.author.name, "Name of human should be the same after changes to parent instance"
post.author.name = "Mungo"
assert_equal author.name, post.author.name, "Name of human should be the same after changes to child-owned instance"
end
end
def test_parent_instance_should_be_shared_with_eager_loaded_children
human = Human.all.merge!(where: { name: "Gordon" }, includes: :interests).first
interests = human.interests
interests.each do |interest|
assert_equal human.name, interest.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to parent instance"
interest.human.name = "Mungo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to child-owned instance"
end
human = Human.all.merge!(where: { name: "Gordon" }, includes: :interests, order: "interests.id").first
interests = human.interests
interests.each do |interest|
assert_equal human.name, interest.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to parent instance"
interest.human.name = "Mungo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to child-owned instance"
end
end
def test_parent_instance_should_be_shared_with_newly_block_style_built_child
human = Human.first
interest = human.interests.build { |ii| ii.topic = "Industrial Revolution Re-enactment" }
assert_not_nil interest.topic, "Child attributes supplied to build via blocks should be populated"
assert_not_nil interest.human
assert_equal human.name, interest.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to parent instance"
interest.human.name = "Mungo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to just-built-child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_created_via_bang_method_child
human = Human.first
interest = human.interests.create!(topic: "Industrial Revolution Re-enactment")
assert_not_nil interest.human
assert_equal human.name, interest.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to parent instance"
interest.human.name = "Mungo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to newly-created-child-owned instance"
end
def test_parent_instance_should_be_shared_with_newly_block_style_created_child
human = Human.first
interest = human.interests.create { |ii| ii.topic = "Industrial Revolution Re-enactment" }
assert_not_nil interest.topic, "Child attributes supplied to create via blocks should be populated"
assert_not_nil interest.human
assert_equal human.name, interest.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to parent instance"
interest.human.name = "Mungo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to newly-created-child-owned instance"
end
def test_parent_instance_should_be_shared_within_create_block_of_new_child
human = Human.first
interest = human.interests.create do |i|
assert i.human.equal?(human), "Human of child should be the same instance as a parent"
end
assert interest.human.equal?(human), "Human of the child should still be the same instance as a parent"
end
def test_parent_instance_should_be_shared_within_build_block_of_new_child
human = Human.first
interest = human.interests.build do |i|
assert i.human.equal?(human), "Human of child should be the same instance as a parent"
end
assert interest.human.equal?(human), "Human of the child should still be the same instance as a parent"
end
def test_parent_instance_should_be_shared_with_poked_in_child
human = humans(:gordon)
interest = Interest.create(topic: "Industrial Revolution Re-enactment")
human.interests << interest
assert_not_nil interest.human
assert_equal human.name, interest.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to parent instance"
interest.human.name = "Mungo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to newly-created-child-owned instance"
end
def test_parent_instance_should_be_shared_with_replaced_via_accessor_children
human = Human.first
interest = Interest.new(topic: "Industrial Revolution Re-enactment")
human.interests = [interest]
assert_not_nil interest.human
assert_equal human.name, interest.human.name, "Name of human should be the same before changes to parent instance"
human.name = "Bongo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to parent instance"
interest.human.name = "Mungo"
assert_equal human.name, interest.human.name, "Name of human should be the same after changes to replaced-child-owned instance"
end
def test_parent_instance_should_be_shared_with_first_and_last_child
human = Human.first
assert human.interests.first.human.equal? human
assert human.interests.last.human.equal? human
end
def test_parent_instance_should_be_shared_with_first_n_and_last_n_children
human = Human.first
interests = human.interests.first(2)
assert interests[0].human.equal? human
assert interests[1].human.equal? human
interests = human.interests.last(2)
assert interests[0].human.equal? human
assert interests[1].human.equal? human
end
def test_parent_instance_should_find_child_instance_using_child_instance_id
human = Human.create!
interest = Interest.create!
human.interests = [interest]
assert interest.equal?(human.interests.first), "The inverse association should use the interest already created and held in memory"
assert interest.equal?(human.interests.find(interest.id)), "The inverse association should use the interest already created and held in memory"
assert human.equal?(human.interests.first.human), "Two inversion should lead back to the same object that was originally held"
assert human.equal?(human.interests.find(interest.id).human), "Two inversions should lead back to the same object that was originally held"
end
def test_parent_instance_should_find_child_instance_using_child_instance_id_when_created
human = Human.create!
interest = Interest.create!(human: human)
assert human.equal?(human.interests.first.human), "Two inverses should lead back to the same object that was originally held"
assert human.equal?(human.interests.find(interest.id).human), "Two inversions should lead back to the same object that was originally held"
assert_nil human.interests.find(interest.id).human.name, "The name of the human should match before the name is changed"
human.name = "Ben Bitdiddle"
assert_equal human.name, human.interests.find(interest.id).human.name, "The name of the human should match after the parent name is changed"
human.interests.find(interest.id).human.name = "Alyssa P. Hacker"
assert_equal human.name, human.interests.find(interest.id).human.name, "The name of the human should match after the child name is changed"
end
def test_find_on_child_instance_with_id_should_not_load_all_child_records
human = Human.create!
interest = Interest.create!(human: human)
human.interests.find(interest.id)
assert_not_predicate human.interests, :loaded?
end
def test_find_on_child_instance_with_id_should_set_inverse_instances
human = Human.create!
interest = Interest.create!(human: human)
child = human.interests.find(interest.id)
assert_predicate child.association(:human), :loaded?
end
def test_find_on_child_instances_with_ids_should_set_inverse_instances
human = Human.create!
interests = Array.new(2) { Interest.create!(human: human) }
children = human.interests.find(interests.pluck(:id))
children.each do |child|
assert_predicate child.association(:human), :loaded?
end
end
def test_raise_record_not_found_error_when_invalid_ids_are_passed
# delete all interest records to ensure that hard coded invalid_id(s)
# are indeed invalid.
Interest.delete_all
human = Human.create!
invalid_id = 245324523
assert_raise(ActiveRecord::RecordNotFound) { human.interests.find(invalid_id) }
invalid_ids = [8432342, 2390102913, 2453245234523452]
assert_raise(ActiveRecord::RecordNotFound) { human.interests.find(invalid_ids) }
end
def test_raise_record_not_found_error_when_no_ids_are_passed
human = Human.create!
exception = assert_raise(ActiveRecord::RecordNotFound) { human.interests.load.find() }
assert_equal exception.model, "Interest"
assert_equal exception.primary_key, "id"
end
def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Human.first.secret_interests }
end
def test_child_instance_should_point_to_parent_without_saving
human = Human.new
interest = Interest.create(topic: "Industrial Revolution Re-enactment")
human.interests << interest
assert_not_nil interest.human
interest.human.name = "Charles"
assert_equal interest.human.name, human.name
assert_not_predicate human, :persisted?
end
def test_inverse_instance_should_be_set_before_find_callbacks_are_run
reset_callbacks(Interest, :find) do
Interest.after_find { raise unless association(:human).loaded? && human.present? }
assert_predicate Human.first.interests.reload, :any?
assert_predicate Human.includes(:interests).first.interests, :any?
assert_predicate Human.joins(:interests).includes(:interests).first.interests, :any?
end
end
def test_inverse_instance_should_be_set_before_initialize_callbacks_are_run
reset_callbacks(Interest, :initialize) do
Interest.after_initialize { raise unless association(:human).loaded? && human.present? }
assert_predicate Human.first.interests.reload, :any?
assert_predicate Human.includes(:interests).first.interests, :any?
assert_predicate Human.joins(:interests).includes(:interests).first.interests, :any?
end
end
def test_inverse_works_when_the_association_self_references_the_same_object
comment = comments(:greetings)
Comment.create!(parent: comment, post_id: comment.post_id, body: "New Comment")
comment.body = "OMG"
assert_equal comment.body, comment.children.first.parent.body
end
def test_changing_the_association_id_makes_the_inversed_association_target_stale
post1 = Post.first
post2 = Post.second
comment = post1.comments.first
assert_same post1, comment.post
comment.update!(post_id: post2.id)
assert_equal post2, comment.post
end
end
class InverseBelongsToTests < ActiveRecord::TestCase
fixtures :humans, :faces, :interests
def test_child_instance_should_be_shared_with_parent_on_find
face = faces(:trusting)
human = face.human
assert_equal face.description, human.face.description, "Description of face should be the same before changes to child instance"
face.description = "gormless"
assert_equal face.description, human.face.description, "Description of face should be the same after changes to child instance"
human.face.description = "pleasing"
assert_equal face.description, human.face.description, "Description of face should be the same after changes to parent-owned instance"
end
def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
face = Face.all.merge!(includes: :human, where: { description: "trusting" }).first
human = face.human
assert_equal face.description, human.face.description, "Description of face should be the same before changes to child instance"
face.description = "gormless"
assert_equal face.description, human.face.description, "Description of face should be the same after changes to child instance"
human.face.description = "pleasing"
assert_equal face.description, human.face.description, "Description of face should be the same after changes to parent-owned instance"
face = Face.all.merge!(includes: :human, order: "humans.id", where: { description: "trusting" }).first
human = face.human
assert_equal face.description, human.face.description, "Description of face should be the same before changes to child instance"
face.description = "gormless"
assert_equal face.description, human.face.description, "Description of face should be the same after changes to child instance"
human.face.description = "pleasing"
assert_equal face.description, human.face.description, "Description of face should be the same after changes to parent-owned instance"
end
def test_child_instance_should_be_shared_with_newly_built_parent
face = faces(:trusting)
human = face.build_human(name: "Charles")
assert_not_nil human.face
assert_equal face.description, human.face.description, "Description of face should be the same before changes to child instance"
face.description = "gormless"
assert_equal face.description, human.face.description, "Description of face should be the same after changes to child instance"
human.face.description = "pleasing"
assert_equal face.description, human.face.description, "Description of face should be the same after changes to just-built-parent-owned instance"
end
def test_child_instance_should_be_shared_with_newly_created_parent
face = faces(:trusting)
human = face.create_human(name: "Charles")
assert_not_nil human.face
assert_equal face.description, human.face.description, "Description of face should be the same before changes to child instance"
face.description = "gormless"
assert_equal face.description, human.face.description, "Description of face should be the same after changes to child instance"
human.face.description = "pleasing"
assert_equal face.description, human.face.description, "Description of face should be the same after changes to newly-created-parent-owned instance"
end
def test_should_not_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
interest = interests(:trainspotting)
human = interest.human
assert_not_nil human.interests
iz = human.interests.detect { |_iz| _iz.id == interest.id }
assert_not_nil iz
assert_equal interest.topic, iz.topic, "Interest topics should be the same before changes to child"
interest.topic = "Eating cheese with a spoon"
assert_not_equal interest.topic, iz.topic, "Interest topics should not be the same after changes to child"
iz.topic = "Cow tipping"
assert_not_equal interest.topic, iz.topic, "Interest topics should not be the same after changes to parent-owned instance"
end
def test_with_has_many_inversing_should_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
with_has_many_inversing(Interest) do
interest = interests(:trainspotting)
human = interest.human
assert_not_nil human.interests
iz = human.interests.detect { |_iz| _iz.id == interest.id }
assert_not_nil iz
assert_equal interest.topic, iz.topic, "Interest topics should be the same before changes to child"
interest.topic = "Eating cheese with a spoon"
assert_equal interest.topic, iz.topic, "Interest topics should be the same after changes to child"
iz.topic = "Cow tipping"
assert_equal interest.topic, iz.topic, "Interest topics should be the same after changes to parent-owned instance"
end
end
def test_with_has_many_inversing_should_have_single_record_when_setting_record_through_attribute_in_build_method
with_has_many_inversing(Interest) do
human = Human.create!
human.interests.build(
human: human
)
assert_equal 1, human.interests.size
human.save!
assert_equal 1, human.interests.size
end
end
def test_with_has_many_inversing_does_not_trigger_association_callbacks_on_set_when_the_inverse_is_a_has_many
with_has_many_inversing(Interest) do
human = interests(:trainspotting).human_with_callbacks
assert_not_predicate human, :add_callback_called?
end
end
def test_with_has_many_inversing_does_not_add_duplicate_associated_objects
with_has_many_inversing(Interest) do
human = Human.new
interest = Interest.new(human: human)
human.interests << interest
assert_equal 1, human.interests.size
end
end
def test_recursive_model_has_many_inversing
with_has_many_inversing do
main = Branch.create!
feature = main.branches.create!
topic = feature.branches.build
assert_equal(main, topic.branch.branch)
end
end
def test_recursive_inverse_on_recursive_model_has_many_inversing
with_has_many_inversing do
main = BrokenBranch.create!
feature = main.branches.create!
topic = feature.branches.build
error = assert_raises(ActiveRecord::InverseOfAssociationRecursiveError) do
topic.branch.branch
end
assert_equal("Inverse association branch (:branch in BrokenBranch) is recursive.", error.message)
end
end
def test_unscope_does_not_set_inverse_when_incorrect
interest = interests(:trainspotting)
human = interest.human
created_human = Human.create(name: "wrong human")
found_interest = created_human.interests.or(human.interests).detect { |this_interest| interest.id == this_interest.id }
assert_equal human, found_interest.human
end
def test_or_does_not_set_inverse_when_incorrect
interest = interests(:trainspotting)
human = interest.human
created_human = Human.create(name: "wrong human")
found_interest = created_human.interests.unscope(:where).detect { |this_interest| interest.id == this_interest.id }
assert_equal human, found_interest.human
end
def test_child_instance_should_be_shared_with_replaced_via_accessor_parent
face = Face.first
human = Human.new(name: "Charles")
face.human = human
assert_not_nil human.face
assert_equal face.description, human.face.description, "Description of face should be the same before changes to child instance"
face.description = "gormless"
assert_equal face.description, human.face.description, "Description of face should be the same after changes to child instance"
human.face.description = "pleasing"
assert_equal face.description, human.face.description, "Description of face should be the same after changes to replaced-parent-owned instance"
end
def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.first.confused_human }
end
def test_trying_to_use_inverses_that_dont_exist_should_have_suggestions_for_fix
error = assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) {
Face.first.confused_human
}
if error.respond_to?(:detailed_message)
assert_match "Did you mean?", error.detailed_message
else
assert_match "Did you mean?", error.message
end
assert_equal "confused_face", error.corrections.first
end
def test_building_has_many_parent_association_inverses_one_record
with_has_many_inversing(Interest) do
interest = Interest.new
interest.build_human
assert_equal 1, interest.human.interests.size
interest.save!
assert_equal 1, interest.human.interests.size
end
end
end
class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
fixtures :humans, :faces, :interests
def test_child_instance_should_be_shared_with_parent_on_find
face = Face.all.merge!(where: { description: "confused" }).first
human = face.polymorphic_human
assert_equal face.description, human.polymorphic_face.description, "Description of face should be the same before changes to child instance"
face.description = "gormless"
assert_equal face.description, human.polymorphic_face.description, "Description of face should be the same after changes to child instance"
human.polymorphic_face.description = "pleasing"
assert_equal face.description, human.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance"
end
def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
face = Face.all.merge!(where: { description: "confused" }, includes: :human).first
human = face.polymorphic_human
assert_equal face.description, human.polymorphic_face.description, "Description of face should be the same before changes to child instance"
face.description = "gormless"
assert_equal face.description, human.polymorphic_face.description, "Description of face should be the same after changes to child instance"
human.polymorphic_face.description = "pleasing"
assert_equal face.description, human.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance"
face = Face.all.merge!(where: { description: "confused" }, includes: :human, order: "humans.id").first
human = face.polymorphic_human
assert_equal face.description, human.polymorphic_face.description, "Description of face should be the same before changes to child instance"
face.description = "gormless"
assert_equal face.description, human.polymorphic_face.description, "Description of face should be the same after changes to child instance"
human.polymorphic_face.description = "pleasing"
assert_equal face.description, human.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance"
end
def test_child_instance_should_be_shared_with_replaced_via_accessor_parent
face = faces(:confused)
new_human = Human.new
assert_not_nil face.polymorphic_human
face.polymorphic_human = new_human
assert_equal face.description, new_human.polymorphic_face.description, "Description of face should be the same before changes to parent instance"
face.description = "Bongo"
assert_equal face.description, new_human.polymorphic_face.description, "Description of face should be the same after changes to parent instance"
new_human.polymorphic_face.description = "Mungo"
assert_equal face.description, new_human.polymorphic_face.description, "Description of face should be the same after changes to replaced-parent-owned instance"
end
def test_inversed_instance_should_not_be_reloaded_after_stale_state_changed
new_human = Human.new
face = Face.new
new_human.face = face
old_inversed_human = face.human
new_human.save!
new_inversed_human = face.human
assert_same old_inversed_human, new_inversed_human
end
def test_inversed_instance_should_not_be_reloaded_after_stale_state_changed_with_validation
face = Face.new human: Human.new
old_inversed_human = face.human
face.save!
new_inversed_human = face.human
assert_same old_inversed_human, new_inversed_human
end
def test_inversed_instance_should_load_after_autosave_if_it_is_not_already_loaded
human = Human.create!
human.create_autosave_face!
human.autosave_face.reload # clear cached load of autosave_human
human.autosave_face.description = "new description"
human.save!
assert_not_nil human.autosave_face.autosave_human
end
def test_should_not_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
interest = interests(:llama_wrangling)
human = interest.polymorphic_human
assert_not_nil human.polymorphic_interests
iz = human.polymorphic_interests.detect { |_iz| _iz.id == interest.id }
assert_not_nil iz
assert_equal interest.topic, iz.topic, "Interest topics should be the same before changes to child"
interest.topic = "Eating cheese with a spoon"
assert_not_equal interest.topic, iz.topic, "Interest topics should not be the same after changes to child"
iz.topic = "Cow tipping"
assert_not_equal interest.topic, iz.topic, "Interest topics should not be the same after changes to parent-owned instance"
end
def test_with_has_many_inversing_should_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
with_has_many_inversing(Interest) do
interest = interests(:llama_wrangling)
human = interest.polymorphic_human
assert_not_nil human.polymorphic_interests
iz = human.polymorphic_interests.detect { |_iz| _iz.id == interest.id }
assert_not_nil iz
assert_equal interest.topic, iz.topic, "Interest topics should be the same before changes to child"
interest.topic = "Eating cheese with a spoon"
assert_equal interest.topic, iz.topic, "Interest topics should be the same after changes to child"
iz.topic = "Cow tipping"
assert_equal interest.topic, iz.topic, "Interest topics should be the same after changes to parent-owned instance"
end
end
def test_with_has_many_inversing_does_not_trigger_association_callbacks_on_set_when_the_inverse_is_a_has_many
with_has_many_inversing(Interest) do
human = interests(:llama_wrangling).polymorphic_human_with_callbacks
assert_not_predicate human, :add_callback_called?
end
end
def test_trying_to_access_inverses_that_dont_exist_shouldnt_raise_an_error
# Ideally this would, if only for symmetry's sake with other association types
assert_nothing_raised { Face.first.puzzled_polymorphic_human }
end
def test_trying_to_set_polymorphic_inverses_that_dont_exist_at_all_should_raise_an_error
# fails because no class has the correct inverse_of for puzzled_polymorphic_human
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.first.puzzled_polymorphic_human = Human.first }
end
def test_trying_to_set_polymorphic_inverses_that_dont_exist_on_the_instance_being_set_should_raise_an_error