shoulda-matchers 3.1.0 → 5.2.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 +5 -5
- data/{MIT-LICENSE → LICENSE} +1 -1
- data/README.md +407 -232
- data/docs/errors/NonCaseSwappableValueError.md +2 -2
- data/lib/shoulda/matchers/action_controller/callback_matcher.rb +7 -80
- data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +4 -3
- data/lib/shoulda/matchers/action_controller/flash_store.rb +2 -4
- data/lib/shoulda/matchers/action_controller/permit_matcher.rb +36 -30
- data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +8 -10
- data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +7 -9
- data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +18 -15
- data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +3 -2
- data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +3 -3
- data/lib/shoulda/matchers/action_controller/route_matcher.rb +88 -29
- data/lib/shoulda/matchers/action_controller/route_params.rb +2 -2
- data/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +4 -4
- data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +3 -3
- data/lib/shoulda/matchers/action_controller/set_session_or_flash_matcher.rb +19 -13
- data/lib/shoulda/matchers/action_controller.rb +2 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_changed_value_error.rb +1 -1
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter.rb +5 -9
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setter_and_validator.rb +2 -2
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters.rb +1 -1
- data/lib/shoulda/matchers/active_model/allow_value_matcher/attribute_setters_and_validators.rb +1 -1
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +42 -39
- data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +52 -26
- data/lib/shoulda/matchers/active_model/helpers.rb +2 -2
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +32 -30
- data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +2 -1
- data/lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb +26 -0
- data/lib/shoulda/matchers/active_model/qualifiers/allow_nil.rb +26 -0
- data/lib/shoulda/matchers/active_model/qualifiers/ignoring_interference_by_writer.rb +1 -1
- data/lib/shoulda/matchers/active_model/qualifiers.rb +2 -0
- data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +30 -6
- data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +8 -3
- data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +31 -16
- data/lib/shoulda/matchers/active_model/validate_exclusion_of_matcher.rb +52 -16
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +137 -84
- data/lib/shoulda/matchers/active_model/validate_length_of_matcher.rb +159 -46
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +130 -66
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +251 -24
- data/lib/shoulda/matchers/active_model/validation_matcher/build_description.rb +12 -9
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +38 -6
- data/lib/shoulda/matchers/active_model/validation_message_finder.rb +2 -4
- data/lib/shoulda/matchers/active_model/validator.rb +4 -9
- data/lib/shoulda/matchers/active_model.rb +3 -5
- data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +10 -7
- data/lib/shoulda/matchers/active_record/association_matcher.rb +386 -111
- data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +5 -2
- data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +4 -4
- data/lib/shoulda/matchers/active_record/association_matchers/inverse_of_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +11 -6
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +14 -15
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +30 -8
- data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +34 -11
- data/lib/shoulda/matchers/active_record/association_matchers/optional_matcher.rb +69 -0
- data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_record/association_matchers/required_matcher.rb +74 -0
- data/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +3 -2
- data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +7 -5
- data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +458 -42
- data/lib/shoulda/matchers/active_record/have_attached_matcher.rb +185 -0
- data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +63 -23
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +164 -48
- data/lib/shoulda/matchers/active_record/have_implicit_order_column.rb +106 -0
- data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +13 -11
- data/lib/shoulda/matchers/active_record/have_rich_text_matcher.rb +83 -0
- data/lib/shoulda/matchers/active_record/have_secure_token_matcher.rb +132 -0
- data/lib/shoulda/matchers/active_record/serialize_matcher.rb +18 -18
- data/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb +1 -3
- data/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +0 -2
- data/lib/shoulda/matchers/active_record/uniqueness.rb +1 -1
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +430 -200
- data/lib/shoulda/matchers/active_record.rb +28 -20
- data/lib/shoulda/matchers/configuration.rb +12 -1
- data/lib/shoulda/matchers/doublespeak/double.rb +1 -1
- data/lib/shoulda/matchers/doublespeak/double_collection.rb +3 -3
- data/lib/shoulda/matchers/doublespeak/double_implementation_registry.rb +8 -5
- data/lib/shoulda/matchers/doublespeak/object_double.rb +6 -2
- data/lib/shoulda/matchers/doublespeak/stub_implementation.rb +1 -5
- data/lib/shoulda/matchers/doublespeak/world.rb +2 -2
- data/lib/shoulda/matchers/doublespeak.rb +2 -1
- data/lib/shoulda/matchers/error.rb +1 -1
- data/lib/shoulda/matchers/independent/delegate_method_matcher.rb +109 -29
- data/lib/shoulda/matchers/independent.rb +2 -2
- data/lib/shoulda/matchers/integrations/configuration.rb +8 -4
- data/lib/shoulda/matchers/integrations/libraries/action_controller.rb +1 -1
- data/lib/shoulda/matchers/integrations/libraries/rails.rb +2 -2
- data/lib/shoulda/matchers/integrations/test_frameworks/active_support_test_case.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/minitest_4.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/minitest_5.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/missing_test_framework.rb +1 -1
- data/lib/shoulda/matchers/integrations/test_frameworks/test_unit.rb +1 -1
- data/lib/shoulda/matchers/rails_shim.rb +172 -51
- data/lib/shoulda/matchers/routing.rb +2 -2
- data/lib/shoulda/matchers/util/word_wrap.rb +17 -12
- data/lib/shoulda/matchers/util.rb +39 -5
- data/lib/shoulda/matchers/version.rb +1 -1
- data/lib/shoulda/matchers/warn.rb +4 -3
- data/shoulda-matchers.gemspec +33 -15
- metadata +31 -338
- data/.gitignore +0 -12
- data/.hound.yml +0 -3
- data/.hound_config/ruby.yml +0 -12
- data/.travis.yml +0 -19
- data/.yardopts +0 -10
- data/Appraisals +0 -73
- data/CONTRIBUTING.md +0 -101
- data/Gemfile +0 -15
- data/Gemfile.lock +0 -70
- data/NEWS.md +0 -986
- data/Rakefile +0 -39
- data/custom_plan.rb +0 -88
- data/doc_config/gh-pages/index.html.erb +0 -9
- data/doc_config/yard/setup.rb +0 -22
- data/doc_config/yard/templates/default/fulldoc/html/css/bootstrap.css +0 -5967
- data/doc_config/yard/templates/default/fulldoc/html/css/full_list.css +0 -12
- data/doc_config/yard/templates/default/fulldoc/html/css/global.css +0 -62
- data/doc_config/yard/templates/default/fulldoc/html/css/solarized.css +0 -69
- data/doc_config/yard/templates/default/fulldoc/html/css/style.css +0 -312
- data/doc_config/yard/templates/default/fulldoc/html/full_list.erb +0 -32
- data/doc_config/yard/templates/default/fulldoc/html/full_list_class.erb +0 -1
- data/doc_config/yard/templates/default/fulldoc/html/full_list_method.erb +0 -8
- data/doc_config/yard/templates/default/fulldoc/html/js/app.js +0 -298
- data/doc_config/yard/templates/default/fulldoc/html/js/full_list.js +0 -1
- data/doc_config/yard/templates/default/fulldoc/html/js/jquery.stickyheaders.js +0 -289
- data/doc_config/yard/templates/default/fulldoc/html/js/underscore.min.js +0 -6
- data/doc_config/yard/templates/default/fulldoc/html/setup.rb +0 -8
- data/doc_config/yard/templates/default/layout/html/breadcrumb.erb +0 -14
- data/doc_config/yard/templates/default/layout/html/fonts.erb +0 -1
- data/doc_config/yard/templates/default/layout/html/footer.erb +0 -6
- data/doc_config/yard/templates/default/layout/html/layout.erb +0 -23
- data/doc_config/yard/templates/default/layout/html/search.erb +0 -13
- data/doc_config/yard/templates/default/layout/html/setup.rb +0 -40
- data/doc_config/yard/templates/default/method_details/html/source.erb +0 -10
- data/doc_config/yard/templates/default/module/html/box_info.erb +0 -31
- data/gemfiles/4.0.0.gemfile +0 -38
- data/gemfiles/4.0.0.gemfile.lock +0 -223
- data/gemfiles/4.0.1.gemfile +0 -38
- data/gemfiles/4.0.1.gemfile.lock +0 -225
- data/gemfiles/4.1.gemfile +0 -38
- data/gemfiles/4.1.gemfile.lock +0 -220
- data/gemfiles/4.2.gemfile +0 -38
- data/gemfiles/4.2.gemfile.lock +0 -243
- data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +0 -159
- data/lib/shoulda/matchers/independent/delegate_method_matcher/stubbed_target.rb +0 -37
- data/script/SUPPORTED_VERSIONS +0 -1
- data/script/install_gems_in_all_appraisals +0 -14
- data/script/run_all_tests +0 -14
- data/script/update_gem_in_all_appraisals +0 -15
- data/script/update_gems_in_all_appraisals +0 -14
- data/spec/acceptance/active_model_integration_spec.rb +0 -23
- data/spec/acceptance/independent_matchers_spec.rb +0 -125
- data/spec/acceptance/multiple_libraries_integration_spec.rb +0 -55
- data/spec/acceptance/rails_integration_spec.rb +0 -156
- data/spec/acceptance_spec_helper.rb +0 -23
- data/spec/doublespeak_spec_helper.rb +0 -2
- data/spec/report_warnings.rb +0 -7
- data/spec/spec_helper.rb +0 -21
- data/spec/support/acceptance/adds_shoulda_matchers_to_project.rb +0 -133
- data/spec/support/acceptance/helpers/active_model_helpers.rb +0 -11
- data/spec/support/acceptance/helpers/array_helpers.rb +0 -13
- data/spec/support/acceptance/helpers/base_helpers.rb +0 -19
- data/spec/support/acceptance/helpers/command_helpers.rb +0 -55
- data/spec/support/acceptance/helpers/file_helpers.rb +0 -19
- data/spec/support/acceptance/helpers/gem_helpers.rb +0 -31
- data/spec/support/acceptance/helpers/minitest_helpers.rb +0 -11
- data/spec/support/acceptance/helpers/n_unit_helpers.rb +0 -25
- data/spec/support/acceptance/helpers/pluralization_helpers.rb +0 -13
- data/spec/support/acceptance/helpers/rails_version_helpers.rb +0 -11
- data/spec/support/acceptance/helpers/rspec_helpers.rb +0 -24
- data/spec/support/acceptance/helpers/ruby_version_helpers.rb +0 -9
- data/spec/support/acceptance/helpers/step_helpers.rb +0 -127
- data/spec/support/acceptance/helpers.rb +0 -31
- data/spec/support/acceptance/matchers/have_output.rb +0 -31
- data/spec/support/acceptance/matchers/indicate_number_of_tests_was_run_matcher.rb +0 -55
- data/spec/support/acceptance/matchers/indicate_that_tests_were_run_matcher.rb +0 -103
- data/spec/support/tests/bundle.rb +0 -94
- data/spec/support/tests/command_runner.rb +0 -230
- data/spec/support/tests/current_bundle.rb +0 -61
- data/spec/support/tests/database.rb +0 -28
- data/spec/support/tests/database_adapters/postgresql.rb +0 -25
- data/spec/support/tests/database_adapters/sqlite3.rb +0 -26
- data/spec/support/tests/database_configuration.rb +0 -33
- data/spec/support/tests/database_configuration_registry.rb +0 -28
- data/spec/support/tests/filesystem.rb +0 -100
- data/spec/support/tests/version.rb +0 -45
- data/spec/support/unit/active_record/create_table.rb +0 -54
- data/spec/support/unit/attribute.rb +0 -47
- data/spec/support/unit/capture.rb +0 -40
- data/spec/support/unit/change_value.rb +0 -111
- data/spec/support/unit/create_model_arguments/basic.rb +0 -135
- data/spec/support/unit/create_model_arguments/has_many.rb +0 -15
- data/spec/support/unit/create_model_arguments/uniqueness_matcher.rb +0 -74
- data/spec/support/unit/helpers/active_model_helpers.rb +0 -27
- data/spec/support/unit/helpers/active_model_versions.rb +0 -28
- data/spec/support/unit/helpers/active_record_versions.rb +0 -24
- data/spec/support/unit/helpers/active_resource_builder.rb +0 -27
- data/spec/support/unit/helpers/allow_value_matcher_helpers.rb +0 -15
- data/spec/support/unit/helpers/class_builder.rb +0 -90
- data/spec/support/unit/helpers/column_type_helpers.rb +0 -26
- data/spec/support/unit/helpers/confirmation_matcher_helpers.rb +0 -17
- data/spec/support/unit/helpers/controller_builder.rb +0 -63
- data/spec/support/unit/helpers/database_helpers.rb +0 -20
- data/spec/support/unit/helpers/i18n_faker.rb +0 -15
- data/spec/support/unit/helpers/mailer_builder.rb +0 -12
- data/spec/support/unit/helpers/model_builder.rb +0 -114
- data/spec/support/unit/helpers/rails_versions.rb +0 -28
- data/spec/support/unit/helpers/validation_matcher_scenario_helpers.rb +0 -44
- data/spec/support/unit/i18n.rb +0 -7
- data/spec/support/unit/load_environment.rb +0 -12
- data/spec/support/unit/matchers/deprecate.rb +0 -60
- data/spec/support/unit/matchers/fail_with_message_including_matcher.rb +0 -51
- data/spec/support/unit/matchers/fail_with_message_matcher.rb +0 -62
- data/spec/support/unit/matchers/print_warning_including.rb +0 -59
- data/spec/support/unit/model_creation_strategies/active_model.rb +0 -111
- data/spec/support/unit/model_creation_strategies/active_record.rb +0 -77
- data/spec/support/unit/model_creators/active_model.rb +0 -39
- data/spec/support/unit/model_creators/active_record/has_and_belongs_to_many.rb +0 -95
- data/spec/support/unit/model_creators/active_record/has_many.rb +0 -67
- data/spec/support/unit/model_creators/active_record/uniqueness_matcher.rb +0 -42
- data/spec/support/unit/model_creators/active_record.rb +0 -43
- data/spec/support/unit/model_creators/basic.rb +0 -97
- data/spec/support/unit/model_creators.rb +0 -19
- data/spec/support/unit/rails_application.rb +0 -126
- data/spec/support/unit/record_builder_with_i18n_validation_message.rb +0 -69
- data/spec/support/unit/record_validating_confirmation_builder.rb +0 -51
- data/spec/support/unit/record_with_different_error_attribute_builder.rb +0 -92
- data/spec/support/unit/shared_examples/ignoring_interference_by_writer.rb +0 -79
- data/spec/support/unit/shared_examples/numerical_submatcher.rb +0 -17
- data/spec/support/unit/shared_examples/set_session_or_flash.rb +0 -360
- data/spec/support/unit/validation_matcher_scenario.rb +0 -62
- data/spec/unit/shoulda/matchers/action_controller/callback_matcher_spec.rb +0 -82
- data/spec/unit/shoulda/matchers/action_controller/filter_param_matcher_spec.rb +0 -28
- data/spec/unit/shoulda/matchers/action_controller/permit_matcher_spec.rb +0 -592
- data/spec/unit/shoulda/matchers/action_controller/redirect_to_matcher_spec.rb +0 -42
- data/spec/unit/shoulda/matchers/action_controller/render_template_matcher_spec.rb +0 -76
- data/spec/unit/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +0 -62
- data/spec/unit/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +0 -90
- data/spec/unit/shoulda/matchers/action_controller/respond_with_matcher_spec.rb +0 -31
- data/spec/unit/shoulda/matchers/action_controller/route_matcher_spec.rb +0 -330
- data/spec/unit/shoulda/matchers/action_controller/route_params_spec.rb +0 -30
- data/spec/unit/shoulda/matchers/action_controller/set_flash_matcher_spec.rb +0 -67
- data/spec/unit/shoulda/matchers/action_controller/set_session_matcher_spec.rb +0 -17
- data/spec/unit/shoulda/matchers/action_controller/set_session_or_flash_matcher_spec.rb +0 -562
- data/spec/unit/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +0 -115
- data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +0 -823
- data/spec/unit/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +0 -86
- data/spec/unit/shoulda/matchers/active_model/have_secure_password_matcher_spec.rb +0 -20
- data/spec/unit/shoulda/matchers/active_model/helpers_spec.rb +0 -162
- data/spec/unit/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +0 -266
- data/spec/unit/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +0 -91
- data/spec/unit/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +0 -149
- data/spec/unit/shoulda/matchers/active_model/validate_exclusion_of_matcher_spec.rb +0 -207
- data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +0 -1015
- data/spec/unit/shoulda/matchers/active_model/validate_length_of_matcher_spec.rb +0 -288
- data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +0 -1837
- data/spec/unit/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +0 -380
- data/spec/unit/shoulda/matchers/active_record/accept_nested_attributes_for_matcher_spec.rb +0 -107
- data/spec/unit/shoulda/matchers/active_record/association_matcher_spec.rb +0 -1242
- data/spec/unit/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +0 -251
- data/spec/unit/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +0 -168
- data/spec/unit/shoulda/matchers/active_record/have_db_column_matcher_spec.rb +0 -111
- data/spec/unit/shoulda/matchers/active_record/have_db_index_matcher_spec.rb +0 -85
- data/spec/unit/shoulda/matchers/active_record/have_readonly_attributes_matcher_spec.rb +0 -41
- data/spec/unit/shoulda/matchers/active_record/serialize_matcher_spec.rb +0 -86
- data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +0 -1418
- data/spec/unit/shoulda/matchers/doublespeak/double_collection_spec.rb +0 -190
- data/spec/unit/shoulda/matchers/doublespeak/double_implementation_registry_spec.rb +0 -21
- data/spec/unit/shoulda/matchers/doublespeak/double_spec.rb +0 -271
- data/spec/unit/shoulda/matchers/doublespeak/object_double_spec.rb +0 -77
- data/spec/unit/shoulda/matchers/doublespeak/proxy_implementation_spec.rb +0 -72
- data/spec/unit/shoulda/matchers/doublespeak/stub_implementation_spec.rb +0 -101
- data/spec/unit/shoulda/matchers/doublespeak/world_spec.rb +0 -80
- data/spec/unit/shoulda/matchers/doublespeak_spec.rb +0 -27
- data/spec/unit/shoulda/matchers/independent/delegate_method_matcher/stubbed_target_spec.rb +0 -43
- data/spec/unit/shoulda/matchers/independent/delegate_method_matcher_spec.rb +0 -517
- data/spec/unit/shoulda/matchers/routing/route_matcher_spec.rb +0 -242
- data/spec/unit/shoulda/matchers/util/word_wrap_spec.rb +0 -252
- data/spec/unit_spec_helper.rb +0 -46
- data/spec/warnings_spy/filesystem.rb +0 -45
- data/spec/warnings_spy/partitioner.rb +0 -36
- data/spec/warnings_spy/reader.rb +0 -53
- data/spec/warnings_spy/reporter.rb +0 -88
- data/spec/warnings_spy.rb +0 -64
- data/tasks/documentation.rb +0 -199
- data/zeus.json +0 -11
@@ -12,7 +12,7 @@ module Shoulda
|
|
12
12
|
# end
|
13
13
|
#
|
14
14
|
# # RSpec
|
15
|
-
# describe Robot do
|
15
|
+
# RSpec.describe Robot, type: :model do
|
16
16
|
# it { should validate_presence_of(:arms) }
|
17
17
|
# end
|
18
18
|
#
|
@@ -36,7 +36,7 @@ module Shoulda
|
|
36
36
|
# validates_presence_of :password
|
37
37
|
# end
|
38
38
|
#
|
39
|
-
# describe User do
|
39
|
+
# RSpec.describe User, type: :model do
|
40
40
|
# subject { User.new(password: '123456') }
|
41
41
|
#
|
42
42
|
# it { should validate_presence_of(:password) }
|
@@ -57,6 +57,48 @@ module Shoulda
|
|
57
57
|
#
|
58
58
|
# #### Qualifiers
|
59
59
|
#
|
60
|
+
# ##### allow_nil
|
61
|
+
#
|
62
|
+
# Use `allow_nil` if your model has an optional attribute.
|
63
|
+
#
|
64
|
+
# class Robot
|
65
|
+
# include ActiveModel::Model
|
66
|
+
# attr_accessor :nickname
|
67
|
+
#
|
68
|
+
# validates_presence_of :nickname, allow_nil: true
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# # RSpec
|
72
|
+
# RSpec.describe Robot, type: :model do
|
73
|
+
# it { should validate_presence_of(:nickname).allow_nil }
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# # Minitest (Shoulda)
|
77
|
+
# class RobotTest < ActiveSupport::TestCase
|
78
|
+
# should validate_presence_of(:nickname).allow_nil
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# #### allow_blank
|
82
|
+
#
|
83
|
+
# Use `allow_blank` to assert that the attribute allows blank.
|
84
|
+
#
|
85
|
+
# class Robot
|
86
|
+
# include ActiveModel::Model
|
87
|
+
# attr_accessor :nickname
|
88
|
+
#
|
89
|
+
# validates_presence_of :nickname, allow_blank: true
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# # RSpec
|
93
|
+
# RSpec.describe Robot, type: :model do
|
94
|
+
# it { should validate_presence_of(:nickname).allow_blank }
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# # Minitest (Shoulda)
|
98
|
+
# class RobotTest < ActiveSupport::TestCase
|
99
|
+
# should validate_presence_of(:nickname).allow_blank
|
100
|
+
# end
|
101
|
+
#
|
60
102
|
# ##### on
|
61
103
|
#
|
62
104
|
# Use `on` if your validation applies only under a certain context.
|
@@ -69,7 +111,7 @@ module Shoulda
|
|
69
111
|
# end
|
70
112
|
#
|
71
113
|
# # RSpec
|
72
|
-
# describe Robot do
|
114
|
+
# RSpec.describe Robot, type: :model do
|
73
115
|
# it { should validate_presence_of(:arms).on(:create) }
|
74
116
|
# end
|
75
117
|
#
|
@@ -90,7 +132,7 @@ module Shoulda
|
|
90
132
|
# end
|
91
133
|
#
|
92
134
|
# # RSpec
|
93
|
-
# describe Robot do
|
135
|
+
# RSpec.describe Robot, type: :model do
|
94
136
|
# it do
|
95
137
|
# should validate_presence_of(:legs).
|
96
138
|
# with_message('Robot has no legs')
|
@@ -111,6 +153,9 @@ module Shoulda
|
|
111
153
|
|
112
154
|
# @private
|
113
155
|
class ValidatePresenceOfMatcher < ValidationMatcher
|
156
|
+
include Qualifiers::AllowNil
|
157
|
+
include Qualifiers::AllowBlank
|
158
|
+
|
114
159
|
def initialize(attribute)
|
115
160
|
super
|
116
161
|
@expected_message = :blank
|
@@ -119,11 +164,41 @@ module Shoulda
|
|
119
164
|
def matches?(subject)
|
120
165
|
super(subject)
|
121
166
|
|
167
|
+
possibly_ignore_interference_by_writer
|
168
|
+
|
169
|
+
if secure_password_being_validated? &&
|
170
|
+
Shoulda::Matchers::RailsShim.active_model_lt_7?
|
171
|
+
ignore_interference_by_writer.default_to(when: :blank?)
|
172
|
+
|
173
|
+
disallowed_values.all? do |value|
|
174
|
+
disallows_and_double_checks_value_of!(value)
|
175
|
+
end
|
176
|
+
else
|
177
|
+
(!expects_to_allow_nil? || allows_value_of(nil)) &&
|
178
|
+
(!expects_to_allow_blank? || allows_value_of('')) &&
|
179
|
+
disallowed_values.all? do |value|
|
180
|
+
disallows_original_or_typecast_value?(value)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def does_not_match?(subject)
|
186
|
+
super(subject)
|
187
|
+
|
188
|
+
possibly_ignore_interference_by_writer
|
189
|
+
|
122
190
|
if secure_password_being_validated?
|
123
191
|
ignore_interference_by_writer.default_to(when: :blank?)
|
124
|
-
|
192
|
+
|
193
|
+
disallowed_values.any? do |value|
|
194
|
+
allows_and_double_checks_value_of!(value)
|
195
|
+
end
|
125
196
|
else
|
126
|
-
|
197
|
+
(expects_to_allow_nil? && disallows_value_of(nil)) ||
|
198
|
+
(expects_to_allow_blank? && disallows_value_of('')) ||
|
199
|
+
disallowed_values.any? do |value|
|
200
|
+
allows_original_or_typecast_value?(value)
|
201
|
+
end
|
127
202
|
end
|
128
203
|
end
|
129
204
|
|
@@ -131,43 +206,195 @@ module Shoulda
|
|
131
206
|
"validate that :#{@attribute} cannot be empty/falsy"
|
132
207
|
end
|
133
208
|
|
209
|
+
def failure_message
|
210
|
+
message = super
|
211
|
+
|
212
|
+
if should_add_footnote_about_belongs_to?
|
213
|
+
message << "\n\n"
|
214
|
+
message << Shoulda::Matchers.word_wrap(<<-MESSAGE.strip, indent: 2)
|
215
|
+
You're getting this error because #{reason_for_existing_presence_validation}.
|
216
|
+
*This* presence validation doesn't use "can't be blank", the usual validation
|
217
|
+
message, but "must exist" instead.
|
218
|
+
|
219
|
+
With that said, did you know that the `belong_to` matcher can test this
|
220
|
+
validation for you? Instead of using `validate_presence_of`, try
|
221
|
+
#{suggestions_for_belongs_to}
|
222
|
+
MESSAGE
|
223
|
+
end
|
224
|
+
|
225
|
+
message
|
226
|
+
end
|
227
|
+
|
134
228
|
private
|
135
229
|
|
136
230
|
def secure_password_being_validated?
|
137
|
-
|
138
|
-
|
139
|
-
@attribute == :password
|
231
|
+
Shoulda::Matchers::RailsShim.digestible_attributes_in(@subject).
|
232
|
+
include?(@attribute)
|
140
233
|
end
|
141
234
|
|
142
|
-
def
|
143
|
-
|
235
|
+
def possibly_ignore_interference_by_writer
|
236
|
+
if secure_password_being_validated? && RailsShim.active_model_lt_7?
|
237
|
+
ignore_interference_by_writer.default_to(when: :blank?)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def allows_and_double_checks_value_of!(value)
|
242
|
+
allows_value_of(value, @expected_message)
|
243
|
+
rescue ActiveModel::AllowValueMatcher::AttributeChangedValueError
|
244
|
+
raise ActiveModel::CouldNotSetPasswordError.create(model)
|
245
|
+
end
|
246
|
+
|
247
|
+
def allows_original_or_typecast_value?(value)
|
248
|
+
allows_value_of(value, @expected_message)
|
249
|
+
end
|
250
|
+
|
251
|
+
def disallows_and_double_checks_value_of!(value)
|
252
|
+
disallows_value_of(value, @expected_message)
|
144
253
|
rescue ActiveModel::AllowValueMatcher::AttributeChangedValueError
|
145
|
-
raise ActiveModel::CouldNotSetPasswordError.create(
|
254
|
+
raise ActiveModel::CouldNotSetPasswordError.create(model)
|
255
|
+
end
|
256
|
+
|
257
|
+
def disallows_original_or_typecast_value?(value)
|
258
|
+
disallows_value_of(value, @expected_message)
|
259
|
+
end
|
260
|
+
|
261
|
+
def disallowed_values
|
262
|
+
if collection_association?
|
263
|
+
[Array.new]
|
264
|
+
elsif attachment?
|
265
|
+
[nil]
|
266
|
+
else
|
267
|
+
values = []
|
268
|
+
|
269
|
+
if attribute_accepts_string_values? && !expects_to_allow_blank?
|
270
|
+
values << ''
|
271
|
+
end
|
272
|
+
|
273
|
+
if !expects_to_allow_nil? && !expects_to_allow_blank?
|
274
|
+
values << nil
|
275
|
+
end
|
276
|
+
|
277
|
+
values
|
278
|
+
end
|
146
279
|
end
|
147
280
|
|
148
|
-
def
|
149
|
-
|
281
|
+
def should_add_footnote_about_belongs_to?
|
282
|
+
belongs_to_association_being_validated? &&
|
283
|
+
presence_validation_exists_on_attribute?
|
150
284
|
end
|
151
285
|
|
152
|
-
def
|
153
|
-
if
|
154
|
-
|
286
|
+
def reason_for_existing_presence_validation
|
287
|
+
if belongs_to_association_configured_to_be_required?
|
288
|
+
"you've instructed your `belongs_to` association to add a "\
|
289
|
+
'presence validation to the attribute'
|
155
290
|
else
|
156
|
-
|
291
|
+
# assume ::ActiveRecord::Base.belongs_to_required_by_default == true
|
292
|
+
'ActiveRecord is configured to add a presence validation to all '\
|
293
|
+
'`belongs_to` associations, and this includes yours'
|
157
294
|
end
|
158
295
|
end
|
159
296
|
|
160
|
-
def
|
161
|
-
if
|
162
|
-
|
297
|
+
def suggestions_for_belongs_to
|
298
|
+
if belongs_to_association_configured_to_be_required?
|
299
|
+
<<~MESSAGE
|
300
|
+
one of the following instead, depending on your use case:
|
301
|
+
|
302
|
+
#{example_of_belongs_to(with: [:optional, false])}
|
303
|
+
#{example_of_belongs_to(with: [:required, true])}
|
304
|
+
MESSAGE
|
163
305
|
else
|
306
|
+
<<~MESSAGE
|
307
|
+
the following instead:
|
308
|
+
|
309
|
+
#{example_of_belongs_to}
|
310
|
+
MESSAGE
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def example_of_belongs_to(with: nil)
|
315
|
+
initial_call = "should belong_to(:#{association_name})"
|
316
|
+
inside =
|
317
|
+
if with
|
318
|
+
"#{initial_call}.#{with.first}(#{with.second})"
|
319
|
+
else
|
320
|
+
initial_call
|
321
|
+
end
|
322
|
+
|
323
|
+
if Shoulda::Matchers.integrations.test_frameworks.any?(&:n_unit?)
|
324
|
+
inside
|
325
|
+
else
|
326
|
+
"it { #{inside} }"
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
def belongs_to_association_configured_to_be_required?
|
331
|
+
association_options[:optional] == false ||
|
332
|
+
association_options[:required] == true
|
333
|
+
end
|
334
|
+
|
335
|
+
def belongs_to_association_being_validated?
|
336
|
+
association? && association_reflection.macro == :belongs_to
|
337
|
+
end
|
338
|
+
|
339
|
+
def attribute_accepts_string_values?
|
340
|
+
if association?
|
164
341
|
false
|
342
|
+
elsif attribute_serialization_coder.respond_to?(:object_class)
|
343
|
+
attribute_serialization_coder.object_class == String
|
344
|
+
else
|
345
|
+
RailsShim.supports_full_attributes_api?(model) &&
|
346
|
+
attribute_type.try(:type) == :string
|
165
347
|
end
|
166
348
|
end
|
167
349
|
|
168
|
-
def
|
169
|
-
|
170
|
-
|
350
|
+
def association?
|
351
|
+
association_reflection.present?
|
352
|
+
end
|
353
|
+
|
354
|
+
def collection_association?
|
355
|
+
association? && [:has_many, :has_and_belongs_to_many].include?(
|
356
|
+
association_reflection.macro,
|
357
|
+
)
|
358
|
+
end
|
359
|
+
|
360
|
+
def attachment?
|
361
|
+
model_has_associations?(
|
362
|
+
["#{@attribute}_attachment", "#{@attribute}_attachments"],
|
363
|
+
)
|
364
|
+
end
|
365
|
+
|
366
|
+
def association_name
|
367
|
+
association_reflection.name
|
368
|
+
end
|
369
|
+
|
370
|
+
def association_options
|
371
|
+
association_reflection&.options
|
372
|
+
end
|
373
|
+
|
374
|
+
def association_reflection
|
375
|
+
model.try(:reflect_on_association, @attribute)
|
376
|
+
end
|
377
|
+
|
378
|
+
def model_has_associations?(associations)
|
379
|
+
associations.any? do |association|
|
380
|
+
!!model.try(:reflect_on_association, association)
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
def attribute_serialization_coder
|
385
|
+
RailsShim.attribute_serialization_coder_for(model, @attribute)
|
386
|
+
end
|
387
|
+
|
388
|
+
def attribute_type
|
389
|
+
RailsShim.attribute_type_for(model, @attribute)
|
390
|
+
end
|
391
|
+
|
392
|
+
def presence_validation_exists_on_attribute?
|
393
|
+
model._validators.include?(@attribute)
|
394
|
+
end
|
395
|
+
|
396
|
+
def model
|
397
|
+
@subject.class
|
171
398
|
end
|
172
399
|
end
|
173
400
|
end
|
@@ -15,11 +15,10 @@ module Shoulda
|
|
15
15
|
|
16
16
|
def call
|
17
17
|
if description_clauses_for_qualifiers.any?
|
18
|
-
main_description
|
19
|
-
|
20
|
-
description_clauses_for_qualifiers.to_sentence
|
18
|
+
"#{main_description}#{clause_for_allow_blank_or_nil},"\
|
19
|
+
" #{description_clauses_for_qualifiers.to_sentence}"
|
21
20
|
else
|
22
|
-
main_description
|
21
|
+
main_description + clause_for_allow_blank_or_nil
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
@@ -29,14 +28,18 @@ module Shoulda
|
|
29
28
|
|
30
29
|
private
|
31
30
|
|
32
|
-
def
|
33
|
-
description_clauses = []
|
34
|
-
|
31
|
+
def clause_for_allow_blank_or_nil
|
35
32
|
if matcher.try(:expects_to_allow_blank?)
|
36
|
-
|
33
|
+
' as long as it is not blank'
|
37
34
|
elsif matcher.try(:expects_to_allow_nil?)
|
38
|
-
|
35
|
+
' as long as it is not nil'
|
36
|
+
else
|
37
|
+
''
|
39
38
|
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def description_clauses_for_qualifiers
|
42
|
+
description_clauses = []
|
40
43
|
|
41
44
|
if matcher.try(:expects_strict?)
|
42
45
|
description_clauses << 'raising a validation exception'
|
@@ -24,6 +24,11 @@ module Shoulda
|
|
24
24
|
self
|
25
25
|
end
|
26
26
|
|
27
|
+
def allow_blank
|
28
|
+
options[:allow_blank] = true
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
27
32
|
def strict
|
28
33
|
@expects_strict = true
|
29
34
|
self
|
@@ -51,13 +56,18 @@ module Shoulda
|
|
51
56
|
false
|
52
57
|
end
|
53
58
|
|
59
|
+
def does_not_match?(subject)
|
60
|
+
@subject = subject
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
54
64
|
def failure_message
|
55
65
|
overall_failure_message.dup.tap do |message|
|
56
66
|
if failure_reason.present?
|
57
67
|
message << "\n"
|
58
68
|
message << Shoulda::Matchers.word_wrap(
|
59
69
|
failure_reason,
|
60
|
-
indent: 2
|
70
|
+
indent: 2,
|
61
71
|
)
|
62
72
|
end
|
63
73
|
end
|
@@ -65,11 +75,11 @@ module Shoulda
|
|
65
75
|
|
66
76
|
def failure_message_when_negated
|
67
77
|
overall_failure_message_when_negated.dup.tap do |message|
|
68
|
-
if
|
78
|
+
if failure_reason.present?
|
69
79
|
message << "\n"
|
70
80
|
message << Shoulda::Matchers.word_wrap(
|
71
|
-
|
72
|
-
indent: 2
|
81
|
+
failure_reason,
|
82
|
+
indent: 2,
|
73
83
|
)
|
74
84
|
end
|
75
85
|
end
|
@@ -111,17 +121,31 @@ module Shoulda
|
|
111
121
|
)
|
112
122
|
end
|
113
123
|
|
124
|
+
def allow_blank_matches?
|
125
|
+
!expects_to_allow_blank? ||
|
126
|
+
blank_values.all? { |value| allows_value_of(value) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def allow_blank_does_not_match?
|
130
|
+
expects_to_allow_blank? &&
|
131
|
+
blank_values.all? { |value| disallows_value_of(value) }
|
132
|
+
end
|
133
|
+
|
114
134
|
private
|
115
135
|
|
136
|
+
attr_reader :options
|
137
|
+
|
116
138
|
def overall_failure_message
|
117
139
|
Shoulda::Matchers.word_wrap(
|
118
|
-
"#{model.name}
|
140
|
+
"Expected #{model.name} to #{description}, but this could not be "\
|
141
|
+
'proved.',
|
119
142
|
)
|
120
143
|
end
|
121
144
|
|
122
145
|
def overall_failure_message_when_negated
|
123
146
|
Shoulda::Matchers.word_wrap(
|
124
|
-
"Expected #{model.name} not to #{description}, but
|
147
|
+
"Expected #{model.name} not to #{description}, but this could "\
|
148
|
+
'not be proved.',
|
125
149
|
)
|
126
150
|
end
|
127
151
|
|
@@ -154,6 +178,14 @@ module Shoulda
|
|
154
178
|
@last_submatcher_run = matcher
|
155
179
|
matcher.matches?(subject)
|
156
180
|
end
|
181
|
+
|
182
|
+
def expects_to_allow_blank?
|
183
|
+
options[:allow_blank]
|
184
|
+
end
|
185
|
+
|
186
|
+
def blank_values
|
187
|
+
['', ' ', "\n", "\r", "\t", "\f"]
|
188
|
+
end
|
157
189
|
end
|
158
190
|
end
|
159
191
|
end
|
@@ -5,7 +5,7 @@ module Shoulda
|
|
5
5
|
class ValidationMessageFinder
|
6
6
|
include Helpers
|
7
7
|
|
8
|
-
def initialize(instance, attribute, context=nil)
|
8
|
+
def initialize(instance, attribute, context = nil)
|
9
9
|
@instance = instance
|
10
10
|
@attribute = attribute
|
11
11
|
@context = context
|
@@ -50,7 +50,7 @@ module Shoulda
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def validated_instance
|
53
|
-
@
|
53
|
+
@_validated_instance ||= validate_instance
|
54
54
|
end
|
55
55
|
|
56
56
|
def validate_instance
|
@@ -58,8 +58,6 @@ module Shoulda
|
|
58
58
|
@instance
|
59
59
|
end
|
60
60
|
end
|
61
|
-
|
62
61
|
end
|
63
62
|
end
|
64
63
|
end
|
65
|
-
|
@@ -98,24 +98,19 @@ module Shoulda
|
|
98
98
|
|
99
99
|
all_validation_errors = record.errors.dup
|
100
100
|
|
101
|
-
validation_error_messages =
|
102
|
-
if record.errors.respond_to?(:[])
|
103
|
-
record.errors[attribute]
|
104
|
-
else
|
105
|
-
record.errors.on(attribute)
|
106
|
-
end
|
101
|
+
validation_error_messages = record.errors[attribute]
|
107
102
|
|
108
103
|
{
|
109
104
|
all_validation_errors: all_validation_errors,
|
110
105
|
validation_error_messages: validation_error_messages,
|
111
|
-
validation_exception_message: nil
|
106
|
+
validation_exception_message: nil,
|
112
107
|
}
|
113
|
-
rescue ::ActiveModel::StrictValidationFailed =>
|
108
|
+
rescue ::ActiveModel::StrictValidationFailed => e
|
114
109
|
@captured_validation_exception = true
|
115
110
|
{
|
116
111
|
all_validation_errors: nil,
|
117
112
|
validation_error_messages: [],
|
118
|
-
validation_exception_message:
|
113
|
+
validation_exception_message: e.message,
|
119
114
|
}
|
120
115
|
end
|
121
116
|
end
|
@@ -26,15 +26,13 @@ require 'shoulda/matchers/active_model/numericality_matchers/comparison_matcher'
|
|
26
26
|
require 'shoulda/matchers/active_model/numericality_matchers/odd_number_matcher'
|
27
27
|
require 'shoulda/matchers/active_model/numericality_matchers/even_number_matcher'
|
28
28
|
require 'shoulda/matchers/active_model/numericality_matchers/only_integer_matcher'
|
29
|
-
require 'shoulda/matchers/active_model/allow_mass_assignment_of_matcher'
|
30
29
|
require 'shoulda/matchers/active_model/errors'
|
31
30
|
require 'shoulda/matchers/active_model/have_secure_password_matcher'
|
32
31
|
|
33
32
|
module Shoulda
|
34
33
|
module Matchers
|
35
|
-
# This
|
36
|
-
#
|
37
|
-
# objects.
|
34
|
+
# This module provides matchers that are used to test behavior within
|
35
|
+
# ActiveModel or ActiveRecord classes.
|
38
36
|
#
|
39
37
|
# ### Testing conditional validations
|
40
38
|
#
|
@@ -60,7 +58,7 @@ module Shoulda
|
|
60
58
|
# end
|
61
59
|
#
|
62
60
|
# # RSpec
|
63
|
-
# describe User do
|
61
|
+
# RSpec.describe User, type: :model do
|
64
62
|
# context "when an admin" do
|
65
63
|
# subject { User.new(admin: true) }
|
66
64
|
#
|
@@ -9,7 +9,7 @@ module Shoulda
|
|
9
9
|
# end
|
10
10
|
#
|
11
11
|
# # RSpec
|
12
|
-
# describe Car do
|
12
|
+
# RSpec.describe Car, type: :model do
|
13
13
|
# it { should accept_nested_attributes_for(:doors) }
|
14
14
|
# end
|
15
15
|
#
|
@@ -30,7 +30,7 @@ module Shoulda
|
|
30
30
|
# end
|
31
31
|
#
|
32
32
|
# # RSpec
|
33
|
-
# describe Car do
|
33
|
+
# RSpec.describe Car, type: :model do
|
34
34
|
# it do
|
35
35
|
# should accept_nested_attributes_for(:mirrors).
|
36
36
|
# allow_destroy(true)
|
@@ -52,7 +52,7 @@ module Shoulda
|
|
52
52
|
# end
|
53
53
|
#
|
54
54
|
# # RSpec
|
55
|
-
# describe Car do
|
55
|
+
# RSpec.describe Car, type: :model do
|
56
56
|
# it do
|
57
57
|
# should accept_nested_attributes_for(:windows).
|
58
58
|
# limit(3)
|
@@ -75,7 +75,7 @@ module Shoulda
|
|
75
75
|
# end
|
76
76
|
#
|
77
77
|
# # RSpec
|
78
|
-
# describe Car do
|
78
|
+
# RSpec.describe Car, type: :model do
|
79
79
|
# it do
|
80
80
|
# should accept_nested_attributes_for(:engine).
|
81
81
|
# update_only(true)
|
@@ -158,17 +158,20 @@ module Shoulda
|
|
158
158
|
end
|
159
159
|
|
160
160
|
def allow_destroy_correct?
|
161
|
-
failure_message = "#{should_or_should_not(@options[:allow_destroy])}
|
161
|
+
failure_message = "#{should_or_should_not(@options[:allow_destroy])}"\
|
162
|
+
' allow destroy'
|
162
163
|
verify_option_is_correct(:allow_destroy, failure_message)
|
163
164
|
end
|
164
165
|
|
165
166
|
def limit_correct?
|
166
|
-
failure_message = "limit should be #{@options[:limit]},
|
167
|
+
failure_message = "limit should be #{@options[:limit]},"\
|
168
|
+
" got #{config[:limit]}"
|
167
169
|
verify_option_is_correct(:limit, failure_message)
|
168
170
|
end
|
169
171
|
|
170
172
|
def update_only_correct?
|
171
|
-
failure_message = "#{should_or_should_not(@options[:update_only])}
|
173
|
+
failure_message = "#{should_or_should_not(@options[:update_only])}"\
|
174
|
+
' be update only'
|
172
175
|
verify_option_is_correct(:update_only, failure_message)
|
173
176
|
end
|
174
177
|
|