shoulda-activemodel 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/shoulda/active_model.rb +16 -0
- data/lib/shoulda/active_model/assertions.rb +61 -0
- data/lib/shoulda/active_model/helpers.rb +31 -0
- data/lib/shoulda/active_model/macros.rb +268 -0
- data/lib/shoulda/active_model/matchers.rb +30 -0
- data/lib/shoulda/active_model/matchers/allow_value_matcher.rb +102 -0
- data/lib/shoulda/active_model/matchers/ensure_inclusion_of_matcher.rb +87 -0
- data/lib/shoulda/active_model/matchers/ensure_length_of_matcher.rb +141 -0
- data/lib/shoulda/active_model/matchers/validate_acceptance_of_matcher.rb +41 -0
- data/lib/shoulda/active_model/matchers/validate_format_of_matcher.rb +67 -0
- data/lib/shoulda/active_model/matchers/validate_numericality_of_matcher.rb +39 -0
- data/lib/shoulda/active_model/matchers/validate_presence_of_matcher.rb +60 -0
- data/lib/shoulda/active_model/matchers/validate_uniqueness_of_matcher.rb +148 -0
- data/lib/shoulda/active_model/matchers/validation_matcher.rb +57 -0
- data/test/fail_macros.rb +39 -0
- data/test/matchers/active_model/ensure_inclusion_of_matcher_test.rb +80 -0
- data/test/matchers/active_model/ensure_length_of_matcher_test.rb +158 -0
- data/test/matchers/active_model/validate_acceptance_of_matcher_test.rb +44 -0
- data/test/matchers/active_model/validate_format_of_matcher_test.rb +39 -0
- data/test/matchers/active_model/validate_numericality_of_matcher_test.rb +52 -0
- data/test/matchers/active_model/validate_presence_of_matcher_test.rb +86 -0
- data/test/matchers/active_model/validate_uniqueness_of_matcher_test.rb +147 -0
- data/test/model_builder.rb +106 -0
- data/test/rspec_test.rb +207 -0
- data/test/test_helper.rb +20 -0
- metadata +106 -0
@@ -0,0 +1,87 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module ActiveModel # :nodoc:
|
3
|
+
module Matchers
|
4
|
+
|
5
|
+
# Ensure that the attribute's value is in the range specified
|
6
|
+
#
|
7
|
+
# Options:
|
8
|
+
# * <tt>in_range</tt> - the range of allowed values for this attribute
|
9
|
+
# * <tt>with_low_message</tt> - value the test expects to find in
|
10
|
+
# <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
|
11
|
+
# translation for :inclusion.
|
12
|
+
# * <tt>with_high_message</tt> - value the test expects to find in
|
13
|
+
# <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
|
14
|
+
# translation for :inclusion.
|
15
|
+
#
|
16
|
+
# Example:
|
17
|
+
# it { should ensure_inclusion_of(:age).in_range(0..100) }
|
18
|
+
#
|
19
|
+
def ensure_inclusion_of(attr)
|
20
|
+
EnsureInclusionOfMatcher.new(attr)
|
21
|
+
end
|
22
|
+
|
23
|
+
class EnsureInclusionOfMatcher < ValidationMatcher # :nodoc:
|
24
|
+
|
25
|
+
def in_range(range)
|
26
|
+
@range = range
|
27
|
+
@minimum = range.first
|
28
|
+
@maximum = range.last
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def with_message(message)
|
33
|
+
if message
|
34
|
+
@low_message = message
|
35
|
+
@high_message = message
|
36
|
+
end
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def with_low_message(message)
|
41
|
+
@low_message = message if message
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def with_high_message(message)
|
46
|
+
@high_message = message if message
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
def description
|
51
|
+
"ensure inclusion of #{@attribute} in #{@range.inspect}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def matches?(subject)
|
55
|
+
super(subject)
|
56
|
+
|
57
|
+
@low_message ||= :inclusion
|
58
|
+
@high_message ||= :inclusion
|
59
|
+
|
60
|
+
disallows_lower_value &&
|
61
|
+
allows_minimum_value &&
|
62
|
+
disallows_higher_value &&
|
63
|
+
allows_maximum_value
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def disallows_lower_value
|
69
|
+
@minimum == 0 || disallows_value_of(@minimum - 1, @low_message)
|
70
|
+
end
|
71
|
+
|
72
|
+
def disallows_higher_value
|
73
|
+
disallows_value_of(@maximum + 1, @high_message)
|
74
|
+
end
|
75
|
+
|
76
|
+
def allows_minimum_value
|
77
|
+
allows_value_of(@minimum, @low_message)
|
78
|
+
end
|
79
|
+
|
80
|
+
def allows_maximum_value
|
81
|
+
allows_value_of(@maximum, @high_message)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module ActiveModel # :nodoc:
|
3
|
+
module Matchers
|
4
|
+
|
5
|
+
# Ensures that the length of the attribute is validated.
|
6
|
+
#
|
7
|
+
# Options:
|
8
|
+
# * <tt>is_at_least</tt> - minimum length of this attribute
|
9
|
+
# * <tt>is_at_most</tt> - maximum length of this attribute
|
10
|
+
# * <tt>is_equal_to</tt> - exact requred length of this attribute
|
11
|
+
# * <tt>with_short_message</tt> - value the test expects to find in
|
12
|
+
# <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
|
13
|
+
# translation for :too_short.
|
14
|
+
# * <tt>with_long_message</tt> - value the test expects to find in
|
15
|
+
# <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
|
16
|
+
# translation for :too_long.
|
17
|
+
# * <tt>with_message</tt> - value the test expects to find in
|
18
|
+
# <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
|
19
|
+
# translation for :wrong_length. Used in conjunction with
|
20
|
+
# <tt>is_equal_to</tt>.
|
21
|
+
#
|
22
|
+
# Examples:
|
23
|
+
# it { should ensure_length_of(:password).
|
24
|
+
# is_at_least(6).
|
25
|
+
# is_at_most(20) }
|
26
|
+
# it { should ensure_length_of(:name).
|
27
|
+
# is_at_least(3).
|
28
|
+
# with_short_message(/not long enough/) }
|
29
|
+
# it { should ensure_length_of(:ssn).
|
30
|
+
# is_equal_to(9).
|
31
|
+
# with_message(/is invalid/) }
|
32
|
+
def ensure_length_of(attr)
|
33
|
+
EnsureLengthOfMatcher.new(attr)
|
34
|
+
end
|
35
|
+
|
36
|
+
class EnsureLengthOfMatcher < ValidationMatcher # :nodoc:
|
37
|
+
include Helpers
|
38
|
+
|
39
|
+
def is_at_least(length)
|
40
|
+
@minimum = length
|
41
|
+
@short_message ||= :too_short
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def is_at_most(length)
|
46
|
+
@maximum = length
|
47
|
+
@long_message ||= :too_long
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def is_equal_to(length)
|
52
|
+
@minimum = length
|
53
|
+
@maximum = length
|
54
|
+
@short_message ||= :wrong_length
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def with_short_message(message)
|
59
|
+
@short_message = message if message
|
60
|
+
self
|
61
|
+
end
|
62
|
+
alias_method :with_message, :with_short_message
|
63
|
+
|
64
|
+
def with_long_message(message)
|
65
|
+
@long_message = message if message
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def description
|
70
|
+
description = "ensure #{@attribute} has a length "
|
71
|
+
if @minimum && @maximum
|
72
|
+
if @minimum == @maximum
|
73
|
+
description << "of exactly #{@minimum}"
|
74
|
+
else
|
75
|
+
description << "between #{@minimum} and #{@maximum}"
|
76
|
+
end
|
77
|
+
else
|
78
|
+
description << "of at least #{@minimum}" if @minimum
|
79
|
+
description << "of at most #{@maximum}" if @maximum
|
80
|
+
end
|
81
|
+
description
|
82
|
+
end
|
83
|
+
|
84
|
+
def matches?(subject)
|
85
|
+
super(subject)
|
86
|
+
translate_messages!
|
87
|
+
disallows_lower_length &&
|
88
|
+
allows_minimum_length &&
|
89
|
+
((@minimum == @maximum) ||
|
90
|
+
(disallows_higher_length &&
|
91
|
+
allows_maximum_length))
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def translate_messages!
|
97
|
+
if Symbol === @short_message
|
98
|
+
@short_message = default_error_message(@short_message,
|
99
|
+
:count => @minimum)
|
100
|
+
end
|
101
|
+
|
102
|
+
if Symbol === @long_message
|
103
|
+
@long_message = default_error_message(@long_message,
|
104
|
+
:count => @maximum)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def disallows_lower_length
|
109
|
+
@minimum == 0 ||
|
110
|
+
@minimum.nil? ||
|
111
|
+
disallows_length_of(@minimum - 1, @short_message)
|
112
|
+
end
|
113
|
+
|
114
|
+
def disallows_higher_length
|
115
|
+
@maximum.nil? || disallows_length_of(@maximum + 1, @long_message)
|
116
|
+
end
|
117
|
+
|
118
|
+
def allows_minimum_length
|
119
|
+
allows_length_of(@minimum, @short_message)
|
120
|
+
end
|
121
|
+
|
122
|
+
def allows_maximum_length
|
123
|
+
allows_length_of(@maximum, @long_message)
|
124
|
+
end
|
125
|
+
|
126
|
+
def allows_length_of(length, message)
|
127
|
+
length.nil? || allows_value_of(string_of_length(length), message)
|
128
|
+
end
|
129
|
+
|
130
|
+
def disallows_length_of(length, message)
|
131
|
+
length.nil? || disallows_value_of(string_of_length(length), message)
|
132
|
+
end
|
133
|
+
|
134
|
+
def string_of_length(length)
|
135
|
+
'x' * length
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module ActiveModel # :nodoc:
|
3
|
+
module Matchers
|
4
|
+
|
5
|
+
# Ensures that the model cannot be saved the given attribute is not
|
6
|
+
# accepted.
|
7
|
+
#
|
8
|
+
# Options:
|
9
|
+
# * <tt>with_message</tt> - value the test expects to find in
|
10
|
+
# <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
|
11
|
+
# translation for <tt>:accepted</tt>.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
# it { should validate_acceptance_of(:eula) }
|
15
|
+
#
|
16
|
+
def validate_acceptance_of(attr)
|
17
|
+
ValidateAcceptanceOfMatcher.new(attr)
|
18
|
+
end
|
19
|
+
|
20
|
+
class ValidateAcceptanceOfMatcher < ValidationMatcher # :nodoc:
|
21
|
+
|
22
|
+
def with_message(message)
|
23
|
+
@expected_message = message if message
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def matches?(subject)
|
28
|
+
super(subject)
|
29
|
+
@expected_message ||= :accepted
|
30
|
+
disallows_value_of(false, @expected_message)
|
31
|
+
end
|
32
|
+
|
33
|
+
def description
|
34
|
+
"require #{@attribute} to be accepted"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module ActiveModel # :nodoc:
|
3
|
+
module Matchers
|
4
|
+
|
5
|
+
# Ensures that the model is not valid if the given attribute is not
|
6
|
+
# formatted correctly.
|
7
|
+
#
|
8
|
+
# Options:
|
9
|
+
# * <tt>with_message</tt> - value the test expects to find in
|
10
|
+
# <tt>errors.on(:attribute)</tt>. <tt>Regexp</tt> or <tt>String</tt>.
|
11
|
+
# Defaults to the translation for <tt>:blank</tt>.
|
12
|
+
# * <tt>with(string to test against)</tt>
|
13
|
+
# * <tt>not_with(string to test against)</tt>
|
14
|
+
#
|
15
|
+
# Examples:
|
16
|
+
# it { should validate_format_of(:name).
|
17
|
+
# with('12345').
|
18
|
+
# with_message(/is not optional/) }
|
19
|
+
# it { should validate_format_of(:name).
|
20
|
+
# not_with('12D45').
|
21
|
+
# with_message(/is not optional/) }
|
22
|
+
#
|
23
|
+
def validate_format_of(attr)
|
24
|
+
ValidateFormatOfMatcher.new(attr)
|
25
|
+
end
|
26
|
+
|
27
|
+
class ValidateFormatOfMatcher < ValidationMatcher # :nodoc:
|
28
|
+
|
29
|
+
def initialize(attribute)
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
def with_message(message)
|
34
|
+
@expected_message = message if message
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def with(value)
|
39
|
+
raise "You may not call both with and not_with" if @value_to_fail
|
40
|
+
@value_to_pass = value
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def not_with(value)
|
46
|
+
raise "You may not call both with and not_with" if @value_to_pass
|
47
|
+
@value_to_fail = value
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def matches?(subject)
|
53
|
+
super(subject)
|
54
|
+
@expected_message ||= :blank
|
55
|
+
return disallows_value_of(@value_to_fail, @expected_message) if @value_to_fail
|
56
|
+
allows_value_of(@value_to_pass, @expected_message) if @value_to_pass
|
57
|
+
end
|
58
|
+
|
59
|
+
def description
|
60
|
+
"#{@attribute} have a valid format"
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module ActiveModel # :nodoc:
|
3
|
+
module Matchers
|
4
|
+
|
5
|
+
# Ensure that the attribute is numeric
|
6
|
+
#
|
7
|
+
# Options:
|
8
|
+
# * <tt>with_message</tt> - value the test expects to find in
|
9
|
+
# <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
|
10
|
+
# translation for <tt>:not_a_number</tt>.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# it { should validate_numericality_of(:age) }
|
14
|
+
#
|
15
|
+
def validate_numericality_of(attr)
|
16
|
+
ValidateNumericalityOfMatcher.new(attr)
|
17
|
+
end
|
18
|
+
|
19
|
+
class ValidateNumericalityOfMatcher < ValidationMatcher # :nodoc:
|
20
|
+
|
21
|
+
def with_message(message)
|
22
|
+
@expected_message = message if message
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def matches?(subject)
|
27
|
+
super(subject)
|
28
|
+
@expected_message ||= :not_a_number
|
29
|
+
disallows_value_of('abcd', @expected_message)
|
30
|
+
end
|
31
|
+
|
32
|
+
def description
|
33
|
+
"only allow numeric values for #{@attribute}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module ActiveModel # :nodoc:
|
3
|
+
module Matchers
|
4
|
+
|
5
|
+
# Ensures that the model is not valid if the given attribute is not
|
6
|
+
# present.
|
7
|
+
#
|
8
|
+
# Options:
|
9
|
+
# * <tt>with_message</tt> - value the test expects to find in
|
10
|
+
# <tt>errors.on(:attribute)</tt>. <tt>Regexp</tt> or <tt>String</tt>.
|
11
|
+
# Defaults to the translation for <tt>:blank</tt>.
|
12
|
+
#
|
13
|
+
# Examples:
|
14
|
+
# it { should validate_presence_of(:name) }
|
15
|
+
# it { should validate_presence_of(:name).
|
16
|
+
# with_message(/is not optional/) }
|
17
|
+
#
|
18
|
+
def validate_presence_of(attr)
|
19
|
+
ValidatePresenceOfMatcher.new(attr)
|
20
|
+
end
|
21
|
+
|
22
|
+
class ValidatePresenceOfMatcher < ValidationMatcher # :nodoc:
|
23
|
+
|
24
|
+
def with_message(message)
|
25
|
+
@expected_message = message if message
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def matches?(subject)
|
30
|
+
super(subject)
|
31
|
+
@expected_message ||= :blank
|
32
|
+
disallows_value_of(blank_value, @expected_message)
|
33
|
+
end
|
34
|
+
|
35
|
+
def description
|
36
|
+
"require #{@attribute} to be set"
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def blank_value
|
42
|
+
if collection?
|
43
|
+
[]
|
44
|
+
else
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def collection?
|
50
|
+
if reflection = @subject.class.reflect_on_association(@attribute)
|
51
|
+
[:has_many, :has_and_belongs_to_many].include?(reflection.macro)
|
52
|
+
else
|
53
|
+
false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module ActiveModel # :nodoc:
|
3
|
+
module Matchers
|
4
|
+
|
5
|
+
# Ensures that the model is invalid if the given attribute is not unique.
|
6
|
+
#
|
7
|
+
# Internally, this uses values from existing models to test validations,
|
8
|
+
# so this will always fail if you have not saved at least one model for
|
9
|
+
# the model being tested, like so:
|
10
|
+
#
|
11
|
+
# describe User do
|
12
|
+
# before(:each) { User.create!(:email => 'address@example.com') }
|
13
|
+
# it { should validate_uniqueness_of(:email) }
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# Options:
|
17
|
+
#
|
18
|
+
# * <tt>with_message</tt> - value the test expects to find in
|
19
|
+
# <tt>errors.on(:attribute)</tt>. <tt>Regexp</tt> or <tt>String</tt>.
|
20
|
+
# Defaults to the translation for <tt>:taken</tt>.
|
21
|
+
# * <tt>scoped_to</tt> - field(s) to scope the uniqueness to.
|
22
|
+
# * <tt>case_insensitive</tt> - ensures that the validation does not
|
23
|
+
# check case. Off by default. Ignored by non-text attributes.
|
24
|
+
#
|
25
|
+
# Examples:
|
26
|
+
# it { should validate_uniqueness_of(:keyword) }
|
27
|
+
# it { should validate_uniqueness_of(:keyword).with_message(/dup/) }
|
28
|
+
# it { should validate_uniqueness_of(:email).scoped_to(:name) }
|
29
|
+
# it { should validate_uniqueness_of(:email).
|
30
|
+
# scoped_to(:first_name, :last_name) }
|
31
|
+
# it { should validate_uniqueness_of(:keyword).case_insensitive }
|
32
|
+
#
|
33
|
+
def validate_uniqueness_of(attr)
|
34
|
+
ValidateUniquenessOfMatcher.new(attr)
|
35
|
+
end
|
36
|
+
|
37
|
+
class ValidateUniquenessOfMatcher < ValidationMatcher # :nodoc:
|
38
|
+
include Helpers
|
39
|
+
|
40
|
+
def initialize(attribute)
|
41
|
+
@attribute = attribute
|
42
|
+
end
|
43
|
+
|
44
|
+
def scoped_to(*scopes)
|
45
|
+
@scopes = [*scopes].flatten
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def with_message(message)
|
50
|
+
@expected_message = message
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def case_insensitive
|
55
|
+
@case_insensitive = true
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def description
|
60
|
+
result = "require "
|
61
|
+
result << "case sensitive " unless @case_insensitive
|
62
|
+
result << "unique value for #{@attribute}"
|
63
|
+
result << " scoped to #{@scopes.join(', ')}" unless @scopes.blank?
|
64
|
+
result
|
65
|
+
end
|
66
|
+
|
67
|
+
def matches?(subject)
|
68
|
+
@subject = subject.class.new
|
69
|
+
@expected_message ||= :taken
|
70
|
+
find_existing &&
|
71
|
+
set_scoped_attributes &&
|
72
|
+
validate_attribute &&
|
73
|
+
validate_after_scope_change
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def find_existing
|
79
|
+
if @existing = @subject.class.find(:first)
|
80
|
+
true
|
81
|
+
else
|
82
|
+
@failure_message = "Can't find first #{class_name}"
|
83
|
+
false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def set_scoped_attributes
|
88
|
+
unless @scopes.blank?
|
89
|
+
@scopes.each do |scope|
|
90
|
+
setter = :"#{scope}="
|
91
|
+
unless @subject.respond_to?(setter)
|
92
|
+
@failure_message =
|
93
|
+
"#{class_name} doesn't seem to have a #{scope} attribute."
|
94
|
+
return false
|
95
|
+
end
|
96
|
+
@subject.send("#{scope}=", @existing.send(scope))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
true
|
100
|
+
end
|
101
|
+
|
102
|
+
def validate_attribute
|
103
|
+
disallows_value_of(existing_value, @expected_message)
|
104
|
+
end
|
105
|
+
|
106
|
+
# TODO: There is a chance that we could change the scoped field
|
107
|
+
# to a value that's already taken. An alternative implementation
|
108
|
+
# could actually find all values for scope and create a unique
|
109
|
+
def validate_after_scope_change
|
110
|
+
if @scopes.blank?
|
111
|
+
true
|
112
|
+
else
|
113
|
+
@scopes.all? do |scope|
|
114
|
+
previous_value = @existing.send(scope)
|
115
|
+
|
116
|
+
# Assume the scope is a foreign key if the field is nil
|
117
|
+
previous_value ||= 0
|
118
|
+
|
119
|
+
next_value = previous_value.next
|
120
|
+
|
121
|
+
@subject.send("#{scope}=", next_value)
|
122
|
+
|
123
|
+
if allows_value_of(existing_value, @expected_message)
|
124
|
+
@negative_failure_message <<
|
125
|
+
" (with different value of #{scope})"
|
126
|
+
true
|
127
|
+
else
|
128
|
+
@failure_message << " (with different value of #{scope})"
|
129
|
+
false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def class_name
|
136
|
+
@subject.class.name
|
137
|
+
end
|
138
|
+
|
139
|
+
def existing_value
|
140
|
+
value = @existing.send(@attribute)
|
141
|
+
value.swapcase! if @case_insensitive && value.respond_to?(:swapcase!)
|
142
|
+
value
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|