shoulda-matchers 3.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +3 -3
- data/CONTRIBUTING.md +60 -28
- data/Gemfile +1 -0
- data/Gemfile.lock +15 -12
- data/NEWS.md +111 -0
- data/README.md +94 -6
- data/Rakefile +10 -8
- data/custom_plan.rb +88 -0
- data/gemfiles/4.0.0.gemfile +1 -0
- data/gemfiles/4.0.0.gemfile.lock +21 -18
- data/gemfiles/4.0.1.gemfile +1 -0
- data/gemfiles/4.0.1.gemfile.lock +21 -18
- data/gemfiles/4.1.gemfile +1 -0
- data/gemfiles/4.1.gemfile.lock +21 -18
- data/gemfiles/4.2.gemfile +1 -0
- data/gemfiles/4.2.gemfile.lock +24 -21
- data/lib/shoulda/matchers/action_controller/permit_matcher.rb +6 -11
- data/lib/shoulda/matchers/active_model.rb +10 -1
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +258 -180
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +45 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error.rb +23 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +236 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +62 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +40 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +48 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/successful_check.rb +14 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/successful_setting.rb +14 -0
- data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +34 -14
- data/lib/shoulda/matchers/active_model/helpers.rb +9 -17
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +13 -6
- data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +13 -2
- data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +19 -35
- data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +13 -2
- data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +12 -2
- data/lib/shoulda/matchers/active_model/qualifiers.rb +12 -0
- data/lib/shoulda/matchers/active_model/qualifiers/ignore_interference_by_writer.rb +101 -0
- data/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +21 -0
- data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +30 -32
- data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +5 -8
- data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +22 -22
- data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +27 -16
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +58 -15
- data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +22 -12
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +165 -87
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +7 -9
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +111 -49
- data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +60 -0
- data/lib/shoulda/matchers/active_model/validator.rb +71 -52
- data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +19 -5
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +450 -124
- data/lib/shoulda/matchers/util.rb +43 -0
- data/lib/shoulda/matchers/util/word_wrap.rb +59 -31
- data/lib/shoulda/matchers/version.rb +1 -1
- data/script/update_gem_in_all_appraisals +1 -1
- data/script/update_gems_in_all_appraisals +1 -1
- data/spec/acceptance/multiple_libraries_integration_spec.rb +5 -2
- data/spec/acceptance/rails_integration_spec.rb +6 -2
- data/spec/spec_helper.rb +1 -3
- data/spec/support/acceptance/helpers/step_helpers.rb +4 -1
- data/spec/support/tests/current_bundle.rb +21 -7
- data/spec/support/unit/active_record/create_table.rb +54 -0
- data/spec/support/unit/attribute.rb +47 -0
- data/spec/support/unit/capture.rb +6 -0
- data/spec/support/unit/change_value.rb +111 -0
- data/spec/support/unit/create_model_arguments/basic.rb +135 -0
- data/spec/support/unit/create_model_arguments/has_many.rb +15 -0
- data/spec/support/unit/create_model_arguments/uniqueness_matcher.rb +74 -0
- data/spec/support/unit/helpers/active_record_versions.rb +1 -1
- data/spec/support/unit/helpers/class_builder.rb +61 -47
- data/spec/support/unit/helpers/database_helpers.rb +5 -3
- data/spec/support/unit/helpers/model_builder.rb +77 -97
- data/spec/support/unit/helpers/validation_matcher_scenario_helpers.rb +44 -0
- data/spec/support/unit/load_environment.rb +12 -0
- data/spec/support/unit/matchers/fail_with_message_including_matcher.rb +2 -2
- data/spec/support/unit/matchers/fail_with_message_matcher.rb +12 -1
- data/spec/support/unit/model_creation_strategies/active_model.rb +111 -0
- data/spec/support/unit/model_creation_strategies/active_record.rb +77 -0
- data/spec/support/unit/model_creators.rb +19 -0
- data/spec/support/unit/model_creators/active_model.rb +39 -0
- data/spec/support/unit/model_creators/active_record.rb +43 -0
- data/spec/support/unit/model_creators/active_record/has_and_belongs_to_many.rb +95 -0
- data/spec/support/unit/model_creators/active_record/has_many.rb +67 -0
- data/spec/support/unit/model_creators/active_record/uniqueness_matcher.rb +42 -0
- data/spec/support/unit/model_creators/basic.rb +97 -0
- data/spec/support/unit/rails_application.rb +1 -1
- data/spec/support/unit/record_validating_confirmation_builder.rb +3 -7
- data/spec/support/unit/shared_examples/ignoring_interference_by_writer.rb +79 -0
- data/spec/support/unit/validation_matcher_scenario.rb +62 -0
- data/spec/unit/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +4 -0
- data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +575 -140
- data/spec/unit/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +115 -15
- data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +42 -4
- data/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +92 -6
- data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +122 -10
- data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +306 -58
- data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +122 -3
- data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +805 -131
- data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +196 -29
- data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +82 -40
- data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +600 -101
- data/spec/unit/shoulda/matchers/util/word_wrap_spec.rb +88 -33
- data/spec/unit_spec_helper.rb +10 -22
- data/zeus.json +11 -0
- metadata +64 -23
- data/lib/shoulda/matchers/active_model/strict_validator.rb +0 -51
- data/spec/support/unit/shared_examples/numerical_type_submatcher.rb +0 -15
- data/spec/unit/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +0 -288
- data/spec/unit/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +0 -100
- data/spec/unit/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +0 -100
- data/spec/unit/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +0 -100
@@ -18,7 +18,6 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
18
18
|
matches_or_not.reverse!
|
19
19
|
to_or_not_to.reverse!
|
20
20
|
end
|
21
|
-
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
@@ -49,6 +48,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
49
48
|
def add_outside_value_to(values)
|
50
49
|
values + [values.last + 1]
|
51
50
|
end
|
51
|
+
|
52
|
+
def validation_matcher_scenario_args
|
53
|
+
super.deep_merge(column_type: :integer, default_value: 1)
|
54
|
+
end
|
52
55
|
end
|
53
56
|
|
54
57
|
context 'against an attribute with a specific column limit' do
|
@@ -95,6 +98,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
95
98
|
def add_outside_value_to(values)
|
96
99
|
values + [values.last + 1]
|
97
100
|
end
|
101
|
+
|
102
|
+
def validation_matcher_scenario_args
|
103
|
+
super.deep_merge(column_type: :float, default_value: 1.0)
|
104
|
+
end
|
98
105
|
end
|
99
106
|
|
100
107
|
context 'against a decimal attribute' do
|
@@ -119,11 +126,20 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
119
126
|
def add_outside_value_to(values)
|
120
127
|
values + [values.last + 1]
|
121
128
|
end
|
129
|
+
|
130
|
+
def validation_matcher_scenario_args
|
131
|
+
super.deep_merge(
|
132
|
+
column_type: :decimal,
|
133
|
+
default_value: BigDecimal.new('1.0')
|
134
|
+
)
|
135
|
+
end
|
122
136
|
end
|
123
137
|
|
124
138
|
context 'against a date attribute' do
|
125
139
|
today = Date.today
|
126
140
|
|
141
|
+
define_method(:today) { today }
|
142
|
+
|
127
143
|
it_behaves_like 'it supports in_array',
|
128
144
|
possible_values: (1..5).map { |n| today + n },
|
129
145
|
reserved_outside_value: described_class::ARBITRARY_OUTSIDE_DATE
|
@@ -141,11 +157,17 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
141
157
|
def add_outside_value_to(values)
|
142
158
|
values + [values.last + 1]
|
143
159
|
end
|
160
|
+
|
161
|
+
def validation_matcher_scenario_args
|
162
|
+
super.deep_merge(column_type: :date, default_value: today)
|
163
|
+
end
|
144
164
|
end
|
145
165
|
|
146
166
|
context 'against a datetime attribute' do
|
147
167
|
now = DateTime.now
|
148
168
|
|
169
|
+
define_method(:now) { now }
|
170
|
+
|
149
171
|
it_behaves_like 'it supports in_array',
|
150
172
|
possible_values: (1..5).map { |n| now + n },
|
151
173
|
reserved_outside_value: described_class::ARBITRARY_OUTSIDE_DATETIME
|
@@ -163,11 +185,17 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
163
185
|
def add_outside_value_to(values)
|
164
186
|
values + [values.last + 1]
|
165
187
|
end
|
188
|
+
|
189
|
+
def validation_matcher_scenario_args
|
190
|
+
super.deep_merge(column_type: :datetime, default_value: now)
|
191
|
+
end
|
166
192
|
end
|
167
193
|
|
168
194
|
context 'against a time attribute' do
|
169
195
|
now = Time.now
|
170
196
|
|
197
|
+
define_method(:now) { now }
|
198
|
+
|
171
199
|
it_behaves_like 'it supports in_array',
|
172
200
|
possible_values: (1..5).map { |n| now + n },
|
173
201
|
reserved_outside_value: described_class::ARBITRARY_OUTSIDE_TIME
|
@@ -185,6 +213,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
185
213
|
def add_outside_value_to(values)
|
186
214
|
values + [values.last + 1]
|
187
215
|
end
|
216
|
+
|
217
|
+
def validation_matcher_scenario_args
|
218
|
+
super.deep_merge(column_type: :time, default_value: now)
|
219
|
+
end
|
188
220
|
end
|
189
221
|
|
190
222
|
context 'against a string attribute' do
|
@@ -202,6 +234,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
202
234
|
def add_outside_value_to(values)
|
203
235
|
values + %w(qux)
|
204
236
|
end
|
237
|
+
|
238
|
+
def validation_matcher_scenario_args
|
239
|
+
super.deep_merge(column_type: :string)
|
240
|
+
end
|
205
241
|
end
|
206
242
|
end
|
207
243
|
|
@@ -210,7 +246,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
210
246
|
|
211
247
|
testing_values_of_option 'allow_nil' do |option_args, matches_or_not, to_or_not_to|
|
212
248
|
it "#{matches_or_not[0]} when the validation specifies allow_nil" do
|
213
|
-
builder = build_object_allowing(
|
249
|
+
builder = build_object_allowing(
|
250
|
+
valid_values,
|
251
|
+
validation_options: { allow_nil: true }
|
252
|
+
)
|
214
253
|
|
215
254
|
__send__("expect_#{to_or_not_to[0]}_match_on_values", builder, valid_values) do |matcher|
|
216
255
|
matcher.allow_nil(*option_args)
|
@@ -225,6 +264,36 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
225
264
|
end
|
226
265
|
end
|
227
266
|
end
|
267
|
+
|
268
|
+
=begin
|
269
|
+
it_supports(
|
270
|
+
'ignoring_interference_by_writer',
|
271
|
+
tests: {
|
272
|
+
accept_if_qualified_but_changing_value_does_not_interfere: {
|
273
|
+
changing_values_with: -> (value) { value || valid_values.first }
|
274
|
+
},
|
275
|
+
reject_if_qualified_but_changing_value_interferes: {
|
276
|
+
attribute_name: :attr,
|
277
|
+
changing_values_with: :never_falsy,
|
278
|
+
expected_message_includes: <<-MESSAGE.strip
|
279
|
+
As indicated in the message above, :attr seems to be changing certain
|
280
|
+
values as they are set, and this could have something to do with why
|
281
|
+
this test is failing. If you've overridden the writer method for this
|
282
|
+
attribute, then you may need to change it to make this test pass, or
|
283
|
+
do something else entirely.
|
284
|
+
MESSAGE
|
285
|
+
}
|
286
|
+
}
|
287
|
+
)
|
288
|
+
=end
|
289
|
+
|
290
|
+
def validation_matcher_scenario_args
|
291
|
+
super.deep_merge(validation_options: { allow_nil: true })
|
292
|
+
end
|
293
|
+
|
294
|
+
def configure_validation_matcher(matcher)
|
295
|
+
super(matcher).allow_nil
|
296
|
+
end
|
228
297
|
end
|
229
298
|
|
230
299
|
shared_examples_for 'it supports allow_blank' do |args|
|
@@ -232,7 +301,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
232
301
|
|
233
302
|
testing_values_of_option 'allow_blank' do |option_args, matches_or_not, to_or_not_to|
|
234
303
|
it "#{matches_or_not[0]} when the validation specifies allow_blank" do
|
235
|
-
builder = build_object_allowing(
|
304
|
+
builder = build_object_allowing(
|
305
|
+
valid_values,
|
306
|
+
validation_options: { allow_blank: true }
|
307
|
+
)
|
236
308
|
|
237
309
|
__send__("expect_#{to_or_not_to[0]}_match_on_values", builder, valid_values) do |matcher|
|
238
310
|
matcher.allow_blank(*option_args)
|
@@ -247,6 +319,38 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
247
319
|
end
|
248
320
|
end
|
249
321
|
end
|
322
|
+
|
323
|
+
=begin
|
324
|
+
it_supports(
|
325
|
+
'ignoring_interference_by_writer',
|
326
|
+
tests: {
|
327
|
+
accept_if_qualified_but_changing_value_does_not_interfere: {
|
328
|
+
changing_values_with: -> (value) {
|
329
|
+
value.presence || valid_values.first
|
330
|
+
}
|
331
|
+
},
|
332
|
+
reject_if_qualified_but_changing_value_interferes: {
|
333
|
+
attribute_name: :attr,
|
334
|
+
changing_values_with: :never_blank,
|
335
|
+
expected_message_includes: <<-MESSAGE.strip
|
336
|
+
As indicated in the message above, :attr seems to be changing certain
|
337
|
+
values as they are set, and this could have something to do with why
|
338
|
+
this test is failing. If you've overridden the writer method for this
|
339
|
+
attribute, then you may need to change it to make this test pass, or
|
340
|
+
do something else entirely.
|
341
|
+
MESSAGE
|
342
|
+
}
|
343
|
+
}
|
344
|
+
)
|
345
|
+
=end
|
346
|
+
|
347
|
+
def validation_matcher_scenario_args
|
348
|
+
super.deep_merge(validation_options: { allow_blank: true })
|
349
|
+
end
|
350
|
+
|
351
|
+
def configure_validation_matcher(matcher)
|
352
|
+
super(matcher).allow_blank
|
353
|
+
end
|
250
354
|
end
|
251
355
|
|
252
356
|
shared_examples_for 'it supports with_message' do |args|
|
@@ -254,7 +358,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
254
358
|
|
255
359
|
context 'given a string' do
|
256
360
|
it 'matches when validation uses given message' do
|
257
|
-
builder = build_object_allowing(
|
361
|
+
builder = build_object_allowing(
|
362
|
+
valid_values,
|
363
|
+
validation_options: { message: 'a message' }
|
364
|
+
)
|
258
365
|
|
259
366
|
expect_to_match_on_values(builder, valid_values) do |matcher|
|
260
367
|
matcher.with_message('a message')
|
@@ -270,7 +377,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
270
377
|
end
|
271
378
|
|
272
379
|
it 'does not match when validation uses a message but it is not same as given' do
|
273
|
-
builder = build_object_allowing(
|
380
|
+
builder = build_object_allowing(
|
381
|
+
valid_values,
|
382
|
+
validation_options: { message: 'a different message' }
|
383
|
+
)
|
274
384
|
|
275
385
|
expect_not_to_match_on_values(builder, valid_values) do |matcher|
|
276
386
|
matcher.with_message('a message')
|
@@ -280,7 +390,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
280
390
|
|
281
391
|
context 'given a regex' do
|
282
392
|
it 'matches when validation uses a message that matches the regex' do
|
283
|
-
builder = build_object_allowing(
|
393
|
+
builder = build_object_allowing(
|
394
|
+
valid_values,
|
395
|
+
validation_options: { message: 'this is a message' }
|
396
|
+
)
|
284
397
|
|
285
398
|
expect_to_match_on_values(builder, valid_values) do |matcher|
|
286
399
|
matcher.with_message(/a message/)
|
@@ -296,7 +409,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
296
409
|
end
|
297
410
|
|
298
411
|
it 'does not match when validation uses a message but it does not match regex' do
|
299
|
-
builder = build_object_allowing(
|
412
|
+
builder = build_object_allowing(
|
413
|
+
valid_values,
|
414
|
+
validation_options: { message: 'a different message' }
|
415
|
+
)
|
300
416
|
|
301
417
|
expect_not_to_match_on_values(builder, valid_values) do |matcher|
|
302
418
|
matcher.with_message(/a message/)
|
@@ -320,6 +436,8 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
320
436
|
zero = args[:zero]
|
321
437
|
reserved_outside_value = args.fetch(:reserved_outside_value)
|
322
438
|
|
439
|
+
define_method(:valid_values) { args.fetch(:possible_values) }
|
440
|
+
|
323
441
|
it 'does not match a record with no validations' do
|
324
442
|
builder = build_object
|
325
443
|
expect_not_to_match_on_values(builder, possible_values)
|
@@ -364,7 +482,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
364
482
|
context '+ strict' do
|
365
483
|
context 'when the validation specifies strict' do
|
366
484
|
it 'matches when the given values match the valid values' do
|
367
|
-
builder = build_object_allowing(
|
485
|
+
builder = build_object_allowing(
|
486
|
+
possible_values,
|
487
|
+
validation_options: { strict: true }
|
488
|
+
)
|
368
489
|
|
369
490
|
expect_to_match_on_values(builder, possible_values) do |matcher|
|
370
491
|
matcher.strict
|
@@ -372,7 +493,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
372
493
|
end
|
373
494
|
|
374
495
|
it 'does not match when the given values do not match the valid values' do
|
375
|
-
builder = build_object_allowing(
|
496
|
+
builder = build_object_allowing(
|
497
|
+
possible_values,
|
498
|
+
validation_options: { strict: true }
|
499
|
+
)
|
376
500
|
|
377
501
|
values = add_outside_value_to(possible_values)
|
378
502
|
expect_not_to_match_on_values(builder, values) do |matcher|
|
@@ -393,6 +517,23 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
393
517
|
end
|
394
518
|
end
|
395
519
|
|
520
|
+
it_supports(
|
521
|
+
'ignoring_interference_by_writer',
|
522
|
+
tests: {
|
523
|
+
reject_if_qualified_but_changing_value_interferes: {
|
524
|
+
attribute_name: :attr,
|
525
|
+
changing_values_with: :next_value,
|
526
|
+
expected_message_includes: <<-MESSAGE.strip
|
527
|
+
As indicated in the message above, :attr seems to be changing certain
|
528
|
+
values as they are set, and this could have something to do with why
|
529
|
+
this test is failing. If you've overridden the writer method for this
|
530
|
+
attribute, then you may need to change it to make this test pass, or
|
531
|
+
do something else entirely.
|
532
|
+
MESSAGE
|
533
|
+
}
|
534
|
+
}
|
535
|
+
)
|
536
|
+
|
396
537
|
def expect_to_match_on_values(builder, values, &block)
|
397
538
|
expect_to_match_in_array(builder, values, &block)
|
398
539
|
end
|
@@ -400,11 +541,21 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
400
541
|
def expect_not_to_match_on_values(builder, values, &block)
|
401
542
|
expect_not_to_match_in_array(builder, values, &block)
|
402
543
|
end
|
544
|
+
|
545
|
+
def validation_matcher_scenario_args
|
546
|
+
super.deep_merge(validation_options: { in: valid_values })
|
547
|
+
end
|
548
|
+
|
549
|
+
def configure_validation_matcher(matcher)
|
550
|
+
super(matcher).in_array(valid_values)
|
551
|
+
end
|
403
552
|
end
|
404
553
|
|
405
554
|
shared_examples_for 'it supports in_range' do |args|
|
406
555
|
possible_values = args[:possible_values]
|
407
556
|
|
557
|
+
define_method(:valid_values) { args.fetch(:possible_values) }
|
558
|
+
|
408
559
|
it 'does not match a record with no validations' do
|
409
560
|
builder = build_object
|
410
561
|
expect_not_to_match_on_values(builder, possible_values)
|
@@ -451,7 +602,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
451
602
|
context '+ strict' do
|
452
603
|
context 'when the validation specifies strict' do
|
453
604
|
it 'matches when the given range matches the range in the validation' do
|
454
|
-
builder = build_object_allowing(
|
605
|
+
builder = build_object_allowing(
|
606
|
+
possible_values,
|
607
|
+
validation_options: { strict: true }
|
608
|
+
)
|
455
609
|
|
456
610
|
expect_to_match_on_values(builder, possible_values) do |matcher|
|
457
611
|
matcher.strict
|
@@ -459,7 +613,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
459
613
|
end
|
460
614
|
|
461
615
|
it 'matches when the given range does not match the range in the validation' do
|
462
|
-
builder = build_object_allowing(
|
616
|
+
builder = build_object_allowing(
|
617
|
+
possible_values,
|
618
|
+
validation_options: { strict: true }
|
619
|
+
)
|
463
620
|
|
464
621
|
range = Range.new(possible_values.first, possible_values.last + 1)
|
465
622
|
expect_not_to_match_on_values(builder, range) do |matcher|
|
@@ -480,6 +637,23 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
480
637
|
end
|
481
638
|
end
|
482
639
|
|
640
|
+
it_supports(
|
641
|
+
'ignoring_interference_by_writer',
|
642
|
+
tests: {
|
643
|
+
reject_if_qualified_but_changing_value_interferes: {
|
644
|
+
attribute_name: :attr,
|
645
|
+
changing_values_with: :next_value,
|
646
|
+
expected_message_includes: <<-MESSAGE.strip
|
647
|
+
As indicated in the message above, :attr seems to be changing certain
|
648
|
+
values as they are set, and this could have something to do with why
|
649
|
+
this test is failing. If you've overridden the writer method for this
|
650
|
+
attribute, then you may need to change it to make this test pass, or
|
651
|
+
do something else entirely.
|
652
|
+
MESSAGE
|
653
|
+
}
|
654
|
+
}
|
655
|
+
)
|
656
|
+
|
483
657
|
def expect_to_match_on_values(builder, range, &block)
|
484
658
|
expect_to_match_in_range(builder, range, &block)
|
485
659
|
end
|
@@ -487,6 +661,14 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
487
661
|
def expect_not_to_match_on_values(builder, range, &block)
|
488
662
|
expect_not_to_match_in_range(builder, range, &block)
|
489
663
|
end
|
664
|
+
|
665
|
+
def validation_matcher_scenario_args
|
666
|
+
super.deep_merge(validation_options: { in: valid_values })
|
667
|
+
end
|
668
|
+
|
669
|
+
def configure_validation_matcher(matcher)
|
670
|
+
super(matcher).in_range(valid_values)
|
671
|
+
end
|
490
672
|
end
|
491
673
|
|
492
674
|
shared_context 'against a boolean attribute for true and false' do
|
@@ -537,6 +719,8 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
537
719
|
context 'against a timestamp column' do
|
538
720
|
now = DateTime.now
|
539
721
|
|
722
|
+
define_method(:now) { now }
|
723
|
+
|
540
724
|
it_behaves_like 'it supports in_array',
|
541
725
|
possible_values: (1..5).map { |n| now + n },
|
542
726
|
reserved_outside_value: described_class::ARBITRARY_OUTSIDE_DATETIME
|
@@ -554,6 +738,10 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
554
738
|
def add_outside_value_to(values)
|
555
739
|
values + [values.last + 1]
|
556
740
|
end
|
741
|
+
|
742
|
+
def validation_matcher_scenario_args
|
743
|
+
super.deep_merge(column_type: :timestamp, default_value: now)
|
744
|
+
end
|
557
745
|
end
|
558
746
|
|
559
747
|
context 'against a boolean attribute' do
|
@@ -615,27 +803,12 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
615
803
|
end
|
616
804
|
end
|
617
805
|
|
618
|
-
def
|
619
|
-
attribute_name
|
620
|
-
|
621
|
-
column_options = {
|
622
|
-
type: column_type,
|
623
|
-
options: options.fetch(:column_options, {})
|
624
|
-
}
|
625
|
-
validation_options = options[:validation_options]
|
626
|
-
custom_validation = options[:custom_validation]
|
627
|
-
|
628
|
-
model = define_model :example, attribute_name => column_options
|
629
|
-
customize_model_class(
|
630
|
-
model,
|
631
|
-
attribute_name,
|
632
|
-
validation_options,
|
633
|
-
custom_validation
|
634
|
-
)
|
635
|
-
|
636
|
-
object = model.new
|
806
|
+
def define_simple_model(attribute_name: :attr, column_options: {}, &block)
|
807
|
+
define_model('Example', attribute_name => column_options, &block)
|
808
|
+
end
|
637
809
|
|
638
|
-
|
810
|
+
def validation_matcher_scenario_args
|
811
|
+
super.deep_merge(model_creator: :active_record)
|
639
812
|
end
|
640
813
|
end
|
641
814
|
|
@@ -658,24 +831,56 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
658
831
|
end
|
659
832
|
end
|
660
833
|
|
661
|
-
def
|
662
|
-
attribute_name
|
663
|
-
|
664
|
-
custom_validation = options[:custom_validation]
|
665
|
-
value = options[:value]
|
834
|
+
def define_simple_model(attribute_name: :attr, column_options: {}, &block)
|
835
|
+
define_active_model_class('Example', accessors: [attribute_name], &block)
|
836
|
+
end
|
666
837
|
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
validation_options,
|
672
|
-
custom_validation
|
673
|
-
)
|
838
|
+
def validation_matcher_scenario_args
|
839
|
+
super.deep_merge(model_creator: :active_model)
|
840
|
+
end
|
841
|
+
end
|
674
842
|
|
675
|
-
|
676
|
-
|
843
|
+
describe '#description' do
|
844
|
+
context 'given an array of values' do
|
845
|
+
context 'when there is one value' do
|
846
|
+
it 'returns the correct string' do
|
847
|
+
matcher = validate_inclusion_of(:attr).in_array([true])
|
677
848
|
|
678
|
-
|
849
|
+
expect(matcher.description).to eq(
|
850
|
+
'validate that :attr is ‹true›'
|
851
|
+
)
|
852
|
+
end
|
853
|
+
end
|
854
|
+
|
855
|
+
context 'when there are two values' do
|
856
|
+
it 'returns the correct string' do
|
857
|
+
matcher = validate_inclusion_of(:attr).in_array([true, 'dog'])
|
858
|
+
|
859
|
+
expect(matcher.description).to eq(
|
860
|
+
'validate that :attr is either ‹true› or ‹"dog"›'
|
861
|
+
)
|
862
|
+
end
|
863
|
+
end
|
864
|
+
|
865
|
+
context 'when there are three or more values' do
|
866
|
+
it 'returns the correct string' do
|
867
|
+
matcher = validate_inclusion_of(:attr).in_array([true, 'dog', 'cat'])
|
868
|
+
|
869
|
+
expect(matcher.description).to eq(
|
870
|
+
'validate that :attr is either ‹true›, ‹"dog"›, or ‹"cat"›'
|
871
|
+
)
|
872
|
+
end
|
873
|
+
end
|
874
|
+
end
|
875
|
+
|
876
|
+
context 'given a range of values' do
|
877
|
+
it 'returns the correct string' do
|
878
|
+
matcher = validate_inclusion_of(:attr).in_range(1..10)
|
879
|
+
|
880
|
+
expect(matcher.description).to eq(
|
881
|
+
'validate that :attr lies inside the range ‹1› to ‹10›'
|
882
|
+
)
|
883
|
+
end
|
679
884
|
end
|
680
885
|
end
|
681
886
|
|
@@ -683,24 +888,63 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
683
888
|
@_object_builder_class ||= Struct.new(:attribute, :object, :validation_options)
|
684
889
|
end
|
685
890
|
|
686
|
-
def
|
687
|
-
|
891
|
+
def build_object_with_generic_attribute(
|
892
|
+
attribute_name: :attr,
|
893
|
+
validation_options: nil,
|
894
|
+
value: nil,
|
895
|
+
**other_options
|
896
|
+
)
|
897
|
+
model = define_model_validating_inclusion(
|
898
|
+
attribute_name: attribute_name,
|
899
|
+
validation_options: validation_options,
|
900
|
+
**other_options
|
901
|
+
)
|
902
|
+
|
903
|
+
object = model.new
|
904
|
+
object.__send__("#{attribute_name}=", value)
|
905
|
+
|
906
|
+
object_builder_class.new(attribute_name, object, validation_options)
|
907
|
+
end
|
908
|
+
|
909
|
+
def define_model_validating_inclusion(
|
910
|
+
attribute_name: :attr,
|
911
|
+
column_type: :string,
|
912
|
+
column_options: {},
|
913
|
+
validation_options: nil,
|
914
|
+
custom_validation: nil,
|
915
|
+
customize_model_class: -> (object) { }
|
916
|
+
)
|
917
|
+
column_options = { type: column_type, options: column_options }
|
918
|
+
|
919
|
+
define_simple_model(
|
920
|
+
attribute_name: attribute_name,
|
921
|
+
column_options: column_options
|
922
|
+
) do |model|
|
688
923
|
if validation_options
|
689
|
-
validates_inclusion_of
|
924
|
+
model.validates_inclusion_of(attribute_name, validation_options)
|
690
925
|
end
|
691
926
|
|
692
927
|
if custom_validation
|
693
|
-
|
694
|
-
|
928
|
+
model.class_eval do
|
929
|
+
define_method :custom_validation do
|
930
|
+
custom_validation.call(self, attribute_name)
|
931
|
+
end
|
932
|
+
|
933
|
+
validate :custom_validation
|
695
934
|
end
|
935
|
+
end
|
696
936
|
|
697
|
-
|
937
|
+
if customize_model_class
|
938
|
+
model.instance_eval(&customize_model_class)
|
698
939
|
end
|
699
940
|
end
|
700
941
|
end
|
701
942
|
|
702
|
-
def build_object_allowing(values,
|
703
|
-
build_object(
|
943
|
+
def build_object_allowing(values, validation_options: {}, **other_options)
|
944
|
+
build_object(
|
945
|
+
validation_options: validation_options.merge(in: values),
|
946
|
+
**other_options
|
947
|
+
)
|
704
948
|
end
|
705
949
|
|
706
950
|
def expect_to_match(builder)
|
@@ -747,13 +991,13 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
747
991
|
low_message = 'too low'
|
748
992
|
high_message = 'too high'
|
749
993
|
|
750
|
-
builder = build_object custom_validation: ->(attribute) {
|
751
|
-
value =
|
994
|
+
builder = build_object custom_validation: -> (object, attribute) {
|
995
|
+
value = object.public_send(attribute)
|
752
996
|
|
753
997
|
if value < low_value
|
754
|
-
errors.add(attribute, low_message)
|
998
|
+
object.errors.add(attribute, low_message)
|
755
999
|
elsif value > high_value
|
756
|
-
errors.add(attribute, high_message)
|
1000
|
+
object.errors.add(attribute, high_message)
|
757
1001
|
end
|
758
1002
|
}
|
759
1003
|
|
@@ -764,4 +1008,8 @@ describe Shoulda::Matchers::ActiveModel::ValidateInclusionOfMatcher, type: :mode
|
|
764
1008
|
with_high_message(high_message)
|
765
1009
|
end
|
766
1010
|
end
|
1011
|
+
|
1012
|
+
def validation_matcher_scenario_args
|
1013
|
+
super.deep_merge(matcher_name: :validate_inclusion_of)
|
1014
|
+
end
|
767
1015
|
end
|