giga-safe-box 0.0.1

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 (140) hide show
  1. checksums.yaml +7 -0
  2. data/giga-safe-box.gemspec +11 -0
  3. data/shoulda-matchers-8.0.1/LICENSE +22 -0
  4. data/shoulda-matchers-8.0.1/README.md +567 -0
  5. data/shoulda-matchers-8.0.1/docs/errors/NonCaseSwappableValueError.md +111 -0
  6. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/callback_matcher.rb +121 -0
  7. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +70 -0
  8. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/flash_store.rb +86 -0
  9. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/permit_matcher.rb +445 -0
  10. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +96 -0
  11. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/render_template_matcher.rb +84 -0
  12. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +145 -0
  13. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +115 -0
  14. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +148 -0
  15. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/route_matcher.rb +207 -0
  16. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/route_params.rb +62 -0
  17. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/session_store.rb +34 -0
  18. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +228 -0
  19. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_session_matcher.rb +163 -0
  20. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +107 -0
  21. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller.rb +24 -0
  22. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +45 -0
  23. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error.rb +23 -0
  24. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +232 -0
  25. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +62 -0
  26. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +40 -0
  27. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +48 -0
  28. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/successful_check.rb +14 -0
  29. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/successful_setting.rb +14 -0
  30. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher.rb +695 -0
  31. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/comparison_matcher.rb +157 -0
  32. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +78 -0
  33. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/errors.rb +52 -0
  34. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +125 -0
  35. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/helpers.rb +37 -0
  36. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +45 -0
  37. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +67 -0
  38. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +45 -0
  39. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +44 -0
  40. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +71 -0
  41. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +53 -0
  42. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers.rb +9 -0
  43. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb +26 -0
  44. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/allow_nil.rb +26 -0
  45. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/ignore_interference_by_writer.rb +101 -0
  46. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +21 -0
  47. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers.rb +14 -0
  48. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +180 -0
  49. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +128 -0
  50. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +567 -0
  51. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +185 -0
  52. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +272 -0
  53. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +664 -0
  54. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +575 -0
  55. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +711 -0
  56. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +428 -0
  57. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +62 -0
  58. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_matcher.rb +195 -0
  59. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_message_finder.rb +63 -0
  60. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validator.rb +127 -0
  61. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model.rb +96 -0
  62. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +217 -0
  63. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matcher.rb +2134 -0
  64. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +74 -0
  65. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +62 -0
  66. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +41 -0
  67. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +105 -0
  68. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +101 -0
  69. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +89 -0
  70. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +142 -0
  71. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +71 -0
  72. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +41 -0
  73. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +76 -0
  74. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +42 -0
  75. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +65 -0
  76. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers.rb +9 -0
  77. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +784 -0
  78. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/encrypt_matcher.rb +174 -0
  79. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_attached_matcher.rb +343 -0
  80. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +366 -0
  81. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +287 -0
  82. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +104 -0
  83. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +95 -0
  84. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_rich_text_matcher.rb +83 -0
  85. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +132 -0
  86. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/normalize_matcher.rb +151 -0
  87. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/serialize_matcher.rb +203 -0
  88. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/model.rb +58 -0
  89. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/namespace.rb +39 -0
  90. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb +49 -0
  91. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +55 -0
  92. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness.rb +14 -0
  93. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +1197 -0
  94. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record.rb +37 -0
  95. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/configuration.rb +31 -0
  96. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double.rb +103 -0
  97. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double_collection.rb +60 -0
  98. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +31 -0
  99. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/method_call.rb +44 -0
  100. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/object_double.rb +43 -0
  101. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +32 -0
  102. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/stub_implementation.rb +31 -0
  103. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/world.rb +63 -0
  104. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak.rb +38 -0
  105. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/error.rb +29 -0
  106. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent/delegate_method_matcher/target_not_defined_error.rb +15 -0
  107. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent/delegate_method_matcher.rb +540 -0
  108. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent.rb +11 -0
  109. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/configuration.rb +70 -0
  110. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/configuration_error.rb +9 -0
  111. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/inclusion.rb +20 -0
  112. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/action_controller.rb +33 -0
  113. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/active_model.rb +26 -0
  114. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/active_record.rb +26 -0
  115. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/missing_library.rb +19 -0
  116. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/rails.rb +31 -0
  117. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/routing.rb +29 -0
  118. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries.rb +16 -0
  119. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/rails.rb +12 -0
  120. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/registry.rb +28 -0
  121. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +37 -0
  122. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +36 -0
  123. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +37 -0
  124. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +40 -0
  125. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +29 -0
  126. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +36 -0
  127. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks.rb +16 -0
  128. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations.rb +43 -0
  129. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/matcher_collection.rb +99 -0
  130. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/matcher_context.rb +36 -0
  131. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/rails_shim.rb +190 -0
  132. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/routing.rb +10 -0
  133. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/util/word_wrap.rb +211 -0
  134. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/util.rb +110 -0
  135. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/version.rb +6 -0
  136. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/warn.rb +28 -0
  137. data/shoulda-matchers-8.0.1/lib/shoulda/matchers.rb +25 -0
  138. data/shoulda-matchers-8.0.1/lib/shoulda-matchers.rb +1 -0
  139. data/shoulda-matchers-8.0.1/shoulda-matchers.gemspec +43 -0
  140. metadata +178 -0
