giga-safe-box 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. checksums.yaml +7 -0
  2. data/giga-safe-box.gemspec +11 -0
  3. data/shoulda-matchers-8.0.1/LICENSE +22 -0
  4. data/shoulda-matchers-8.0.1/README.md +567 -0
  5. data/shoulda-matchers-8.0.1/docs/errors/NonCaseSwappableValueError.md +111 -0
  6. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/callback_matcher.rb +121 -0
  7. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +70 -0
  8. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/flash_store.rb +86 -0
  9. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/permit_matcher.rb +445 -0
  10. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +96 -0
  11. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/render_template_matcher.rb +84 -0
  12. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +145 -0
  13. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +115 -0
  14. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +148 -0
  15. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/route_matcher.rb +207 -0
  16. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/route_params.rb +62 -0
  17. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/session_store.rb +34 -0
  18. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +228 -0
  19. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_session_matcher.rb +163 -0
  20. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +107 -0
  21. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/action_controller.rb +24 -0
  22. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +45 -0
  23. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error.rb +23 -0
  24. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +232 -0
  25. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +62 -0
  26. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +40 -0
  27. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +48 -0
  28. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/successful_check.rb +14 -0
  29. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher/successful_setting.rb +14 -0
  30. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/allow_value_matcher.rb +695 -0
  31. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/comparison_matcher.rb +157 -0
  32. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +78 -0
  33. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/errors.rb +52 -0
  34. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +125 -0
  35. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/helpers.rb +37 -0
  36. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +45 -0
  37. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +67 -0
  38. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +45 -0
  39. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +44 -0
  40. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/range_matcher.rb +71 -0
  41. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers/submatchers.rb +53 -0
  42. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/numericality_matchers.rb +9 -0
  43. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb +26 -0
  44. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/allow_nil.rb +26 -0
  45. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/ignore_interference_by_writer.rb +101 -0
  46. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +21 -0
  47. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/qualifiers.rb +14 -0
  48. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +180 -0
  49. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +128 -0
  50. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_comparison_of_matcher.rb +567 -0
  51. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +185 -0
  52. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +272 -0
  53. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +664 -0
  54. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +575 -0
  55. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +711 -0
  56. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +428 -0
  57. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +62 -0
  58. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_matcher.rb +195 -0
  59. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validation_message_finder.rb +63 -0
  60. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model/validator.rb +127 -0
  61. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_model.rb +96 -0
  62. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +217 -0
  63. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matcher.rb +2134 -0
  64. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +74 -0
  65. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +62 -0
  66. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +41 -0
  67. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +105 -0
  68. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +101 -0
  69. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +89 -0
  70. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +142 -0
  71. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +71 -0
  72. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +41 -0
  73. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +76 -0
  74. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +42 -0
  75. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +65 -0
  76. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/association_matchers.rb +9 -0
  77. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +784 -0
  78. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/encrypt_matcher.rb +174 -0
  79. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_attached_matcher.rb +343 -0
  80. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +366 -0
  81. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +287 -0
  82. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +104 -0
  83. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +95 -0
  84. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_rich_text_matcher.rb +83 -0
  85. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +132 -0
  86. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/normalize_matcher.rb +151 -0
  87. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/serialize_matcher.rb +203 -0
  88. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/model.rb +58 -0
  89. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/namespace.rb +39 -0
  90. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb +49 -0
  91. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +55 -0
  92. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/uniqueness.rb +14 -0
  93. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +1197 -0
  94. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/active_record.rb +37 -0
  95. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/configuration.rb +31 -0
  96. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double.rb +103 -0
  97. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double_collection.rb +60 -0
  98. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +31 -0
  99. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/method_call.rb +44 -0
  100. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/object_double.rb +43 -0
  101. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/proxy_implementation.rb +32 -0
  102. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/stub_implementation.rb +31 -0
  103. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak/world.rb +63 -0
  104. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/doublespeak.rb +38 -0
  105. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/error.rb +29 -0
  106. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent/delegate_method_matcher/target_not_defined_error.rb +15 -0
  107. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent/delegate_method_matcher.rb +540 -0
  108. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/independent.rb +11 -0
  109. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/configuration.rb +70 -0
  110. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/configuration_error.rb +9 -0
  111. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/inclusion.rb +20 -0
  112. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/action_controller.rb +33 -0
  113. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/active_model.rb +26 -0
  114. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/active_record.rb +26 -0
  115. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/missing_library.rb +19 -0
  116. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/rails.rb +31 -0
  117. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries/routing.rb +29 -0
  118. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/libraries.rb +16 -0
  119. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/rails.rb +12 -0
  120. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/registry.rb +28 -0
  121. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +37 -0
  122. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +36 -0
  123. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +37 -0
  124. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +40 -0
  125. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/rspec.rb +29 -0
  126. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +36 -0
  127. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations/test_frameworks.rb +16 -0
  128. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/integrations.rb +43 -0
  129. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/matcher_collection.rb +99 -0
  130. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/matcher_context.rb +36 -0
  131. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/rails_shim.rb +190 -0
  132. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/routing.rb +10 -0
  133. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/util/word_wrap.rb +211 -0
  134. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/util.rb +110 -0
  135. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/version.rb +6 -0
  136. data/shoulda-matchers-8.0.1/lib/shoulda/matchers/warn.rb +28 -0
  137. data/shoulda-matchers-8.0.1/lib/shoulda/matchers.rb +25 -0
  138. data/shoulda-matchers-8.0.1/lib/shoulda-matchers.rb +1 -0
  139. data/shoulda-matchers-8.0.1/shoulda-matchers.gemspec +43 -0
  140. metadata +178 -0
