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,575 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ # The `validate_length_of` matcher tests usage of the
5
+ # `validates_length_of` matcher. Note that this matcher is intended to be
6
+ # used against string columns and associations and not integer columns.
7
+ #
8
+ # #### Qualifiers
9
+ #
10
+ # Use `on` if your validation applies only under a certain context.
11
+ #
12
+ # class User
13
+ # include ActiveModel::Model
14
+ # attr_accessor :password
15
+ #
16
+ # validates_length_of :password, minimum: 10, on: :create
17
+ # end
18
+ #
19
+ # # RSpec
20
+ # RSpec.describe User, type: :model do
21
+ # it do
22
+ # should validate_length_of(:password).
23
+ # is_at_least(10).
24
+ # on(:create)
25
+ # end
26
+ # end
27
+ #
28
+ # # Minitest (Shoulda)
29
+ # class UserTest < ActiveSupport::TestCase
30
+ # should validate_length_of(:password).
31
+ # is_at_least(10).
32
+ # on(:create)
33
+ # end
34
+ #
35
+ # ##### is_at_least
36
+ #
37
+ # Use `is_at_least` to test usage of the `:minimum` option. This asserts
38
+ # that the attribute can take a string which is equal to or longer than
39
+ # the given length and cannot take a string which is shorter. This qualifier
40
+ # also works for associations.
41
+ #
42
+ # class User
43
+ # include ActiveModel::Model
44
+ # attr_accessor :bio
45
+ #
46
+ # validates_length_of :bio, minimum: 15
47
+ # end
48
+ #
49
+ # # RSpec
50
+ #
51
+ # RSpec.describe User, type: :model do
52
+ # it { should validate_length_of(:bio).is_at_least(15) }
53
+ # end
54
+ #
55
+ # # Minitest (Shoulda)
56
+ #
57
+ # class UserTest < ActiveSupport::TestCase
58
+ # should validate_length_of(:bio).is_at_least(15)
59
+ # end
60
+ #
61
+ # ##### is_at_most
62
+ #
63
+ # Use `is_at_most` to test usage of the `:maximum` option. This asserts
64
+ # that the attribute can take a string which is equal to or shorter than
65
+ # the given length and cannot take a string which is longer. This qualifier
66
+ # also works for associations.
67
+ #
68
+ # class User
69
+ # include ActiveModel::Model
70
+ # attr_accessor :status_update
71
+ #
72
+ # validates_length_of :status_update, maximum: 140
73
+ # end
74
+ #
75
+ # # RSpec
76
+ # RSpec.describe User, type: :model do
77
+ # it { should validate_length_of(:status_update).is_at_most(140) }
78
+ # end
79
+ #
80
+ # # Minitest (Shoulda)
81
+ # class UserTest < ActiveSupport::TestCase
82
+ # should validate_length_of(:status_update).is_at_most(140)
83
+ # end
84
+ #
85
+ # ##### is_equal_to
86
+ #
87
+ # Use `is_equal_to` to test usage of the `:is` option. This asserts that
88
+ # the attribute can take a string which is exactly equal to the given
89
+ # length and cannot take a string which is shorter or longer. This qualifier
90
+ # also works for associations.
91
+ #
92
+ # class User
93
+ # include ActiveModel::Model
94
+ # attr_accessor :favorite_superhero
95
+ #
96
+ # validates_length_of :favorite_superhero, is: 6
97
+ # end
98
+ #
99
+ # # RSpec
100
+ # RSpec.describe User, type: :model do
101
+ # it { should validate_length_of(:favorite_superhero).is_equal_to(6) }
102
+ # end
103
+ #
104
+ # # Minitest (Shoulda)
105
+ # class UserTest < ActiveSupport::TestCase
106
+ # should validate_length_of(:favorite_superhero).is_equal_to(6)
107
+ # end
108
+ #
109
+ # ##### is_at_least + is_at_most
110
+ #
111
+ # Use `is_at_least` and `is_at_most` together to test usage of the `:in`
112
+ # option. This qualifies also works for associations.
113
+ #
114
+ # class User
115
+ # include ActiveModel::Model
116
+ # attr_accessor :password
117
+ #
118
+ # validates_length_of :password, in: 5..30
119
+ # end
120
+ #
121
+ # # RSpec
122
+ # RSpec.describe User, type: :model do
123
+ # it do
124
+ # should validate_length_of(:password).
125
+ # is_at_least(5).is_at_most(30)
126
+ # end
127
+ # end
128
+ #
129
+ # # Minitest (Shoulda)
130
+ # class UserTest < ActiveSupport::TestCase
131
+ # should validate_length_of(:password).
132
+ # is_at_least(5).is_at_most(30)
133
+ # end
134
+ #
135
+ # ##### with_message
136
+ #
137
+ # Use `with_message` if you are using a custom validation message.
138
+ #
139
+ # class User
140
+ # include ActiveModel::Model
141
+ # attr_accessor :password
142
+ #
143
+ # validates_length_of :password,
144
+ # minimum: 10,
145
+ # message: "Password isn't long enough"
146
+ # end
147
+ #
148
+ # # RSpec
149
+ # RSpec.describe User, type: :model do
150
+ # it do
151
+ # should validate_length_of(:password).
152
+ # is_at_least(10).
153
+ # with_message("Password isn't long enough")
154
+ # end
155
+ # end
156
+ #
157
+ # # Minitest (Shoulda)
158
+ # class UserTest < ActiveSupport::TestCase
159
+ # should validate_length_of(:password).
160
+ # is_at_least(10).
161
+ # with_message("Password isn't long enough")
162
+ # end
163
+ #
164
+ # ##### with_short_message
165
+ #
166
+ # Use `with_short_message` if you are using a custom "too short" message.
167
+ #
168
+ # class User
169
+ # include ActiveModel::Model
170
+ # attr_accessor :secret_key
171
+ #
172
+ # validates_length_of :secret_key,
173
+ # in: 15..100,
174
+ # too_short: 'Secret key must be more than 15 characters'
175
+ # end
176
+ #
177
+ # # RSpec
178
+ # RSpec.describe User, type: :model do
179
+ # it do
180
+ # should validate_length_of(:secret_key).
181
+ # is_at_least(15).
182
+ # with_short_message('Secret key must be more than 15 characters')
183
+ # end
184
+ # end
185
+ #
186
+ # # Minitest (Shoulda)
187
+ # class UserTest < ActiveSupport::TestCase
188
+ # should validate_length_of(:secret_key).
189
+ # is_at_least(15).
190
+ # with_short_message('Secret key must be more than 15 characters')
191
+ # end
192
+ #
193
+ # ##### with_long_message
194
+ #
195
+ # Use `with_long_message` if you are using a custom "too long" message.
196
+ #
197
+ # class User
198
+ # include ActiveModel::Model
199
+ # attr_accessor :secret_key
200
+ #
201
+ # validates_length_of :secret_key,
202
+ # in: 15..100,
203
+ # too_long: 'Secret key must be less than 100 characters'
204
+ # end
205
+ #
206
+ # # RSpec
207
+ # RSpec.describe User, type: :model do
208
+ # it do
209
+ # should validate_length_of(:secret_key).
210
+ # is_at_most(100).
211
+ # with_long_message('Secret key must be less than 100 characters')
212
+ # end
213
+ # end
214
+ #
215
+ # # Minitest (Shoulda)
216
+ # class UserTest < ActiveSupport::TestCase
217
+ # should validate_length_of(:secret_key).
218
+ # is_at_most(100).
219
+ # with_long_message('Secret key must be less than 100 characters')
220
+ # end
221
+ #
222
+ # ##### allow_nil
223
+ #
224
+ # Use `allow_nil` to assert that the attribute allows nil.
225
+ #
226
+ # class User
227
+ # include ActiveModel::Model
228
+ # attr_accessor :bio
229
+ #
230
+ # validates_length_of :bio, minimum: 15, allow_nil: true
231
+ # end
232
+ #
233
+ # # RSpec
234
+ # describe User do
235
+ # it { should validate_length_of(:bio).is_at_least(15).allow_nil }
236
+ # end
237
+ #
238
+ # # Minitest (Shoulda)
239
+ # class UserTest < ActiveSupport::TestCase
240
+ # should validate_length_of(:bio).is_at_least(15).allow_nil
241
+ # end
242
+ #
243
+ #
244
+ # ##### allow_blank
245
+ #
246
+ # Use `allow_blank` to assert that the attribute allows blank.
247
+ #
248
+ # class User
249
+ # include ActiveModel::Model
250
+ # attr_accessor :bio
251
+ #
252
+ # validates_length_of :bio, minimum: 15, allow_blank: true
253
+ # end
254
+ #
255
+ # # RSpec
256
+ # describe User do
257
+ # it { should validate_length_of(:bio).is_at_least(15).allow_blank }
258
+ # end
259
+ #
260
+ # # Minitest (Shoulda)
261
+ # class UserTest < ActiveSupport::TestCase
262
+ # should validate_length_of(:bio).is_at_least(15).allow_blank
263
+ # end
264
+ #
265
+ # ##### as_array
266
+ #
267
+ # Use `as_array` if you have an ActiveModel model and the attribute being validated
268
+ # is designed to store an array. (This is not necessary if you have an ActiveRecord
269
+ # model with an array column, as the matcher will detect this automatically.)
270
+ #
271
+ # class User
272
+ # include ActiveModel::Model
273
+ # attribute :arr, array: true
274
+ #
275
+ # validates_length_of :arr, minimum: 15
276
+ # end
277
+ #
278
+ # # RSpec
279
+ # describe User do
280
+ # it { should validate_length_of(:arr).as_array.is_at_least(15) }
281
+ # end
282
+ #
283
+ # # Minitest (Shoulda)
284
+ # class UserTest < ActiveSupport::TestCase
285
+ # should validate_length_of(:arr).as_array.is_at_least(15)
286
+ # end
287
+ #
288
+ # #### Multiple attributes
289
+ #
290
+ # You can pass multiple attributes to assert that each one has the
291
+ # validation. Any qualifier chained on the matcher is applied to
292
+ # every attribute uniformly.
293
+ #
294
+ # class User
295
+ # include ActiveModel::Model
296
+ # attr_accessor :first_name, :last_name
297
+ #
298
+ # validates_length_of :first_name, :last_name, minimum: 2
299
+ # end
300
+ #
301
+ # # RSpec
302
+ # RSpec.describe User, type: :model do
303
+ # it { should validate_length_of(:first_name, :last_name).is_at_least(2) }
304
+ # end
305
+ #
306
+ # # Minitest (Shoulda)
307
+ # class UserTest < ActiveSupport::TestCase
308
+ # should validate_length_of(:first_name, :last_name).is_at_least(2)
309
+ # end
310
+ #
311
+ # @return [ValidateLengthOfMatcher]
312
+ def validate_length_of(*attrs)
313
+ MatcherCollection.build(attrs) { |attr| ValidateLengthOfMatcher.new(attr) }
314
+ end
315
+
316
+ # @private
317
+ class ValidateLengthOfMatcher < ValidationMatcher
318
+ include Helpers
319
+
320
+ def initialize(attribute)
321
+ super(attribute)
322
+ @options = {}
323
+ @short_message = nil
324
+ @long_message = nil
325
+ end
326
+
327
+ def as_array
328
+ @options[:array] = true
329
+ self
330
+ end
331
+
332
+ def is_at_least(length)
333
+ @options[:minimum] = length
334
+ @short_message ||= :too_short
335
+ self
336
+ end
337
+
338
+ def is_at_most(length)
339
+ @options[:maximum] = length
340
+ @long_message ||= :too_long
341
+ self
342
+ end
343
+
344
+ def is_equal_to(length)
345
+ @options[:minimum] = length
346
+ @options[:maximum] = length
347
+ @short_message ||= :wrong_length
348
+ @long_message ||= :wrong_length
349
+ self
350
+ end
351
+
352
+ def with_message(message)
353
+ if message
354
+ @expects_custom_validation_message = true
355
+ @short_message = message
356
+ @long_message = message
357
+ end
358
+
359
+ self
360
+ end
361
+
362
+ def with_short_message(message)
363
+ if message
364
+ @expects_custom_validation_message = true
365
+ @short_message = message
366
+ end
367
+
368
+ self
369
+ end
370
+
371
+ def with_long_message(message)
372
+ if message
373
+ @expects_custom_validation_message = true
374
+ @long_message = message
375
+ end
376
+
377
+ self
378
+ end
379
+
380
+ def allow_nil
381
+ @options[:allow_nil] = true
382
+ self
383
+ end
384
+
385
+ def simple_description
386
+ description = "validate that the length of :#{@attribute}"
387
+
388
+ if @options.key?(:minimum) && @options.key?(:maximum)
389
+ if @options[:minimum] == @options[:maximum]
390
+ description << " is #{@options[:minimum]}"
391
+ else
392
+ description << " is between #{@options[:minimum]}"
393
+ description << " and #{@options[:maximum]}"
394
+ end
395
+ elsif @options.key?(:minimum)
396
+ description << " is at least #{@options[:minimum]}"
397
+ elsif @options.key?(:maximum)
398
+ description << " is at most #{@options[:maximum]}"
399
+ end
400
+
401
+ description
402
+ end
403
+
404
+ def matches?(subject)
405
+ super(subject)
406
+
407
+ lower_bound_matches? &&
408
+ upper_bound_matches? &&
409
+ allow_nil_matches? &&
410
+ allow_blank_matches?
411
+ end
412
+
413
+ def does_not_match?(subject)
414
+ super(subject)
415
+
416
+ lower_bound_does_not_match? ||
417
+ upper_bound_does_not_match? ||
418
+ allow_nil_does_not_match? ||
419
+ allow_blank_does_not_match?
420
+ end
421
+
422
+ private
423
+
424
+ def expects_to_allow_nil?
425
+ @options[:allow_nil]
426
+ end
427
+
428
+ def lower_bound_matches?
429
+ disallows_lower_length? && allows_minimum_length?
430
+ end
431
+
432
+ def lower_bound_does_not_match?
433
+ allows_lower_length? || disallows_minimum_length?
434
+ end
435
+
436
+ def upper_bound_matches?
437
+ disallows_higher_length? && allows_maximum_length?
438
+ end
439
+
440
+ def upper_bound_does_not_match?
441
+ allows_higher_length? || disallows_maximum_length?
442
+ end
443
+
444
+ def allows_lower_length?
445
+ @options.key?(:minimum) &&
446
+ @options[:minimum] > 0 &&
447
+ allows_length_of?(
448
+ @options[:minimum] - 1,
449
+ translated_short_message,
450
+ )
451
+ end
452
+
453
+ def disallows_lower_length?
454
+ !@options.key?(:minimum) ||
455
+ @options[:minimum] == 0 ||
456
+ (@options[:minimum] == 1 && expects_to_allow_blank?) ||
457
+ disallows_length_of?(
458
+ @options[:minimum] - 1,
459
+ translated_short_message,
460
+ )
461
+ end
462
+
463
+ def allows_higher_length?
464
+ @options.key?(:maximum) &&
465
+ allows_length_of?(
466
+ @options[:maximum] + 1,
467
+ translated_long_message,
468
+ )
469
+ end
470
+
471
+ def disallows_higher_length?
472
+ !@options.key?(:maximum) ||
473
+ disallows_length_of?(
474
+ @options[:maximum] + 1,
475
+ translated_long_message,
476
+ )
477
+ end
478
+
479
+ def allows_minimum_length?
480
+ !@options.key?(:minimum) ||
481
+ allows_length_of?(@options[:minimum], translated_short_message)
482
+ end
483
+
484
+ def disallows_minimum_length?
485
+ @options.key?(:minimum) &&
486
+ disallows_length_of?(@options[:minimum], translated_short_message)
487
+ end
488
+
489
+ def allows_maximum_length?
490
+ !@options.key?(:maximum) ||
491
+ allows_length_of?(@options[:maximum], translated_long_message)
492
+ end
493
+
494
+ def disallows_maximum_length?
495
+ @options.key?(:maximum) &&
496
+ disallows_length_of?(@options[:maximum], translated_long_message)
497
+ end
498
+
499
+ def allow_nil_matches?
500
+ !expects_to_allow_nil? || allows_value_of(nil)
501
+ end
502
+
503
+ def allow_nil_does_not_match?
504
+ expects_to_allow_nil? && disallows_value_of(nil)
505
+ end
506
+
507
+ def allows_length_of?(length, message)
508
+ allows_value_of(value_of_length(length), message)
509
+ end
510
+
511
+ def disallows_length_of?(length, message)
512
+ disallows_value_of(value_of_length(length), message)
513
+ end
514
+
515
+ def value_of_length(length)
516
+ if array_column?
517
+ ['x'] * length
518
+ elsif collection_association?
519
+ Array.new(length) { association_reflection.klass.new }
520
+ else
521
+ 'x' * length
522
+ end
523
+ end
524
+
525
+ def array_column?
526
+ @options[:array] || super
527
+ end
528
+
529
+ def collection_association?
530
+ association? && [:has_many, :has_and_belongs_to_many].include?(
531
+ association_reflection.macro,
532
+ )
533
+ end
534
+
535
+ def association?
536
+ association_reflection.present?
537
+ end
538
+
539
+ def association_reflection
540
+ model.try(:reflect_on_association, @attribute)
541
+ end
542
+
543
+ def translated_short_message
544
+ @_translated_short_message ||=
545
+ if @short_message.is_a?(Symbol)
546
+ default_error_message(
547
+ @short_message,
548
+ model_name: @subject.class.to_s.underscore,
549
+ instance: @subject,
550
+ attribute: @attribute,
551
+ count: @options[:minimum],
552
+ )
553
+ else
554
+ @short_message
555
+ end
556
+ end
557
+
558
+ def translated_long_message
559
+ @_translated_long_message ||=
560
+ if @long_message.is_a?(Symbol)
561
+ default_error_message(
562
+ @long_message,
563
+ model_name: @subject.class.to_s.underscore,
564
+ instance: @subject,
565
+ attribute: @attribute,
566
+ count: @options[:maximum],
567
+ )
568
+ else
569
+ @long_message
570
+ end
571
+ end
572
+ end
573
+ end
574
+ end
575
+ end