shoulda-activemodel 0.0.2
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.
- 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
|