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
@@ -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
|