mongoid-rspec 1.8.0 → 1.9.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.
- data/.travis.yml +9 -1
- data/README.md +145 -120
- data/lib/matchers/associations.rb +94 -17
- data/lib/matchers/document.rb +2 -2
- data/lib/matchers/indexes.rb +1 -1
- data/lib/matchers/validations/acceptance_of.rb +2 -2
- data/lib/matchers/validations/associated.rb +5 -5
- data/lib/matchers/validations/confirmation_of.rb +2 -2
- data/lib/matchers/validations/custom_validation_of.rb +2 -19
- data/lib/matchers/validations/exclusion_of.rb +8 -1
- data/lib/matchers/validations/format_of.rb +21 -21
- data/lib/matchers/validations/length_of.rb +22 -0
- data/lib/matchers/validations/numericality_of.rb +10 -10
- data/lib/matchers/validations/presence_of.rb +2 -2
- data/lib/matchers/validations/uniqueness_of.rb +13 -30
- data/lib/matchers/validations/with_message.rb +27 -0
- data/lib/matchers/validations.rb +14 -2
- data/lib/mongoid-rspec/version.rb +1 -1
- data/lib/mongoid-rspec.rb +1 -0
- data/mongoid-rspec.gemspec +1 -1
- data/spec/models/article.rb +13 -9
- data/spec/models/comment.rb +2 -2
- data/spec/models/movie_article.rb +6 -5
- data/spec/models/permalink.rb +1 -1
- data/spec/models/person.rb +0 -3
- data/spec/models/profile.rb +9 -7
- data/spec/models/record.rb +1 -1
- data/spec/models/site.rb +2 -3
- data/spec/models/user.rb +14 -13
- data/spec/unit/allow_mass_assignment_spec.rb +1 -1
- data/spec/unit/associations_spec.rb +3 -3
- data/spec/unit/indexes_spec.rb +1 -1
- data/spec/unit/validations_spec.rb +4 -0
- data/spec/validators/ssn_validator.rb +3 -1
- metadata +7 -6
|
@@ -1,71 +1,71 @@
|
|
|
1
1
|
module Mongoid
|
|
2
2
|
module Matchers
|
|
3
|
-
module Validations
|
|
3
|
+
module Validations
|
|
4
4
|
class ValidateFormatOfMatcher < HaveValidationMatcher
|
|
5
5
|
def initialize(field)
|
|
6
6
|
super(field, :format)
|
|
7
7
|
end
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
def with_format(format)
|
|
10
10
|
@format = format
|
|
11
11
|
self
|
|
12
12
|
end
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
def to_allow(valid_value)
|
|
15
15
|
@valid_value = valid_value
|
|
16
|
-
self
|
|
16
|
+
self
|
|
17
17
|
end
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
def not_to_allow(invalid_value)
|
|
20
20
|
@invalid_value = invalid_value
|
|
21
|
-
self
|
|
21
|
+
self
|
|
22
22
|
end
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
def matches?(actual)
|
|
25
25
|
return false unless result = super(actual)
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
if @format
|
|
28
28
|
if @validator.options[:with] == @format
|
|
29
29
|
@positive_result_message = @positive_result_message << " with format #{@validator.options[:format].inspect}"
|
|
30
30
|
else
|
|
31
31
|
@negative_result_message = @negative_result_message << " with format #{@validator.options[:format].inspect}"
|
|
32
32
|
result = false
|
|
33
|
-
end
|
|
33
|
+
end
|
|
34
34
|
end
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
if @valid_value
|
|
37
37
|
if @validator.options[:with] =~ @valid_value
|
|
38
38
|
@positive_result_message = @positive_result_message << " with #{@valid_value.inspect} as a valid value"
|
|
39
39
|
else
|
|
40
40
|
@negative_result_message = @negative_result_message << " with #{@valid_value.inspect} as an invalid value"
|
|
41
41
|
result = false
|
|
42
|
-
end
|
|
42
|
+
end
|
|
43
43
|
end
|
|
44
|
-
|
|
45
|
-
if @invalid_value
|
|
44
|
+
|
|
45
|
+
if @invalid_value
|
|
46
46
|
if !(@invalid_value =~ @validator.options[:with])
|
|
47
47
|
@positive_result_message = @positive_result_message << " with #{@invalid_value.inspect} as an invalid value"
|
|
48
48
|
else
|
|
49
49
|
@negative_result_message = @negative_result_message << " with #{@invalid_value.inspect} as a valid value"
|
|
50
50
|
result = false
|
|
51
|
-
end
|
|
51
|
+
end
|
|
52
52
|
end
|
|
53
|
-
|
|
54
|
-
result
|
|
53
|
+
|
|
54
|
+
result
|
|
55
55
|
end
|
|
56
|
-
|
|
56
|
+
|
|
57
57
|
def description
|
|
58
58
|
options_desc = []
|
|
59
59
|
options_desc << " with format #{@format.inspect}" if @format
|
|
60
60
|
options_desc << " allowing the value #{@valid_value.inspect}" if @valid_value
|
|
61
|
-
options_desc << " not allowing the value #{@invalid_value.inspect}" if @invalid_value
|
|
61
|
+
options_desc << " not allowing the value #{@invalid_value.inspect}" if @invalid_value
|
|
62
62
|
super << options_desc.to_sentence
|
|
63
|
-
end
|
|
63
|
+
end
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def validate_format_of(field)
|
|
67
67
|
ValidateFormatOfMatcher.new(field)
|
|
68
|
-
end
|
|
68
|
+
end
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
|
-
end
|
|
71
|
+
end
|
|
@@ -2,6 +2,8 @@ module Mongoid
|
|
|
2
2
|
module Matchers
|
|
3
3
|
module Validations
|
|
4
4
|
class ValidateLengthOfMatcher < HaveValidationMatcher
|
|
5
|
+
include WithMessage
|
|
6
|
+
|
|
5
7
|
def initialize(name)
|
|
6
8
|
super(name, :length)
|
|
7
9
|
end
|
|
@@ -30,6 +32,11 @@ module Mongoid
|
|
|
30
32
|
end
|
|
31
33
|
alias :is :as_exactly
|
|
32
34
|
|
|
35
|
+
def with_message(message)
|
|
36
|
+
@expected_message = message
|
|
37
|
+
self
|
|
38
|
+
end
|
|
39
|
+
|
|
33
40
|
def matches?(actual)
|
|
34
41
|
return false unless @result = super(actual)
|
|
35
42
|
|
|
@@ -37,6 +44,7 @@ module Mongoid
|
|
|
37
44
|
check_minimum if @minimum
|
|
38
45
|
check_range if @within
|
|
39
46
|
check_exact if @is
|
|
47
|
+
check_expected_message if @expected_message
|
|
40
48
|
|
|
41
49
|
@result
|
|
42
50
|
end
|
|
@@ -47,6 +55,7 @@ module Mongoid
|
|
|
47
55
|
options_desc << "with maximum of #{@maximum}" if @maximum
|
|
48
56
|
options_desc << "within the range of #{@within}" if @within
|
|
49
57
|
options_desc << "as exactly #{@is}" if @is
|
|
58
|
+
options_desc << "with message '#{@expected_message}'" if @expected_message
|
|
50
59
|
super << " #{options_desc.to_sentence}"
|
|
51
60
|
end
|
|
52
61
|
|
|
@@ -104,6 +113,19 @@ module Mongoid
|
|
|
104
113
|
end
|
|
105
114
|
end
|
|
106
115
|
|
|
116
|
+
def check_expected_message
|
|
117
|
+
actual_message = @validator.options[:message]
|
|
118
|
+
if actual_message.nil?
|
|
119
|
+
@negative_result_message << " with no custom message"
|
|
120
|
+
@result = false
|
|
121
|
+
elsif actual_message == @expected_message
|
|
122
|
+
@positive_result_message << " with custom message '#{@expected_message}'"
|
|
123
|
+
else
|
|
124
|
+
@negative_result_message << " got message '#{actual_message}'"
|
|
125
|
+
@result = false
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
107
129
|
def actual_is
|
|
108
130
|
actual_is = @validator.options[:is]
|
|
109
131
|
end
|
|
@@ -2,14 +2,14 @@ module Mongoid
|
|
|
2
2
|
module Matchers
|
|
3
3
|
module Validations
|
|
4
4
|
class ValidateNumericalityOfMatcher < HaveValidationMatcher
|
|
5
|
-
@@allowed_options = [:equal_to, :greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to,
|
|
5
|
+
@@allowed_options = [:equal_to, :greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to,
|
|
6
6
|
:even, :odd, :only_integer, :allow_nil, :nil]
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
def initialize(field)
|
|
9
9
|
super(field, :numericality)
|
|
10
10
|
@options = {}
|
|
11
11
|
end
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
def to_allow(options)
|
|
14
14
|
options[:equal_to] = options if options.is_a?(Numeric)
|
|
15
15
|
options[:allow_nil] = options.delete(:nil) if options.has_key?(:nil)
|
|
@@ -21,7 +21,7 @@ module Mongoid
|
|
|
21
21
|
|
|
22
22
|
def matches?(actual)
|
|
23
23
|
return false unless result = super(actual)
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
@@allowed_options.each do |comparator|
|
|
26
26
|
if @options.has_key?(comparator) and !([:even, :odd, :only_integer].include?(comparator) and !@validator.options.include?(comparator))
|
|
27
27
|
result &= (@validator.options[comparator] == @options[comparator])
|
|
@@ -35,12 +35,12 @@ module Mongoid
|
|
|
35
35
|
def description
|
|
36
36
|
super << options_message(@options)
|
|
37
37
|
end
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
protected
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
def options_message(options)
|
|
42
42
|
type_msg = []
|
|
43
|
-
comp_msg = []
|
|
43
|
+
comp_msg = []
|
|
44
44
|
options.each_pair do |key, value|
|
|
45
45
|
case key
|
|
46
46
|
when :allow_nil
|
|
@@ -52,10 +52,10 @@ module Mongoid
|
|
|
52
52
|
comp_msg << "#{key.to_s.gsub("_", " ")} #{value.inspect}"
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
|
-
allow_nil = (options[:allow_nil] ? "nil" : "non-nil") if options.has_key?(:allow_nil)
|
|
55
|
+
allow_nil = (options[:allow_nil] ? "nil" : "non-nil") if options.has_key?(:allow_nil)
|
|
56
56
|
["", "allowing", allow_nil, type_msg.any? ? type_msg.to_sentence : nil, "values", comp_msg.any? ? comp_msg.to_sentence : nil].compact.join(" ")
|
|
57
|
-
end
|
|
58
|
-
|
|
57
|
+
end
|
|
58
|
+
|
|
59
59
|
def method_missing(m, *args, &block)
|
|
60
60
|
if @@allowed_options.include?(m.to_sym)
|
|
61
61
|
raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" if args.length > 1
|
|
@@ -1,40 +1,36 @@
|
|
|
1
1
|
module Mongoid
|
|
2
2
|
module Matchers
|
|
3
|
-
module Validations
|
|
3
|
+
module Validations
|
|
4
4
|
class ValidateUniquenessOfMatcher < HaveValidationMatcher
|
|
5
|
+
include WithMessage
|
|
5
6
|
def initialize(field)
|
|
6
7
|
super(field, :uniqueness)
|
|
7
8
|
end
|
|
8
|
-
|
|
9
|
+
|
|
9
10
|
def scoped_to(*scope)
|
|
10
11
|
@scope = [scope].flatten.map(&:to_sym)
|
|
11
12
|
self
|
|
12
|
-
end
|
|
13
|
+
end
|
|
13
14
|
alias_method :scoped_on, :scoped_to
|
|
14
|
-
|
|
15
|
+
|
|
15
16
|
def case_insensitive
|
|
16
17
|
@case_insensitive = true
|
|
17
18
|
self
|
|
18
19
|
end
|
|
19
|
-
|
|
20
|
+
|
|
20
21
|
def allow_blank?(allow_blank)
|
|
21
22
|
@allow_blank = allow_blank
|
|
22
23
|
self
|
|
23
24
|
end
|
|
24
25
|
|
|
25
|
-
def with_message(message)
|
|
26
|
-
@expected_message = message
|
|
27
|
-
self
|
|
28
|
-
end
|
|
29
|
-
|
|
30
26
|
def matches?(actual)
|
|
31
27
|
return false unless @result = super(actual)
|
|
32
|
-
|
|
28
|
+
|
|
33
29
|
check_scope if @scope
|
|
34
30
|
check_allow_blank if @allow_blank
|
|
35
31
|
check_case_sensitivity if @case_insensitive
|
|
36
32
|
check_expected_message if @expected_message
|
|
37
|
-
|
|
33
|
+
|
|
38
34
|
@result
|
|
39
35
|
end
|
|
40
36
|
|
|
@@ -46,7 +42,7 @@ module Mongoid
|
|
|
46
42
|
options_desc << " with message '#{@expected_message}'" if @expected_message
|
|
47
43
|
super << options_desc.to_sentence
|
|
48
44
|
end
|
|
49
|
-
|
|
45
|
+
|
|
50
46
|
private
|
|
51
47
|
|
|
52
48
|
def check_allow_blank
|
|
@@ -67,7 +63,7 @@ module Mongoid
|
|
|
67
63
|
@result = false
|
|
68
64
|
end
|
|
69
65
|
end
|
|
70
|
-
|
|
66
|
+
|
|
71
67
|
def check_case_sensitivity
|
|
72
68
|
if @validator.options[:case_sensitive] == false
|
|
73
69
|
@positive_result_message << " with case insensitive values"
|
|
@@ -76,24 +72,11 @@ module Mongoid
|
|
|
76
72
|
@result = false
|
|
77
73
|
end
|
|
78
74
|
end
|
|
79
|
-
|
|
80
|
-
def check_expected_message
|
|
81
|
-
actual_message = @validator.options[:message]
|
|
82
|
-
if actual_message.nil?
|
|
83
|
-
@negative_result_message << " with no custom message"
|
|
84
|
-
@result = false
|
|
85
|
-
elsif actual_message == @expected_message
|
|
86
|
-
@positive_result_message << " with custom message '#{@expected_message}'"
|
|
87
|
-
else
|
|
88
|
-
@negative_result_message << " got message '#{actual_message}'"
|
|
89
|
-
@result = false
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
75
|
end
|
|
93
|
-
|
|
76
|
+
|
|
94
77
|
def validate_uniqueness_of(field)
|
|
95
78
|
ValidateUniquenessOfMatcher.new(field)
|
|
96
|
-
end
|
|
79
|
+
end
|
|
97
80
|
end
|
|
98
81
|
end
|
|
99
|
-
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Mongoid
|
|
2
|
+
module Matchers
|
|
3
|
+
module Validations
|
|
4
|
+
module WithMessage
|
|
5
|
+
def with_message(message)
|
|
6
|
+
@expected_message = message
|
|
7
|
+
self
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def check_expected_message
|
|
13
|
+
actual_message = @validator.options[:message]
|
|
14
|
+
if actual_message.nil?
|
|
15
|
+
@negative_result_message << " with no custom message"
|
|
16
|
+
@result = false
|
|
17
|
+
elsif actual_message == @expected_message
|
|
18
|
+
@positive_result_message << " with custom message '#{@expected_message}'"
|
|
19
|
+
else
|
|
20
|
+
@negative_result_message << " got message '#{actual_message}'"
|
|
21
|
+
@result = false
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/matchers/validations.rb
CHANGED
|
@@ -13,7 +13,9 @@ module Mongoid
|
|
|
13
13
|
def matches?(actual)
|
|
14
14
|
@klass = actual.is_a?(Class) ? actual : actual.class
|
|
15
15
|
|
|
16
|
-
@validator = @klass.validators_on(@field).detect{|v|
|
|
16
|
+
@validator = @klass.validators_on(@field).detect{ |v|
|
|
17
|
+
v.kind.to_s == @type and (!v.options[:on] or on_options_matches?(v))
|
|
18
|
+
}
|
|
17
19
|
|
|
18
20
|
if @validator
|
|
19
21
|
@negative_result_message = "#{@type.inspect} validator on #{@field.inspect}"
|
|
@@ -52,7 +54,7 @@ module Mongoid
|
|
|
52
54
|
if validator_on_methods.any?
|
|
53
55
|
message = " on methods: #{validator_on_methods}"
|
|
54
56
|
|
|
55
|
-
if (@
|
|
57
|
+
if on_options_covered_by?( @validator )
|
|
56
58
|
@positive_result_message << message
|
|
57
59
|
else
|
|
58
60
|
@negative_result_message << message
|
|
@@ -60,6 +62,16 @@ module Mongoid
|
|
|
60
62
|
end
|
|
61
63
|
end
|
|
62
64
|
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def on_options_matches?(validator)
|
|
69
|
+
@options[:on] and validator.options[:on] and on_options_covered_by?(validator)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def on_options_covered_by?(validator)
|
|
73
|
+
([@options[:on]].flatten - [validator.options[:on]].flatten).empty?
|
|
74
|
+
end
|
|
63
75
|
end
|
|
64
76
|
end
|
|
65
77
|
end
|
data/lib/mongoid-rspec.rb
CHANGED
|
@@ -10,6 +10,7 @@ require 'matchers/indexes'
|
|
|
10
10
|
require 'matchers/allow_mass_assignment'
|
|
11
11
|
require 'matchers/accept_nested_attributes'
|
|
12
12
|
require 'matchers/validations'
|
|
13
|
+
require 'matchers/validations/with_message'
|
|
13
14
|
require 'matchers/validations/associated'
|
|
14
15
|
require 'matchers/validations/confirmation_of'
|
|
15
16
|
require 'matchers/validations/exclusion_of'
|
data/mongoid-rspec.gemspec
CHANGED
data/spec/models/article.rb
CHANGED
|
@@ -5,20 +5,24 @@ class Article
|
|
|
5
5
|
include Mongoid::Versioning
|
|
6
6
|
include Mongoid::MultiParameterAttributes
|
|
7
7
|
|
|
8
|
-
field :title, :
|
|
8
|
+
field :title, localize: true
|
|
9
9
|
field :content
|
|
10
|
-
field :published, :
|
|
11
|
-
field :allow_comments, :
|
|
12
|
-
field :number_of_comments, :
|
|
10
|
+
field :published, type: Boolean, default: false
|
|
11
|
+
field :allow_comments, type: Boolean, default: true
|
|
12
|
+
field :number_of_comments, type: Integer
|
|
13
|
+
field :status, type: Symbol
|
|
13
14
|
|
|
14
|
-
embeds_many :comments
|
|
15
|
+
embeds_many :comments, cascade_callbacks: true
|
|
15
16
|
embeds_one :permalink
|
|
16
|
-
belongs_to :author, :
|
|
17
|
+
belongs_to :author, class_name: 'User', inverse_of: :articles, index: true
|
|
17
18
|
|
|
18
|
-
validates :title, :
|
|
19
|
+
validates :title, presence: true
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
validates_inclusion_of :status, in: [:pending], on: :create
|
|
22
|
+
validates_inclusion_of :status, in: [:approved, :rejected ], on: :update
|
|
23
|
+
|
|
24
|
+
validates_length_of :title, within: 8..16
|
|
25
|
+
validates_length_of :content, minimum: 200
|
|
22
26
|
|
|
23
27
|
index({ title: 1 }, { unique: true, background: true })
|
|
24
28
|
index({ published: 1 })
|
data/spec/models/comment.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
class MovieArticle < Article
|
|
2
|
-
|
|
3
|
-
field :
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
|
|
3
|
+
field :rating, type: Float
|
|
4
|
+
field :classification, type: Integer
|
|
5
|
+
|
|
6
|
+
validates :rating, numericality: { greater_than: 0, less_than_or_equal_to: 5 }
|
|
7
|
+
validates :classification, numericality: { even: true, only_integer: true, allow_nil: false }
|
|
7
8
|
end
|
data/spec/models/permalink.rb
CHANGED
data/spec/models/person.rb
CHANGED
data/spec/models/profile.rb
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
class Profile
|
|
2
2
|
include Mongoid::Document
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
field :first_name
|
|
5
5
|
field :last_name
|
|
6
6
|
field :age
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
field :hobbies, type: Array, default: []
|
|
8
|
+
|
|
9
|
+
embedded_in :user, inverse_of: :profile
|
|
10
|
+
|
|
11
|
+
validates :age, numericality: { greater_than: 0 }
|
|
12
|
+
validates :terms_of_service, acceptance: true
|
|
13
|
+
validates :hobbies, length: { minimum: 1, message: "requires at least one hobby" }
|
|
12
14
|
|
|
13
|
-
index({first_name: 1, last_name: 1 })
|
|
15
|
+
index({ first_name: 1, last_name: 1 })
|
|
14
16
|
end
|
data/spec/models/record.rb
CHANGED
data/spec/models/site.rb
CHANGED
data/spec/models/user.rb
CHANGED
|
@@ -10,25 +10,26 @@ class User
|
|
|
10
10
|
field :provider_uid
|
|
11
11
|
field :locale
|
|
12
12
|
|
|
13
|
-
belongs_to :site, :
|
|
14
|
-
has_many :articles, :
|
|
15
|
-
has_many :comments, :
|
|
16
|
-
has_and_belongs_to_many :children, :
|
|
17
|
-
has_one :record, :
|
|
13
|
+
belongs_to :site, inverse_of: :users
|
|
14
|
+
has_many :articles, foreign_key: :author_id, order: :title
|
|
15
|
+
has_many :comments, dependent: :destroy, autosave: true
|
|
16
|
+
has_and_belongs_to_many :children, class_name: "User"
|
|
17
|
+
has_one :record, autobuild: true
|
|
18
18
|
|
|
19
19
|
embeds_one :profile
|
|
20
20
|
|
|
21
|
-
validates :login, :
|
|
22
|
-
validates :email, :
|
|
23
|
-
validates :role, :
|
|
24
|
-
validates :profile, :
|
|
25
|
-
validates :age, :
|
|
26
|
-
validates :password, :
|
|
21
|
+
validates :login, presence: true, uniqueness: { scope: :site }, format: { with: /^[\w\-]+$/ }, exclusion: { in: ["super", "index", "edit"] }
|
|
22
|
+
validates :email, uniqueness: { case_sensitive: false, scope: :site, message: "is already taken" }, confirmation: true
|
|
23
|
+
validates :role, presence: true, inclusion: { in: ["admin", "moderator", "member"] }
|
|
24
|
+
validates :profile, presence: true, associated: true
|
|
25
|
+
validates :age, presence: true, numericality: true, inclusion: { in: 23..42 }, on: [:create, :update]
|
|
26
|
+
validates :password, presence: true, on: [:create, :update]
|
|
27
|
+
validates :password, exclusion: { in: ->(user) { ['password'] } }
|
|
27
28
|
validates :provider_uid, presence: true
|
|
28
|
-
validates :locale, :
|
|
29
|
+
validates :locale, inclusion: { in: ->(user) { [:en, :ru] } }
|
|
29
30
|
|
|
30
31
|
attr_accessible :login, :email, :age, :password
|
|
31
|
-
attr_accessible :role, :
|
|
32
|
+
attr_accessible :role, as: :admin
|
|
32
33
|
|
|
33
34
|
accepts_nested_attributes_for :articles, :comments
|
|
34
35
|
|
|
@@ -8,7 +8,7 @@ describe "AllowMassAssignment" do
|
|
|
8
8
|
it { should allow_mass_assignment_of(:password) }
|
|
9
9
|
it { should allow_mass_assignment_of(:password) }
|
|
10
10
|
it { should allow_mass_assignment_of(:role).as(:admin) }
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
it { should_not allow_mass_assignment_of(:role) }
|
|
13
13
|
end
|
|
14
14
|
end
|
|
@@ -19,12 +19,12 @@ describe "Associations" do
|
|
|
19
19
|
|
|
20
20
|
describe Article do
|
|
21
21
|
it { should belong_to(:author).of_type(User).as_inverse_of(:articles).with_index }
|
|
22
|
-
it { should embed_many(:comments) }
|
|
22
|
+
it { should embed_many(:comments).with_cascading_callbacks }
|
|
23
23
|
it { should embed_one(:permalink) }
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
describe Comment do
|
|
27
|
-
it { should be_embedded_in(:article).as_inverse_of(:comments) }
|
|
27
|
+
it { should be_embedded_in(:article).as_inverse_of(:comments).with_polymorphism }
|
|
28
28
|
it { should belong_to(:user).as_inverse_of(:comments) }
|
|
29
29
|
end
|
|
30
30
|
|
|
@@ -37,6 +37,6 @@ describe "Associations" do
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
describe Site do
|
|
40
|
-
it { should have_many(:users).as_inverse_of(:site) }
|
|
40
|
+
it { should have_many(:users).as_inverse_of(:site).ordered_by(:email.desc) }
|
|
41
41
|
end
|
|
42
42
|
end
|
data/spec/unit/indexes_spec.rb
CHANGED
|
@@ -13,6 +13,7 @@ describe "Validations" do
|
|
|
13
13
|
it { should validate_format_of(:login).to_allow("valid_login").not_to_allow("invalid login") }
|
|
14
14
|
it { should validate_associated(:profile) }
|
|
15
15
|
it { should validate_exclusion_of(:login).to_not_allow("super", "index", "edit") }
|
|
16
|
+
it { should validate_exclusion_of(:password).to_not_allow("password") }
|
|
16
17
|
it { should validate_inclusion_of(:role).to_allow("admin", "member") }
|
|
17
18
|
it { should validate_confirmation_of(:email) }
|
|
18
19
|
it { should validate_presence_of(:age).on(:create, :update) }
|
|
@@ -26,12 +27,15 @@ describe "Validations" do
|
|
|
26
27
|
describe Profile do
|
|
27
28
|
it { should validate_numericality_of(:age).greater_than(0) }
|
|
28
29
|
it { should validate_acceptance_of(:terms_of_service) }
|
|
30
|
+
it { should validate_length_of(:hobbies).with_minimum(1).with_message("requires at least one hobby") }
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
describe Article do
|
|
32
34
|
it { should validate_length_of(:title).within(8..16) }
|
|
33
35
|
it { should_not validate_length_of(:content).greater_than(200).less_than(16) }
|
|
34
36
|
it { should validate_length_of(:content).greater_than(200) }
|
|
37
|
+
it { should validate_inclusion_of(:status).to_allow([:pending]).on( :create ) }
|
|
38
|
+
it { should validate_inclusion_of(:status).to_allow([:approved, :rejected]).on( :update ) }
|
|
35
39
|
end
|
|
36
40
|
|
|
37
41
|
describe MovieArticle do
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
class SsnValidator < ActiveModel::EachValidator
|
|
2
2
|
|
|
3
3
|
def validate_each(record, attribute, value)
|
|
4
|
-
|
|
4
|
+
unless valid_ssn?(record, attribute, value)
|
|
5
|
+
record.errors[attribute] << "#{value} is not a valid Social Security Number"
|
|
6
|
+
end
|
|
5
7
|
end
|
|
6
8
|
|
|
7
9
|
def self.kind() :custom end
|