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,664 @@
1
+ require 'bigdecimal'
2
+ require 'date'
3
+
4
+ module Shoulda
5
+ module Matchers
6
+ # @private
7
+ class ExampleClass; end
8
+
9
+ module ActiveModel
10
+ # The `validate_inclusion_of` matcher tests usage of the
11
+ # `validates_inclusion_of` validation, asserting that an attribute can
12
+ # take a allowlist of values and cannot take values outside of this list.
13
+ #
14
+ # If your allowlist is an array of values, use `in_array`:
15
+ #
16
+ # class Issue
17
+ # include ActiveModel::Model
18
+ # attr_accessor :state
19
+ #
20
+ # validates_inclusion_of :state,
21
+ # in: ['open', 'resolved', 'unresolved']
22
+ # end
23
+ #
24
+ # # RSpec
25
+ # RSpec.describe Issue, type: :model do
26
+ # it do
27
+ # should validate_inclusion_of(:state).
28
+ # in_array(['open', 'resolved', 'unresolved'])
29
+ # end
30
+ # end
31
+ #
32
+ # # Minitest (Shoulda)
33
+ # class IssueTest < ActiveSupport::TestCase
34
+ # should validate_inclusion_of(:state).
35
+ # in_array(['open', 'resolved', 'unresolved'])
36
+ # end
37
+ #
38
+ # If your allowlist is a range of values, use `in_range`:
39
+ #
40
+ # class Issue
41
+ # include ActiveModel::Model
42
+ # attr_accessor :priority
43
+ #
44
+ # validates_inclusion_of :priority, in: 1..5
45
+ # end
46
+ #
47
+ # # RSpec
48
+ # RSpec.describe Issue, type: :model do
49
+ # it { should validate_inclusion_of(:priority).in_range(1..5) }
50
+ # end
51
+ #
52
+ # # Minitest (Shoulda)
53
+ # class IssueTest < ActiveSupport::TestCase
54
+ # should validate_inclusion_of(:priority).in_range(1..5)
55
+ # end
56
+ #
57
+ # #### Caveats
58
+ #
59
+ # We discourage using `validate_inclusion_of` with boolean columns. In
60
+ # fact, there is never a case where a boolean column will be anything but
61
+ # true, false, or nil, as ActiveRecord will type-cast an incoming value to
62
+ # one of these three values. That means there isn't any way we can refute
63
+ # this logic in a test. Hence, this will produce a warning:
64
+ #
65
+ # it do
66
+ # should validate_inclusion_of(:imported).
67
+ # in_array([true, false])
68
+ # end
69
+ #
70
+ # The only case where `validate_inclusion_of` *could* be appropriate is
71
+ # for ensuring that a boolean column accepts nil, but we recommend
72
+ # using `allow_value` instead, like this:
73
+ #
74
+ # it { should allow_value(nil).for(:imported) }
75
+ #
76
+ # #### Qualifiers
77
+ #
78
+ # Use `on` if your validation applies only under a certain context.
79
+ #
80
+ # class Issue
81
+ # include ActiveModel::Model
82
+ # attr_accessor :severity
83
+ #
84
+ # validates_inclusion_of :severity,
85
+ # in: %w(low medium high),
86
+ # on: :create
87
+ # end
88
+ #
89
+ # # RSpec
90
+ # RSpec.describe Issue, type: :model do
91
+ # it do
92
+ # should validate_inclusion_of(:severity).
93
+ # in_array(%w(low medium high)).
94
+ # on(:create)
95
+ # end
96
+ # end
97
+ #
98
+ # # Minitest (Shoulda)
99
+ # class IssueTest < ActiveSupport::TestCase
100
+ # should validate_inclusion_of(:severity).
101
+ # in_array(%w(low medium high)).
102
+ # on(:create)
103
+ # end
104
+ #
105
+ # ##### with_message
106
+ #
107
+ # Use `with_message` if you are using a custom validation message.
108
+ #
109
+ # class Issue
110
+ # include ActiveModel::Model
111
+ # attr_accessor :severity
112
+ #
113
+ # validates_inclusion_of :severity,
114
+ # in: %w(low medium high),
115
+ # message: 'Severity must be low, medium, or high'
116
+ # end
117
+ #
118
+ # # RSpec
119
+ # RSpec.describe Issue, type: :model do
120
+ # it do
121
+ # should validate_inclusion_of(:severity).
122
+ # in_array(%w(low medium high)).
123
+ # with_message('Severity must be low, medium, or high')
124
+ # end
125
+ # end
126
+ #
127
+ # # Minitest (Shoulda)
128
+ # class IssueTest < ActiveSupport::TestCase
129
+ # should validate_inclusion_of(:severity).
130
+ # in_array(%w(low medium high)).
131
+ # with_message('Severity must be low, medium, or high')
132
+ # end
133
+ #
134
+ # ##### with_low_message
135
+ #
136
+ # Use `with_low_message` if you have a custom validation message for when
137
+ # a given value is too low.
138
+ #
139
+ # class Person
140
+ # include ActiveModel::Model
141
+ # attr_accessor :age
142
+ #
143
+ # validate :age_must_be_valid
144
+ #
145
+ # private
146
+ #
147
+ # def age_must_be_valid
148
+ # if age < 65
149
+ # self.errors.add :age, 'You do not receive any benefits'
150
+ # end
151
+ # end
152
+ # end
153
+ #
154
+ # # RSpec
155
+ # RSpec.describe Person, type: :model do
156
+ # it do
157
+ # should validate_inclusion_of(:age).
158
+ # in_range(0..65).
159
+ # with_low_message('You do not receive any benefits')
160
+ # end
161
+ # end
162
+ #
163
+ # # Minitest (Shoulda)
164
+ # class PersonTest < ActiveSupport::TestCase
165
+ # should validate_inclusion_of(:age).
166
+ # in_range(0..65).
167
+ # with_low_message('You do not receive any benefits')
168
+ # end
169
+ #
170
+ # ##### with_high_message
171
+ #
172
+ # Use `with_high_message` if you have a custom validation message for
173
+ # when a given value is too high.
174
+ #
175
+ # class Person
176
+ # include ActiveModel::Model
177
+ # attr_accessor :age
178
+ #
179
+ # validate :age_must_be_valid
180
+ #
181
+ # private
182
+ #
183
+ # def age_must_be_valid
184
+ # if age > 21
185
+ # self.errors.add :age, "You're too old for this stuff"
186
+ # end
187
+ # end
188
+ # end
189
+ #
190
+ # # RSpec
191
+ # RSpec.describe Person, type: :model do
192
+ # it do
193
+ # should validate_inclusion_of(:age).
194
+ # in_range(0..21).
195
+ # with_high_message("You're too old for this stuff")
196
+ # end
197
+ # end
198
+ #
199
+ # # Minitest (Shoulda)
200
+ # class PersonTest < ActiveSupport::TestCase
201
+ # should validate_inclusion_of(:age).
202
+ # in_range(0..21).
203
+ # with_high_message("You're too old for this stuff")
204
+ # end
205
+ #
206
+ # ##### allow_nil
207
+ #
208
+ # Use `allow_nil` to assert that the attribute allows nil.
209
+ #
210
+ # class Issue
211
+ # include ActiveModel::Model
212
+ # attr_accessor :state
213
+ #
214
+ # validates_presence_of :state
215
+ # validates_inclusion_of :state,
216
+ # in: ['open', 'resolved', 'unresolved'],
217
+ # allow_nil: true
218
+ # end
219
+ #
220
+ # # RSpec
221
+ # RSpec.describe Issue, type: :model do
222
+ # it do
223
+ # should validate_inclusion_of(:state).
224
+ # in_array(['open', 'resolved', 'unresolved']).
225
+ # allow_nil
226
+ # end
227
+ # end
228
+ #
229
+ # # Minitest (Shoulda)
230
+ # class IssueTest < ActiveSupport::TestCase
231
+ # should validate_inclusion_of(:state).
232
+ # in_array(['open', 'resolved', 'unresolved']).
233
+ # allow_nil
234
+ # end
235
+ #
236
+ # ##### allow_blank
237
+ #
238
+ # Use `allow_blank` to assert that the attribute allows blank.
239
+ #
240
+ # class Issue
241
+ # include ActiveModel::Model
242
+ # attr_accessor :state
243
+ #
244
+ # validates_presence_of :state
245
+ # validates_inclusion_of :state,
246
+ # in: ['open', 'resolved', 'unresolved'],
247
+ # allow_blank: true
248
+ # end
249
+ #
250
+ # # RSpec
251
+ # RSpec.describe Issue, type: :model do
252
+ # it do
253
+ # should validate_inclusion_of(:state).
254
+ # in_array(['open', 'resolved', 'unresolved']).
255
+ # allow_blank
256
+ # end
257
+ # end
258
+ #
259
+ # # Minitest (Shoulda)
260
+ # class IssueTest < ActiveSupport::TestCase
261
+ # should validate_inclusion_of(:state).
262
+ # in_array(['open', 'resolved', 'unresolved']).
263
+ # allow_blank
264
+ # end
265
+ #
266
+ # #### Multiple attributes
267
+ #
268
+ # You can pass multiple attributes to assert that each one has the
269
+ # validation. Any qualifier chained on the matcher is applied to
270
+ # every attribute uniformly.
271
+ #
272
+ # class Article
273
+ # include ActiveModel::Model
274
+ # attr_accessor :status, :category
275
+ #
276
+ # validates_inclusion_of :status, :category, in: %w[draft published]
277
+ # end
278
+ #
279
+ # # RSpec
280
+ # RSpec.describe Article, type: :model do
281
+ # it { should validate_inclusion_of(:status, :category).in_array(%w[draft published]) }
282
+ # end
283
+ #
284
+ # # Minitest (Shoulda)
285
+ # class ArticleTest < ActiveSupport::TestCase
286
+ # should validate_inclusion_of(:status, :category).in_array(%w[draft published])
287
+ # end
288
+ #
289
+ # @return [ValidateInclusionOfMatcher]
290
+ #
291
+ def validate_inclusion_of(*attrs)
292
+ MatcherCollection.build(attrs) { |attr| ValidateInclusionOfMatcher.new(attr) }
293
+ end
294
+
295
+ # @private
296
+ class ValidateInclusionOfMatcher < ValidationMatcher
297
+ BLANK_VALUES = ['', ' ', "\n", "\r", "\t", "\f"].freeze
298
+ ARBITRARY_OUTSIDE_STRING = Shoulda::Matchers::ExampleClass.name
299
+ ARBITRARY_OUTSIDE_INTEGER = 123456789
300
+ ARBITRARY_OUTSIDE_DECIMAL = BigDecimal('0.123456789')
301
+ ARBITRARY_OUTSIDE_DATE = Date.jd(9999999)
302
+ ARBITRARY_OUTSIDE_DATETIME = DateTime.jd(9999999)
303
+ ARBITRARY_OUTSIDE_TIME = Time.at(9999999999)
304
+
305
+ def initialize(attribute)
306
+ super(attribute)
307
+ @options = {}
308
+ @array = nil
309
+ @range = nil
310
+ @minimum = nil
311
+ @maximum = nil
312
+ @low_message = :inclusion
313
+ @high_message = :inclusion
314
+ end
315
+
316
+ def in_array(array)
317
+ @array = array
318
+ self
319
+ end
320
+
321
+ def in_range(range)
322
+ @range = range
323
+ @minimum = minimum_range_value
324
+ @maximum = maximum_range_value
325
+ self
326
+ end
327
+
328
+ def allow_nil
329
+ @options[:allow_nil] = true
330
+ self
331
+ end
332
+
333
+ def expects_to_allow_nil?
334
+ @options[:allow_nil]
335
+ end
336
+
337
+ def with_message(message)
338
+ if message
339
+ @expects_custom_validation_message = true
340
+ @low_message = message
341
+ @high_message = message
342
+ end
343
+
344
+ self
345
+ end
346
+
347
+ def with_low_message(message)
348
+ if message
349
+ @expects_custom_validation_message = true
350
+ @low_message = message
351
+ end
352
+
353
+ self
354
+ end
355
+
356
+ def with_high_message(message)
357
+ if message
358
+ @high_message = message
359
+ end
360
+
361
+ self
362
+ end
363
+
364
+ def simple_description
365
+ if @range
366
+ "validate that :#{@attribute} lies inside the range " +
367
+ Shoulda::Matchers::Util.inspect_range(@range)
368
+ else
369
+ description = "validate that :#{@attribute}"
370
+
371
+ description <<
372
+ if @array.length > 1
373
+ " is either #{inspected_array}"
374
+ else
375
+ " is #{inspected_array}"
376
+ end
377
+
378
+ description
379
+ end
380
+ end
381
+
382
+ def matches?(subject)
383
+ super(subject)
384
+
385
+ if @range
386
+ matches_for_range?
387
+ elsif @array
388
+ if matches_for_array?
389
+ true
390
+ else
391
+ @failure_message = "#{@array} doesn't match array in validation"
392
+ false
393
+ end
394
+ end
395
+ end
396
+
397
+ def does_not_match?(subject)
398
+ super(subject)
399
+
400
+ if @range
401
+ does_not_match_for_range?
402
+ elsif @array
403
+ if does_not_match_for_array?
404
+ true
405
+ else
406
+ @failure_message = "#{@array} matches array in validation"
407
+ false
408
+ end
409
+ end
410
+ end
411
+
412
+ private
413
+
414
+ def boolean_allows_boolean_message
415
+ <<-EOT.strip
416
+ You are using `validate_inclusion_of` to assert that the column '#{@subject.class.name}##{attribute}'
417
+ allows boolean values and disallows non-boolean ones. Be aware that it
418
+ is not possible to fully test this, as boolean columns will automatically
419
+ convert non-boolean values to boolean ones. Hence, you should consider
420
+ removing this test.
421
+ EOT
422
+ end
423
+
424
+ def boolean_allows_nil_message
425
+ <<-EOT.strip
426
+ You are using `validate_inclusion_of` to assert that the column '#{@subject.class.name}##{attribute}'
427
+ allows nil. Be aware that it is not possible to fully test this, as anything other than
428
+ true, false or nil will be converted to false. Hence, you should consider removing this test.
429
+ EOT
430
+ end
431
+
432
+ def minimum_range_value
433
+ @range.begin
434
+ end
435
+
436
+ def maximum_range_value
437
+ if @range.exclude_end?
438
+ @range.end ? (@range.end - 1) : nil
439
+ else
440
+ @range.end
441
+ end
442
+ end
443
+
444
+ def matches_for_range?
445
+ disallows_lower_value &&
446
+ allows_minimum_value &&
447
+ allows_maximum_value &&
448
+ disallows_higher_value
449
+ end
450
+
451
+ def does_not_match_for_range?
452
+ allows_lower_value ||
453
+ disallows_minimum_value ||
454
+ disallows_maximum_value ||
455
+ allows_higher_value
456
+ end
457
+
458
+ def matches_for_array?
459
+ allows_all_values_in_array? &&
460
+ disallows_all_values_outside_of_array? &&
461
+ allows_nil_value? &&
462
+ allow_blank_matches?
463
+ end
464
+
465
+ def does_not_match_for_array?
466
+ disallows_any_values_in_array? ||
467
+ allows_any_value_outside_of_array? ||
468
+ disallows_nil_value? ||
469
+ allow_blank_does_not_match?
470
+ end
471
+
472
+ def allows_lower_value
473
+ @minimum &&
474
+ @minimum != 0 &&
475
+ allows_value_of(@minimum - 1, @low_message)
476
+ end
477
+
478
+ def disallows_lower_value
479
+ @minimum.nil? ||
480
+ @minimum == 0 ||
481
+ disallows_value_of(@minimum - 1, @low_message)
482
+ end
483
+
484
+ def allows_minimum_value
485
+ @minimum.nil? || allows_value_of(@minimum, @low_message)
486
+ end
487
+
488
+ def disallows_minimum_value
489
+ @minimum.nil? || disallows_value_of(@minimum, @low_message)
490
+ end
491
+
492
+ def allows_maximum_value
493
+ @maximum.nil? || allows_value_of(@maximum, @high_message)
494
+ end
495
+
496
+ def disallows_maximum_value
497
+ @maximum.nil? || disallows_value_of(@maximum, @high_message)
498
+ end
499
+
500
+ def allows_higher_value
501
+ @maximum.nil? || allows_value_of(@maximum + 1, @high_message)
502
+ end
503
+
504
+ def disallows_higher_value
505
+ @maximum.nil? || disallows_value_of(@maximum + 1, @high_message)
506
+ end
507
+
508
+ def allows_all_values_in_array?
509
+ @array.all? do |value|
510
+ allows_value_of(value, @low_message)
511
+ end
512
+ end
513
+
514
+ def disallows_any_values_in_array?
515
+ @array.any? do |value|
516
+ disallows_value_of(value, @low_message)
517
+ end
518
+ end
519
+
520
+ def allows_any_value_outside_of_array?
521
+ if attribute_type == :boolean
522
+ case @array
523
+ when [false, true], [true, false]
524
+ Shoulda::Matchers.warn boolean_allows_boolean_message
525
+ return true
526
+ when [nil]
527
+ if attribute_column.null
528
+ Shoulda::Matchers.warn boolean_allows_nil_message
529
+ return true
530
+ else
531
+ raise NonNullableBooleanError.create(@attribute)
532
+ end
533
+ end
534
+ end
535
+
536
+ values_outside_of_array.any? do |value|
537
+ allows_value_of(value, @low_message)
538
+ end
539
+ end
540
+
541
+ def disallows_all_values_outside_of_array?
542
+ if attribute_type == :boolean
543
+ case @array
544
+ when [false, true], [true, false]
545
+ Shoulda::Matchers.warn boolean_allows_boolean_message
546
+ return true
547
+ when [nil]
548
+ if attribute_column.null
549
+ Shoulda::Matchers.warn boolean_allows_nil_message
550
+ return true
551
+ else
552
+ raise NonNullableBooleanError.create(@attribute)
553
+ end
554
+ end
555
+ end
556
+
557
+ values_outside_of_array.all? do |value|
558
+ disallows_value_of(value, @low_message)
559
+ end
560
+ end
561
+
562
+ def values_outside_of_array
563
+ if !(@array & outside_values).empty?
564
+ raise CouldNotDetermineValueOutsideOfArray
565
+ else
566
+ outside_values
567
+ end
568
+ end
569
+
570
+ def outside_values
571
+ case attribute_type
572
+ when :boolean
573
+ boolean_outside_values
574
+ when :integer
575
+ [ARBITRARY_OUTSIDE_INTEGER]
576
+ when :decimal
577
+ [ARBITRARY_OUTSIDE_DECIMAL]
578
+ when :date
579
+ [ARBITRARY_OUTSIDE_DATE]
580
+ when :datetime
581
+ [ARBITRARY_OUTSIDE_DATETIME]
582
+ when :time
583
+ [ARBITRARY_OUTSIDE_TIME]
584
+ else
585
+ [ARBITRARY_OUTSIDE_STRING]
586
+ end
587
+ end
588
+
589
+ def boolean_outside_values
590
+ values = []
591
+
592
+ values << case @array
593
+ when [true] then false
594
+ when [false] then true
595
+ else raise CouldNotDetermineValueOutsideOfArray
596
+ end
597
+
598
+ if attribute_allows_nil?
599
+ values << nil
600
+ end
601
+
602
+ values
603
+ end
604
+
605
+ def attribute_type
606
+ if attribute_column
607
+ column_type_to_attribute_type(attribute_column.type)
608
+ else
609
+ value_to_attribute_type(@subject.__send__(@attribute))
610
+ end
611
+ end
612
+
613
+ def attribute_allows_nil?
614
+ if attribute_column
615
+ attribute_column.null
616
+ else
617
+ true
618
+ end
619
+ end
620
+
621
+ def attribute_column
622
+ if @subject.class.respond_to?(:columns_hash)
623
+ @subject.class.columns_hash[@attribute.to_s]
624
+ end
625
+ end
626
+
627
+ def column_type_to_attribute_type(type)
628
+ case type
629
+ when :float then :integer
630
+ when :timestamp then :datetime
631
+ else type
632
+ end
633
+ end
634
+
635
+ def value_to_attribute_type(value)
636
+ case value
637
+ when true, false then :boolean
638
+ when BigDecimal then :decimal
639
+ when Integer then :integer
640
+ when Date then :date
641
+ when DateTime then :datetime
642
+ when Time then :time
643
+ else :unknown
644
+ end
645
+ end
646
+
647
+ def allows_nil_value?
648
+ @options[:allow_nil] != true || allows_value_of(nil)
649
+ end
650
+
651
+ def disallows_nil_value?
652
+ @options[:allow_nil] && disallows_value_of(nil)
653
+ end
654
+
655
+ def inspected_array
656
+ Shoulda::Matchers::Util.inspect_values(@array).to_sentence(
657
+ two_words_connector: ' or ',
658
+ last_word_connector: ', or ',
659
+ )
660
+ end
661
+ end
662
+ end
663
+ end
664
+ end