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.
- checksums.yaml +7 -0
- data/giga-safe-box.gemspec +11 -0
- data/shoulda-matchers-8.0.1/LICENSE +22 -0
- data/shoulda-matchers-8.0.1/README.md +567 -0
- data/shoulda-matchers-8.0.1/docs/errors/NonCaseSwappableValueError.md +111 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/callback_matcher.rb +121 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +70 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/flash_store.rb +86 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/permit_matcher.rb +445 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +96 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/render_template_matcher.rb +84 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +145 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +115 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +148 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/route_matcher.rb +207 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/route_params.rb +62 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/session_store.rb +34 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +228 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_session_matcher.rb +163 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +107 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller.rb +24 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +45 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error.rb +23 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +232 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +62 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +40 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +48 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/successful_check.rb +14 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/successful_setting.rb +14 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher.rb +695 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/comparison_matcher.rb +157 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +78 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/errors.rb +52 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +125 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/helpers.rb +37 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +45 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +67 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +45 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +44 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +71 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +53 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers.rb +9 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb +26 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/allow_nil.rb +26 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/ignore_interference_by_writer.rb +101 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +21 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers.rb +14 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +180 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +128 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +567 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +185 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +272 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +664 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +575 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +711 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +428 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +62 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_matcher.rb +195 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_message_finder.rb +63 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validator.rb +127 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model.rb +96 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +217 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matcher.rb +2134 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +74 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +62 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +41 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +105 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +101 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +89 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +142 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +71 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +41 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +76 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +42 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +65 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers.rb +9 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +784 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/encrypt_matcher.rb +174 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_attached_matcher.rb +343 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +366 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +287 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +104 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +95 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_rich_text_matcher.rb +83 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +132 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/normalize_matcher.rb +151 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/serialize_matcher.rb +203 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/model.rb +58 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/namespace.rb +39 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb +49 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +55 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness.rb +14 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +1197 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record.rb +37 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/configuration.rb +31 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double.rb +103 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double_collection.rb +60 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +31 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/method_call.rb +44 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/object_double.rb +43 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +32 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/stub_implementation.rb +31 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/world.rb +63 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak.rb +38 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/error.rb +29 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent/delegate_method_matcher/target_not_defined_error.rb +15 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent/delegate_method_matcher.rb +540 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent.rb +11 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/configuration.rb +70 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/configuration_error.rb +9 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/inclusion.rb +20 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/action_controller.rb +33 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/active_model.rb +26 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/active_record.rb +26 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/missing_library.rb +19 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/rails.rb +31 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/routing.rb +29 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries.rb +16 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/rails.rb +12 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/registry.rb +28 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +37 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +36 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +37 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +40 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +29 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +36 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks.rb +16 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations.rb +43 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/matcher_collection.rb +99 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/matcher_context.rb +36 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/rails_shim.rb +190 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/routing.rb +10 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/util/word_wrap.rb +211 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/util.rb +110 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/version.rb +6 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers/warn.rb +28 -0
- data/shoulda-matchers-8.0.1/lib/shoulda/matchers.rb +25 -0
- data/shoulda-matchers-8.0.1/lib/shoulda-matchers.rb +1 -0
- data/shoulda-matchers-8.0.1/shoulda-matchers.gemspec +43 -0
- 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
|