@@ -0,0 +1,428 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ # The `validate_presence_of` matcher tests usage of the
5
+ # `validates_presence_of` validation.
6
+ #
7
+ # class Robot
8
+ # include ActiveModel::Model
9
+ # attr_accessor :arms
10
+ #
11
+ # validates_presence_of :arms
12
+ # end
13
+ #
14
+ # # RSpec
15
+ # RSpec.describe Robot, type: :model do
16
+ # it { should validate_presence_of(:arms) }
17
+ # end
18
+ #
19
+ # # Minitest (Shoulda)
20
+ # class RobotTest < ActiveSupport::TestCase
21
+ # should validate_presence_of(:arms)
22
+ # end
23
+ #
24
+ # #### Caveats
25
+ #
26
+ # Under Rails 4 and greater, if your model `has_secure_password` and you
27
+ # are validating presence of the password using a record whose password
28
+ # has already been set prior to calling the matcher, you will be
29
+ # instructed to use a record whose password is empty instead.
30
+ #
31
+ # For example, given this scenario:
32
+ #
33
+ # class User < ActiveRecord::Base
34
+ # has_secure_password validations: false
35
+ #
36
+ # validates_presence_of :password
37
+ # end
38
+ #
39
+ # RSpec.describe User, type: :model do
40
+ # subject { User.new(password: '123456') }
41
+ #
42
+ # it { should validate_presence_of(:password) }
43
+ # end
44
+ #
45
+ # the above test will raise an error like this:
46
+ #
47
+ # The validation failed because your User model declares
48
+ # `has_secure_password`, and `validate_presence_of` was called on a
49
+ # user which has `password` already set to a value. Please use a user
50
+ # with an empty `password` instead.
51
+ #
52
+ # This happens because `has_secure_password` itself overrides your model
53
+ # so that it is impossible to set `password` to nil. This means that it is
54
+ # impossible to test that setting `password` to nil places your model in
55
+ # an invalid state (which in turn means that the validation itself is
56
+ # unnecessary).
57
+ #
58
+ # #### Qualifiers
59
+ #
60
+ # ##### allow_nil
61
+ #
62
+ # Use `allow_nil` if your model has an optional attribute.
63
+ #
64
+ # class Robot
65
+ # include ActiveModel::Model
66
+ # attr_accessor :nickname
67
+ #
68
+ # validates_presence_of :nickname, allow_nil: true
69
+ # end
70
+ #
71
+ # # RSpec
72
+ # RSpec.describe Robot, type: :model do
73
+ # it { should validate_presence_of(:nickname).allow_nil }
74
+ # end
75
+ #
76
+ # # Minitest (Shoulda)
77
+ # class RobotTest < ActiveSupport::TestCase
78
+ # should validate_presence_of(:nickname).allow_nil
79
+ # end
80
+ #
81
+ # #### allow_blank
82
+ #
83
+ # Use `allow_blank` to assert that the attribute allows blank.
84
+ #
85
+ # class Robot
86
+ # include ActiveModel::Model
87
+ # attr_accessor :nickname
88
+ #
89
+ # validates_presence_of :nickname, allow_blank: true
90
+ # end
91
+ #
92
+ # # RSpec
93
+ # RSpec.describe Robot, type: :model do
94
+ # it { should validate_presence_of(:nickname).allow_blank }
95
+ # end
96
+ #
97
+ # # Minitest (Shoulda)
98
+ # class RobotTest < ActiveSupport::TestCase
99
+ # should validate_presence_of(:nickname).allow_blank
100
+ # end
101
+ #
102
+ # ##### on
103
+ #
104
+ # Use `on` if your validation applies only under a certain context.
105
+ #
106
+ # class Robot
107
+ # include ActiveModel::Model
108
+ # attr_accessor :arms
109
+ #
110
+ # validates_presence_of :arms, on: :create
111
+ # end
112
+ #
113
+ # # RSpec
114
+ # RSpec.describe Robot, type: :model do
115
+ # it { should validate_presence_of(:arms).on(:create) }
116
+ # end
117
+ #
118
+ # # Minitest (Shoulda)
119
+ # class RobotTest < ActiveSupport::TestCase
120
+ # should validate_presence_of(:arms).on(:create)
121
+ # end
122
+ #
123
+ # ##### with_message
124
+ #
125
+ # Use `with_message` if you are using a custom validation message.
126
+ #
127
+ # class Robot
128
+ # include ActiveModel::Model
129
+ # attr_accessor :legs
130
+ #
131
+ # validates_presence_of :legs, message: 'Robot has no legs'
132
+ # end
133
+ #
134
+ # # RSpec
135
+ # RSpec.describe Robot, type: :model do
136
+ # it do
137
+ # should validate_presence_of(:legs).
138
+ # with_message('Robot has no legs')
139
+ # end
140
+ # end
141
+ #
142
+ # # Minitest (Shoulda)
143
+ # class RobotTest < ActiveSupport::TestCase
144
+ # should validate_presence_of(:legs).
145
+ # with_message('Robot has no legs')
146
+ # end
147
+ #
148
+ # #### Multiple attributes
149
+ #
150
+ # You can pass multiple attributes to assert that each one has the
151
+ # validation. Any qualifier chained on the matcher is applied to
152
+ # every attribute uniformly.
153
+ #
154
+ # class Robot
155
+ # include ActiveModel::Model
156
+ # attr_accessor :arms, :legs
157
+ #
158
+ # validates_presence_of :arms, :legs
159
+ # end
160
+ #
161
+ # # RSpec
162
+ # RSpec.describe Robot, type: :model do
163
+ # it { should validate_presence_of(:arms, :legs) }
164
+ # end
165
+ #
166
+ # # Minitest (Shoulda)
167
+ # class RobotTest < ActiveSupport::TestCase
168
+ # should validate_presence_of(:arms, :legs)
169
+ # end
170
+ #
171
+ # @return [ValidatePresenceOfMatcher]
172
+ #
173
+
174
+ def validate_presence_of(*attrs)
175
+ MatcherCollection.build(attrs) { |attr| ValidatePresenceOfMatcher.new(attr) }
176
+ end
177
+
178
+ # @private
179
+ class ValidatePresenceOfMatcher < ValidationMatcher
180
+ include Qualifiers::AllowNil
181
+ include Qualifiers::AllowBlank
182
+
183
+ def initialize(attribute)
184
+ super
185
+ @expected_message = :blank
186
+ end
187
+
188
+ def matches?(subject)
189
+ super(subject)
190
+
191
+ possibly_ignore_interference_by_writer
192
+
193
+ if secure_password_being_validated? &&
194
+ Shoulda::Matchers::RailsShim.active_model_lt_7?
195
+ ignore_interference_by_writer.default_to(when: :blank?)
196
+
197
+ disallowed_values.all? do |value|
198
+ disallows_and_double_checks_value_of!(value)
199
+ end
200
+ else
201
+ (!expects_to_allow_nil? || allows_value_of(nil)) &&
202
+ (!expects_to_allow_blank? || allows_value_of('')) &&
203
+ disallowed_values.all? do |value|
204
+ disallows_original_or_typecast_value?(value)
205
+ end
206
+ end
207
+ end
208
+
209
+ def does_not_match?(subject)
210
+ super(subject)
211
+
212
+ possibly_ignore_interference_by_writer
213
+
214
+ if secure_password_being_validated?
215
+ ignore_interference_by_writer.default_to(when: :blank?)
216
+
217
+ disallowed_values.any? do |value|
218
+ allows_and_double_checks_value_of!(value)
219
+ end
220
+ else
221
+ (expects_to_allow_nil? && disallows_value_of(nil)) ||
222
+ (expects_to_allow_blank? && disallows_value_of('')) ||
223
+ disallowed_values.any? do |value|
224
+ allows_original_or_typecast_value?(value)
225
+ end
226
+ end
227
+ end
228
+
229
+ def simple_description
230
+ "validate that :#{@attribute} cannot be empty/falsy"
231
+ end
232
+
233
+ def failure_reason
234
+ reason = super
235
+ if should_add_footnote_about_belongs_to?
236
+ "#{reason}\n\n#{belongs_to_footnote}"
237
+ else
238
+ reason
239
+ end
240
+ end
241
+
242
+ private
243
+
244
+ def belongs_to_footnote
245
+ <<~MESSAGE.strip
246
+ You're getting this error because #{reason_for_existing_presence_validation}.
247
+ *This* presence validation doesn't use "can't be blank", the usual validation
248
+ message, but "must exist" instead.
249
+
250
+ With that said, did you know that the `belong_to` matcher can test this
251
+ validation for you? Instead of using `validate_presence_of`, try
252
+ #{suggestions_for_belongs_to}
253
+ MESSAGE
254
+ end
255
+
256
+ def secure_password_being_validated?
257
+ Shoulda::Matchers::RailsShim.digestible_attributes_in(@subject).
258
+ include?(@attribute)
259
+ end
260
+
261
+ def possibly_ignore_interference_by_writer
262
+ if secure_password_being_validated? && RailsShim.active_model_lt_7?
263
+ ignore_interference_by_writer.default_to(when: :blank?)
264
+ end
265
+ end
266
+
267
+ def allows_and_double_checks_value_of!(value)
268
+ allows_value_of(value, @expected_message)
269
+ rescue ActiveModel::AllowValueMatcher::AttributeChangedValueError
270
+ raise ActiveModel::CouldNotSetPasswordError.create(model)
271
+ end
272
+
273
+ def allows_original_or_typecast_value?(value)
274
+ allows_value_of(value, @expected_message)
275
+ end
276
+
277
+ def disallows_and_double_checks_value_of!(value)
278
+ disallows_value_of(value, @expected_message)
279
+ rescue ActiveModel::AllowValueMatcher::AttributeChangedValueError
280
+ raise ActiveModel::CouldNotSetPasswordError.create(model)
281
+ end
282
+
283
+ def disallows_original_or_typecast_value?(value)
284
+ disallows_value_of(value, @expected_message)
285
+ end
286
+
287
+ def disallowed_values
288
+ if collection_association?
289
+ [Array.new]
290
+ elsif attachment?
291
+ [nil]
292
+ else
293
+ values = []
294
+
295
+ if attribute_accepts_string_values? && !expects_to_allow_blank?
296
+ values << ''
297
+ end
298
+
299
+ if !expects_to_allow_nil? && !expects_to_allow_blank?
300
+ values << nil
301
+ end
302
+
303
+ values
304
+ end
305
+ end
306
+
307
+ def should_add_footnote_about_belongs_to?
308
+ belongs_to_association_being_validated? &&
309
+ presence_validation_exists_on_attribute?
310
+ end
311
+
312
+ def reason_for_existing_presence_validation
313
+ if belongs_to_association_configured_to_be_required?
314
+ "you've instructed your `belongs_to` association to add a "\
315
+ 'presence validation to the attribute'
316
+ else
317
+ # assume ::ActiveRecord::Base.belongs_to_required_by_default == true
318
+ 'ActiveRecord is configured to add a presence validation to all '\
319
+ '`belongs_to` associations, and this includes yours'
320
+ end
321
+ end
322
+
323
+ def suggestions_for_belongs_to
324
+ if belongs_to_association_configured_to_be_required?
325
+ <<~MESSAGE
326
+ one of the following instead, depending on your use case:
327
+
328
+ #{example_of_belongs_to(with: [:optional, false])}
329
+ #{example_of_belongs_to(with: [:required, true])}
330
+ MESSAGE
331
+ else
332
+ <<~MESSAGE
333
+ the following instead:
334
+
335
+ #{example_of_belongs_to}
336
+ MESSAGE
337
+ end
338
+ end
339
+
340
+ def example_of_belongs_to(with: nil)
341
+ initial_call = "should belong_to(:#{association_name})"
342
+ inside =
343
+ if with
344
+ "#{initial_call}.#{with.first}(#{with.second})"
345
+ else
346
+ initial_call
347
+ end
348
+
349
+ if Shoulda::Matchers.integrations.test_frameworks.any?(&:n_unit?)
350
+ inside
351
+ else
352
+ "it { #{inside} }"
353
+ end
354
+ end
355
+
356
+ def belongs_to_association_configured_to_be_required?
357
+ association_options[:optional] == false ||
358
+ association_options[:required] == true
359
+ end
360
+
361
+ def belongs_to_association_being_validated?
362
+ association? && association_reflection.macro == :belongs_to
363
+ end
364
+
365
+ def attribute_accepts_string_values?
366
+ if association?
367
+ false
368
+ elsif attribute_serialization_coder.respond_to?(:object_class)
369
+ attribute_serialization_coder.object_class == String
370
+ else
371
+ RailsShim.supports_full_attributes_api?(model) &&
372
+ attribute_type.try(:type) == :string
373
+ end
374
+ end
375
+
376
+ def association?
377
+ association_reflection.present?
378
+ end
379
+
380
+ def collection_association?
381
+ association? && [:has_many, :has_and_belongs_to_many].include?(
382
+ association_reflection.macro,
383
+ )
384
+ end
385
+
386
+ def attachment?
387
+ model_has_associations?(
388
+ ["#{@attribute}_attachment", "#{@attribute}_attachments"],
389
+ )
390
+ end
391
+
392
+ def association_name
393
+ association_reflection.name
394
+ end
395
+
396
+ def association_options
397
+ association_reflection&.options
398
+ end
399
+
400
+ def association_reflection
401
+ model.try(:reflect_on_association, @attribute)
402
+ end
403
+
404
+ def model_has_associations?(associations)
405
+ associations.any? do |association|
406
+ !!model.try(:reflect_on_association, association)
407
+ end
408
+ end
409
+
410
+ def attribute_serialization_coder
411
+ RailsShim.attribute_serialization_coder_for(model, @attribute)
412
+ end
413
+
414
+ def attribute_type
415
+ RailsShim.attribute_type_for(model, @attribute)
416
+ end
417
+
418
+ def presence_validation_exists_on_attribute?
419
+ model._validators.include?(@attribute)
420
+ end
421
+
422
+ def model
423
+ @subject.class
424
+ end
425
+ end
426
+ end
427
+ end
428
+ end
@@ -0,0 +1,62 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ class ValidationMatcher
5
+ # @private
6
+ class BuildDescription
7
+ def self.call(matcher, main_description)
8
+ new(matcher, main_description).call
9
+ end
10
+
11
+ def initialize(matcher, main_description)
12
+ @matcher = matcher
13
+ @main_description = main_description
14
+ end
15
+
16
+ def call
17
+ if description_clauses_for_qualifiers.any?
18
+ "#{main_description}#{clause_for_allow_blank_or_nil},"\
19
+ " #{description_clauses_for_qualifiers.to_sentence}"
20
+ else
21
+ main_description + clause_for_allow_blank_or_nil
22
+ end
23
+ end
24
+
25
+ protected
26
+
27
+ attr_reader :matcher, :main_description
28
+
29
+ private
30
+
31
+ def clause_for_allow_blank_or_nil
32
+ if matcher.try(:expects_to_allow_blank?)
33
+ ' as long as it is not blank'
34
+ elsif matcher.try(:expects_to_allow_nil?)
35
+ ' as long as it is not nil'
36
+ else
37
+ ''
38
+ end
39
+ end
40
+
41
+ def description_clauses_for_qualifiers
42
+ description_clauses = []
43
+
44
+ if matcher.try(:expects_strict?)
45
+ description_clauses <<
46
+ if matcher.try(:expects_custom_validation_message?)
47
+ 'raising a validation exception with a custom message on failure'
48
+ else
49
+ 'raising a validation exception on failure'
50
+ end
51
+ elsif matcher.try(:expects_custom_validation_message?)
52
+ description_clauses <<
53
+ 'producing a custom validation error on failure'
54
+ end
55
+
56
+ description_clauses
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,195 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ # @private
5
+ class ValidationMatcher
6
+ include Qualifiers::IgnoringInterferenceByWriter
7
+
8
+ def initialize(attribute)
9
+ super
10
+ @attribute = attribute
11
+ @options = {}
12
+ @expects_strict = false
13
+ @subject = nil
14
+ @last_submatcher_run = nil
15
+ @expected_message = nil
16
+ @expects_custom_validation_message = false
17
+ end
18
+
19
+ def description
20
+ ValidationMatcher::BuildDescription.call(self, simple_description)
21
+ end
22
+
23
+ def on(context)
24
+ @context = context
25
+ self
26
+ end
27
+
28
+ def allow_blank
29
+ options[:allow_blank] = true
30
+ self
31
+ end
32
+
33
+ def strict
34
+ @expects_strict = true
35
+ self
36
+ end
37
+
38
+ def expects_strict?
39
+ @expects_strict
40
+ end
41
+
42
+ def with_message(expected_message)
43
+ if expected_message
44
+ @expects_custom_validation_message = true
45
+ @expected_message = expected_message
46
+ end
47
+
48
+ self
49
+ end
50
+
51
+ def expects_custom_validation_message?
52
+ @expects_custom_validation_message
53
+ end
54
+
55
+ def matches?(subject)
56
+ @subject = subject
57
+ false
58
+ end
59
+
60
+ def does_not_match?(subject)
61
+ @subject = subject
62
+ true
63
+ end
64
+
65
+ def failure_message
66
+ overall_failure_message.dup.tap do |message|
67
+ if failure_reason.present?
68
+ message << "\n"
69
+ message << Shoulda::Matchers.word_wrap(
70
+ failure_reason,
71
+ indent: 2,
72
+ )
73
+ end
74
+ end
75
+ end
76
+
77
+ def failure_message_when_negated
78
+ overall_failure_message_when_negated.dup.tap do |message|
79
+ if failure_reason.present?
80
+ message << "\n"
81
+ message << Shoulda::Matchers.word_wrap(
82
+ failure_reason,
83
+ indent: 2,
84
+ )
85
+ end
86
+ end
87
+ end
88
+
89
+ def failure_reason
90
+ last_submatcher_run.try(:failure_message)
91
+ end
92
+
93
+ protected
94
+
95
+ attr_reader :attribute, :context, :subject, :last_submatcher_run
96
+
97
+ def model
98
+ subject.class
99
+ end
100
+
101
+ def allows_value_of(value, message = nil, &block)
102
+ matcher = allow_value_matcher(value, message, &block)
103
+ run_allow_or_disallow_matcher(matcher)
104
+ end
105
+
106
+ def disallows_value_of(value, message = nil, &block)
107
+ matcher = disallow_value_matcher(value, message, &block)
108
+ run_allow_or_disallow_matcher(matcher)
109
+ end
110
+
111
+ def allow_value_matcher(value, message = nil, &block)
112
+ build_allow_or_disallow_value_matcher(
113
+ matcher_class: AllowValueMatcher,
114
+ value:,
115
+ message:,
116
+ &block
117
+ )
118
+ end
119
+
120
+ def disallow_value_matcher(value, message = nil, &block)
121
+ build_allow_or_disallow_value_matcher(
122
+ matcher_class: DisallowValueMatcher,
123
+ value:,
124
+ message:,
125
+ &block
126
+ )
127
+ end
128
+
129
+ def allow_blank_matches?
130
+ !expects_to_allow_blank? ||
131
+ blank_values.all? { |value| allows_value_of(value) }
132
+ end
133
+
134
+ def allow_blank_does_not_match?
135
+ expects_to_allow_blank? &&
136
+ blank_values.all? { |value| disallows_value_of(value) }
137
+ end
138
+
139
+ private
140
+
141
+ attr_reader :options
142
+
143
+ def overall_failure_message
144
+ Shoulda::Matchers.word_wrap(
145
+ "Expected #{model.name} to #{description}, but this could not be "\
146
+ 'proved.',
147
+ )
148
+ end
149
+
150
+ def overall_failure_message_when_negated
151
+ Shoulda::Matchers.word_wrap(
152
+ "Expected #{model.name} not to #{description}, but this could "\
153
+ 'not be proved.',
154
+ )
155
+ end
156
+
157
+ def build_allow_or_disallow_value_matcher(args)
158
+ matcher_class = args.fetch(:matcher_class)
159
+ value = args.fetch(:value)
160
+ message = args[:message]
161
+
162
+ matcher = matcher_class.new(value).
163
+ for(attribute).
164
+ with_message(message).
165
+ on(context).
166
+ strict(expects_strict?).
167
+ ignoring_interference_by_writer(ignore_interference_by_writer)
168
+
169
+ yield matcher if block_given?
170
+
171
+ matcher
172
+ end
173
+
174
+ def run_allow_or_disallow_matcher(matcher)
175
+ @last_submatcher_run = matcher
176
+ matcher.matches?(subject)
177
+ end
178
+
179
+ def expects_to_allow_blank?
180
+ options[:allow_blank]
181
+ end
182
+
183
+ def blank_values
184
+ ['', ' ', "\n", "\r", "\t", "\f"]
185
+ end
186
+
187
+ def array_column?
188
+ @subject.class.respond_to?(:columns_hash) &&
189
+ @subject.class.columns_hash[@attribute.to_s].respond_to?(:array) &&
190
+ @subject.class.columns_hash[@attribute.to_s].array
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end