ultra-fast-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/shoulda-matchers-8.0.1/LICENSE +22 -0
  3. data/shoulda-matchers-8.0.1/README.md +567 -0
  4. data/shoulda-matchers-8.0.1/docs/errors/NonCaseSwappableValueError.md +111 -0
  5. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/callback_matcher.rb +121 -0
  6. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +70 -0
  7. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/flash_store.rb +86 -0
  8. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/permit_matcher.rb +445 -0
  9. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +96 -0
  10. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/render_template_matcher.rb +84 -0
  11. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +145 -0
  12. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +115 -0
  13. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +148 -0
  14. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/route_matcher.rb +207 -0
  15. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/route_params.rb +62 -0
  16. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/session_store.rb +34 -0
  17. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +228 -0
  18. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_session_matcher.rb +163 -0
  19. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +107 -0
  20. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller.rb +24 -0
  21. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +45 -0
  22. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error.rb +23 -0
  23. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +232 -0
  24. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +62 -0
  25. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +40 -0
  26. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +48 -0
  27. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/successful_check.rb +14 -0
  28. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/successful_setting.rb +14 -0
  29. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher.rb +695 -0
  30. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/comparison_matcher.rb +157 -0
  31. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +78 -0
  32. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/errors.rb +52 -0
  33. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +125 -0
  34. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/helpers.rb +37 -0
  35. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +45 -0
  36. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +67 -0
  37. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +45 -0
  38. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +44 -0
  39. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +71 -0
  40. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +53 -0
  41. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers.rb +9 -0
  42. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb +26 -0
  43. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/allow_nil.rb +26 -0
  44. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/ignore_interference_by_writer.rb +101 -0
  45. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +21 -0
  46. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers.rb +14 -0
  47. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +180 -0
  48. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +128 -0
  49. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +567 -0
  50. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +185 -0
  51. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +272 -0
  52. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +664 -0
  53. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +575 -0
  54. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +711 -0
  55. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +428 -0
  56. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +62 -0
  57. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_matcher.rb +195 -0
  58. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_message_finder.rb +63 -0
  59. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validator.rb +127 -0
  60. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model.rb +96 -0
  61. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +217 -0
  62. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matcher.rb +2134 -0
  63. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +74 -0
  64. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +62 -0
  65. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +41 -0
  66. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +105 -0
  67. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +101 -0
  68. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +89 -0
  69. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +142 -0
  70. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +71 -0
  71. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +41 -0
  72. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +76 -0
  73. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +42 -0
  74. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +65 -0
  75. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers.rb +9 -0
  76. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +784 -0
  77. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/encrypt_matcher.rb +174 -0
  78. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_attached_matcher.rb +343 -0
  79. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +366 -0
  80. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +287 -0
  81. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +104 -0
  82. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +95 -0
  83. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_rich_text_matcher.rb +83 -0
  84. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +132 -0
  85. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/normalize_matcher.rb +151 -0
  86. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/serialize_matcher.rb +203 -0
  87. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/model.rb +58 -0
  88. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/namespace.rb +39 -0
  89. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb +49 -0
  90. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +55 -0
  91. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness.rb +14 -0
  92. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +1197 -0
  93. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record.rb +37 -0
  94. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/configuration.rb +31 -0
  95. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double.rb +103 -0
  96. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double_collection.rb +60 -0
  97. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +31 -0
  98. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/method_call.rb +44 -0
  99. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/object_double.rb +43 -0
  100. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +32 -0
  101. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/stub_implementation.rb +31 -0
  102. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/world.rb +63 -0
  103. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak.rb +38 -0
  104. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/error.rb +29 -0
  105. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent/delegate_method_matcher/target_not_defined_error.rb +15 -0
  106. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent/delegate_method_matcher.rb +540 -0
  107. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent.rb +11 -0
  108. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/configuration.rb +70 -0
  109. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/configuration_error.rb +9 -0
  110. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/inclusion.rb +20 -0
  111. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/action_controller.rb +33 -0
  112. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/active_model.rb +26 -0
  113. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/active_record.rb +26 -0
  114. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/missing_library.rb +19 -0
  115. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/rails.rb +31 -0
  116. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/routing.rb +29 -0
  117. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries.rb +16 -0
  118. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/rails.rb +12 -0
  119. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/registry.rb +28 -0
  120. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +37 -0
  121. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +36 -0
  122. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +37 -0
  123. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +40 -0
  124. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +29 -0
  125. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +36 -0
  126. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks.rb +16 -0
  127. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations.rb +43 -0
  128. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/matcher_collection.rb +99 -0
  129. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/matcher_context.rb +36 -0
  130. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/rails_shim.rb +190 -0
  131. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/routing.rb +10 -0
  132. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/util/word_wrap.rb +211 -0
  133. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/util.rb +110 -0
  134. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/version.rb +6 -0
  135. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/warn.rb +28 -0
  136. data/shoulda-matchers-8.0.1/lib/shoulda/matchers.rb +25 -0
  137. data/shoulda-matchers-8.0.1/lib/shoulda-matchers.rb +1 -0
  138. data/shoulda-matchers-8.0.1/shoulda-matchers.gemspec +43 -0
  139. data/ultra-fast-box.gemspec +12 -0
  140. metadata +179 -0
