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
@@ -7,46 +7,66 @@ module Shoulda
|
|
7
7
|
class DisallowValueMatcher
|
8
8
|
extend Forwardable
|
9
9
|
|
10
|
-
def_delegators
|
10
|
+
def_delegators(
|
11
|
+
:allow_matcher,
|
12
|
+
:_after_setting_value,
|
13
|
+
:attribute_changed_value_message=,
|
14
|
+
:attribute_to_set,
|
15
|
+
:description,
|
16
|
+
:expects_strict?,
|
17
|
+
:failure_message_preface,
|
18
|
+
:failure_message_preface=,
|
19
|
+
:ignore_interference_by_writer,
|
20
|
+
:last_attribute_setter_used,
|
21
|
+
:last_value_set,
|
22
|
+
:model,
|
23
|
+
:simple_description,
|
24
|
+
:values_to_preset=,
|
25
|
+
)
|
26
|
+
|
11
27
|
def initialize(value)
|
12
28
|
@allow_matcher = AllowValueMatcher.new(value)
|
13
29
|
end
|
14
30
|
|
15
31
|
def matches?(subject)
|
16
|
-
|
32
|
+
allow_matcher.does_not_match?(subject)
|
33
|
+
end
|
34
|
+
|
35
|
+
def does_not_match?(subject)
|
36
|
+
allow_matcher.matches?(subject)
|
17
37
|
end
|
18
38
|
|
19
39
|
def for(attribute)
|
20
|
-
|
40
|
+
allow_matcher.for(attribute)
|
21
41
|
self
|
22
42
|
end
|
23
43
|
|
24
44
|
def on(context)
|
25
|
-
|
45
|
+
allow_matcher.on(context)
|
26
46
|
self
|
27
47
|
end
|
28
48
|
|
29
49
|
def with_message(message, options={})
|
30
|
-
|
50
|
+
allow_matcher.with_message(message, options)
|
31
51
|
self
|
32
52
|
end
|
33
53
|
|
34
|
-
def
|
35
|
-
|
54
|
+
def strict(strict = true)
|
55
|
+
allow_matcher.strict(strict)
|
36
56
|
self
|
37
57
|
end
|
38
58
|
|
39
|
-
def
|
40
|
-
|
59
|
+
def ignoring_interference_by_writer(value = :always)
|
60
|
+
allow_matcher.ignoring_interference_by_writer(value)
|
61
|
+
self
|
41
62
|
end
|
42
63
|
|
43
|
-
def
|
44
|
-
|
64
|
+
def failure_message
|
65
|
+
allow_matcher.failure_message_when_negated
|
45
66
|
end
|
46
67
|
|
47
|
-
def
|
48
|
-
|
49
|
-
self
|
68
|
+
def failure_message_when_negated
|
69
|
+
allow_matcher.failure_message
|
50
70
|
end
|
51
71
|
|
52
72
|
protected
|
@@ -3,25 +3,17 @@ module Shoulda
|
|
3
3
|
module ActiveModel
|
4
4
|
# @private
|
5
5
|
module Helpers
|
6
|
-
def pretty_error_messages(
|
7
|
-
|
8
|
-
|
9
|
-
parenthetical_parts = []
|
10
|
-
|
11
|
-
unless attribute.to_sym == :base
|
12
|
-
parenthetical_parts << "attribute: #{attribute}"
|
13
|
-
|
14
|
-
if obj.respond_to?(attribute)
|
15
|
-
parenthetical_parts << "value: #{obj.__send__(attribute).inspect}"
|
16
|
-
end
|
17
|
-
end
|
6
|
+
def pretty_error_messages(object)
|
7
|
+
format_validation_errors(object.errors)
|
8
|
+
end
|
18
9
|
|
19
|
-
|
20
|
-
|
21
|
-
|
10
|
+
def format_validation_errors(errors)
|
11
|
+
list_items = errors.keys.map do |attribute|
|
12
|
+
messages = errors[attribute]
|
13
|
+
"* #{attribute}: #{messages}"
|
14
|
+
end
|
22
15
|
|
23
|
-
|
24
|
-
end.join("\n")
|
16
|
+
list_items.join("\n")
|
25
17
|
end
|
26
18
|
|
27
19
|
def default_error_message(type, options = {})
|
@@ -13,6 +13,7 @@ module Shoulda
|
|
13
13
|
}
|
14
14
|
|
15
15
|
def initialize(numericality_matcher, value, operator)
|
16
|
+
super(nil)
|
16
17
|
unless numericality_matcher.respond_to? :diff_to_compare
|
17
18
|
raise ArgumentError, 'numericality_matcher is invalid'
|
18
19
|
end
|
@@ -20,17 +21,18 @@ module Shoulda
|
|
20
21
|
@value = value
|
21
22
|
@operator = operator
|
22
23
|
@message = ERROR_MESSAGES[operator]
|
23
|
-
@strict = false
|
24
24
|
end
|
25
25
|
|
26
|
-
def
|
27
|
-
|
26
|
+
def simple_description
|
27
|
+
description = ''
|
28
28
|
|
29
|
-
if
|
30
|
-
|
29
|
+
if expects_strict?
|
30
|
+
description << ' strictly'
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
description +
|
34
|
+
"disallow :#{attribute} from being a number that is not " +
|
35
|
+
"#{comparison_expectation} #{@value}"
|
34
36
|
end
|
35
37
|
|
36
38
|
def for(attribute)
|
@@ -39,10 +41,15 @@ module Shoulda
|
|
39
41
|
end
|
40
42
|
|
41
43
|
def with_message(message)
|
44
|
+
@expects_custom_validation_message = true
|
42
45
|
@message = message
|
43
46
|
self
|
44
47
|
end
|
45
48
|
|
49
|
+
def expects_custom_validation_message?
|
50
|
+
@expects_custom_validation_message
|
51
|
+
end
|
52
|
+
|
46
53
|
def matches?(subject)
|
47
54
|
@subject = subject
|
48
55
|
all_bounds_correct?
|
@@ -6,8 +6,19 @@ module Shoulda
|
|
6
6
|
class EvenNumberMatcher < NumericTypeMatcher
|
7
7
|
NON_EVEN_NUMBER_VALUE = 1
|
8
8
|
|
9
|
-
def
|
10
|
-
|
9
|
+
def simple_description
|
10
|
+
description = ''
|
11
|
+
|
12
|
+
if expects_strict?
|
13
|
+
description << 'strictly '
|
14
|
+
end
|
15
|
+
|
16
|
+
description +
|
17
|
+
"disallow :#{attribute} from being an odd number"
|
18
|
+
end
|
19
|
+
|
20
|
+
def allowed_type_adjective
|
21
|
+
'even'
|
11
22
|
end
|
12
23
|
|
13
24
|
def diff_to_compare
|
@@ -8,35 +8,31 @@ module Shoulda
|
|
8
8
|
class NumericTypeMatcher
|
9
9
|
extend Forwardable
|
10
10
|
|
11
|
-
def_delegators
|
12
|
-
:
|
13
|
-
|
14
|
-
|
11
|
+
def_delegators(
|
12
|
+
:disallow_value_matcher,
|
13
|
+
:expects_custom_validation_message?,
|
14
|
+
:expects_strict?,
|
15
|
+
:failure_message,
|
16
|
+
:failure_message_when_negated,
|
17
|
+
:ignore_interference_by_writer,
|
18
|
+
:ignoring_interference_by_writer,
|
19
|
+
:matches?,
|
20
|
+
:on,
|
21
|
+
:strict,
|
22
|
+
:with_message,
|
23
|
+
)
|
24
|
+
|
25
|
+
def initialize(numeric_type_matcher, attribute)
|
15
26
|
@numeric_type_matcher = numeric_type_matcher
|
16
27
|
@attribute = attribute
|
17
|
-
@options = options
|
18
|
-
@message = nil
|
19
|
-
@context = nil
|
20
|
-
@strict = false
|
21
|
-
end
|
22
|
-
|
23
|
-
def with_message(message)
|
24
|
-
@message = message
|
25
|
-
self
|
26
28
|
end
|
27
29
|
|
28
|
-
def
|
29
|
-
|
30
|
-
self
|
30
|
+
def allowed_type_name
|
31
|
+
'number'
|
31
32
|
end
|
32
33
|
|
33
|
-
def
|
34
|
-
|
35
|
-
self
|
36
|
-
end
|
37
|
-
|
38
|
-
def allowed_type
|
39
|
-
raise NotImplementedError
|
34
|
+
def allowed_type_adjective
|
35
|
+
''
|
40
36
|
end
|
41
37
|
|
42
38
|
def diff_to_compare
|
@@ -62,18 +58,6 @@ module Shoulda
|
|
62
58
|
DisallowValueMatcher.new(disallowed_value).tap do |matcher|
|
63
59
|
matcher.for(attribute)
|
64
60
|
wrap_disallow_value_matcher(matcher)
|
65
|
-
|
66
|
-
if @message
|
67
|
-
matcher.with_message(@message)
|
68
|
-
end
|
69
|
-
|
70
|
-
if @strict
|
71
|
-
matcher.strict
|
72
|
-
end
|
73
|
-
|
74
|
-
if @context
|
75
|
-
matcher.on(@context)
|
76
|
-
end
|
77
61
|
end
|
78
62
|
end
|
79
63
|
end
|
@@ -6,8 +6,19 @@ module Shoulda
|
|
6
6
|
class OddNumberMatcher < NumericTypeMatcher
|
7
7
|
NON_ODD_NUMBER_VALUE = 2
|
8
8
|
|
9
|
-
def
|
10
|
-
|
9
|
+
def simple_description
|
10
|
+
description = ''
|
11
|
+
|
12
|
+
if expects_strict?
|
13
|
+
description << 'strictly '
|
14
|
+
end
|
15
|
+
|
16
|
+
description +
|
17
|
+
"disallow :#{attribute} from being an even number"
|
18
|
+
end
|
19
|
+
|
20
|
+
def allowed_type_adjective
|
21
|
+
'odd'
|
11
22
|
end
|
12
23
|
|
13
24
|
def diff_to_compare
|
@@ -6,8 +6,18 @@ module Shoulda
|
|
6
6
|
class OnlyIntegerMatcher < NumericTypeMatcher
|
7
7
|
NON_INTEGER_VALUE = 0.1
|
8
8
|
|
9
|
-
def
|
10
|
-
'
|
9
|
+
def simple_description
|
10
|
+
description = ''
|
11
|
+
|
12
|
+
if expects_strict?
|
13
|
+
description << ' strictly'
|
14
|
+
end
|
15
|
+
|
16
|
+
description + "disallow :#{attribute} from being a decimal number"
|
17
|
+
end
|
18
|
+
|
19
|
+
def allowed_type_name
|
20
|
+
'integer'
|
11
21
|
end
|
12
22
|
|
13
23
|
def diff_to_compare
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Shoulda
|
2
|
+
module Matchers
|
3
|
+
module ActiveModel
|
4
|
+
module Qualifiers
|
5
|
+
# @private
|
6
|
+
class IgnoreInterferenceByWriter
|
7
|
+
attr_reader :setting, :condition
|
8
|
+
|
9
|
+
def initialize(argument = :always)
|
10
|
+
set(argument)
|
11
|
+
@changed = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def set(argument)
|
15
|
+
if argument.is_a?(self.class)
|
16
|
+
@setting = argument.setting
|
17
|
+
@condition = argument.condition
|
18
|
+
else
|
19
|
+
case argument
|
20
|
+
when true, :always
|
21
|
+
@setting = :always
|
22
|
+
when false, :never
|
23
|
+
@setting = :never
|
24
|
+
else
|
25
|
+
@setting = :sometimes
|
26
|
+
|
27
|
+
if argument.is_a?(Hash)
|
28
|
+
@condition = argument.fetch(:when)
|
29
|
+
else
|
30
|
+
raise invalid_argument_error(argument)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
@changed = true
|
36
|
+
|
37
|
+
self
|
38
|
+
rescue KeyError
|
39
|
+
raise invalid_argument_error(argument)
|
40
|
+
end
|
41
|
+
|
42
|
+
def default_to(argument)
|
43
|
+
temporary_ignore_interference_by_writer =
|
44
|
+
IgnoreInterferenceByWriter.new(argument)
|
45
|
+
|
46
|
+
unless changed?
|
47
|
+
@setting = temporary_ignore_interference_by_writer.setting
|
48
|
+
@condition = temporary_ignore_interference_by_writer.condition
|
49
|
+
end
|
50
|
+
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def considering?(value)
|
55
|
+
case setting
|
56
|
+
when :always then true
|
57
|
+
when :never then false
|
58
|
+
else condition_matches?(value)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def always?
|
63
|
+
setting == :always
|
64
|
+
end
|
65
|
+
|
66
|
+
def never?
|
67
|
+
setting == :never
|
68
|
+
end
|
69
|
+
|
70
|
+
def changed?
|
71
|
+
@changed
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def invalid_argument_error(invalid_argument)
|
77
|
+
ArgumentError.new(<<-ERROR)
|
78
|
+
Unknown argument: #{invalid_argument.inspect}.
|
79
|
+
|
80
|
+
ignoring_interference_by_writer takes one of three arguments:
|
81
|
+
|
82
|
+
* A symbol, either :never or :always.
|
83
|
+
* A boolean, either true (which means always) or false (which means
|
84
|
+
never).
|
85
|
+
* A hash with a single key, :when, and a single value, which is either
|
86
|
+
the name of a method or a Proc.
|
87
|
+
ERROR
|
88
|
+
end
|
89
|
+
|
90
|
+
def condition_matches?(value)
|
91
|
+
if condition.respond_to?(:call)
|
92
|
+
condition.call(value)
|
93
|
+
else
|
94
|
+
value.public_send(condition)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|