@@ -0,0 +1,2134 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+
3
+ module Shoulda
4
+ module Matchers
5
+ module ActiveRecord
6
+ # The `belong_to` matcher is used to ensure that a `belong_to` association
7
+ # exists on your model.
8
+ #
9
+ # class Person < ActiveRecord::Base
10
+ # belongs_to :organization
11
+ # end
12
+ #
13
+ # # RSpec
14
+ # RSpec.describe Person, type: :model do
15
+ # it { should belong_to(:organization) }
16
+ # end
17
+ #
18
+ # # Minitest (Shoulda)
19
+ # class PersonTest < ActiveSupport::TestCase
20
+ # should belong_to(:organization)
21
+ # end
22
+ #
23
+ # Note that polymorphic associations are automatically detected and do not
24
+ # need any qualifiers:
25
+ #
26
+ # class Comment < ActiveRecord::Base
27
+ # belongs_to :commentable, polymorphic: true
28
+ # end
29
+ #
30
+ # # RSpec
31
+ # RSpec.describe Comment, type: :model do
32
+ # it { should belong_to(:commentable) }
33
+ # end
34
+ #
35
+ # # Minitest (Shoulda)
36
+ # class CommentTest < ActiveSupport::TestCase
37
+ # should belong_to(:commentable)
38
+ # end
39
+ #
40
+ # #### Qualifiers
41
+ #
42
+ # ##### conditions
43
+ #
44
+ # Use `conditions` if your association is defined with a scope that sets
45
+ # the `where` clause.
46
+ #
47
+ # class Person < ActiveRecord::Base
48
+ # belongs_to :family, -> { where(everyone_is_perfect: false) }
49
+ # end
50
+ #
51
+ # # RSpec
52
+ # RSpec.describe Person, type: :model do
53
+ # it do
54
+ # should belong_to(:family).
55
+ # conditions(everyone_is_perfect: false)
56
+ # end
57
+ # end
58
+ #
59
+ # # Minitest (Shoulda)
60
+ # class PersonTest < ActiveSupport::TestCase
61
+ # should belong_to(:family).
62
+ # conditions(everyone_is_perfect: false)
63
+ # end
64
+ #
65
+ # ##### order
66
+ #
67
+ # Use `order` if your association is defined with a scope that sets the
68
+ # `order` clause.
69
+ #
70
+ # class Person < ActiveRecord::Base
71
+ # belongs_to :previous_company, -> { order('hired_on desc') }
72
+ # end
73
+ #
74
+ # # RSpec
75
+ # RSpec.describe Person, type: :model do
76
+ # it { should belong_to(:previous_company).order('hired_on desc') }
77
+ # end
78
+ #
79
+ # # Minitest (Shoulda)
80
+ # class PersonTest < ActiveSupport::TestCase
81
+ # should belong_to(:previous_company).order('hired_on desc')
82
+ # end
83
+ #
84
+ # ##### class_name
85
+ #
86
+ # Use `class_name` to test usage of the `:class_name` option. This
87
+ # asserts that the model you're referring to actually exists.
88
+ #
89
+ # class Person < ActiveRecord::Base
90
+ # belongs_to :ancient_city, class_name: 'City'
91
+ # end
92
+ #
93
+ # # RSpec
94
+ # RSpec.describe Person, type: :model do
95
+ # it { should belong_to(:ancient_city).class_name('City') }
96
+ # end
97
+ #
98
+ # # Minitest (Shoulda)
99
+ # class PersonTest < ActiveSupport::TestCase
100
+ # should belong_to(:ancient_city).class_name('City')
101
+ # end
102
+ #
103
+ # ##### with_primary_key
104
+ #
105
+ # Use `with_primary_key` to test usage of the `:primary_key` option.
106
+ #
107
+ # class Person < ActiveRecord::Base
108
+ # belongs_to :great_country, primary_key: 'country_id'
109
+ # end
110
+ #
111
+ # # RSpec
112
+ # RSpec.describe Person, type: :model do
113
+ # it do
114
+ # should belong_to(:great_country).
115
+ # with_primary_key('country_id')
116
+ # end
117
+ # end
118
+ #
119
+ # # Minitest (Shoulda)
120
+ # class PersonTest < ActiveSupport::TestCase
121
+ # should belong_to(:great_country).
122
+ # with_primary_key('country_id')
123
+ # end
124
+ #
125
+ # ##### with_foreign_key
126
+ #
127
+ # Use `with_foreign_key` to test usage of the `:foreign_key` option.
128
+ #
129
+ # class Person < ActiveRecord::Base
130
+ # belongs_to :great_country, foreign_key: 'country_id'
131
+ # end
132
+ #
133
+ # # RSpec
134
+ # RSpec.describe Person, type: :model do
135
+ # it do
136
+ # should belong_to(:great_country).
137
+ # with_foreign_key('country_id')
138
+ # end
139
+ # end
140
+ #
141
+ # # Minitest (Shoulda)
142
+ # class PersonTest < ActiveSupport::TestCase
143
+ # should belong_to(:great_country).
144
+ # with_foreign_key('country_id')
145
+ # end
146
+ #
147
+ # ##### with_foreign_type
148
+ #
149
+ # Use `with_foreign_type` to test usage of the `:foreign_type` option.
150
+ #
151
+ # class Visitor < ActiveRecord::Base
152
+ # belongs_to :location, foreign_type: 'facility_type', polymorphic: true
153
+ # end
154
+ #
155
+ # # RSpec
156
+ # RSpec.describe Visitor, type: :model do
157
+ # it do
158
+ # should belong_to(:location).
159
+ # with_foreign_type('facility_type')
160
+ # end
161
+ # end
162
+ #
163
+ # # Minitest (Shoulda)
164
+ # class VisitorTest < ActiveSupport::TestCase
165
+ # should belong_to(:location).
166
+ # with_foreign_type('facility_type')
167
+ # end
168
+ #
169
+ # ##### dependent
170
+ #
171
+ # Use `dependent` to assert that the `:dependent` option was specified.
172
+ #
173
+ # class Person < ActiveRecord::Base
174
+ # belongs_to :world, dependent: :destroy
175
+ # end
176
+ #
177
+ # # RSpec
178
+ # RSpec.describe Person, type: :model do
179
+ # it { should belong_to(:world).dependent(:destroy) }
180
+ # end
181
+ #
182
+ # # Minitest (Shoulda)
183
+ # class PersonTest < ActiveSupport::TestCase
184
+ # should belong_to(:world).dependent(:destroy)
185
+ # end
186
+ #
187
+ # To assert that *any* `:dependent` option was specified, use `true`:
188
+ #
189
+ # # RSpec
190
+ # RSpec.describe Person, type: :model do
191
+ # it { should belong_to(:world).dependent(true) }
192
+ # end
193
+ #
194
+ # To assert that *no* `:dependent` option was specified, use `false`:
195
+ #
196
+ # class Person < ActiveRecord::Base
197
+ # belongs_to :company
198
+ # end
199
+ #
200
+ # # RSpec
201
+ # RSpec.describe Person, type: :model do
202
+ # it { should belong_to(:company).dependent(false) }
203
+ # end
204
+ #
205
+ # ##### counter_cache
206
+ #
207
+ # Use `counter_cache` to assert that the `:counter_cache` option was
208
+ # specified.
209
+ #
210
+ # class Person < ActiveRecord::Base
211
+ # belongs_to :organization, counter_cache: true
212
+ # end
213
+ #
214
+ # # RSpec
215
+ # RSpec.describe Person, type: :model do
216
+ # it { should belong_to(:organization).counter_cache(true) }
217
+ # end
218
+ #
219
+ # # Minitest (Shoulda)
220
+ # class PersonTest < ActiveSupport::TestCase
221
+ # should belong_to(:organization).counter_cache(true)
222
+ # end
223
+ #
224
+ # ##### touch
225
+ #
226
+ # Use `touch` to assert that the `:touch` option was specified.
227
+ #
228
+ # class Person < ActiveRecord::Base
229
+ # belongs_to :organization, touch: true
230
+ # end
231
+ #
232
+ # # RSpec
233
+ # RSpec.describe Person, type: :model do
234
+ # it { should belong_to(:organization).touch(true) }
235
+ # end
236
+ #
237
+ # # Minitest (Shoulda)
238
+ # class PersonTest < ActiveSupport::TestCase
239
+ # should belong_to(:organization).touch(true)
240
+ # end
241
+ #
242
+ # ##### strict_loading
243
+ #
244
+ # Use `strict_loading` to assert that the `:strict_loading` option was specified.
245
+ #
246
+ # class Organization < ActiveRecord::Base
247
+ # has_many :people, strict_loading: true
248
+ # end
249
+ #
250
+ # # RSpec
251
+ # RSpec.describe Organization, type: :model do
252
+ # it { should have_many(:people).strict_loading(true) }
253
+ # end
254
+ #
255
+ # # Minitest (Shoulda)
256
+ # class OrganizationTest < ActiveSupport::TestCase
257
+ # should have_many(:people).strict_loading(true)
258
+ # end
259
+ #
260
+ # Default value is true when no argument is specified
261
+ #
262
+ # # RSpec
263
+ # RSpec.describe Organization, type: :model do
264
+ # it { should have_many(:people).strict_loading }
265
+ # end
266
+ #
267
+ # # Minitest (Shoulda)
268
+ # class OrganizationTest < ActiveSupport::TestCase
269
+ # should have_many(:people).strict_loading
270
+ # end
271
+ #
272
+ # ##### autosave
273
+ #
274
+ # Use `autosave` to assert that the `:autosave` option was specified.
275
+ #
276
+ # class Account < ActiveRecord::Base
277
+ # belongs_to :bank, autosave: true
278
+ # end
279
+ #
280
+ # # RSpec
281
+ # RSpec.describe Account, type: :model do
282
+ # it { should belong_to(:bank).autosave(true) }
283
+ # end
284
+ #
285
+ # # Minitest (Shoulda)
286
+ # class AccountTest < ActiveSupport::TestCase
287
+ # should belong_to(:bank).autosave(true)
288
+ # end
289
+ #
290
+ # ##### inverse_of
291
+ #
292
+ # Use `inverse_of` to assert that the `:inverse_of` option was specified.
293
+ #
294
+ # class Person < ActiveRecord::Base
295
+ # belongs_to :organization, inverse_of: :employees
296
+ # end
297
+ #
298
+ # # RSpec
299
+ # describe Person
300
+ # it { should belong_to(:organization).inverse_of(:employees) }
301
+ # end
302
+ #
303
+ # # Minitest (Shoulda)
304
+ # class PersonTest < ActiveSupport::TestCase
305
+ # should belong_to(:organization).inverse_of(:employees)
306
+ # end
307
+ #
308
+ # ##### required
309
+ #
310
+ # Use `required` to assert that the association is not allowed to be nil.
311
+ # (Enabled by default in Rails 5+.)
312
+ #
313
+ # class Person < ActiveRecord::Base
314
+ # belongs_to :organization, required: true
315
+ # end
316
+ #
317
+ # # RSpec
318
+ # describe Person
319
+ # it { should belong_to(:organization).required }
320
+ # end
321
+ #
322
+ # # Minitest (Shoulda)
323
+ # class PersonTest < ActiveSupport::TestCase
324
+ # should belong_to(:organization).required
325
+ # end
326
+ #
327
+ # ##### without_validating_presence
328
+ #
329
+ # Use `without_validating_presence` with `belong_to` to prevent the
330
+ # matcher from checking whether the association disallows nil (Rails 5+
331
+ # only). This can be helpful if you have a custom hook that always sets
332
+ # the association to a meaningful value:
333
+ #
334
+ # class Person < ActiveRecord::Base
335
+ # belongs_to :organization
336
+ #
337
+ # before_validation :autoassign_organization
338
+ #
339
+ # private
340
+ #
341
+ # def autoassign_organization
342
+ # self.organization = Organization.create!
343
+ # end
344
+ # end
345
+ #
346
+ # # RSpec
347
+ # describe Person
348
+ # it { should belong_to(:organization).without_validating_presence }
349
+ # end
350
+ #
351
+ # # Minitest (Shoulda)
352
+ # class PersonTest < ActiveSupport::TestCase
353
+ # should belong_to(:organization).without_validating_presence
354
+ # end
355
+ #
356
+ # ##### optional
357
+ #
358
+ # Use `optional` to assert that the association is allowed to be nil.
359
+ # (Rails 5+ only.)
360
+ #
361
+ # class Person < ActiveRecord::Base
362
+ # belongs_to :organization, optional: true
363
+ # end
364
+ #
365
+ # # RSpec
366
+ # describe Person
367
+ # it { should belong_to(:organization).optional }
368
+ # end
369
+ #
370
+ # # Minitest (Shoulda)
371
+ # class PersonTest < ActiveSupport::TestCase
372
+ # should belong_to(:organization).optional
373
+ # end
374
+ #
375
+ # ##### deprecated
376
+ #
377
+ # Use `deprecated` to assert that the `:deprecated` option was specified.
378
+ # (Enabled by default in Rails 8.1+).
379
+ #
380
+ # class Account < ActiveRecord::Base
381
+ # belongs_to :bank, deprecated: true
382
+ # end
383
+ #
384
+ # # RSpec
385
+ # RSpec.describe Account, type: :model do
386
+ # it { should belong_to(:bank).deprecated(true) }
387
+ # end
388
+ #
389
+ # # Minitest (Shoulda)
390
+ # class AccountTest < ActiveSupport::TestCase
391
+ # should belong_to(:bank).deprecated(true)
392
+ # end
393
+ #
394
+ # @return [AssociationMatcher]
395
+ #
396
+ def belong_to(name)
397
+ AssociationMatcher.new(:belongs_to, name)
398
+ end
399
+
400
+ # The `have_delegated_type` matcher is used to ensure that a `belong_to` association
401
+ # exists on your model using the delegated_type macro.
402
+ #
403
+ # class Vehicle < ActiveRecord::Base
404
+ # delegated_type :drivable, types: %w(Car Truck)
405
+ # end
406
+ #
407
+ # # RSpec
408
+ # RSpec.describe Vehicle, type: :model do
409
+ # it { should have_delegated_type(:drivable) }
410
+ # end
411
+ #
412
+ # # Minitest (Shoulda)
413
+ # class VehicleTest < ActiveSupport::TestCase
414
+ # should have_delegated_type(:drivable)
415
+ # end
416
+ #
417
+ # #### Qualifiers
418
+ #
419
+ # ##### types
420
+ #
421
+ # Use `types` to test the types that are allowed for the association.
422
+ #
423
+ # class Vehicle < ActiveRecord::Base
424
+ # delegated_type :drivable, types: %w(Car Truck)
425
+ # end
426
+ #
427
+ # # RSpec
428
+ # RSpec.describe Vehicle, type: :model do
429
+ # it do
430
+ # should have_delegated_type(:drivable).
431
+ # types(%w(Car Truck))
432
+ # end
433
+ # end
434
+ #
435
+ # # Minitest (Shoulda)
436
+ # class VehicleTest < ActiveSupport::TestCase
437
+ # should have_delegated_type(:drivable).
438
+ # types(%w(Car Truck))
439
+ # end
440
+ #
441
+ # ##### conditions
442
+ #
443
+ # Use `conditions` if your association is defined with a scope that sets
444
+ # the `where` clause.
445
+ #
446
+ # class Vehicle < ActiveRecord::Base
447
+ # delegated_type :drivable, types: %w(Car Truck), scope: -> { where(with_wheels: true) }
448
+ # end
449
+ #
450
+ # # RSpec
451
+ # RSpec.describe Vehicle, type: :model do
452
+ # it do
453
+ # should have_delegated_type(:drivable).
454
+ # conditions(with_wheels: true)
455
+ # end
456
+ # end
457
+ #
458
+ # # Minitest (Shoulda)
459
+ # class VehicleTest < ActiveSupport::TestCase
460
+ # should have_delegated_type(:drivable).
461
+ # conditions(everyone_is_perfect: false)
462
+ # end
463
+ #
464
+ # ##### order
465
+ #
466
+ # Use `order` if your association is defined with a scope that sets the
467
+ # `order` clause.
468
+ #
469
+ # class Person < ActiveRecord::Base
470
+ # delegated_type :drivable, types: %w(Car Truck), scope: -> { order('wheels desc') }
471
+ # end
472
+ #
473
+ # # RSpec
474
+ # RSpec.describe Vehicle, type: :model do
475
+ # it { should have_delegated_type(:drivable).order('wheels desc') }
476
+ # end
477
+ #
478
+ # # Minitest (Shoulda)
479
+ # class VehicleTest < ActiveSupport::TestCase
480
+ # should have_delegated_type(:drivable).order('wheels desc')
481
+ # end
482
+ #
483
+ # ##### with_primary_key
484
+ #
485
+ # Use `with_primary_key` to test usage of the `:primary_key` option.
486
+ #
487
+ # class Vehicle < ActiveRecord::Base
488
+ # delegated_type :drivable, types: %w(Car Truck), primary_key: 'vehicle_id'
489
+ # end
490
+ #
491
+ # # RSpec
492
+ # RSpec.describe Vehicle, type: :model do
493
+ # it do
494
+ # should have_delegated_type(:drivable).
495
+ # with_primary_key('vehicle_id')
496
+ # end
497
+ # end
498
+ #
499
+ # # Minitest (Shoulda)
500
+ # class VehicleTest < ActiveSupport::TestCase
501
+ # should have_delegated_type(:drivable).
502
+ # with_primary_key('vehicle_id')
503
+ # end
504
+ #
505
+ # ##### with_foreign_key
506
+ #
507
+ # Use `with_foreign_key` to test usage of the `:foreign_key` option.
508
+ #
509
+ # class Vehicle < ActiveRecord::Base
510
+ # delegated_type :drivable, types: %w(Car Truck), foreign_key: 'drivable_uuid'
511
+ # end
512
+ #
513
+ # # RSpec
514
+ # RSpec.describe Vehicle, type: :model do
515
+ # it do
516
+ # should have_delegated_type(:drivable).
517
+ # with_foreign_key('drivable_uuid')
518
+ # end
519
+ # end
520
+ #
521
+ # # Minitest (Shoulda)
522
+ # class VehicleTest < ActiveSupport::TestCase
523
+ # should have_delegated_type(:drivable).
524
+ # with_foreign_key('drivable_uuid')
525
+ # end
526
+ #
527
+ # ##### dependent
528
+ #
529
+ # Use `dependent` to assert that the `:dependent` option was specified.
530
+ #
531
+ # class Vehicle < ActiveRecord::Base
532
+ # delegated_type :drivable, types: %w(Car Truck), dependent: :destroy
533
+ # end
534
+ #
535
+ # # RSpec
536
+ # RSpec.describe Vehicle, type: :model do
537
+ # it { should have_delegated_type(:drivable).dependent(:destroy) }
538
+ # end
539
+ #
540
+ # # Minitest (Shoulda)
541
+ # class VehicleTest < ActiveSupport::TestCase
542
+ # should have_delegated_type(:drivable).dependent(:destroy)
543
+ # end
544
+ #
545
+ # To assert that *any* `:dependent` option was specified, use `true`:
546
+ #
547
+ # # RSpec
548
+ # RSpec.describe Vehicle, type: :model do
549
+ # it { should have_delegated_type(:drivable).dependent(true) }
550
+ # end
551
+ #
552
+ # To assert that *no* `:dependent` option was specified, use `false`:
553
+ #
554
+ # class Vehicle < ActiveRecord::Base
555
+ # delegated_type :drivable, types: %w(Car Truck)
556
+ # end
557
+ #
558
+ # # RSpec
559
+ # RSpec.describe Vehicle, type: :model do
560
+ # it { should have_delegated_type(:drivable).dependent(false) }
561
+ # end
562
+ #
563
+ # ##### counter_cache
564
+ #
565
+ # Use `counter_cache` to assert that the `:counter_cache` option was
566
+ # specified.
567
+ #
568
+ # class Vehicle < ActiveRecord::Base
569
+ # delegated_type :drivable, types: %w(Car Truck), counter_cache: true
570
+ # end
571
+ #
572
+ # # RSpec
573
+ # RSpec.describe Vehicle, type: :model do
574
+ # it { should have_delegated_type(:drivable).counter_cache(true) }
575
+ # end
576
+ #
577
+ # # Minitest (Shoulda)
578
+ # class VehicleTest < ActiveSupport::TestCase
579
+ # should have_delegated_type(:drivable).counter_cache(true)
580
+ # end
581
+ #
582
+ # ##### touch
583
+ #
584
+ # Use `touch` to assert that the `:touch` option was specified.
585
+ #
586
+ # class Vehicle < ActiveRecord::Base
587
+ # delegated_type :drivable, types: %w(Car Truck), touch: true
588
+ # end
589
+ #
590
+ # # RSpec
591
+ # RSpec.describe Vehicle, type: :model do
592
+ # it { should have_delegated_type(:drivable).touch(true) }
593
+ # end
594
+ #
595
+ # # Minitest (Shoulda)
596
+ # class VehicleTest < ActiveSupport::TestCase
597
+ # should have_delegated_type(:drivable).touch(true)
598
+ # end
599
+ #
600
+ # ##### autosave
601
+ #
602
+ # Use `autosave` to assert that the `:autosave` option was specified.
603
+ #
604
+ # class Vehicle < ActiveRecord::Base
605
+ # delegated_type :drivable, types: %w(Car Truck), autosave: true
606
+ # end
607
+ #
608
+ # # RSpec
609
+ # RSpec.describe Vehicle, type: :model do
610
+ # it { should have_delegated_type(:drivable).autosave(true) }
611
+ # end
612
+ #
613
+ # # Minitest (Shoulda)
614
+ # class VehicleTest < ActiveSupport::TestCase
615
+ # should have_delegated_type(:drivable).autosave(true)
616
+ # end
617
+ #
618
+ # ##### inverse_of
619
+ #
620
+ # Use `inverse_of` to assert that the `:inverse_of` option was specified.
621
+ #
622
+ # class Vehicle < ActiveRecord::Base
623
+ # delegated_type :drivable, types: %w(Car Truck), inverse_of: :vehicle
624
+ # end
625
+ #
626
+ # # RSpec
627
+ # describe Vehicle
628
+ # it { should have_delegated_type(:drivable).inverse_of(:vehicle) }
629
+ # end
630
+ #
631
+ # # Minitest (Shoulda)
632
+ # class VehicleTest < ActiveSupport::TestCase
633
+ # should have_delegated_type(:drivable).inverse_of(:vehicle)
634
+ # end
635
+ #
636
+ # ##### required
637
+ #
638
+ # Use `required` to assert that the association is not allowed to be nil.
639
+ # (Enabled by default in Rails 5+.)
640
+ #
641
+ # class Vehicle < ActiveRecord::Base
642
+ # delegated_type :drivable, types: %w(Car Truck), required: true
643
+ # end
644
+ #
645
+ # # RSpec
646
+ # describe Vehicle
647
+ # it { should have_delegated_type(:drivable).required }
648
+ # end
649
+ #
650
+ # # Minitest (Shoulda)
651
+ # class VehicleTest < ActiveSupport::TestCase
652
+ # should have_delegated_type(:drivable).required
653
+ # end
654
+ #
655
+ # ##### without_validating_presence
656
+ #
657
+ # Use `without_validating_presence` with `belong_to` to prevent the
658
+ # matcher from checking whether the association disallows nil (Rails 5+
659
+ # only). This can be helpful if you have a custom hook that always sets
660
+ # the association to a meaningful value:
661
+ #
662
+ # class Vehicle < ActiveRecord::Base
663
+ # delegated_type :drivable, types: %w(Car Truck)
664
+ #
665
+ # before_validation :autoassign_drivable
666
+ #
667
+ # private
668
+ #
669
+ # def autoassign_drivable
670
+ # self.drivable = Car.create!
671
+ # end
672
+ # end
673
+ #
674
+ # # RSpec
675
+ # describe Vehicle
676
+ # it { should have_delegated_type(:drivable).without_validating_presence }
677
+ # end
678
+ #
679
+ # # Minitest (Shoulda)
680
+ # class VehicleTest < ActiveSupport::TestCase
681
+ # should have_delegated_type(:drivable).without_validating_presence
682
+ # end
683
+ #
684
+ # ##### optional
685
+ #
686
+ # Use `optional` to assert that the association is allowed to be nil.
687
+ # (Rails 5+ only.)
688
+ #
689
+ # class Vehicle < ActiveRecord::Base
690
+ # delegated_type :drivable, types: %w(Car Truck), optional: true
691
+ # end
692
+ #
693
+ # # RSpec
694
+ # describe Vehicle
695
+ # it { should have_delegated_type(:drivable).optional }
696
+ # end
697
+ #
698
+ # # Minitest (Shoulda)
699
+ # class VehicleTest < ActiveSupport::TestCase
700
+ # should have_delegated_type(:drivable).optional
701
+ # end
702
+ #
703
+ # ##### deprecated
704
+ #
705
+ # Use `deprecated` to assert that the `:deprecated` option was specified.
706
+ # (Enabled by default in Rails 8.1+).
707
+ #
708
+ # class Vehicle < ActiveRecord::Base
709
+ # delegated_type :drivable, types: %w(Car Truck), deprecated: true
710
+ # end
711
+ #
712
+ # # RSpec
713
+ # describe Vehicle
714
+ # it { should have_delegated_type(:drivable).deprecated }
715
+ # end
716
+ #
717
+ # # Minitest (Shoulda)
718
+ # class VehicleTest < ActiveSupport::TestCase
719
+ # should have_delegated_type(:drivable).deprecated
720
+ # end
721
+ #
722
+ # @return [AssociationMatcher]
723
+ #
724
+ def have_delegated_type(name)
725
+ AssociationMatcher.new(:belongs_to, name)
726
+ end
727
+
728
+ # The `have_many` matcher is used to test that a `has_many` or `has_many
729
+ # :through` association exists on your model.
730
+ #
731
+ # class Person < ActiveRecord::Base
732
+ # has_many :friends
733
+ # end
734
+ #
735
+ # # RSpec
736
+ # RSpec.describe Person, type: :model do
737
+ # it { should have_many(:friends) }
738
+ # end
739
+ #
740
+ # # Minitest (Shoulda)
741
+ # class PersonTest < ActiveSupport::TestCase
742
+ # should have_many(:friends)
743
+ # end
744
+ #
745
+ # Note that polymorphic associations are automatically detected and do not
746
+ # need any qualifiers:
747
+ #
748
+ # class Person < ActiveRecord::Base
749
+ # has_many :pictures, as: :imageable
750
+ # end
751
+ #
752
+ # # RSpec
753
+ # RSpec.describe Person, type: :model do
754
+ # it { should have_many(:pictures) }
755
+ # end
756
+ #
757
+ # # Minitest (Shoulda)
758
+ # class PersonTest < ActiveSupport::TestCase
759
+ # should have_many(:pictures)
760
+ # end
761
+ #
762
+ # #### Qualifiers
763
+ #
764
+ # ##### conditions
765
+ #
766
+ # Use `conditions` if your association is defined with a scope that sets
767
+ # the `where` clause.
768
+ #
769
+ # class Person < ActiveRecord::Base
770
+ # has_many :coins, -> { where(quality: 'mint') }
771
+ # end
772
+ #
773
+ # # RSpec
774
+ # RSpec.describe Person, type: :model do
775
+ # it { should have_many(:coins).conditions(quality: 'mint') }
776
+ # end
777
+ #
778
+ # # Minitest (Shoulda)
779
+ # class PersonTest < ActiveSupport::TestCase
780
+ # should have_many(:coins).conditions(quality: 'mint')
781
+ # end
782
+ #
783
+ # ##### order
784
+ #
785
+ # Use `order` if your association is defined with a scope that sets the
786
+ # `order` clause.
787
+ #
788
+ # class Person < ActiveRecord::Base
789
+ # has_many :shirts, -> { order('color') }
790
+ # end
791
+ #
792
+ # # RSpec
793
+ # RSpec.describe Person, type: :model do
794
+ # it { should have_many(:shirts).order('color') }
795
+ # end
796
+ #
797
+ # # Minitest (Shoulda)
798
+ # class PersonTest < ActiveSupport::TestCase
799
+ # should have_many(:shirts).order('color')
800
+ # end
801
+ #
802
+ # ##### class_name
803
+ #
804
+ # Use `class_name` to test usage of the `:class_name` option. This
805
+ # asserts that the model you're referring to actually exists.
806
+ #
807
+ # class Person < ActiveRecord::Base
808
+ # has_many :hopes, class_name: 'Dream'
809
+ # end
810
+ #
811
+ # # RSpec
812
+ # RSpec.describe Person, type: :model do
813
+ # it { should have_many(:hopes).class_name('Dream') }
814
+ # end
815
+ #
816
+ # # Minitest (Shoulda)
817
+ # class PersonTest < ActiveSupport::TestCase
818
+ # should have_many(:hopes).class_name('Dream')
819
+ # end
820
+ #
821
+ # ##### with_primary_key
822
+ #
823
+ # Use `with_primary_key` to test usage of the `:primary_key` option.
824
+ #
825
+ # class Person < ActiveRecord::Base
826
+ # has_many :worries, primary_key: 'worrier_id'
827
+ # end
828
+ #
829
+ # # RSpec
830
+ # RSpec.describe Person, type: :model do
831
+ # it { should have_many(:worries).with_primary_key('worrier_id') }
832
+ # end
833
+ #
834
+ # # Minitest (Shoulda)
835
+ # class PersonTest < ActiveSupport::TestCase
836
+ # should have_many(:worries).with_primary_key('worrier_id')
837
+ # end
838
+ #
839
+ # ##### with_foreign_key
840
+ #
841
+ # Use `with_foreign_key` to test usage of the `:foreign_key` option.
842
+ #
843
+ # class Person < ActiveRecord::Base
844
+ # has_many :worries, foreign_key: 'worrier_id'
845
+ # end
846
+ #
847
+ # # RSpec
848
+ # RSpec.describe Person, type: :model do
849
+ # it { should have_many(:worries).with_foreign_key('worrier_id') }
850
+ # end
851
+ #
852
+ # # Minitest (Shoulda)
853
+ # class PersonTest < ActiveSupport::TestCase
854
+ # should have_many(:worries).with_foreign_key('worrier_id')
855
+ # end
856
+ #
857
+ # ##### with_foreign_type
858
+ #
859
+ # Use `with_foreign_type` to test usage of the `:foreign_type` option.
860
+ #
861
+ # class Hotel < ActiveRecord::Base
862
+ # has_many :visitors, foreign_type: 'facility_type', as: :location
863
+ # end
864
+ #
865
+ # # RSpec
866
+ # RSpec.describe Hotel, type: :model do
867
+ # it { should have_many(:visitors).with_foreign_type('facility_type') }
868
+ # end
869
+ #
870
+ # # Minitest (Shoulda)
871
+ # class HotelTest < ActiveSupport::TestCase
872
+ # should have_many(:visitors).with_foreign_type('facility_type')
873
+ # end
874
+ #
875
+ # ##### dependent
876
+ #
877
+ # Use `dependent` to assert that the `:dependent` option was specified.
878
+ #
879
+ # class Person < ActiveRecord::Base
880
+ # has_many :secret_documents, dependent: :destroy
881
+ # end
882
+ #
883
+ # # RSpec
884
+ # RSpec.describe Person, type: :model do
885
+ # it { should have_many(:secret_documents).dependent(:destroy) }
886
+ # end
887
+ #
888
+ # # Minitest (Shoulda)
889
+ # class PersonTest < ActiveSupport::TestCase
890
+ # should have_many(:secret_documents).dependent(:destroy)
891
+ # end
892
+ #
893
+ # ##### through
894
+ #
895
+ # Use `through` to test usage of the `:through` option. This asserts that
896
+ # the association you are going through actually exists.
897
+ #
898
+ # class Person < ActiveRecord::Base
899
+ # has_many :acquaintances, through: :friends
900
+ # end
901
+ #
902
+ # # RSpec
903
+ # RSpec.describe Person, type: :model do
904
+ # it { should have_many(:acquaintances).through(:friends) }
905
+ # end
906
+ #
907
+ # # Minitest (Shoulda)
908
+ # class PersonTest < ActiveSupport::TestCase
909
+ # should have_many(:acquaintances).through(:friends)
910
+ # end
911
+ #
912
+ # ##### source
913
+ #
914
+ # Use `source` to test usage of the `:source` option on a `:through`
915
+ # association.
916
+ #
917
+ # class Person < ActiveRecord::Base
918
+ # has_many :job_offers, through: :friends, source: :opportunities
919
+ # end
920
+ #
921
+ # # RSpec
922
+ # RSpec.describe Person, type: :model do
923
+ # it do
924
+ # should have_many(:job_offers).
925
+ # through(:friends).
926
+ # source(:opportunities)
927
+ # end
928
+ # end
929
+ #
930
+ # # Minitest (Shoulda)
931
+ # class PersonTest < ActiveSupport::TestCase
932
+ # should have_many(:job_offers).
933
+ # through(:friends).
934
+ # source(:opportunities)
935
+ # end
936
+ #
937
+ # ##### validate
938
+ #
939
+ # Use `validate` to assert that the `:validate` option was specified.
940
+ #
941
+ # class Person < ActiveRecord::Base
942
+ # has_many :ideas, validate: false
943
+ # end
944
+ #
945
+ # # RSpec
946
+ # RSpec.describe Person, type: :model do
947
+ # it { should have_many(:ideas).validate(false) }
948
+ # end
949
+ #
950
+ # # Minitest (Shoulda)
951
+ # class PersonTest < ActiveSupport::TestCase
952
+ # should have_many(:ideas).validate(false)
953
+ # end
954
+ #
955
+ # ##### autosave
956
+ #
957
+ # Use `autosave` to assert that the `:autosave` option was specified.
958
+ #
959
+ # class Player < ActiveRecord::Base
960
+ # has_many :games, autosave: true
961
+ # end
962
+ #
963
+ # # RSpec
964
+ # RSpec.describe Player, type: :model do
965
+ # it { should have_many(:games).autosave(true) }
966
+ # end
967
+ #
968
+ # # Minitest (Shoulda)
969
+ # class PlayerTest < ActiveSupport::TestCase
970
+ # should have_many(:games).autosave(true)
971
+ # end
972
+ #
973
+ # ##### index_errors
974
+ #
975
+ # Use `index_errors` to assert that the `:index_errors` option was
976
+ # specified.
977
+ #
978
+ # class Player < ActiveRecord::Base
979
+ # has_many :games, index_errors: true
980
+ # end
981
+ #
982
+ # # RSpec
983
+ # RSpec.describe Player, type: :model do
984
+ # it { should have_many(:games).index_errors(true) }
985
+ # end
986
+ #
987
+ # # Minitest (Shoulda)
988
+ # class PlayerTest < ActiveSupport::TestCase
989
+ # should have_many(:games).index_errors(true)
990
+ # end
991
+ #
992
+ # ##### inverse_of
993
+ #
994
+ # Use `inverse_of` to assert that the `:inverse_of` option was specified.
995
+ #
996
+ # class Organization < ActiveRecord::Base
997
+ # has_many :employees, inverse_of: :company
998
+ # end
999
+ #
1000
+ # # RSpec
1001
+ # describe Organization
1002
+ # it { should have_many(:employees).inverse_of(:company) }
1003
+ # end
1004
+ #
1005
+ # # Minitest (Shoulda)
1006
+ # class OrganizationTest < ActiveSupport::TestCase
1007
+ # should have_many(:employees).inverse_of(:company)
1008
+ # end
1009
+ #
1010
+ #
1011
+ # ##### deprecated
1012
+ #
1013
+ # Use `deprecated` to assert that the `:deprecated` option was specified.
1014
+ # (Enabled by default in Rails 8.1+)
1015
+ #
1016
+ # class Player < ActiveRecord::Base
1017
+ # has_many :games, deprecated: true
1018
+ # end
1019
+ #
1020
+ # # RSpec
1021
+ # RSpec.describe Player, type: :model do
1022
+ # it { should have_many(:games).deprecated(true) }
1023
+ # end
1024
+ #
1025
+ # # Minitest (Shoulda)
1026
+ # class PlayerTest < ActiveSupport::TestCase
1027
+ # should have_many(:games).deprecated(true)
1028
+ # end
1029
+ #
1030
+ # @return [AssociationMatcher]
1031
+ #
1032
+ def have_many(name)
1033
+ AssociationMatcher.new(:has_many, name)
1034
+ end
1035
+
1036
+ # The `have_one` matcher is used to test that a `has_one` or `has_one
1037
+ # :through` association exists on your model.
1038
+ #
1039
+ # class Person < ActiveRecord::Base
1040
+ # has_one :partner
1041
+ # end
1042
+ #
1043
+ # # RSpec
1044
+ # RSpec.describe Person, type: :model do
1045
+ # it { should have_one(:partner) }
1046
+ # end
1047
+ #
1048
+ # # Minitest (Shoulda)
1049
+ # class PersonTest < ActiveSupport::TestCase
1050
+ # should have_one(:partner)
1051
+ # end
1052
+ #
1053
+ # #### Qualifiers
1054
+ #
1055
+ # ##### conditions
1056
+ #
1057
+ # Use `conditions` if your association is defined with a scope that sets
1058
+ # the `where` clause.
1059
+ #
1060
+ # class Person < ActiveRecord::Base
1061
+ # has_one :pet, -> { where('weight < 80') }
1062
+ # end
1063
+ #
1064
+ # # RSpec
1065
+ # RSpec.describe Person, type: :model do
1066
+ # it { should have_one(:pet).conditions('weight < 80') }
1067
+ # end
1068
+ #
1069
+ # # Minitest (Shoulda)
1070
+ # class PersonTest < ActiveSupport::TestCase
1071
+ # should have_one(:pet).conditions('weight < 80')
1072
+ # end
1073
+ #
1074
+ # ##### order
1075
+ #
1076
+ # Use `order` if your association is defined with a scope that sets the
1077
+ # `order` clause.
1078
+ #
1079
+ # class Person < ActiveRecord::Base
1080
+ # has_one :focus, -> { order('priority desc') }
1081
+ # end
1082
+ #
1083
+ # # RSpec
1084
+ # RSpec.describe Person, type: :model do
1085
+ # it { should have_one(:focus).order('priority desc') }
1086
+ # end
1087
+ #
1088
+ # # Minitest (Shoulda)
1089
+ # class PersonTest < ActiveSupport::TestCase
1090
+ # should have_one(:focus).order('priority desc')
1091
+ # end
1092
+ #
1093
+ # ##### class_name
1094
+ #
1095
+ # Use `class_name` to test usage of the `:class_name` option. This
1096
+ # asserts that the model you're referring to actually exists.
1097
+ #
1098
+ # class Person < ActiveRecord::Base
1099
+ # has_one :chance, class_name: 'Opportunity'
1100
+ # end
1101
+ #
1102
+ # # RSpec
1103
+ # RSpec.describe Person, type: :model do
1104
+ # it { should have_one(:chance).class_name('Opportunity') }
1105
+ # end
1106
+ #
1107
+ # # Minitest (Shoulda)
1108
+ # class PersonTest < ActiveSupport::TestCase
1109
+ # should have_one(:chance).class_name('Opportunity')
1110
+ # end
1111
+ #
1112
+ # ##### dependent
1113
+ #
1114
+ # Use `dependent` to test that the `:dependent` option was specified.
1115
+ #
1116
+ # class Person < ActiveRecord::Base
1117
+ # has_one :contract, dependent: :nullify
1118
+ # end
1119
+ #
1120
+ # # RSpec
1121
+ # RSpec.describe Person, type: :model do
1122
+ # it { should have_one(:contract).dependent(:nullify) }
1123
+ # end
1124
+ #
1125
+ # # Minitest (Shoulda)
1126
+ # class PersonTest < ActiveSupport::TestCase
1127
+ # should have_one(:contract).dependent(:nullify)
1128
+ # end
1129
+ #
1130
+ # ##### with_primary_key
1131
+ #
1132
+ # Use `with_primary_key` to test usage of the `:primary_key` option.
1133
+ #
1134
+ # class Person < ActiveRecord::Base
1135
+ # has_one :job, primary_key: 'worker_id'
1136
+ # end
1137
+ #
1138
+ # # RSpec
1139
+ # RSpec.describe Person, type: :model do
1140
+ # it { should have_one(:job).with_primary_key('worker_id') }
1141
+ # end
1142
+ #
1143
+ # # Minitest (Shoulda)
1144
+ # class PersonTest < ActiveSupport::TestCase
1145
+ # should have_one(:job).with_primary_key('worker_id')
1146
+ # end
1147
+ #
1148
+ # ##### with_foreign_key
1149
+ #
1150
+ # Use `with_foreign_key` to test usage of the `:foreign_key` option.
1151
+ #
1152
+ # class Person < ActiveRecord::Base
1153
+ # has_one :job, foreign_key: 'worker_id'
1154
+ # end
1155
+ #
1156
+ # # RSpec
1157
+ # RSpec.describe Person, type: :model do
1158
+ # it { should have_one(:job).with_foreign_key('worker_id') }
1159
+ # end
1160
+ #
1161
+ # # Minitest (Shoulda)
1162
+ # class PersonTest < ActiveSupport::TestCase
1163
+ # should have_one(:job).with_foreign_key('worker_id')
1164
+ # end
1165
+ #
1166
+ # ##### with_foreign_type
1167
+ #
1168
+ # Use `with_foreign_type` to test usage of the `:foreign_type` option.
1169
+ #
1170
+ # class Hotel < ActiveRecord::Base
1171
+ # has_one :special_guest, foreign_type: 'facility_type', as: :location
1172
+ # end
1173
+ #
1174
+ # # RSpec
1175
+ # RSpec.describe Hotel, type: :model do
1176
+ # it { should have_one(:special_guest).with_foreign_type('facility_type') }
1177
+ # end
1178
+ #
1179
+ # # Minitest (Shoulda)
1180
+ # class HotelTest < ActiveSupport::TestCase
1181
+ # should have_one(:special_guest).with_foreign_type('facility_type')
1182
+ # end
1183
+ #
1184
+ # ##### through
1185
+ #
1186
+ # Use `through` to test usage of the `:through` option. This asserts that
1187
+ # the association you are going through actually exists.
1188
+ #
1189
+ # class Person < ActiveRecord::Base
1190
+ # has_one :life, through: :partner
1191
+ # end
1192
+ #
1193
+ # # RSpec
1194
+ # RSpec.describe Person, type: :model do
1195
+ # it { should have_one(:life).through(:partner) }
1196
+ # end
1197
+ #
1198
+ # # Minitest (Shoulda)
1199
+ # class PersonTest < ActiveSupport::TestCase
1200
+ # should have_one(:life).through(:partner)
1201
+ # end
1202
+ #
1203
+ # ##### source
1204
+ #
1205
+ # Use `source` to test usage of the `:source` option on a `:through`
1206
+ # association.
1207
+ #
1208
+ # class Person < ActiveRecord::Base
1209
+ # has_one :car, through: :partner, source: :vehicle
1210
+ # end
1211
+ #
1212
+ # # RSpec
1213
+ # RSpec.describe Person, type: :model do
1214
+ # it { should have_one(:car).through(:partner).source(:vehicle) }
1215
+ # end
1216
+ #
1217
+ # # Minitest (Shoulda)
1218
+ # class PersonTest < ActiveSupport::TestCase
1219
+ # should have_one(:car).through(:partner).source(:vehicle)
1220
+ # end
1221
+ #
1222
+ # ##### validate
1223
+ #
1224
+ # Use `validate` to assert that the the `:validate` option was specified.
1225
+ #
1226
+ # class Person < ActiveRecord::Base
1227
+ # has_one :parking_card, validate: false
1228
+ # end
1229
+ #
1230
+ # # RSpec
1231
+ # RSpec.describe Person, type: :model do
1232
+ # it { should have_one(:parking_card).validate(false) }
1233
+ # end
1234
+ #
1235
+ # # Minitest (Shoulda)
1236
+ # class PersonTest < ActiveSupport::TestCase
1237
+ # should have_one(:parking_card).validate(false)
1238
+ # end
1239
+ #
1240
+ # ##### autosave
1241
+ #
1242
+ # Use `autosave` to assert that the `:autosave` option was specified.
1243
+ #
1244
+ # class Account < ActiveRecord::Base
1245
+ # has_one :bank, autosave: true
1246
+ # end
1247
+ #
1248
+ # # RSpec
1249
+ # RSpec.describe Account, type: :model do
1250
+ # it { should have_one(:bank).autosave(true) }
1251
+ # end
1252
+ #
1253
+ # # Minitest (Shoulda)
1254
+ # class AccountTest < ActiveSupport::TestCase
1255
+ # should have_one(:bank).autosave(true)
1256
+ # end
1257
+ #
1258
+ # ##### required
1259
+ #
1260
+ # Use `required` to assert that the association is not allowed to be nil.
1261
+ # (Rails 5+ only.)
1262
+ #
1263
+ # class Person < ActiveRecord::Base
1264
+ # has_one :brain, required: true
1265
+ # end
1266
+ #
1267
+ # # RSpec
1268
+ # describe Person
1269
+ # it { should have_one(:brain).required }
1270
+ # end
1271
+ #
1272
+ # # Minitest (Shoulda)
1273
+ # class PersonTest < ActiveSupport::TestCase
1274
+ # should have_one(:brain).required
1275
+ # end
1276
+ #
1277
+ #
1278
+ # ##### deprecated
1279
+ #
1280
+ # Use `deprecated` to assert that the `:deprecated` option was specified.
1281
+ # (Enabled by default in Rails 8.1+).
1282
+ #
1283
+ # class Account < ActiveRecord::Base
1284
+ # has_one :bank, deprecated: true
1285
+ # end
1286
+ #
1287
+ # # RSpec
1288
+ # RSpec.describe Account, type: :model do
1289
+ # it { should have_one(:bank).deprecated(true) }
1290
+ # end
1291
+ #
1292
+ # # Minitest (Shoulda)
1293
+ # class AccountTest < ActiveSupport::TestCase
1294
+ # should have_one(:bank).deprecated(true)
1295
+ # end
1296
+ #
1297
+ # @return [AssociationMatcher]
1298
+ #
1299
+ def have_one(name)
1300
+ AssociationMatcher.new(:has_one, name)
1301
+ end
1302
+
1303
+ # The `have_and_belong_to_many` matcher is used to test that a
1304
+ # `has_and_belongs_to_many` association exists on your model and that the
1305
+ # join table exists in the database.
1306
+ #
1307
+ # class Person < ActiveRecord::Base
1308
+ # has_and_belongs_to_many :awards
1309
+ # end
1310
+ #
1311
+ # # RSpec
1312
+ # RSpec.describe Person, type: :model do
1313
+ # it { should have_and_belong_to_many(:awards) }
1314
+ # end
1315
+ #
1316
+ # # Minitest (Shoulda)
1317
+ # class PersonTest < ActiveSupport::TestCase
1318
+ # should have_and_belong_to_many(:awards)
1319
+ # end
1320
+ #
1321
+ # #### Qualifiers
1322
+ #
1323
+ # ##### conditions
1324
+ #
1325
+ # Use `conditions` if your association is defined with a scope that sets
1326
+ # the `where` clause.
1327
+ #
1328
+ # class Person < ActiveRecord::Base
1329
+ # has_and_belongs_to_many :issues, -> { where(difficulty: 'hard') }
1330
+ # end
1331
+ #
1332
+ # # RSpec
1333
+ # RSpec.describe Person, type: :model do
1334
+ # it do
1335
+ # should have_and_belong_to_many(:issues).
1336
+ # conditions(difficulty: 'hard')
1337
+ # end
1338
+ # end
1339
+ #
1340
+ # # Minitest (Shoulda)
1341
+ # class PersonTest < ActiveSupport::TestCase
1342
+ # should have_and_belong_to_many(:issues).
1343
+ # conditions(difficulty: 'hard')
1344
+ # end
1345
+ #
1346
+ # ##### order
1347
+ #
1348
+ # Use `order` if your association is defined with a scope that sets the
1349
+ # `order` clause.
1350
+ #
1351
+ # class Person < ActiveRecord::Base
1352
+ # has_and_belongs_to_many :projects, -> { order('time_spent') }
1353
+ # end
1354
+ #
1355
+ # # RSpec
1356
+ # RSpec.describe Person, type: :model do
1357
+ # it do
1358
+ # should have_and_belong_to_many(:projects).
1359
+ # order('time_spent')
1360
+ # end
1361
+ # end
1362
+ #
1363
+ # # Minitest (Shoulda)
1364
+ # class PersonTest < ActiveSupport::TestCase
1365
+ # should have_and_belong_to_many(:projects).
1366
+ # order('time_spent')
1367
+ # end
1368
+ #
1369
+ # ##### class_name
1370
+ #
1371
+ # Use `class_name` to test usage of the `:class_name` option. This
1372
+ # asserts that the model you're referring to actually exists.
1373
+ #
1374
+ # class Person < ActiveRecord::Base
1375
+ # has_and_belongs_to_many :places_visited, class_name: 'City'
1376
+ # end
1377
+ #
1378
+ # # RSpec
1379
+ # RSpec.describe Person, type: :model do
1380
+ # it do
1381
+ # should have_and_belong_to_many(:places_visited).
1382
+ # class_name('City')
1383
+ # end
1384
+ # end
1385
+ #
1386
+ # # Minitest (Shoulda)
1387
+ # class PersonTest < ActiveSupport::TestCase
1388
+ # should have_and_belong_to_many(:places_visited).
1389
+ # class_name('City')
1390
+ # end
1391
+ #
1392
+ # ##### join_table
1393
+ #
1394
+ # Use `join_table` to test usage of the `:join_table` option. This
1395
+ # asserts that the table you're referring to actually exists.
1396
+ #
1397
+ # class Person < ActiveRecord::Base
1398
+ # has_and_belongs_to_many :issues, join_table: :people_tickets
1399
+ # end
1400
+ #
1401
+ # # RSpec
1402
+ # RSpec.describe Person, type: :model do
1403
+ # it do
1404
+ # should have_and_belong_to_many(:issues).
1405
+ # join_table(:people_tickets)
1406
+ # end
1407
+ # end
1408
+ #
1409
+ # # Minitest (Shoulda)
1410
+ # class PersonTest < ActiveSupport::TestCase
1411
+ # should have_and_belong_to_many(:issues).
1412
+ # join_table(:people_tickets)
1413
+ # end
1414
+ #
1415
+ # ##### validate
1416
+ #
1417
+ # Use `validate` to test that the `:validate` option was specified.
1418
+ #
1419
+ # class Person < ActiveRecord::Base
1420
+ # has_and_belongs_to_many :interviews, validate: false
1421
+ # end
1422
+ #
1423
+ # # RSpec
1424
+ # RSpec.describe Person, type: :model do
1425
+ # it do
1426
+ # should have_and_belong_to_many(:interviews).
1427
+ # validate(false)
1428
+ # end
1429
+ # end
1430
+ #
1431
+ # # Minitest (Shoulda)
1432
+ # class PersonTest < ActiveSupport::TestCase
1433
+ # should have_and_belong_to_many(:interviews).
1434
+ # validate(false)
1435
+ # end
1436
+ #
1437
+ # ##### autosave
1438
+ #
1439
+ # Use `autosave` to assert that the `:autosave` option was specified.
1440
+ #
1441
+ # class Publisher < ActiveRecord::Base
1442
+ # has_and_belongs_to_many :advertisers, autosave: true
1443
+ # end
1444
+ #
1445
+ # # RSpec
1446
+ # RSpec.describe Publisher, type: :model do
1447
+ # it { should have_and_belong_to_many(:advertisers).autosave(true) }
1448
+ # end
1449
+ #
1450
+ # # Minitest (Shoulda)
1451
+ # class AccountTest < ActiveSupport::TestCase
1452
+ # should have_and_belong_to_many(:advertisers).autosave(true)
1453
+ # end
1454
+ #
1455
+ # ##### deprecated
1456
+ #
1457
+ # Use `deprecated` to assert that the `:deprecated` option was specified.
1458
+ # (Enabled by default in Rails 8.1+).
1459
+ #
1460
+ # class Publisher < ActiveRecord::Base
1461
+ # has_and_belongs_to_many :advertisers, deprecated: true
1462
+ # end
1463
+ #
1464
+ # # RSpec
1465
+ # RSpec.describe Publisher, type: :model do
1466
+ # it { should have_and_belong_to_many(:advertisers).deprecated(true) }
1467
+ # end
1468
+ #
1469
+ # # Minitest (Shoulda)
1470
+ # class AccountTest < ActiveSupport::TestCase
1471
+ # should have_and_belong_to_many(:advertisers).deprecated(true)
1472
+ # end
1473
+ #
1474
+ # @return [AssociationMatcher]
1475
+ #
1476
+ def have_and_belong_to_many(name)
1477
+ AssociationMatcher.new(:has_and_belongs_to_many, name)
1478
+ end
1479
+
1480
+ # @private
1481
+ class AssociationMatcher
1482
+ MACROS = {
1483
+ 'belongs_to' => 'belong to',
1484
+ 'has_many' => 'have many',
1485
+ 'has_one' => 'have one',
1486
+ 'has_and_belongs_to_many' => 'have and belong to many',
1487
+ }.freeze
1488
+
1489
+ delegate :reflection, :model_class, :associated_class, :through?,
1490
+ :polymorphic?, to: :reflector
1491
+
1492
+ attr_reader :name, :options
1493
+
1494
+ def initialize(macro, name)
1495
+ @macro = macro
1496
+ @name = name
1497
+ @options = {}
1498
+ @submatchers = []
1499
+ @missing = ''
1500
+
1501
+ if macro == :belongs_to
1502
+ required(belongs_to_required_by_default?)
1503
+ end
1504
+ end
1505
+
1506
+ def through(through)
1507
+ add_submatcher(
1508
+ AssociationMatchers::ThroughMatcher,
1509
+ through,
1510
+ name,
1511
+ )
1512
+ self
1513
+ end
1514
+
1515
+ def dependent(dependent)
1516
+ add_submatcher(
1517
+ AssociationMatchers::DependentMatcher,
1518
+ dependent,
1519
+ name,
1520
+ )
1521
+ self
1522
+ end
1523
+
1524
+ def order(order)
1525
+ add_submatcher(
1526
+ AssociationMatchers::OrderMatcher,
1527
+ order,
1528
+ name,
1529
+ )
1530
+ self
1531
+ end
1532
+
1533
+ def counter_cache(counter_cache = true)
1534
+ add_submatcher(
1535
+ AssociationMatchers::CounterCacheMatcher,
1536
+ counter_cache,
1537
+ name,
1538
+ )
1539
+ self
1540
+ end
1541
+
1542
+ def inverse_of(inverse_of)
1543
+ add_submatcher(
1544
+ AssociationMatchers::InverseOfMatcher,
1545
+ inverse_of,
1546
+ name,
1547
+ )
1548
+ self
1549
+ end
1550
+
1551
+ def source(source)
1552
+ add_submatcher(
1553
+ AssociationMatchers::SourceMatcher,
1554
+ source,
1555
+ name,
1556
+ )
1557
+ self
1558
+ end
1559
+
1560
+ def conditions(conditions)
1561
+ @options[:conditions] = conditions
1562
+ self
1563
+ end
1564
+
1565
+ def types(types)
1566
+ @options[:types] = types
1567
+ self
1568
+ end
1569
+
1570
+ def autosave(autosave)
1571
+ @options[:autosave] = autosave
1572
+ self
1573
+ end
1574
+
1575
+ def index_errors(index_errors)
1576
+ @options[:index_errors] = index_errors
1577
+ self
1578
+ end
1579
+
1580
+ def class_name(class_name)
1581
+ @options[:class_name] = class_name
1582
+ self
1583
+ end
1584
+
1585
+ def with_foreign_key(foreign_key)
1586
+ @options[:foreign_key] = foreign_key
1587
+ self
1588
+ end
1589
+
1590
+ def with_foreign_type(foreign_type)
1591
+ @options[:foreign_type] = foreign_type
1592
+ self
1593
+ end
1594
+
1595
+ def with_primary_key(primary_key)
1596
+ @options[:primary_key] = primary_key
1597
+ self
1598
+ end
1599
+
1600
+ def with_query_constraints(query_constraints)
1601
+ @options[:query_constraints] = query_constraints
1602
+ self
1603
+ end
1604
+
1605
+ def required(required = true)
1606
+ remove_submatcher(AssociationMatchers::OptionalMatcher)
1607
+ add_submatcher(
1608
+ AssociationMatchers::RequiredMatcher,
1609
+ name,
1610
+ required,
1611
+ )
1612
+ self
1613
+ end
1614
+
1615
+ def optional(optional = true)
1616
+ remove_submatcher(AssociationMatchers::RequiredMatcher)
1617
+ add_submatcher(
1618
+ AssociationMatchers::OptionalMatcher,
1619
+ name,
1620
+ optional,
1621
+ )
1622
+ self
1623
+ end
1624
+
1625
+ def validate(validate = true)
1626
+ @options[:validate] = validate
1627
+ self
1628
+ end
1629
+
1630
+ def touch(touch = true)
1631
+ @options[:touch] = touch
1632
+ self
1633
+ end
1634
+
1635
+ def strict_loading(strict_loading = true)
1636
+ @options[:strict_loading] = strict_loading
1637
+ self
1638
+ end
1639
+
1640
+ def join_table(join_table_name)
1641
+ @options[:join_table_name] = join_table_name
1642
+ self
1643
+ end
1644
+
1645
+ def deprecated(deprecated = true)
1646
+ if Shoulda::Matchers::RailsShim.active_record_gte_8_1?
1647
+ @options[:deprecated] = deprecated
1648
+ self
1649
+ else
1650
+ raise NotImplementedError,
1651
+ '`deprecated` association matcher is only available on Active Record >= 8.1.'
1652
+ end
1653
+ end
1654
+
1655
+ def without_validating_presence
1656
+ remove_submatcher(AssociationMatchers::RequiredMatcher)
1657
+ self
1658
+ end
1659
+
1660
+ def description
1661
+ description = "#{macro_description} #{name}"
1662
+ if options.key?(:class_name)
1663
+ description += " class_name => #{options[:class_name]}"
1664
+ end
1665
+ [description, submatchers.map(&:description)].flatten.join(' ')
1666
+ end
1667
+
1668
+ def failure_message
1669
+ "Expected #{expectation} (#{missing_options})"
1670
+ end
1671
+
1672
+ def failure_message_when_negated
1673
+ "Did not expect #{expectation}"
1674
+ end
1675
+
1676
+ def matches?(subject)
1677
+ @subject = subject
1678
+ association_exists? &&
1679
+ macro_correct? &&
1680
+ validate_inverse_of_through_association &&
1681
+ (polymorphic? || class_exists?) &&
1682
+ foreign_type_matches? &&
1683
+ foreign_key_exists? &&
1684
+ primary_key_exists? &&
1685
+ query_constraints_exists? &&
1686
+ class_name_correct? &&
1687
+ join_table_correct? &&
1688
+ autosave_correct? &&
1689
+ index_errors_correct? &&
1690
+ conditions_correct? &&
1691
+ validate_correct? &&
1692
+ touch_correct? &&
1693
+ types_correct? &&
1694
+ strict_loading_correct? &&
1695
+ deprecated_correct? &&
1696
+ submatchers_match?
1697
+ end
1698
+
1699
+ def join_table_name
1700
+ options[:join_table_name] || reflector.join_table_name
1701
+ end
1702
+
1703
+ def option_verifier
1704
+ @_option_verifier ||=
1705
+ AssociationMatchers::OptionVerifier.new(reflector)
1706
+ end
1707
+
1708
+ protected
1709
+
1710
+ attr_reader :submatchers, :missing, :subject, :macro
1711
+
1712
+ def reflector
1713
+ @_reflector ||= AssociationMatchers::ModelReflector.new(subject, name)
1714
+ end
1715
+
1716
+ def add_submatcher(matcher_class, *args)
1717
+ remove_submatcher(matcher_class)
1718
+ submatchers << matcher_class.new(*args)
1719
+ end
1720
+
1721
+ def remove_submatcher(matcher_class)
1722
+ submatchers.delete_if do |submatcher|
1723
+ submatcher.is_a?(matcher_class)
1724
+ end
1725
+ end
1726
+
1727
+ def macro_description
1728
+ MACROS[macro.to_s]
1729
+ end
1730
+
1731
+ def expectation
1732
+ expectation =
1733
+ "#{model_class.name} to have a #{macro} association called #{name}"
1734
+
1735
+ if through?
1736
+ expectation << " through #{reflector.has_and_belongs_to_many_name}"
1737
+ end
1738
+
1739
+ expectation
1740
+ end
1741
+
1742
+ def missing_options
1743
+ missing_options = [missing, missing_options_for_failing_submatchers]
1744
+ missing_options.flatten.select(&:present?).join(', ')
1745
+ end
1746
+
1747
+ def failing_submatchers
1748
+ @_failing_submatchers ||= submatchers.reject do |matcher|
1749
+ matcher.matches?(subject)
1750
+ end
1751
+ end
1752
+
1753
+ def missing_options_for_failing_submatchers
1754
+ if defined?(@_failing_submatchers)
1755
+ @_failing_submatchers.map(&:missing_option)
1756
+ else
1757
+ []
1758
+ end
1759
+ end
1760
+
1761
+ def association_exists?
1762
+ if reflection.nil?
1763
+ @missing = "no association called #{name}"
1764
+ false
1765
+ else
1766
+ true
1767
+ end
1768
+ end
1769
+
1770
+ def macro_correct?
1771
+ if reflection.macro == macro
1772
+ true
1773
+ elsif reflection.macro == :has_many
1774
+ macro == :has_and_belongs_to_many &&
1775
+ reflection.name == @name
1776
+ else
1777
+ @missing = "actual association type was #{reflection.macro}"
1778
+ false
1779
+ end
1780
+ end
1781
+
1782
+ def validate_inverse_of_through_association
1783
+ reflector.validate_inverse_of_through_association!
1784
+ true
1785
+ rescue ::ActiveRecord::ActiveRecordError => e
1786
+ @missing = e.message
1787
+ false
1788
+ end
1789
+
1790
+ def macro_is_not_through?
1791
+ macro == :belongs_to || has_association_not_through?
1792
+ end
1793
+
1794
+ def has_association_not_through?
1795
+ [:has_many, :has_one].include?(macro) && !through?
1796
+ end
1797
+
1798
+ def foreign_key_exists?
1799
+ !(belongs_foreign_key_missing? || has_foreign_key_missing?)
1800
+ end
1801
+
1802
+ def foreign_type_matches?
1803
+ !options.key?(:foreign_type) || (
1804
+ !belongs_foreign_type_missing? &&
1805
+ !has_foreign_type_missing?
1806
+ )
1807
+ end
1808
+
1809
+ def primary_key_exists?
1810
+ !macro_is_not_through? || primary_key_correct?(model_class)
1811
+ end
1812
+
1813
+ def query_constraints_exists?
1814
+ !macro_is_not_through? || query_constraints_correct?
1815
+ end
1816
+
1817
+ def query_constraints_correct?
1818
+ if options.key?(:query_constraints)
1819
+ if option_verifier.correct_for_string?(:query_constraints, options[:query_constraints])
1820
+ true
1821
+ else
1822
+ @missing = "#{model_class} should have \:query_constraints"\
1823
+ " options set to #{options[:query_constraints]}"
1824
+ false
1825
+ end
1826
+ else
1827
+ true
1828
+ end
1829
+ end
1830
+
1831
+ def belongs_foreign_key_missing?
1832
+ macro == :belongs_to && !class_has_foreign_key?(model_class)
1833
+ end
1834
+
1835
+ def belongs_foreign_type_missing?
1836
+ macro == :belongs_to && !class_has_foreign_type?(model_class)
1837
+ end
1838
+
1839
+ def has_foreign_key_missing?
1840
+ has_association_not_through? &&
1841
+ !class_has_foreign_key?(associated_class)
1842
+ end
1843
+
1844
+ def has_foreign_type_missing?
1845
+ has_association_not_through? &&
1846
+ !class_has_foreign_type?(associated_class)
1847
+ end
1848
+
1849
+ def class_name_correct?
1850
+ if options.key?(:class_name)
1851
+ if option_verifier.correct_for_constant?(
1852
+ :class_name,
1853
+ options[:class_name],
1854
+ )
1855
+ true
1856
+ else
1857
+ @missing = "#{name} should resolve to #{options[:class_name]}"\
1858
+ ' for class_name'
1859
+ false
1860
+ end
1861
+ else
1862
+ true
1863
+ end
1864
+ end
1865
+
1866
+ def join_table_correct?
1867
+ if macro != :has_and_belongs_to_many || join_table_matcher.matches?(@subject)
1868
+ true
1869
+ else
1870
+ @missing = join_table_matcher.failure_message
1871
+ false
1872
+ end
1873
+ end
1874
+
1875
+ def join_table_matcher
1876
+ @_join_table_matcher ||= AssociationMatchers::JoinTableMatcher.new(
1877
+ self,
1878
+ reflector,
1879
+ )
1880
+ end
1881
+
1882
+ def class_exists?
1883
+ associated_class
1884
+ true
1885
+ rescue NameError
1886
+ @missing = "#{reflection.class_name} does not exist"
1887
+ false
1888
+ end
1889
+
1890
+ def autosave_correct?
1891
+ if options.key?(:autosave)
1892
+ if option_verifier.correct_for_boolean?(
1893
+ :autosave,
1894
+ options[:autosave],
1895
+ )
1896
+ true
1897
+ else
1898
+ @missing = "#{name} should have autosave set to"\
1899
+ " #{options[:autosave]}"
1900
+ false
1901
+ end
1902
+ else
1903
+ true
1904
+ end
1905
+ end
1906
+
1907
+ def index_errors_correct?
1908
+ return true unless options.key?(:index_errors)
1909
+
1910
+ if option_verifier.correct_for_boolean?(
1911
+ :index_errors,
1912
+ options[:index_errors],
1913
+ )
1914
+ true
1915
+ else
1916
+ @missing =
1917
+ "#{name} should have index_errors set to " +
1918
+ options[:index_errors].to_s
1919
+ false
1920
+ end
1921
+ end
1922
+
1923
+ def conditions_correct?
1924
+ if options.key?(:conditions)
1925
+ if option_verifier.correct_for_relation_clause?(
1926
+ :conditions,
1927
+ options[:conditions],
1928
+ )
1929
+ true
1930
+ else
1931
+ @missing = "#{name} should have the following conditions:" +
1932
+ " #{options[:conditions]}"
1933
+ false
1934
+ end
1935
+ else
1936
+ true
1937
+ end
1938
+ end
1939
+
1940
+ def validate_correct?
1941
+ if option_verifier.correct_for_boolean?(:validate, options[:validate])
1942
+ true
1943
+ else
1944
+ @missing = "#{name} should have validate: #{options[:validate]}"
1945
+ false
1946
+ end
1947
+ end
1948
+
1949
+ def touch_correct?
1950
+ if option_verifier.correct_for_boolean?(:touch, options[:touch])
1951
+ true
1952
+ else
1953
+ @missing = "#{name} should have touch: #{options[:touch]}"
1954
+ false
1955
+ end
1956
+ end
1957
+
1958
+ def deprecated_correct?
1959
+ if option_verifier.correct_for_boolean?(:deprecated, options[:deprecated])
1960
+ true
1961
+ else
1962
+ @missing = "#{name} should have deprecated set to"\
1963
+ " #{options[:deprecated]}"
1964
+ false
1965
+ end
1966
+ end
1967
+
1968
+ def types_correct?
1969
+ if options.key?(:types)
1970
+ types = options[:types]
1971
+
1972
+ correct = types.all? do |type|
1973
+ scope_name = type.tableize.tr('/', '_')
1974
+ singular = scope_name.singularize
1975
+ query = "#{singular}?"
1976
+
1977
+ Object.const_defined?(type) && @subject.respond_to?(query) &&
1978
+ @subject.respond_to?(singular)
1979
+ end
1980
+
1981
+ if correct
1982
+ true
1983
+ else
1984
+ @missing = "#{name} should have types: #{options[:types]}"
1985
+ false
1986
+ end
1987
+ else
1988
+ true
1989
+ end
1990
+ end
1991
+
1992
+ def strict_loading_correct?
1993
+ return true unless options.key?(:strict_loading)
1994
+
1995
+ if option_verifier.correct_for_boolean?(:strict_loading, options[:strict_loading])
1996
+ return true
1997
+ end
1998
+
1999
+ @missing = [
2000
+ "#{name} should have strict_loading set to ",
2001
+ options[:strict_loading].to_s,
2002
+ ].join
2003
+
2004
+ false
2005
+ end
2006
+
2007
+ def class_has_foreign_key?(klass)
2008
+ @missing = validate_foreign_key(klass)
2009
+
2010
+ @missing.nil?
2011
+ end
2012
+
2013
+ def validate_foreign_key(klass)
2014
+ if options.key?(:foreign_key) && !foreign_key_correct?
2015
+ foreign_key_failure_message(klass, options[:foreign_key])
2016
+ elsif !has_column?(klass, actual_foreign_key)
2017
+ foreign_key_failure_message(klass, actual_foreign_key)
2018
+ end
2019
+ end
2020
+
2021
+ def class_has_foreign_type?(klass)
2022
+ if options.key?(:foreign_type) && !foreign_type_correct?
2023
+ @missing = foreign_type_failure_message(
2024
+ klass,
2025
+ options[:foreign_type],
2026
+ )
2027
+
2028
+ false
2029
+ elsif !has_column?(klass, foreign_type)
2030
+ @missing = foreign_type_failure_message(klass, foreign_type)
2031
+ false
2032
+ else
2033
+ true
2034
+ end
2035
+ end
2036
+
2037
+ def has_column?(klass, column)
2038
+ case column
2039
+ when Array
2040
+ column.all? { |c| has_column?(klass, c.to_s) }
2041
+ else
2042
+ column_names_for(klass).include?(column.to_s)
2043
+ end
2044
+ end
2045
+
2046
+ def foreign_key_correct?
2047
+ option_verifier.correct_for_string?(
2048
+ :foreign_key,
2049
+ options[:foreign_key],
2050
+ )
2051
+ end
2052
+
2053
+ def foreign_type_correct?
2054
+ option_verifier.correct_for_string?(
2055
+ :foreign_type,
2056
+ options[:foreign_type],
2057
+ )
2058
+ end
2059
+
2060
+ def foreign_key_failure_message(klass, foreign_key)
2061
+ "#{klass} does not have a #{foreign_key} foreign key."
2062
+ end
2063
+
2064
+ def foreign_type_failure_message(klass, foreign_type)
2065
+ "#{klass} does not have a #{foreign_type} foreign type."
2066
+ end
2067
+
2068
+ def primary_key_correct?(klass)
2069
+ if options.key?(:primary_key)
2070
+ if option_verifier.correct_for_string?(
2071
+ :primary_key,
2072
+ options[:primary_key],
2073
+ )
2074
+ true
2075
+ else
2076
+ @missing = "#{klass} does not have a #{options[:primary_key]}"\
2077
+ ' primary key'
2078
+ false
2079
+ end
2080
+ else
2081
+ true
2082
+ end
2083
+ end
2084
+
2085
+ def actual_foreign_key
2086
+ return unless foreign_key_reflection
2087
+
2088
+ if foreign_key_reflection.options[:foreign_key]
2089
+ foreign_key_reflection.options[:foreign_key]
2090
+ elsif foreign_key_reflection.respond_to?(:foreign_key)
2091
+ foreign_key_reflection.foreign_key
2092
+ else
2093
+ foreign_key_reflection.primary_key_name
2094
+ end
2095
+ end
2096
+
2097
+ def foreign_key_reflection
2098
+ if [:has_one, :has_many].include?(macro) &&
2099
+ reflection.options.include?(:inverse_of) &&
2100
+ reflection.options[:inverse_of] != false
2101
+
2102
+ associated_class.reflect_on_association(
2103
+ reflection.options[:inverse_of],
2104
+ )
2105
+ else
2106
+ reflection
2107
+ end
2108
+ end
2109
+
2110
+ def foreign_type
2111
+ if [:has_one, :has_many].include?(macro)
2112
+ reflection.type
2113
+ else
2114
+ reflection.foreign_type
2115
+ end
2116
+ end
2117
+
2118
+ def submatchers_match?
2119
+ failing_submatchers.empty?
2120
+ end
2121
+
2122
+ def column_names_for(klass)
2123
+ klass.column_names
2124
+ rescue ::ActiveRecord::StatementInvalid
2125
+ []
2126
+ end
2127
+
2128
+ def belongs_to_required_by_default?
2129
+ ::ActiveRecord::Base.belongs_to_required_by_default
2130
+ end
2131
+ end
2132
+ end
2133
+ end
2134
+ end