shoulda-matchers 3.0.1 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -62,9 +62,10 @@ module Shoulda
|
|
62
62
|
# password: 'password'
|
63
63
|
# }
|
64
64
|
# }
|
65
|
-
#
|
65
|
+
# matcher = permit(:first_name, :last_name, :email, :password).
|
66
66
|
# for(:create, params: params).
|
67
67
|
# on(:user)
|
68
|
+
# assert_accepts matcher, subject
|
68
69
|
# end
|
69
70
|
# end
|
70
71
|
#
|
@@ -132,9 +133,10 @@ module Shoulda
|
|
132
133
|
# password: 'password'
|
133
134
|
# }
|
134
135
|
# }
|
135
|
-
#
|
136
|
+
# matcher = permit(:first_name, :last_name, :email, :password).
|
136
137
|
# for(:update, params: params).
|
137
138
|
# on(:user)
|
139
|
+
# assert_accepts matcher, subject
|
138
140
|
# end
|
139
141
|
# end
|
140
142
|
#
|
@@ -189,9 +191,10 @@ module Shoulda
|
|
189
191
|
#
|
190
192
|
# should "(for PUT #toggle) restrict parameters on :user to :activated" do
|
191
193
|
# params = { id: 1, user: { activated: true } }
|
192
|
-
#
|
194
|
+
# matcher = permit(:activated).
|
193
195
|
# for(:toggle, params: params, verb: :put).
|
194
196
|
# on(:user)
|
197
|
+
# assert_accepts matcher, subject
|
195
198
|
# end
|
196
199
|
# end
|
197
200
|
#
|
@@ -305,18 +308,10 @@ module Shoulda
|
|
305
308
|
end
|
306
309
|
end
|
307
310
|
|
308
|
-
def permit_called?
|
309
|
-
actual_permitted_parameter_names.any?
|
310
|
-
end
|
311
|
-
|
312
311
|
def unpermitted_parameter_names
|
313
312
|
expected_permitted_parameter_names - actual_permitted_parameter_names
|
314
313
|
end
|
315
314
|
|
316
|
-
def verified_permitted_parameter_names
|
317
|
-
expected_permitted_parameter_names & actual_permitted_parameter_names
|
318
|
-
end
|
319
|
-
|
320
315
|
def ensure_action_and_verb_present!
|
321
316
|
if action.blank?
|
322
317
|
raise ActionNotDefinedError
|
@@ -1,8 +1,17 @@
|
|
1
1
|
require 'shoulda/matchers/active_model/helpers'
|
2
|
+
require 'shoulda/matchers/active_model/qualifiers'
|
2
3
|
require 'shoulda/matchers/active_model/validation_matcher'
|
4
|
+
require 'shoulda/matchers/active_model/validation_matcher/build_description'
|
3
5
|
require 'shoulda/matchers/active_model/validator'
|
4
|
-
require 'shoulda/matchers/active_model/strict_validator'
|
5
6
|
require 'shoulda/matchers/active_model/allow_value_matcher'
|
7
|
+
require 'shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error'
|
8
|
+
require 'shoulda/matchers/active_model/allow_value_matcher/attribute_does_not_exist_error'
|
9
|
+
require 'shoulda/matchers/active_model/allow_value_matcher/attribute_setter'
|
10
|
+
require 'shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator'
|
11
|
+
require 'shoulda/matchers/active_model/allow_value_matcher/attribute_setters'
|
12
|
+
require 'shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators'
|
13
|
+
require 'shoulda/matchers/active_model/allow_value_matcher/successful_check'
|
14
|
+
require 'shoulda/matchers/active_model/allow_value_matcher/successful_setting'
|
6
15
|
require 'shoulda/matchers/active_model/disallow_value_matcher'
|
7
16
|
require 'shoulda/matchers/active_model/validate_length_of_matcher'
|
8
17
|
require 'shoulda/matchers/active_model/validate_inclusion_of_matcher'
|
@@ -58,7 +58,7 @@ module Shoulda
|
|
58
58
|
# #### Caveats
|
59
59
|
#
|
60
60
|
# When using `allow_value` or any matchers that depend on it, you may
|
61
|
-
# encounter
|
61
|
+
# encounter an AttributeChangedValueError. This exception is raised if the
|
62
62
|
# matcher, in attempting to set a value on the attribute, detects that
|
63
63
|
# the value set is different from the value that the attribute returns
|
64
64
|
# upon reading it back.
|
@@ -86,7 +86,7 @@ module Shoulda
|
|
86
86
|
# it do
|
87
87
|
# foo = Foo.new
|
88
88
|
# foo.bar = "baz"
|
89
|
-
# # This will raise
|
89
|
+
# # This will raise an AttributeChangedValueError since `foo.bar` is now "123"
|
90
90
|
# expect(foo).not_to allow_value(nil).for(:bar)
|
91
91
|
# end
|
92
92
|
# end
|
@@ -108,7 +108,7 @@ module Shoulda
|
|
108
108
|
# describe Foo do
|
109
109
|
# it do
|
110
110
|
# foo = Foo.new
|
111
|
-
# # This will raise
|
111
|
+
# # This will raise an AttributeChangedValueError since `foo.bar` is now "123"
|
112
112
|
# expect(foo).not_to allow_value("abc123").for(:bar)
|
113
113
|
# end
|
114
114
|
# end
|
@@ -118,15 +118,13 @@ module Shoulda
|
|
118
118
|
#
|
119
119
|
# describe Foo do
|
120
120
|
# # Assume that `attr` is a string
|
121
|
-
# # This will raise
|
121
|
+
# # This will raise an AttributeChangedValueError since `attr` typecasts `[]` to `"[]"`
|
122
122
|
# it { should_not allow_value([]).for(:attr) }
|
123
123
|
# end
|
124
124
|
#
|
125
|
-
#
|
126
|
-
#
|
127
|
-
#
|
128
|
-
# get around this exception, you can add the
|
129
|
-
# `ignoring_interference_by_writer` qualifier like so:
|
125
|
+
# Fortunately, if you understand why this is happening, and wish to get
|
126
|
+
# around this exception, it is possible to do so. You can use the
|
127
|
+
# `ignoring_interference_by_writer` qualifier like so:
|
130
128
|
#
|
131
129
|
# it do
|
132
130
|
# should_not allow_value([]).
|
@@ -134,16 +132,11 @@ module Shoulda
|
|
134
132
|
# ignoring_interference_by_writer
|
135
133
|
# end
|
136
134
|
#
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
# incoming data before it's stored in your model, there's no need to
|
143
|
-
# ensure that sanitization places the model in a valid state, if such
|
144
|
-
# sanitization creates valid data. In terms of testing, the sanitization
|
145
|
-
# code should probably be tested, but not the effects of that
|
146
|
-
# sanitization on the validness of the model.
|
135
|
+
# Please note, however, that this qualifier won't magically cause your
|
136
|
+
# test to pass. It may just so happen that the final value that ends up
|
137
|
+
# being set causes the model to fail validation. In that case, you'll have
|
138
|
+
# to figure out what to do. You may need to write your own test, or
|
139
|
+
# perhaps even remove your test altogether.
|
147
140
|
#
|
148
141
|
# #### Qualifiers
|
149
142
|
#
|
@@ -274,9 +267,9 @@ module Shoulda
|
|
274
267
|
#
|
275
268
|
# ##### ignoring_interference_by_writer
|
276
269
|
#
|
277
|
-
# Use `ignoring_interference_by_writer`
|
278
|
-
#
|
279
|
-
# section above for more information.
|
270
|
+
# Use `ignoring_interference_by_writer` to bypass an
|
271
|
+
# AttributeChangedValueError that you have encountered. Please read the
|
272
|
+
# Caveats section above for more information.
|
280
273
|
#
|
281
274
|
# class Address < ActiveRecord::Base
|
282
275
|
# # Address has a zip_code field which is a string
|
@@ -286,16 +279,16 @@ module Shoulda
|
|
286
279
|
# describe Address do
|
287
280
|
# it do
|
288
281
|
# should_not allow_value([]).
|
289
|
-
#
|
290
|
-
#
|
282
|
+
# for(:zip_code).
|
283
|
+
# ignoring_interference_by_writer
|
291
284
|
# end
|
292
285
|
# end
|
293
286
|
#
|
294
287
|
# # Minitest (Shoulda)
|
295
288
|
# class AddressTest < ActiveSupport::TestCase
|
296
289
|
# should_not allow_value([]).
|
297
|
-
#
|
298
|
-
#
|
290
|
+
# for(:zip_code).
|
291
|
+
# ignoring_interference_by_writer
|
299
292
|
# end
|
300
293
|
#
|
301
294
|
# @return [AllowValueMatcher]
|
@@ -312,235 +305,314 @@ module Shoulda
|
|
312
305
|
|
313
306
|
# @private
|
314
307
|
class AllowValueMatcher
|
315
|
-
# @private
|
316
|
-
class CouldNotSetAttributeError < Shoulda::Matchers::Error
|
317
|
-
def self.create(model, attribute, expected_value, actual_value)
|
318
|
-
super(
|
319
|
-
model: model,
|
320
|
-
attribute: attribute,
|
321
|
-
expected_value: expected_value,
|
322
|
-
actual_value: actual_value
|
323
|
-
)
|
324
|
-
end
|
325
|
-
|
326
|
-
attr_accessor :model, :attribute, :expected_value, :actual_value
|
327
|
-
|
328
|
-
def message
|
329
|
-
Shoulda::Matchers.word_wrap <<-MESSAGE
|
330
|
-
The allow_value matcher attempted to set :#{attribute} on #{model.name} to
|
331
|
-
#{expected_value.inspect}, but when the attribute was read back, it
|
332
|
-
had stored #{actual_value.inspect} instead.
|
333
|
-
|
334
|
-
This creates a problem because it means that the model is behaving in a way that
|
335
|
-
is interfering with the test -- there's a mismatch between the test that was
|
336
|
-
written and test that was actually run.
|
337
|
-
|
338
|
-
There are a couple of reasons why this could be happening:
|
339
|
-
|
340
|
-
* The writer method for :#{attribute} has been overridden and contains custom
|
341
|
-
logic to prevent certain values from being set or change which values are
|
342
|
-
stored.
|
343
|
-
* ActiveRecord is typecasting the incoming value.
|
344
|
-
|
345
|
-
Regardless, the fact you're seeing this message usually indicates a larger
|
346
|
-
problem. Please file an issue on the GitHub repo for shoulda-matchers,
|
347
|
-
including details about your model and the test you've written, and we can point
|
348
|
-
you in the right direction:
|
349
|
-
|
350
|
-
https://github.com/thoughtbot/shoulda-matchers/issues
|
351
|
-
MESSAGE
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
308
|
include Helpers
|
356
|
-
|
357
|
-
|
358
|
-
|
309
|
+
include Qualifiers::IgnoringInterferenceByWriter
|
310
|
+
|
311
|
+
attr_reader(
|
312
|
+
:after_setting_value_callback,
|
313
|
+
:attribute_to_check_message_against,
|
314
|
+
:attribute_to_set,
|
315
|
+
:context,
|
316
|
+
:instance
|
317
|
+
)
|
318
|
+
|
319
|
+
attr_writer(
|
320
|
+
:attribute_changed_value_message,
|
321
|
+
:failure_message_preface,
|
322
|
+
:values_to_preset,
|
323
|
+
)
|
359
324
|
|
360
325
|
def initialize(*values)
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
@
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
326
|
+
super
|
327
|
+
@values_to_set = values
|
328
|
+
@options = {}
|
329
|
+
@after_setting_value_callback = -> {}
|
330
|
+
@expects_strict = false
|
331
|
+
@expects_custom_validation_message = false
|
332
|
+
@context = nil
|
333
|
+
@values_to_preset = {}
|
334
|
+
@failure_message_preface = nil
|
335
|
+
end
|
336
|
+
|
337
|
+
def for(attribute_name)
|
338
|
+
@attribute_to_set = attribute_name
|
339
|
+
@attribute_to_check_message_against = attribute_name
|
371
340
|
self
|
372
341
|
end
|
373
342
|
|
374
343
|
def on(context)
|
375
|
-
|
344
|
+
if context.present?
|
345
|
+
@context = context
|
346
|
+
end
|
347
|
+
|
376
348
|
self
|
377
349
|
end
|
378
350
|
|
379
|
-
def with_message(message,
|
380
|
-
|
381
|
-
|
351
|
+
def with_message(message, given_options = {})
|
352
|
+
if message.present?
|
353
|
+
@expects_custom_validation_message = true
|
354
|
+
options[:expected_message] = message
|
355
|
+
options[:expected_message_values] = given_options.fetch(:values, {})
|
382
356
|
|
383
|
-
|
384
|
-
|
357
|
+
if given_options.key?(:against)
|
358
|
+
@attribute_to_check_message_against = given_options[:against]
|
359
|
+
end
|
385
360
|
end
|
386
361
|
|
387
362
|
self
|
388
363
|
end
|
389
364
|
|
390
|
-
def
|
391
|
-
|
392
|
-
|
365
|
+
def expected_message
|
366
|
+
if options.key?(:expected_message)
|
367
|
+
if Symbol === options[:expected_message]
|
368
|
+
default_expected_message
|
369
|
+
else
|
370
|
+
options[:expected_message]
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
def expects_custom_validation_message?
|
376
|
+
@expects_custom_validation_message
|
393
377
|
end
|
394
378
|
|
395
|
-
def
|
396
|
-
@
|
379
|
+
def strict(expects_strict = true)
|
380
|
+
@expects_strict = expects_strict
|
397
381
|
self
|
398
382
|
end
|
399
383
|
|
384
|
+
def expects_strict?
|
385
|
+
@expects_strict
|
386
|
+
end
|
387
|
+
|
400
388
|
def _after_setting_value(&callback)
|
401
|
-
|
389
|
+
@after_setting_value_callback = callback
|
402
390
|
end
|
403
391
|
|
404
392
|
def matches?(instance)
|
405
|
-
|
406
|
-
|
393
|
+
@instance = instance
|
394
|
+
@result = run(:first_failing)
|
395
|
+
@result.nil?
|
407
396
|
end
|
408
397
|
|
409
398
|
def does_not_match?(instance)
|
410
|
-
|
411
|
-
|
399
|
+
@instance = instance
|
400
|
+
@result = run(:first_passing)
|
401
|
+
@result.nil?
|
412
402
|
end
|
413
403
|
|
414
404
|
def failure_message
|
415
|
-
|
405
|
+
attribute_setter = result.attribute_setter
|
406
|
+
|
407
|
+
if result.attribute_setter.unsuccessfully_checked?
|
408
|
+
message = attribute_setter.failure_message
|
409
|
+
else
|
410
|
+
validator = result.validator
|
411
|
+
message = failure_message_preface.call
|
412
|
+
message << ' valid, but it was invalid instead,'
|
413
|
+
|
414
|
+
if validator.captured_validation_exception?
|
415
|
+
message << ' raising a validation exception with the message '
|
416
|
+
message << validator.validation_exception_message.inspect
|
417
|
+
message << '.'
|
418
|
+
else
|
419
|
+
message << " producing these validation errors:\n\n"
|
420
|
+
message << validator.all_formatted_validation_error_messages
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
if include_attribute_changed_value_message?
|
425
|
+
message << "\n\n" + attribute_changed_value_message.call
|
426
|
+
end
|
427
|
+
|
428
|
+
Shoulda::Matchers.word_wrap(message)
|
416
429
|
end
|
417
430
|
|
418
431
|
def failure_message_when_negated
|
419
|
-
|
420
|
-
end
|
432
|
+
attribute_setter = result.attribute_setter
|
421
433
|
|
422
|
-
|
423
|
-
|
424
|
-
|
434
|
+
if attribute_setter.unsuccessfully_checked?
|
435
|
+
message = attribute_setter.failure_message
|
436
|
+
else
|
437
|
+
validator = result.validator
|
438
|
+
message = failure_message_preface.call + ' invalid'
|
439
|
+
|
440
|
+
if validator.type_of_message_matched?
|
441
|
+
if validator.has_messages?
|
442
|
+
message << ' and to'
|
443
|
+
|
444
|
+
if validator.captured_validation_exception?
|
445
|
+
message << ' raise a validation exception with message'
|
446
|
+
else
|
447
|
+
message << ' produce'
|
448
|
+
|
449
|
+
if expected_message.is_a?(Regexp)
|
450
|
+
message << ' a'
|
451
|
+
else
|
452
|
+
message << ' the'
|
453
|
+
end
|
454
|
+
|
455
|
+
message << ' validation error'
|
456
|
+
end
|
457
|
+
|
458
|
+
if expected_message.is_a?(Regexp)
|
459
|
+
message << ' matching '
|
460
|
+
message << Shoulda::Matchers::Util.inspect_value(
|
461
|
+
expected_message
|
462
|
+
)
|
463
|
+
else
|
464
|
+
message << " #{expected_message.inspect}"
|
465
|
+
end
|
466
|
+
|
467
|
+
unless validator.captured_validation_exception?
|
468
|
+
message << " on :#{attribute_to_check_message_against}"
|
469
|
+
end
|
470
|
+
|
471
|
+
message << '. The record was indeed invalid, but'
|
472
|
+
|
473
|
+
if validator.captured_validation_exception?
|
474
|
+
message << ' the exception message was '
|
475
|
+
message << validator.validation_exception_message.inspect
|
476
|
+
message << ' instead.'
|
477
|
+
else
|
478
|
+
message << " it produced these validation errors instead:\n\n"
|
479
|
+
message << validator.all_formatted_validation_error_messages
|
480
|
+
end
|
481
|
+
else
|
482
|
+
message << ', but it was valid instead.'
|
483
|
+
end
|
484
|
+
elsif validator.captured_validation_exception?
|
485
|
+
message << ' and to produce validation errors, but the record'
|
486
|
+
message << ' raised a validation exception instead.'
|
487
|
+
else
|
488
|
+
message << ' and to raise a validation exception, but the record'
|
489
|
+
message << ' produced validation errors instead.'
|
490
|
+
end
|
491
|
+
end
|
425
492
|
|
426
|
-
|
493
|
+
if include_attribute_changed_value_message?
|
494
|
+
message << "\n\n" + attribute_changed_value_message.call
|
495
|
+
end
|
427
496
|
|
428
|
-
|
429
|
-
|
430
|
-
:matched_error, :after_setting_value_callback, :validator
|
497
|
+
Shoulda::Matchers.word_wrap(message)
|
498
|
+
end
|
431
499
|
|
432
|
-
def
|
433
|
-
|
434
|
-
validator.record = instance
|
500
|
+
def description
|
501
|
+
ValidationMatcher::BuildDescription.call(self, simple_description)
|
435
502
|
end
|
436
503
|
|
437
|
-
def
|
438
|
-
|
439
|
-
validator.attribute = attribute
|
504
|
+
def simple_description
|
505
|
+
"allow :#{attribute_to_set} to be #{inspected_values_to_set}"
|
440
506
|
end
|
441
507
|
|
442
|
-
def
|
443
|
-
|
508
|
+
def model
|
509
|
+
instance.class
|
444
510
|
end
|
445
511
|
|
446
|
-
def
|
447
|
-
|
448
|
-
set_attribute(value)
|
449
|
-
!(errors_match? || any_range_error_occurred?)
|
512
|
+
def last_attribute_setter_used
|
513
|
+
result.attribute_setter
|
450
514
|
end
|
451
515
|
|
452
|
-
def
|
453
|
-
|
454
|
-
ensure_that_attribute_was_set!(value)
|
455
|
-
after_setting_value_callback.call
|
516
|
+
def last_value_set
|
517
|
+
last_attribute_setter_used.value_written
|
456
518
|
end
|
457
519
|
|
458
|
-
|
459
|
-
actual_value = instance.__send__(attribute_to_set)
|
520
|
+
protected
|
460
521
|
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
)
|
468
|
-
end
|
469
|
-
end
|
522
|
+
attr_reader(
|
523
|
+
:options,
|
524
|
+
:result,
|
525
|
+
:values_to_preset,
|
526
|
+
:values_to_set,
|
527
|
+
)
|
470
528
|
|
471
|
-
|
472
|
-
has_messages? && errors_for_attribute_match?
|
473
|
-
end
|
529
|
+
private
|
474
530
|
|
475
|
-
def
|
476
|
-
|
531
|
+
def run(strategy)
|
532
|
+
attribute_setters_for_values_to_preset.first_failing ||
|
533
|
+
attribute_setters_and_validators_for_values_to_set.public_send(strategy)
|
477
534
|
end
|
478
535
|
|
479
|
-
def
|
480
|
-
|
481
|
-
self.matched_error = errors_match_regexp? || errors_match_string?
|
482
|
-
else
|
483
|
-
errors_for_attribute.compact.any?
|
484
|
-
end
|
536
|
+
def failure_message_preface
|
537
|
+
@failure_message_preface || method(:default_failure_message_preface)
|
485
538
|
end
|
486
539
|
|
487
|
-
def
|
488
|
-
|
489
|
-
|
540
|
+
def default_failure_message_preface
|
541
|
+
''.tap do |preface|
|
542
|
+
if descriptions_for_preset_values.any?
|
543
|
+
preface << 'After setting '
|
544
|
+
preface << descriptions_for_preset_values.to_sentence
|
545
|
+
preface << ', then '
|
546
|
+
else
|
547
|
+
preface << 'After '
|
548
|
+
end
|
490
549
|
|
491
|
-
|
492
|
-
|
493
|
-
|
550
|
+
preface << 'setting '
|
551
|
+
preface << description_for_resulting_attribute_setter
|
552
|
+
|
553
|
+
unless preface.end_with?('--')
|
554
|
+
preface << ','
|
555
|
+
end
|
556
|
+
|
557
|
+
preface << " the matcher expected the #{model.name} to be"
|
494
558
|
end
|
495
559
|
end
|
496
560
|
|
497
|
-
def
|
498
|
-
|
499
|
-
|
500
|
-
end
|
561
|
+
def include_attribute_changed_value_message?
|
562
|
+
!ignore_interference_by_writer.never? &&
|
563
|
+
result.attribute_setter.attribute_changed_value?
|
501
564
|
end
|
502
565
|
|
503
|
-
def
|
504
|
-
|
566
|
+
def attribute_changed_value_message
|
567
|
+
@attribute_changed_value_message ||
|
568
|
+
method(:default_attribute_changed_value_message)
|
505
569
|
end
|
506
570
|
|
507
|
-
def
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
571
|
+
def default_attribute_changed_value_message
|
572
|
+
<<-MESSAGE.strip
|
573
|
+
As indicated in the message above, :#{result.attribute_setter.attribute_name}
|
574
|
+
seems to be changing certain values as they are set, and this could have
|
575
|
+
something to do with why this test is failing. If you've overridden the writer
|
576
|
+
method for this attribute, then you may need to change it to make this test
|
577
|
+
pass, or do something else entirely.
|
578
|
+
MESSAGE
|
579
|
+
end
|
512
580
|
|
513
|
-
|
581
|
+
def descriptions_for_preset_values
|
582
|
+
attribute_setters_for_values_to_preset.
|
583
|
+
map(&:attribute_setter_description)
|
514
584
|
end
|
515
585
|
|
516
|
-
def
|
517
|
-
|
586
|
+
def description_for_resulting_attribute_setter
|
587
|
+
result.attribute_setter_description
|
518
588
|
end
|
519
589
|
|
520
|
-
def
|
521
|
-
|
590
|
+
def attribute_setters_for_values_to_preset
|
591
|
+
@_attribute_setters_for_values_to_preset ||=
|
592
|
+
AttributeSetters.new(self, values_to_preset)
|
522
593
|
end
|
523
594
|
|
524
|
-
def
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
595
|
+
def attribute_setters_and_validators_for_values_to_set
|
596
|
+
@_attribute_setters_and_validators_for_values_to_set ||=
|
597
|
+
AttributeSettersAndValidators.new(
|
598
|
+
self,
|
599
|
+
values_to_set.map { |value| [attribute_to_set, value] }
|
600
|
+
)
|
530
601
|
end
|
531
602
|
|
532
|
-
def
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
options[:expected_message]
|
538
|
-
end
|
539
|
-
end
|
603
|
+
def inspected_values_to_set
|
604
|
+
Shoulda::Matchers::Util.inspect_values(values_to_set).to_sentence(
|
605
|
+
two_words_connector: " or ",
|
606
|
+
last_word_connector: ", or"
|
607
|
+
)
|
540
608
|
end
|
541
609
|
|
542
610
|
def default_expected_message
|
543
|
-
|
611
|
+
if expects_strict?
|
612
|
+
"#{human_attribute_name} #{default_attribute_message}"
|
613
|
+
else
|
614
|
+
default_attribute_message
|
615
|
+
end
|
544
616
|
end
|
545
617
|
|
546
618
|
def default_attribute_message
|
@@ -563,6 +635,12 @@ https://github.com/thoughtbot/shoulda-matchers/issues
|
|
563
635
|
def model_name
|
564
636
|
instance.class.to_s.underscore
|
565
637
|
end
|
638
|
+
|
639
|
+
def human_attribute_name
|
640
|
+
instance.class.human_attribute_name(
|
641
|
+
attribute_to_check_message_against
|
642
|
+
)
|
643
|
+
end
|
566
644
|
end
|
567
645
|
end
|
568
646
|
end
|