@@ -0,0 +1,784 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveRecord
4
+ # The `define_enum_for` matcher is used to test that the `enum` macro has
5
+ # been used to decorate an attribute with enum capabilities.
6
+ #
7
+ # class Process < ActiveRecord::Base
8
+ # enum status: [:running, :stopped, :suspended]
9
+ #
10
+ # alias_attribute :kind, :SomeLegacyField
11
+ #
12
+ # enum kind: [:foo, :bar]
13
+ # end
14
+ #
15
+ # # RSpec
16
+ # RSpec.describe Process, type: :model do
17
+ # it { should define_enum_for(:status) }
18
+ # it { should define_enum_for(:kind) }
19
+ # end
20
+ #
21
+ # # Minitest (Shoulda)
22
+ # class ProcessTest < ActiveSupport::TestCase
23
+ # should define_enum_for(:status)
24
+ # should define_enum_for(:kind)
25
+ # end
26
+ #
27
+ # #### Qualifiers
28
+ #
29
+ # ##### with_values
30
+ #
31
+ # Use `with_values` to test that the attribute can only receive a certain
32
+ # set of possible values.
33
+ #
34
+ # class Process < ActiveRecord::Base
35
+ # enum status: [:running, :stopped, :suspended]
36
+ # end
37
+ #
38
+ # # RSpec
39
+ # RSpec.describe Process, type: :model do
40
+ # it do
41
+ # should define_enum_for(:status).
42
+ # with_values([:running, :stopped, :suspended])
43
+ # end
44
+ # end
45
+ #
46
+ # # Minitest (Shoulda)
47
+ # class ProcessTest < ActiveSupport::TestCase
48
+ # should define_enum_for(:status).
49
+ # with_values([:running, :stopped, :suspended])
50
+ # end
51
+ #
52
+ # If the values backing your enum attribute are arbitrary instead of a
53
+ # series of integers starting from 0, pass a hash to `with_values` instead
54
+ # of an array:
55
+ #
56
+ # class Process < ActiveRecord::Base
57
+ # enum status: {
58
+ # running: 0,
59
+ # stopped: 1,
60
+ # suspended: 3,
61
+ # other: 99
62
+ # }
63
+ # end
64
+ #
65
+ # # RSpec
66
+ # RSpec.describe Process, type: :model do
67
+ # it do
68
+ # should define_enum_for(:status).
69
+ # with_values(running: 0, stopped: 1, suspended: 3, other: 99)
70
+ # end
71
+ # end
72
+ #
73
+ # # Minitest (Shoulda)
74
+ # class ProcessTest < ActiveSupport::TestCase
75
+ # should define_enum_for(:status).
76
+ # with_values(running: 0, stopped: 1, suspended: 3, other: 99)
77
+ # end
78
+ #
79
+ # ##### backed_by_column_of_type
80
+ #
81
+ # Use `backed_by_column_of_type` when the column backing your column type
82
+ # is a string instead of an integer:
83
+ #
84
+ # class LoanApplication < ActiveRecord::Base
85
+ # enum status: {
86
+ # active: "active",
87
+ # pending: "pending",
88
+ # rejected: "rejected"
89
+ # }
90
+ # end
91
+ #
92
+ # # RSpec
93
+ # RSpec.describe LoanApplication, type: :model do
94
+ # it do
95
+ # should define_enum_for(:status).
96
+ # with_values(
97
+ # active: "active",
98
+ # pending: "pending",
99
+ # rejected: "rejected"
100
+ # ).
101
+ # backed_by_column_of_type(:string)
102
+ # end
103
+ # end
104
+ #
105
+ # # Minitest (Shoulda)
106
+ # class LoanApplicationTest < ActiveSupport::TestCase
107
+ # should define_enum_for(:status).
108
+ # with_values(
109
+ # active: "active",
110
+ # pending: "pending",
111
+ # rejected: "rejected"
112
+ # ).
113
+ # backed_by_column_of_type(:string)
114
+ # end
115
+ #
116
+ ## ##### with_prefix
117
+ #
118
+ # Use `with_prefix` to test that the enum is defined with a `_prefix`
119
+ # option (Rails 6+ only). Can take either a boolean or a symbol:
120
+ #
121
+ # class Issue < ActiveRecord::Base
122
+ # enum status: [:open, :closed], _prefix: :old
123
+ # end
124
+ #
125
+ # # RSpec
126
+ # RSpec.describe Issue, type: :model do
127
+ # it do
128
+ # should define_enum_for(:status).
129
+ # with_values([:open, :closed]).
130
+ # with_prefix(:old)
131
+ # end
132
+ # end
133
+ #
134
+ # # Minitest (Shoulda)
135
+ # class ProcessTest < ActiveSupport::TestCase
136
+ # should define_enum_for(:status).
137
+ # with_values([:open, :closed]).
138
+ # with_prefix(:old)
139
+ # end
140
+ #
141
+ # ##### with_suffix
142
+ #
143
+ # Use `with_suffix` to test that the enum is defined with a `_suffix`
144
+ # option (Rails 5 only). Can take either a boolean or a symbol:
145
+ #
146
+ # class Issue < ActiveRecord::Base
147
+ # enum status: [:open, :closed], _suffix: true
148
+ # end
149
+ #
150
+ # # RSpec
151
+ # RSpec.describe Issue, type: :model do
152
+ # it do
153
+ # should define_enum_for(:status).
154
+ # with_values([:open, :closed]).
155
+ # with_suffix
156
+ # end
157
+ # end
158
+ #
159
+ # # Minitest (Shoulda)
160
+ # class ProcessTest < ActiveSupport::TestCase
161
+ # should define_enum_for(:status).
162
+ # with_values([:open, :closed]).
163
+ # with_suffix
164
+ # end
165
+ #
166
+ # ##### without_scopes
167
+ #
168
+ # Use `without_scopes` to test that the enum is defined with
169
+ # '_scopes: false' option (Rails 5 only). Can take either a boolean or a
170
+ # symbol:
171
+ #
172
+ # class Issue < ActiveRecord::Base
173
+ # enum status: [:open, :closed], _scopes: false
174
+ # end
175
+ #
176
+ # # RSpec
177
+ # RSpec.describe Issue, type: :model do
178
+ # it do
179
+ # should define_enum_for(:status).
180
+ # without_scopes
181
+ # end
182
+ # end
183
+ #
184
+ # # Minitest (Shoulda)
185
+ # class ProcessTest < ActiveSupport::TestCase
186
+ # should define_enum_for(:status).
187
+ # without_scopes
188
+ # end
189
+ #
190
+ # ##### with_default
191
+ #
192
+ # Use `with_default` to test that the enum is defined with a
193
+ # default value. A proc can also be passed, and will be called once each
194
+ # time a new value is needed. (If using Time or Date, it's recommended to
195
+ # freeze time or date to avoid flaky tests):
196
+ #
197
+ # class Issue < ActiveRecord::Base
198
+ # enum status: [:open, :closed], default: :closed
199
+ # end
200
+ #
201
+ # # RSpec
202
+ # RSpec.describe Issue, type: :model do
203
+ # it do
204
+ # should define_enum_for(:status).
205
+ # with_default(:closed)
206
+ # end
207
+ # end
208
+ #
209
+ # # Minitest (Shoulda)
210
+ # class ProcessTest < ActiveSupport::TestCase
211
+ # should define_enum_for(:status).
212
+ # with_default(:closed)
213
+ # end
214
+ #
215
+ # ##### validating
216
+ #
217
+ # Use `validating` to test that the enum is being validated.
218
+ # Can take a boolean value and an allowing_nil keyword argument:
219
+ #
220
+ # class Issue < ActiveRecord::Base
221
+ # enum status: [:open, :closed], validate: true
222
+ # end
223
+ #
224
+ # # RSpec
225
+ # RSpec.describe Issue, type: :model do
226
+ # it do
227
+ # should define_enum_for(:status).
228
+ # validating
229
+ # end
230
+ # end
231
+ #
232
+ # # Minitest (Shoulda)
233
+ # class ProcessTest < ActiveSupport::TestCase
234
+ # should define_enum_for(:status).
235
+ # validating
236
+ # end
237
+ #
238
+ # class Issue < ActiveRecord::Base
239
+ # enum status: [:open, :closed], validate: { allow_nil: true }
240
+ # end
241
+ #
242
+ # # RSpec
243
+ # RSpec.describe Issue, type: :model do
244
+ # it do
245
+ # should define_enum_for(:status).
246
+ # validating(allowing_nil: true)
247
+ # end
248
+ # end
249
+ #
250
+ # # Minitest (Shoulda)
251
+ # class ProcessTest < ActiveSupport::TestCase
252
+ # should define_enum_for(:status).
253
+ # validating(allowing_nil: true)
254
+ # end
255
+ #
256
+ # ##### without_instance_methods
257
+ #
258
+ # Use `without_instance_methods` to exclude the check for instance methods.
259
+ #
260
+ # class Issue < ActiveRecord::Base
261
+ # enum status: [:open, :closed], instance_methods: false
262
+ # end
263
+ #
264
+ # # RSpec
265
+ # RSpec.describe Issue, type: :model do
266
+ # it do
267
+ # should define_enum_for(:status).
268
+ # without_instance_methods
269
+ # end
270
+ # end
271
+ #
272
+ # # Minitest (Shoulda)
273
+ # class ProcessTest < ActiveSupport::TestCase
274
+ # should define_enum_for(:status).
275
+ # without_instance_methods
276
+ # end
277
+ #
278
+ # @return [DefineEnumForMatcher]
279
+ def define_enum_for(attribute_name)
280
+ DefineEnumForMatcher.new(attribute_name)
281
+ end
282
+
283
+ # @private
284
+ class DefineEnumForMatcher
285
+ def initialize(attribute_name)
286
+ @attribute_name = attribute_name
287
+ @options = { expected_enum_values: [], scopes: true, instance_methods: true }
288
+ end
289
+
290
+ def description
291
+ description = "#{simple_description} backed by "
292
+ description << Shoulda::Matchers::Util.a_or_an(expected_column_type)
293
+
294
+ if expected_enum_values.any?
295
+ description << ' with values '
296
+ description << Shoulda::Matchers::Util.inspect_value(
297
+ expected_enum_values,
298
+ )
299
+ end
300
+
301
+ if options[:prefix]
302
+ description << ", prefix: #{options[:prefix].inspect}"
303
+ end
304
+
305
+ if options[:suffix]
306
+ description << ", suffix: #{options[:suffix].inspect}"
307
+ end
308
+
309
+ description
310
+ end
311
+
312
+ def validating(value = true, allowing_nil: false)
313
+ options[:validating] = value
314
+ options[:allowing_nil] = allowing_nil
315
+ self
316
+ end
317
+
318
+ def with_values(expected_enum_values)
319
+ options[:expected_enum_values] = expected_enum_values
320
+ self
321
+ end
322
+
323
+ def with_options(**)
324
+ raise NotImplementedError,
325
+ 'with_options is not a valid qualifier for the define_enum_for matcher. '\
326
+ 'Did you mean to use with_values instead?'
327
+ end
328
+
329
+ def with_prefix(expected_prefix = true)
330
+ options[:prefix] = expected_prefix
331
+ self
332
+ end
333
+
334
+ def with_suffix(expected_suffix = true)
335
+ options[:suffix] = expected_suffix
336
+ self
337
+ end
338
+
339
+ def backed_by_column_of_type(expected_column_type)
340
+ options[:expected_column_type] = expected_column_type
341
+ self
342
+ end
343
+
344
+ def without_scopes
345
+ options[:scopes] = false
346
+ self
347
+ end
348
+
349
+ def without_instance_methods
350
+ options[:instance_methods] = false
351
+ self
352
+ end
353
+
354
+ def with_default(default_value)
355
+ options[:default] = default_value
356
+ self
357
+ end
358
+
359
+ def matches?(subject)
360
+ @record = subject
361
+
362
+ enum_defined? &&
363
+ enum_values_match? &&
364
+ column_type_matches? &&
365
+ enum_value_methods_exist? &&
366
+ scope_presence_matches? &&
367
+ default_value_matches? &&
368
+ validating_matches?
369
+ end
370
+
371
+ def failure_message
372
+ message =
373
+ if enum_defined?
374
+ "Expected #{model} to #{expectation}. "
375
+ else
376
+ "Expected #{model} to #{expectation}, but "
377
+ end
378
+
379
+ message << "#{failure_message_continuation}."
380
+
381
+ Shoulda::Matchers.word_wrap(message)
382
+ end
383
+
384
+ def failure_message_when_negated
385
+ message = "Expected #{model} not to #{expectation}, but it did."
386
+ Shoulda::Matchers.word_wrap(message)
387
+ end
388
+
389
+ private
390
+
391
+ def validating_matches?
392
+ return true if options[:validating].nil?
393
+
394
+ validator = find_enum_validator
395
+
396
+ if expected_validating? == !!validator
397
+ if validator&.options&.dig(:allow_nil).present? == expected_allowing_nil?
398
+ true
399
+ else
400
+ @failure_message_continuation =
401
+ "However, #{attribute_name.inspect} is allowing nil values"
402
+ false
403
+ end
404
+ else
405
+ @failure_message_continuation =
406
+ if expected_validating?
407
+ "However, #{attribute_name.inspect} is not being validated"
408
+ else
409
+ "However, #{attribute_name.inspect} is being validated"
410
+ end
411
+ false
412
+ end
413
+ end
414
+
415
+ attr_reader :attribute_name, :options, :record,
416
+ :failure_message_continuation
417
+
418
+ def expectation # rubocop:disable Metrics/MethodLength
419
+ if enum_defined?
420
+ expectation = "#{simple_description} backed by "
421
+ expectation << Shoulda::Matchers::Util.a_or_an(expected_column_type)
422
+
423
+ if expected_enum_values.any?
424
+ expectation << ', mapping '
425
+ expectation << presented_enum_mapping(
426
+ normalized_expected_enum_values,
427
+ )
428
+ end
429
+
430
+ if options[:default].present?
431
+ expectation << ', with a default value of '
432
+ expectation << Shoulda::Matchers::Util.inspect_value(expected_default_value)
433
+ end
434
+
435
+ if expected_validating?
436
+ expectation << ', and being validated '
437
+ expectation <<
438
+ if expected_allowing_nil?
439
+ 'allowing nil values'
440
+ else
441
+ 'not allowing nil values'
442
+ end
443
+ end
444
+
445
+ if expected_prefix
446
+ expectation <<
447
+ if expected_suffix
448
+ ', '
449
+ else
450
+ ' and '
451
+ end
452
+
453
+ expectation << 'prefixing accessor methods with '
454
+ expectation << "#{expected_prefix}_".inspect
455
+ end
456
+
457
+ if expected_suffix
458
+ expectation <<
459
+ if expected_prefix
460
+ ', and '
461
+ else
462
+ ' and '
463
+ end
464
+
465
+ expectation << 'suffixing accessor methods with '
466
+ expectation << "_#{expected_suffix}".inspect
467
+ end
468
+
469
+ if exclude_scopes?
470
+ expectation << ' with no scopes'
471
+ end
472
+
473
+ expectation
474
+ else
475
+ simple_description
476
+ end
477
+ end
478
+
479
+ def simple_description
480
+ "define :#{attribute_name} as an enum"
481
+ end
482
+
483
+ def presented_enum_mapping(enum_values)
484
+ enum_values.
485
+ map { |output_to_input|
486
+ output_to_input.
487
+ map(&Shoulda::Matchers::Util.method(:inspect_value)).
488
+ join(' to ')
489
+ }.
490
+ to_sentence
491
+ end
492
+
493
+ def normalized_expected_enum_values
494
+ to_hash(expected_enum_values)
495
+ end
496
+
497
+ def expected_enum_value_names
498
+ to_array(expected_enum_values)
499
+ end
500
+
501
+ def expected_enum_values
502
+ options[:expected_enum_values]
503
+ end
504
+
505
+ def normalized_actual_enum_values
506
+ to_hash(actual_enum_values)
507
+ end
508
+
509
+ def actual_enum_values
510
+ model.send(attribute_name.to_s.pluralize)
511
+ end
512
+
513
+ def enum_defined?
514
+ if model.defined_enums.include?(attribute_name.to_s)
515
+ true
516
+ else
517
+ @failure_message_continuation =
518
+ "no such enum exists on #{model}"
519
+ false
520
+ end
521
+ end
522
+
523
+ def enum_values_match?
524
+ passed =
525
+ expected_enum_values.empty? ||
526
+ normalized_actual_enum_values == normalized_expected_enum_values
527
+
528
+ if passed
529
+ true
530
+ else
531
+ @failure_message_continuation =
532
+ "However, #{attribute_name.inspect} actually maps " +
533
+ presented_enum_mapping(normalized_actual_enum_values)
534
+ false
535
+ end
536
+ end
537
+
538
+ def column_type_matches?
539
+ if column.type == expected_column_type.to_sym
540
+ true
541
+ else
542
+ @failure_message_continuation =
543
+ "However, #{attribute_name.inspect} is "\
544
+ "#{Shoulda::Matchers::Util.a_or_an(column.type)}"\
545
+ ' column'
546
+ false
547
+ end
548
+ end
549
+
550
+ def expected_column_type
551
+ options[:expected_column_type] || :integer
552
+ end
553
+
554
+ def column
555
+ key = attribute_name.to_s
556
+ column_name = model.attribute_alias(key) || key
557
+
558
+ model.columns_hash[column_name]
559
+ end
560
+
561
+ def model
562
+ record.class
563
+ end
564
+
565
+ def enum_value_methods_exist?
566
+ if options[:instance_methods]
567
+ return true if instance_methods_exist?
568
+
569
+ message = missing_methods_message
570
+ message << " (we can't tell which)" if [expected_prefix, expected_suffix].any?
571
+
572
+ @failure_message_continuation = message
573
+
574
+ false
575
+ elsif instance_methods_exist?
576
+ message = "#{attribute_name.inspect} does map to these values"
577
+ message << ' with instance methods, but expected no instance methods'
578
+
579
+ @failure_message_continuation = message
580
+
581
+ false
582
+ else
583
+ true
584
+ end
585
+ end
586
+
587
+ def scope_presence_matches?
588
+ if exclude_scopes?
589
+ if singleton_methods_exist?
590
+ message = "#{attribute_name.inspect} does map to these values "
591
+ message << 'but class scope methods were present'
592
+
593
+ @failure_message_continuation = message
594
+
595
+ false
596
+ else
597
+ true
598
+ end
599
+ elsif singleton_methods_exist?
600
+ true
601
+ else
602
+ if enum_defined?
603
+ message = 'But the class scope methods are not present'
604
+ else
605
+ message = missing_methods_message
606
+
607
+ message << 'or the class scope methods are not present'
608
+ message << " (we can't tell which)"
609
+ end
610
+
611
+ @failure_message_continuation = message
612
+
613
+ false
614
+ end
615
+ end
616
+
617
+ def missing_methods_message
618
+ message = "#{attribute_name.inspect} does map to these "
619
+ message << 'values, but the enum is '
620
+
621
+ if expected_prefix
622
+ if expected_suffix
623
+ message << 'configured with either a different prefix or '
624
+ message << 'suffix, or no prefix or suffix at all'
625
+ else
626
+ message << 'configured with either a different prefix or no '
627
+ message << 'prefix at all'
628
+ end
629
+ elsif expected_suffix
630
+ message << 'configured with either a different suffix or no '
631
+ message << 'suffix at all'
632
+ elsif expected_instance_methods?
633
+ message << 'configured with no instance methods'
634
+ else
635
+ ''
636
+ end
637
+ end
638
+
639
+ def default_value_matches?
640
+ return true if options[:default].blank?
641
+
642
+ if actual_default_value.nil?
643
+ @failure_message_continuation = 'However, no default value was set'
644
+ return false
645
+ end
646
+
647
+ if actual_default_value == expected_default_value
648
+ true
649
+ else
650
+ String.new.tap do |message|
651
+ message << 'However, the default value is '
652
+ message << Shoulda::Matchers::Util.inspect_value(actual_default_value)
653
+ @failure_message_continuation = message
654
+ end
655
+ false
656
+ end
657
+ end
658
+
659
+ def expected_default_value
660
+ options[:default].respond_to?(:call) ? options[:default].call : options[:default]
661
+ end
662
+
663
+ def actual_default_value
664
+ attribute_schema = if model.respond_to?(:_default_attributes)
665
+ model._default_attributes[attribute_name.to_s]
666
+ else
667
+ model.attributes_to_define_after_schema_loads[attribute_name.to_s]
668
+ end
669
+
670
+ if Kernel.const_defined?('ActiveModel::Attribute::UserProvidedDefault') &&
671
+ attribute_schema.is_a?(::ActiveModel::Attribute::UserProvidedDefault)
672
+ attribute_schema = attribute_schema.marshal_dump
673
+ end
674
+
675
+ value = case attribute_schema
676
+ in [_, { default: default_value }]
677
+ default_value
678
+ in [_, default_value, *]
679
+ default_value
680
+ in [_, default_value]
681
+ default_value
682
+ end
683
+
684
+ value.respond_to?(:call) ? value.call : value
685
+ end
686
+
687
+ def singleton_methods_exist?
688
+ expected_singleton_methods.all? do |method|
689
+ model.singleton_methods.include?(method)
690
+ end
691
+ end
692
+
693
+ def instance_methods_exist?
694
+ expected_instance_methods.all? do |method|
695
+ record.methods.include?(method)
696
+ end
697
+ end
698
+
699
+ def expected_singleton_methods
700
+ expected_enum_value_names.map do |name|
701
+ [expected_prefix, name, expected_suffix].
702
+ select(&:present?).
703
+ join('_').
704
+ to_sym
705
+ end
706
+ end
707
+
708
+ def expected_instance_methods
709
+ methods = expected_enum_value_names.map do |name|
710
+ [expected_prefix, name, expected_suffix].
711
+ select(&:present?).
712
+ join('_')
713
+ end
714
+
715
+ methods.flat_map do |m|
716
+ ["#{m}?".to_sym, "#{m}!".to_sym]
717
+ end
718
+ end
719
+
720
+ def expected_instance_methods?
721
+ options[:instance_methods]
722
+ end
723
+
724
+ def expected_prefix
725
+ if options.include?(:prefix)
726
+ if options[:prefix] == true
727
+ attribute_name
728
+ else
729
+ options[:prefix]
730
+ end
731
+ end
732
+ end
733
+
734
+ def expected_suffix
735
+ if options.include?(:suffix)
736
+ if options[:suffix] == true
737
+ attribute_name
738
+ else
739
+ options[:suffix]
740
+ end
741
+ end
742
+ end
743
+
744
+ def expected_validating?
745
+ options[:validating].present?
746
+ end
747
+
748
+ def expected_allowing_nil?
749
+ options[:allowing_nil].present?
750
+ end
751
+
752
+ def find_enum_validator
753
+ record.class.validators.detect do |validator|
754
+ validator.kind == :inclusion &&
755
+ validator.attributes.include?(attribute_name.to_s) &&
756
+ validator.options[:in] == expected_enum_value_names
757
+ end
758
+ end
759
+
760
+ def exclude_scopes?
761
+ !options[:scopes]
762
+ end
763
+
764
+ def to_hash(value)
765
+ if value.is_a?(Array)
766
+ value.each_with_index.inject({}) do |hash, (item, index)|
767
+ hash.merge!(item.to_s => index)
768
+ end
769
+ else
770
+ value.stringify_keys
771
+ end
772
+ end
773
+
774
+ def to_array(value)
775
+ if value.is_a?(Array)
776
+ value.map(&:to_s)
777
+ else
778
+ value.keys.map(&:to_s)
779
+ end
780
+ end
781
+ end
782
+ end
783
+ end
784
+ end