nested_validator 1.0.4 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/Gemfile.lock +7 -7
- data/README.md +18 -0
- data/lib/nested_validator/nested_validator.rb +23 -7
- data/lib/nested_validator/validate_nested_matcher.rb +27 -8
- data/lib/nested_validator/version.rb +1 -1
- data/spec/lib/nested_validator/nested_validator_context.rb +42 -0
- data/spec/lib/nested_validator/nested_validator_spec.rb +203 -152
- data/spec/lib/nested_validator/usage_spec.rb +10 -0
- data/spec/lib/nested_validator/validate_nested_matcher_spec.rb +84 -105
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
OTc4N2MxNWMwNGRjZGRlNjA0MzBkNzZlZjRlZjczMGU5M2I4YjE3OQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YmNhNWI2YzdjYmE1YjNhNGE0ZDdiMzY4Yzg0YWYwZmRiODUzNjk2Yg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NWY5NmU2ZjA4NmY4OWI3ZGMwMjI0NzU0ZDM4YWVjZjRlNzYzZDc5MTE4ZDM4
|
10
|
+
NjgxZWQyNTc3ODZhMDhlYzc4OGQyYTk4MTI1Y2Q1N2NhYmFiNmMxMTgwYzBl
|
11
|
+
NTEzYjhhMDM2MjMzMTI0MzQ3ZGZkY2EyNmRiNzAxZDJjZThhMTE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
YzkyZmRiNGJiYjFkZTRhMWIxODY0ZmFiNGZkN2Y5Mjk5ZjYyMTU5YzJkODFi
|
14
|
+
ODEzMWRmZDg2NjdhNjNhYzdjNTUxZWEwODY1ZjcwNTE2N2YyMjQxMjM1ZmNj
|
15
|
+
MzczMjU5ZmQ3MmY1ZDlmYTBiYzA1ZjYwNGUxNGM1NjA0Mzg5ODM=
|
data/Gemfile.lock
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
nested_validator (1.0.
|
4
|
+
nested_validator (1.0.5)
|
5
5
|
activemodel
|
6
6
|
activesupport
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
activemodel (4.1.
|
12
|
-
activesupport (= 4.1.
|
11
|
+
activemodel (4.1.1)
|
12
|
+
activesupport (= 4.1.1)
|
13
13
|
builder (~> 3.1)
|
14
|
-
activesupport (4.1.
|
14
|
+
activesupport (4.1.1)
|
15
15
|
i18n (~> 0.6, >= 0.6.9)
|
16
16
|
json (~> 1.7, >= 1.7.7)
|
17
17
|
minitest (~> 5.1)
|
@@ -26,8 +26,8 @@ GEM
|
|
26
26
|
thor (= 0.18.1)
|
27
27
|
diff-lcs (1.2.5)
|
28
28
|
docile (1.1.5)
|
29
|
-
i18n (0.
|
30
|
-
json (1.8.
|
29
|
+
i18n (0.7.0)
|
30
|
+
json (1.8.2)
|
31
31
|
mime-types (2.4.3)
|
32
32
|
minitest (5.3.3)
|
33
33
|
multi_json (1.10.1)
|
@@ -57,7 +57,7 @@ GEM
|
|
57
57
|
term-ansicolor (1.2.2)
|
58
58
|
tins (~> 0.8)
|
59
59
|
thor (0.18.1)
|
60
|
-
thread_safe (0.3.
|
60
|
+
thread_safe (0.3.4)
|
61
61
|
tins (0.13.2)
|
62
62
|
tzinfo (1.1.0)
|
63
63
|
thread_safe (~> 0.1)
|
data/README.md
CHANGED
@@ -94,6 +94,23 @@ puts parent.errors.messages
|
|
94
94
|
# => {:"child attribute1"=>["can't be blank"]}
|
95
95
|
```
|
96
96
|
|
97
|
+
### OK, is there a way to require one of several attributes?
|
98
|
+
|
99
|
+
Yes. You can use the ```any``` option which requires that at least
|
100
|
+
one of the specified child attributes is valid:
|
101
|
+
|
102
|
+
``` ruby
|
103
|
+
class ParentExcept < ParentBase
|
104
|
+
validates :child, nested: { any: [:attribute1, :attribute2] }
|
105
|
+
end
|
106
|
+
|
107
|
+
parent = ParentExcept.new
|
108
|
+
parent.valid?
|
109
|
+
puts parent.errors.messages
|
110
|
+
|
111
|
+
# => {:"child attribute1"=>["can't be blank"], :"child attribute2"=>["can't be blank"]}
|
112
|
+
```
|
113
|
+
|
97
114
|
### Alright, what if I want a custom message?
|
98
115
|
|
99
116
|
You can specify a ```prefix``` instead of the child's attribute name:
|
@@ -183,6 +200,7 @@ describe Parent do
|
|
183
200
|
it { should validate_nested(:child).only(:attribute1, :attribute2) }
|
184
201
|
it { should validate_nested(:child).except(:attribute1) }
|
185
202
|
it { should validate_nested(:child).except(:attribute1, :attribute2) }
|
203
|
+
it { should validate_nested(:child).any(:attribute1, :attribute2) }
|
186
204
|
end
|
187
205
|
```
|
188
206
|
## Contributing
|
@@ -37,7 +37,12 @@ module ActiveModel
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def record_error(record, prefix, value)
|
40
|
+
if any.present?
|
41
|
+
valid_keys = any - value.errors.keys.map{|k| k.to_s.split.first}
|
42
|
+
return if valid_keys.present?
|
43
|
+
end
|
40
44
|
value.errors.each do |key, error|
|
45
|
+
key = key.to_s.split.first
|
41
46
|
record.errors.add(nested_key(prefix, key), error) if include?(key)
|
42
47
|
end
|
43
48
|
end
|
@@ -47,21 +52,31 @@ module ActiveModel
|
|
47
52
|
end
|
48
53
|
|
49
54
|
def include?(key)
|
50
|
-
if
|
51
|
-
only.
|
52
|
-
elsif
|
53
|
-
except.
|
55
|
+
if only.present?
|
56
|
+
only.include?(key.to_s)
|
57
|
+
elsif except.present?
|
58
|
+
!except.include?(key.to_s)
|
59
|
+
elsif any.present?
|
60
|
+
any.include?(key.to_s)
|
54
61
|
else
|
55
62
|
true
|
56
63
|
end
|
57
64
|
end
|
58
65
|
|
59
66
|
def only
|
60
|
-
@only ||=
|
67
|
+
@only ||= prepare_options(:only)
|
61
68
|
end
|
62
69
|
|
63
70
|
def except
|
64
|
-
@except ||=
|
71
|
+
@except ||= prepare_options(:except)
|
72
|
+
end
|
73
|
+
|
74
|
+
def any
|
75
|
+
@any ||= prepare_options(:any)
|
76
|
+
end
|
77
|
+
|
78
|
+
def prepare_options(key)
|
79
|
+
Array.wrap(options[key]).map(&:to_s).map{|k| k.split(/\s+|,/)}.flatten.reject(&:blank?)
|
65
80
|
end
|
66
81
|
end
|
67
82
|
|
@@ -75,7 +90,8 @@ module ActiveModel
|
|
75
90
|
# end
|
76
91
|
#
|
77
92
|
# class Child < ActiveRecord::Base
|
78
|
-
#
|
93
|
+
# attr_accessor :attribute
|
94
|
+
# validates :attribute, presence: true
|
79
95
|
#
|
80
96
|
# validates_presence_of :attribute
|
81
97
|
# end
|
@@ -12,10 +12,11 @@ require 'rspec/expectations'
|
|
12
12
|
# it { should validate_nested(:child).only(:attribute1) }
|
13
13
|
# it { should validate_nested(:child).only(:attribute1, :attribute2) }
|
14
14
|
# it { should validate_nested(:child).except(:attribute1) }
|
15
|
+
# it { should validate_nested(:child).any(:attribute1, :attribute2) }
|
15
16
|
# end
|
16
17
|
RSpec::Matchers.define :validate_nested do |child_name|
|
17
18
|
|
18
|
-
attr_accessor :child_name, :prefix, :only_keys, :except_keys
|
19
|
+
attr_accessor :child_name, :prefix, :only_keys, :except_keys, :any_keys
|
19
20
|
attr_accessor :parent, :actual_keys
|
20
21
|
|
21
22
|
TEST_KEY ||= :__test_key__
|
@@ -24,6 +25,7 @@ RSpec::Matchers.define :validate_nested do |child_name|
|
|
24
25
|
self.prefix ||= ''
|
25
26
|
self.only_keys ||= []
|
26
27
|
self.except_keys ||= []
|
28
|
+
self.any_keys ||= []
|
27
29
|
|
28
30
|
self.child_name = child_name
|
29
31
|
self.parent = parent
|
@@ -32,20 +34,28 @@ RSpec::Matchers.define :validate_nested do |child_name|
|
|
32
34
|
self.actual_keys = (error_keys_when_child_validity_is(false) - error_keys_when_child_validity_is(true))
|
33
35
|
return false if invalid_child_keys.present?
|
34
36
|
|
35
|
-
|
36
37
|
actual_keys == expected_keys
|
37
38
|
end
|
38
39
|
|
40
|
+
def prepare_keys(keys)
|
41
|
+
if keys.length == 1 && keys[0].is_a?(String)
|
42
|
+
keys[0].split(/\s+|,/).reject(&:blank?)
|
43
|
+
else
|
44
|
+
keys
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
39
48
|
chain(:with_prefix) { |prefix| self.prefix = prefix }
|
40
|
-
chain(:only) { |*only| self.only_keys = only }
|
41
|
-
chain(:except) { |*except| self.except_keys = except }
|
49
|
+
chain(:only) { |*only| self.only_keys = prepare_keys(only) }
|
50
|
+
chain(:except) { |*except| self.except_keys = prepare_keys(except) }
|
51
|
+
chain(:any) { |*any| self.any_keys = prepare_keys(any) }
|
42
52
|
|
43
53
|
def child
|
44
|
-
parent.
|
54
|
+
parent.public_send child_name
|
45
55
|
end
|
46
56
|
|
47
57
|
def error_keys_when_child_validity_is(valid)
|
48
|
-
child_error_keys = combine TEST_KEY, only_keys, except_keys
|
58
|
+
child_error_keys = combine TEST_KEY, only_keys, except_keys, any_keys
|
49
59
|
child_errors = child_error_keys.inject({}){|result, key| result[key] = ['error message'];result }
|
50
60
|
|
51
61
|
allow(child).to receive(:valid?) { valid }
|
@@ -68,8 +78,15 @@ RSpec::Matchers.define :validate_nested do |child_name|
|
|
68
78
|
end
|
69
79
|
|
70
80
|
def expected_child_keys
|
71
|
-
expected_keys =
|
72
|
-
|
81
|
+
expected_keys = case
|
82
|
+
when only_keys.present?
|
83
|
+
only_keys
|
84
|
+
when any_keys.present?
|
85
|
+
any_keys
|
86
|
+
else
|
87
|
+
[TEST_KEY]
|
88
|
+
end
|
89
|
+
unique_except_keys = except_keys - only_keys - any_keys
|
73
90
|
combine expected_keys - unique_except_keys
|
74
91
|
end
|
75
92
|
|
@@ -78,6 +95,7 @@ RSpec::Matchers.define :validate_nested do |child_name|
|
|
78
95
|
end
|
79
96
|
|
80
97
|
def invalid_child_keys
|
98
|
+
#binding.pry
|
81
99
|
(only_keys + except_keys).reject{|key| child.respond_to? key}
|
82
100
|
end
|
83
101
|
|
@@ -85,6 +103,7 @@ RSpec::Matchers.define :validate_nested do |child_name|
|
|
85
103
|
message = "validate nested #{show child_name}"
|
86
104
|
message << " with only: #{show only_keys}" if only_keys.present?
|
87
105
|
message << " except: #{show except_keys}" if except_keys.present?
|
106
|
+
message << " any: #{show any_keys}" if any_keys.present?
|
88
107
|
message << " with prefix #{show prefix}" if prefix.present?
|
89
108
|
message
|
90
109
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'nested_validator'
|
3
|
+
|
4
|
+
shared_context 'nested validator' do
|
5
|
+
let(:parent_class) do
|
6
|
+
opts = options
|
7
|
+
Class.new {
|
8
|
+
include ActiveModel::Validations
|
9
|
+
|
10
|
+
attr_accessor :child, :other
|
11
|
+
|
12
|
+
instance_eval "validates :child, nested: #{opts}"
|
13
|
+
|
14
|
+
def initialize(child=nil)
|
15
|
+
self.child = child
|
16
|
+
self.other = ''
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
'parent'
|
21
|
+
end
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:options) { 'true' }
|
26
|
+
|
27
|
+
let(:child_class) do
|
28
|
+
Class.new {
|
29
|
+
include ActiveModel::Validations
|
30
|
+
|
31
|
+
attr_accessor :attribute, :attribute2, :attribute3
|
32
|
+
|
33
|
+
validates :attribute, presence: true
|
34
|
+
validates :attribute2, presence: true
|
35
|
+
validates :attribute3, presence: true
|
36
|
+
|
37
|
+
def self.model_name
|
38
|
+
ActiveModel::Name.new(self, nil, 'temp')
|
39
|
+
end
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
@@ -1,218 +1,269 @@
|
|
1
|
-
|
2
|
-
require 'nested_validator'
|
1
|
+
require_relative 'nested_validator_context'
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# To keep ActiveModel happy
|
10
|
-
def self.model_name
|
11
|
-
ActiveModel::Name.new(self, nil, 'temp')
|
12
|
-
end
|
13
|
-
}
|
3
|
+
RSpec::Matchers.define :detect_nested_error_for do
|
4
|
+
match do
|
5
|
+
subject.valid?
|
6
|
+
error.present?
|
14
7
|
end
|
15
8
|
|
16
|
-
|
17
|
-
|
18
|
-
attr_accessor :child1, :child2
|
19
|
-
}
|
9
|
+
def description
|
10
|
+
"detect an error for '#{child_attribute}'"
|
20
11
|
end
|
21
12
|
|
22
|
-
|
23
|
-
|
24
|
-
attr_accessor :attribute1
|
25
|
-
validates :attribute1, presence: true
|
26
|
-
|
27
|
-
attr_accessor :attribute2
|
28
|
-
validates :attribute2, presence: true
|
29
|
-
|
30
|
-
attr_accessor :attribute3
|
31
|
-
validates :attribute3, presence: true
|
32
|
-
|
33
|
-
def initialize
|
34
|
-
@attribute1 = 'valid'
|
35
|
-
@attribute2 = 'valid'
|
36
|
-
@attribute3 = 'valid'
|
37
|
-
end
|
38
|
-
}
|
13
|
+
failure_message do
|
14
|
+
"has no error for '#{child_attribute}'"
|
39
15
|
end
|
40
16
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
def parent_with(&block)
|
45
|
-
parent = Class.new(parent_class) { instance_exec &block }.new
|
46
|
-
parent.child1 = child1
|
47
|
-
parent.child2 = child2
|
48
|
-
parent
|
17
|
+
failure_message_when_negated do
|
18
|
+
"has an error '#{child_attribute}' => '#{error}'"
|
49
19
|
end
|
50
20
|
|
51
|
-
def
|
52
|
-
|
21
|
+
def error
|
22
|
+
subject.errors[child_attribute].first
|
53
23
|
end
|
54
24
|
|
55
|
-
|
56
|
-
|
57
|
-
specify "when #{child_name}.#{attribute} set to nil" do
|
58
|
-
send(child_name).send("#{attribute}=", nil)
|
59
|
-
expect(subject).to be_valid
|
60
|
-
end
|
61
|
-
end
|
25
|
+
def attribute
|
26
|
+
expected
|
62
27
|
end
|
63
28
|
|
64
|
-
|
65
|
-
|
66
|
-
specify "when #{child_name}.#{attribute} set to nil" do
|
67
|
-
send(child_name).send("#{attribute}=", nil)
|
68
|
-
expect(subject).to be_invalid
|
69
|
-
end
|
70
|
-
end
|
29
|
+
def attribute_display_value
|
30
|
+
"#{subject.child.send(attribute) || 'nil'}"
|
71
31
|
end
|
72
32
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
it_should_validate_nested 'invalid:', :child1, :attribute1, :attribute2, :attribute3
|
33
|
+
def child_attribute
|
34
|
+
:"child #{attribute}"
|
77
35
|
end
|
36
|
+
end
|
78
37
|
|
79
|
-
|
80
|
-
context 'with scalar values' do
|
81
|
-
before { child1.attribute1 = nil;subject.valid? }
|
82
|
-
|
83
|
-
describe 'with no prefix' do
|
84
|
-
subject { with_nested_options true }
|
38
|
+
describe 'nested validation' do
|
85
39
|
|
86
|
-
|
87
|
-
end
|
40
|
+
include_context 'nested validator'
|
88
41
|
|
89
|
-
|
90
|
-
|
42
|
+
let(:options) { 'true' }
|
43
|
+
let(:parent) { parent_class.new child }
|
44
|
+
let(:child) { child_class.new }
|
91
45
|
|
92
|
-
|
93
|
-
end
|
46
|
+
subject { parent }
|
94
47
|
|
95
|
-
|
96
|
-
subject { with_nested_options prefix: '' }
|
48
|
+
let(:options) { self.class.description }
|
97
49
|
|
98
|
-
|
99
|
-
|
50
|
+
describe 'should support boolean value' do
|
51
|
+
describe 'true' do
|
52
|
+
it { should detect_nested_error_for :attribute }
|
100
53
|
end
|
101
54
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
subject { with_nested_options true }
|
55
|
+
describe 'false' do
|
56
|
+
it { should_not detect_nested_error_for :attribute }
|
57
|
+
end
|
58
|
+
end
|
107
59
|
|
108
|
-
|
109
|
-
|
60
|
+
describe '"only" should be ignored if value missing' do
|
61
|
+
['{only: ""}',
|
62
|
+
'{only: nil}',
|
63
|
+
].each do |only|
|
64
|
+
describe only do
|
65
|
+
it { should detect_nested_error_for :attribute }
|
66
|
+
it { should detect_nested_error_for :attribute2 }
|
67
|
+
it { should detect_nested_error_for :attribute3 }
|
110
68
|
end
|
69
|
+
end
|
70
|
+
end
|
111
71
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
72
|
+
describe '"only" should support a single key' do
|
73
|
+
['{only: :attribute}',
|
74
|
+
'{only: "attribute"}',
|
75
|
+
'{only: " \tattribute\t\n"}'
|
76
|
+
].each do |only|
|
77
|
+
describe only do
|
78
|
+
it { should detect_nested_error_for :attribute }
|
79
|
+
it { should_not detect_nested_error_for :attribute2 }
|
80
|
+
it { should_not detect_nested_error_for :attribute3 }
|
116
81
|
end
|
82
|
+
end
|
83
|
+
end
|
117
84
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
85
|
+
describe '"only" should support multiple keys' do
|
86
|
+
['{only: [:attribute, :attribute2]}',
|
87
|
+
'{only: "attribute attribute2"}',
|
88
|
+
'{only: "attribute, attribute2"}',
|
89
|
+
'{only: " \tattribute,\n\tattribute2\t\n"}'
|
90
|
+
].each do |only|
|
91
|
+
describe only do
|
92
|
+
it { should detect_nested_error_for :attribute }
|
93
|
+
it { should detect_nested_error_for :attribute2 }
|
94
|
+
it { should_not detect_nested_error_for :attribute3 }
|
122
95
|
end
|
123
96
|
end
|
124
97
|
end
|
125
98
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
99
|
+
describe '"except" should be ignored if value missing' do
|
100
|
+
['{except: ""}',
|
101
|
+
'{except: nil}',
|
102
|
+
].each do |only|
|
103
|
+
describe only do
|
104
|
+
it { should detect_nested_error_for :attribute }
|
105
|
+
it { should detect_nested_error_for :attribute2 }
|
106
|
+
it { should detect_nested_error_for :attribute3 }
|
107
|
+
end
|
134
108
|
end
|
109
|
+
end
|
135
110
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
111
|
+
describe '"except" should support a single key' do
|
112
|
+
['{except: :attribute}',
|
113
|
+
'{except: "attribute"}',
|
114
|
+
'{except: " \tattribute\t\n"}'
|
115
|
+
].each do |only|
|
116
|
+
describe only do
|
117
|
+
it { should_not detect_nested_error_for :attribute }
|
118
|
+
it { should detect_nested_error_for :attribute2 }
|
119
|
+
it { should detect_nested_error_for :attribute2 }
|
120
|
+
end
|
140
121
|
end
|
122
|
+
end
|
141
123
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
124
|
+
describe '"except" should support multiple keys' do
|
125
|
+
['{except: [:attribute, :attribute2]}',
|
126
|
+
'{except: "attribute attribute2"}',
|
127
|
+
'{except: "attribute, attribute2"}',
|
128
|
+
'{except: " \tattribute,\n\tattribute2\t\n"}'
|
129
|
+
].each do |only|
|
130
|
+
describe only do
|
131
|
+
it { should_not detect_nested_error_for :attribute }
|
132
|
+
it { should_not detect_nested_error_for :attribute2 }
|
133
|
+
it { should detect_nested_error_for :attribute3 }
|
134
|
+
end
|
146
135
|
end
|
147
136
|
end
|
148
137
|
|
149
|
-
describe '"
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
138
|
+
describe '"any" should be ignored if value missing' do
|
139
|
+
['{any: ""}',
|
140
|
+
'{any: nil}',
|
141
|
+
].each do |only|
|
142
|
+
describe only do
|
143
|
+
it { should detect_nested_error_for :attribute }
|
144
|
+
it { should detect_nested_error_for :attribute2 }
|
145
|
+
it { should detect_nested_error_for :attribute3 }
|
146
|
+
end
|
157
147
|
end
|
148
|
+
end
|
158
149
|
|
159
|
-
|
150
|
+
describe '"any" should behave like "only" with a single key' do
|
151
|
+
[
|
152
|
+
'{any: :attribute}',
|
153
|
+
'{any: "attribute"}',
|
154
|
+
'{any: " \tattribute\t\n"}'
|
155
|
+
].each do |only|
|
156
|
+
describe only do
|
157
|
+
it { should detect_nested_error_for :attribute }
|
158
|
+
it { should_not detect_nested_error_for :attribute2 }
|
159
|
+
it { should_not detect_nested_error_for :attribute3 }
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
160
163
|
|
161
|
-
|
164
|
+
describe '"any" should not detect errors if first attribute valid' do
|
165
|
+
before { child.attribute = true }
|
166
|
+
|
167
|
+
['{any: [:attribute, :attribute2]}',
|
168
|
+
'{any: "attribute attribute2"}',
|
169
|
+
'{any: "attribute, attribute2"}',
|
170
|
+
'{any: " \tattribute,\n\tattribute2\t\n"}'
|
171
|
+
].each do |only|
|
172
|
+
describe only do
|
173
|
+
it { should_not detect_nested_error_for :attribute }
|
174
|
+
it { should_not detect_nested_error_for :attribute2 }
|
175
|
+
it { should_not detect_nested_error_for :attribute3 }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
162
179
|
|
163
|
-
|
164
|
-
|
180
|
+
describe '"any" should not detect errors if second attribute valid' do
|
181
|
+
before { child.attribute2 = true }
|
182
|
+
|
183
|
+
['{any: [:attribute, :attribute2]}',
|
184
|
+
'{any: "attribute attribute2"}',
|
185
|
+
'{any: "attribute, attribute2"}',
|
186
|
+
'{any: " \tattribute,\n\tattribute2\t\n"}'
|
187
|
+
].each do |only|
|
188
|
+
describe only do
|
189
|
+
it { should_not detect_nested_error_for :attribute }
|
190
|
+
it { should_not detect_nested_error_for :attribute2 }
|
191
|
+
it { should_not detect_nested_error_for :attribute3 }
|
192
|
+
end
|
165
193
|
end
|
166
194
|
end
|
167
195
|
|
168
|
-
describe '
|
169
|
-
|
170
|
-
|
196
|
+
describe 'error message keys' do
|
197
|
+
before { parent.valid? }
|
198
|
+
subject { parent.errors.messages.keys.first }
|
171
199
|
|
172
|
-
|
173
|
-
|
174
|
-
|
200
|
+
context('true') { it { should eq :'child attribute' } }
|
201
|
+
context('{prefix: ""}') { it { should eq :'attribute' } }
|
202
|
+
context('{prefix: nil}') { it { should eq :'attribute' } }
|
203
|
+
context('{prefix: "OMG"}') { it { should eq :'OMG attribute' } }
|
175
204
|
|
176
|
-
|
177
|
-
|
205
|
+
context 'with arrays' do
|
206
|
+
let(:child) { [child_class.new] }
|
178
207
|
|
179
|
-
|
180
|
-
|
208
|
+
context('true') { it { should eq :'child[0] attribute' } }
|
209
|
+
context('{prefix: ""}') { it { should eq :'[0] attribute' } }
|
210
|
+
context('{prefix: nil}') { it { should eq :'[0] attribute' } }
|
211
|
+
context('{prefix: "OMG"}') { it { should eq :'OMG[0] attribute' } }
|
181
212
|
end
|
182
|
-
end
|
183
213
|
|
184
|
-
|
185
|
-
|
186
|
-
subject { with_nested_options only: :attribute1, except: :attribute1 }
|
214
|
+
context('with hashes') do
|
215
|
+
let(:child) { {key: child_class.new} }
|
187
216
|
|
188
|
-
|
189
|
-
|
217
|
+
context('true') { it { should eq :'child[key] attribute' } }
|
218
|
+
context('{prefix: ""}') { it { should eq :'[key] attribute' } }
|
219
|
+
context('{prefix: nil}') { it { should eq :'[key] attribute' } }
|
220
|
+
context('{prefix: "OMG"}') { it { should eq :'OMG[key] attribute' } }
|
190
221
|
end
|
191
222
|
end
|
192
223
|
|
193
|
-
describe '
|
224
|
+
describe 'multiple levels' do
|
225
|
+
let(:grand_parent_class) do
|
226
|
+
opts = parent_options
|
227
|
+
Class.new {
|
228
|
+
include ActiveModel::Validations
|
194
229
|
|
195
|
-
|
196
|
-
subject { parent_with { validates_nested :child1 } }
|
230
|
+
attr_accessor :parent
|
197
231
|
|
198
|
-
|
199
|
-
it_should_validate_nested 'valid:', :child2, :attribute1, :attribute2, :attribute3
|
200
|
-
end
|
232
|
+
instance_eval "validates :parent, nested: #{opts}"
|
201
233
|
|
202
|
-
|
203
|
-
|
234
|
+
def initialize(parent=nil)
|
235
|
+
self.parent = parent
|
236
|
+
end
|
204
237
|
|
205
|
-
|
206
|
-
|
238
|
+
def to_s
|
239
|
+
'grand_parent'
|
240
|
+
end
|
241
|
+
}
|
207
242
|
end
|
208
243
|
|
209
|
-
|
210
|
-
|
244
|
+
let(:grand_parent) { grand_parent_class.new parent }
|
245
|
+
let(:parent_options) { self.class.description }
|
246
|
+
let(:options) { 'true' }
|
247
|
+
|
248
|
+
[
|
249
|
+
'{only: :other}',
|
250
|
+
'{any: :other}',
|
251
|
+
'{except: :child}'
|
252
|
+
].each do |parent_options|
|
253
|
+
describe parent_options do
|
254
|
+
it { expect(grand_parent).to be_valid }
|
255
|
+
end
|
256
|
+
end
|
211
257
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
258
|
+
[
|
259
|
+
'true',
|
260
|
+
'{only: :child}',
|
261
|
+
'{any: :child}',
|
262
|
+
'{except: :other}'
|
263
|
+
].each do |parent_options|
|
264
|
+
describe parent_options do
|
265
|
+
it { expect(grand_parent).to_not be_valid }
|
266
|
+
end
|
216
267
|
end
|
217
268
|
end
|
218
269
|
end
|
@@ -51,6 +51,16 @@ describe 'Typical usage' do
|
|
51
51
|
puts parent.errors.messages
|
52
52
|
end
|
53
53
|
|
54
|
+
specify 'any child attributes' do
|
55
|
+
class ParentExcept < ParentBase
|
56
|
+
validates :child, nested: { any: [:attribute1, :attribute2] }
|
57
|
+
end
|
58
|
+
|
59
|
+
parent = ParentExcept.new
|
60
|
+
parent.valid?
|
61
|
+
puts parent.errors.messages
|
62
|
+
end
|
63
|
+
|
54
64
|
specify 'custom message prefix' do
|
55
65
|
class ParentPrefix < ParentBase
|
56
66
|
validates :child, nested: { only: :attribute1, prefix: 'OMG'}
|
@@ -1,110 +1,84 @@
|
|
1
|
-
|
2
|
-
require 'nested_validator'
|
1
|
+
require_relative 'nested_validator_context'
|
3
2
|
|
4
|
-
describe 'validates_nested with [parent class with "validates,
|
5
|
-
let(:parent_class) do
|
6
|
-
Class.new {
|
7
|
-
include ActiveModel::Validations
|
3
|
+
describe 'validates_nested with [parent class with "validates, child]"' do
|
8
4
|
|
9
|
-
|
5
|
+
include_context 'nested validator'
|
10
6
|
|
11
|
-
|
12
|
-
self.child1 = child1
|
13
|
-
self.child2 = child2
|
14
|
-
end
|
15
|
-
|
16
|
-
def to_s
|
17
|
-
'parent'
|
18
|
-
end
|
19
|
-
}
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
let(:child_class) do
|
24
|
-
Class.new {
|
25
|
-
include ActiveModel::Validations
|
26
|
-
|
27
|
-
attr_accessor :attribute1, :attribute2, :attribute3
|
28
|
-
|
29
|
-
validates :attribute1, presence: true
|
30
|
-
validates :attribute2, presence: true
|
31
|
-
validates :attribute3, presence: true
|
32
|
-
|
33
|
-
def initialize
|
34
|
-
@attribute1 = 'valid'
|
35
|
-
@attribute2 = 'valid'
|
36
|
-
@attribute3 = 'valid'
|
37
|
-
end
|
38
|
-
}
|
39
|
-
end
|
40
|
-
|
41
|
-
let(:parent) { parent_class.new child_class.new, child_class.new }
|
7
|
+
let(:parent) { parent_class.new child_class.new }
|
42
8
|
let(:options) { { nested: true } }
|
43
9
|
let(:validator) { instance_eval self.class.description }
|
44
10
|
|
45
|
-
before { parent_class.class_eval "validates :child1, #{options}" }
|
46
|
-
|
47
11
|
describe 'its validations with options:' do
|
48
12
|
let(:options) { self.class.description }
|
49
13
|
|
50
14
|
subject { parent }
|
51
15
|
|
52
|
-
context '
|
53
|
-
it { should validate_nested(:
|
54
|
-
it { should validate_nested('
|
16
|
+
context 'true' do
|
17
|
+
it { should validate_nested(:child) }
|
18
|
+
it { should validate_nested('child') }
|
55
19
|
|
56
20
|
it { should_not validate_nested(:child2) }
|
57
21
|
it { should_not validate_nested(:invalid_child_name) }
|
58
22
|
end
|
59
23
|
|
60
|
-
context '
|
61
|
-
it { should validate_nested(:
|
62
|
-
it { should validate_nested(:
|
24
|
+
context '{prefix: "OMG"}' do
|
25
|
+
it { should validate_nested(:child).with_prefix('OMG') }
|
26
|
+
it { should validate_nested(:child).with_prefix(:OMG) }
|
63
27
|
|
64
|
-
it { should_not validate_nested(:
|
65
|
-
it { should_not validate_nested(:
|
66
|
-
it { should_not validate_nested(:
|
28
|
+
it { should_not validate_nested(:child) }
|
29
|
+
it { should_not validate_nested(:child).with_prefix('WTF') }
|
30
|
+
it { should_not validate_nested(:child).with_prefix(:WTF) }
|
67
31
|
end
|
68
32
|
|
69
|
-
context '
|
70
|
-
it { should validate_nested(:
|
71
|
-
it { should validate_nested(:
|
33
|
+
context '{only: :attribute}' do
|
34
|
+
it { should validate_nested(:child).only(:attribute) }
|
35
|
+
it { should validate_nested(:child).only('attribute') }
|
72
36
|
|
73
|
-
it { should_not validate_nested(:
|
74
|
-
it { should_not validate_nested(:
|
75
|
-
it { should_not validate_nested(:
|
76
|
-
it { should_not validate_nested(:
|
37
|
+
it { should_not validate_nested(:child).only(:attribute2) }
|
38
|
+
it { should_not validate_nested(:child).only('attribute2') }
|
39
|
+
it { should_not validate_nested(:child).only(:invalid_attribute_name) }
|
40
|
+
it { should_not validate_nested(:child).only(:attribute, :attribute2) }
|
77
41
|
end
|
78
42
|
|
79
|
-
context '
|
80
|
-
it { should validate_nested(:
|
81
|
-
it { should validate_nested(:
|
82
|
-
it { should validate_nested(:
|
83
|
-
it { should validate_nested(:
|
43
|
+
context '{only: [:attribute, :attribute2]}' do
|
44
|
+
it { should validate_nested(:child).only(:attribute) }
|
45
|
+
it { should validate_nested(:child).only(:attribute2) }
|
46
|
+
it { should validate_nested(:child).only(:attribute, :attribute2) }
|
47
|
+
it { should validate_nested(:child).only('attribute', 'attribute2') }
|
48
|
+
it { should validate_nested(:child).only('attribute, attribute2') }
|
84
49
|
|
85
|
-
it { should_not validate_nested(:
|
86
|
-
it { should_not validate_nested(:
|
50
|
+
it { should_not validate_nested(:child).only(:attribute2, :attribute3) }
|
51
|
+
it { should_not validate_nested(:child).only(:invalid_attribute_name) }
|
87
52
|
end
|
88
53
|
|
89
|
-
context '
|
90
|
-
it { should validate_nested(:
|
91
|
-
it { should validate_nested(:
|
54
|
+
context '{except: :attribute}' do
|
55
|
+
it { should validate_nested(:child).except(:attribute) }
|
56
|
+
it { should validate_nested(:child).except('attribute') }
|
92
57
|
|
93
|
-
it { should_not validate_nested(:
|
58
|
+
it { should_not validate_nested(:child).except(:attribute2) }
|
94
59
|
end
|
95
60
|
|
96
|
-
context '
|
97
|
-
it { should validate_nested(:
|
61
|
+
context '{except: [:attribute, :attribute2]}' do
|
62
|
+
it { should validate_nested(:child).except(:attribute, :attribute2) }
|
63
|
+
end
|
64
|
+
|
65
|
+
context '{any: :attribute}' do
|
66
|
+
it { should validate_nested(:child).any(:attribute) }
|
67
|
+
it { should validate_nested(:child).any('attribute') }
|
68
|
+
|
69
|
+
it { should_not validate_nested(:child).any(:attribute2) }
|
70
|
+
it { should_not validate_nested(:child).any('attribute2') }
|
98
71
|
end
|
99
72
|
end
|
100
73
|
|
101
74
|
describe 'its description for:' do
|
102
75
|
subject { validator.description }
|
103
76
|
|
104
|
-
context('validate_nested(:
|
105
|
-
context('validate_nested(:
|
106
|
-
context('validate_nested(:
|
107
|
-
context('validate_nested(:
|
77
|
+
context('validate_nested(:child)') { it { should eq 'validate nested :child' } }
|
78
|
+
context('validate_nested(:child).only(:attribute)') { it { should eq 'validate nested :child with only: :attribute' } }
|
79
|
+
context('validate_nested(:child).except(:attribute)') { it { should eq 'validate nested :child except: :attribute' } }
|
80
|
+
context('validate_nested(:child).any(:attribute)') { it { should eq 'validate nested :child any: :attribute' } }
|
81
|
+
context('validate_nested(:child).with_prefix(:OMG)') { it { should eq 'validate nested :child with prefix :OMG' } }
|
108
82
|
end
|
109
83
|
|
110
84
|
describe 'its error messages:' do
|
@@ -113,70 +87,75 @@ describe 'validates_nested with [parent class with "validates, child1]"' do
|
|
113
87
|
|
114
88
|
before { expect(validator.matches? parent).to be expect_match }
|
115
89
|
|
116
|
-
describe '
|
90
|
+
describe 'common failure messages' do
|
117
91
|
[:failure_message, :failure_message_when_negated].each do |message|
|
118
92
|
subject { validator.send message }
|
119
93
|
|
120
|
-
context '
|
121
|
-
describe('validate_nested(:invalid_child_name)')
|
122
|
-
describe('validate_nested(:
|
123
|
-
describe('validate_nested(:
|
94
|
+
context 'true' do
|
95
|
+
describe('validate_nested(:invalid_child_name)') { it { should eq "parent doesn't respond to :invalid_child_name" } }
|
96
|
+
describe('validate_nested(:child).only(:invalid_attribute_name)') { it { should eq "child doesn't respond to :invalid_attribute_name" } }
|
97
|
+
describe('validate_nested(:child).except(:invalid_attribute_name)') { it { should eq "child doesn't respond to :invalid_attribute_name" } }
|
124
98
|
end
|
125
99
|
end
|
126
100
|
end
|
127
101
|
|
128
|
-
describe '
|
102
|
+
describe 'failure_message' do
|
129
103
|
subject { validator.failure_message }
|
130
104
|
|
131
|
-
context '
|
132
|
-
describe('validate_nested(:
|
105
|
+
context 'true' do
|
106
|
+
describe('validate_nested(:other)') { it { should eq "parent doesn't nest validations for :other" } }
|
107
|
+
end
|
108
|
+
|
109
|
+
context '{prefix: :OMG}' do
|
110
|
+
describe('validate_nested(:child)') { it { should eq "parent has a prefix of :OMG. Are you missing '.with_prefix(:OMG)'?" } }
|
111
|
+
describe('validate_nested(:child).with_prefix(:WTF)') { it { should eq 'parent uses a prefix of :OMG rather than :WTF' } }
|
133
112
|
end
|
134
113
|
|
135
|
-
context '
|
136
|
-
describe('validate_nested(:
|
137
|
-
describe('validate_nested(:
|
114
|
+
context '{only: :attribute}' do
|
115
|
+
describe('validate_nested(:child).only(:attribute2)') { it { should eq "parent doesn't nest validations for: :attribute2" } }
|
116
|
+
describe('validate_nested(:child).only(:attribute, :attribute2)') { it { should eq "parent doesn't nest validations for: :attribute2" } }
|
138
117
|
end
|
139
118
|
|
140
|
-
context '
|
141
|
-
describe('validate_nested(:
|
142
|
-
describe('validate_nested(:
|
119
|
+
context '{except: :attribute}' do
|
120
|
+
describe('validate_nested(:child).except(:attribute2)') { it { should eq 'parent does nest validations for: :attribute2' } }
|
121
|
+
describe('validate_nested(:child).except(:attribute, :attribute2)') { it { should eq 'parent does nest validations for: :attribute2' } }
|
143
122
|
end
|
144
123
|
|
145
|
-
context '
|
146
|
-
describe('validate_nested(:
|
147
|
-
describe('validate_nested(:
|
124
|
+
context '{any: :attribute}' do
|
125
|
+
describe('validate_nested(:child).any(:attribute2)') { it { should eq "parent doesn't nest validations for: :attribute2" } }
|
126
|
+
describe('validate_nested(:child).any(:attribute, :attribute2)') { it { should eq "parent doesn't nest validations for: :attribute2" } }
|
148
127
|
end
|
149
128
|
end
|
150
129
|
|
151
|
-
describe '
|
130
|
+
describe 'failure_message_when_negated' do
|
152
131
|
let(:expect_match) { true }
|
153
132
|
|
154
133
|
subject { validator.failure_message_when_negated }
|
155
134
|
|
156
|
-
context '
|
157
|
-
describe('validate_nested(:
|
135
|
+
context 'true' do
|
136
|
+
describe('validate_nested(:child)') { it { should eq 'parent does nest validations for: :child' } }
|
158
137
|
end
|
159
138
|
|
160
|
-
context '
|
161
|
-
describe('validate_nested(:
|
139
|
+
context '{prefix: :OMG}' do
|
140
|
+
describe('validate_nested(:child).with_prefix(:OMG)') { it { should eq 'parent does nest validations for: :child with a prefix of :OMG' } }
|
162
141
|
end
|
163
142
|
|
164
|
-
context '
|
165
|
-
describe('validate_nested(:
|
143
|
+
context '{only: :attribute}' do
|
144
|
+
describe('validate_nested(:child).only(:attribute)') { it { should eq 'parent does nest :child validations for: :attribute' } }
|
166
145
|
end
|
167
146
|
|
168
|
-
context '
|
169
|
-
describe('validate_nested(:
|
170
|
-
describe('validate_nested(:
|
147
|
+
context '{only: [:attribute, :attribute2]}' do
|
148
|
+
describe('validate_nested(:child).only(:attribute)') { it { should eq 'parent does nest :child validations for: :attribute' } }
|
149
|
+
describe('validate_nested(:child).only(:attribute, :attribute2)') { it { should eq 'parent does nest :child validations for: :attribute, :attribute2' } }
|
171
150
|
end
|
172
151
|
|
173
|
-
context '
|
174
|
-
describe('validate_nested(:
|
152
|
+
context '{except: :attribute}' do
|
153
|
+
describe('validate_nested(:child).except(:attribute)') { it { should eq "parent doesn't nest :child validations for: :attribute" } }
|
175
154
|
end
|
176
155
|
|
177
|
-
context '
|
178
|
-
describe('validate_nested(:
|
179
|
-
describe('validate_nested(:
|
156
|
+
context '{except: [:attribute, :attribute2]}' do
|
157
|
+
describe('validate_nested(:child).except(:attribute)') { it { should eq "parent doesn't nest :child validations for: :attribute" } }
|
158
|
+
describe('validate_nested(:child).except(:attribute, :attribute2)') { it { should eq "parent doesn't nest :child validations for: :attribute, :attribute2" } }
|
180
159
|
end
|
181
160
|
end
|
182
161
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nested_validator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Declan Whelan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -129,6 +129,7 @@ files:
|
|
129
129
|
- lib/nested_validator/validate_nested_matcher.rb
|
130
130
|
- lib/nested_validator/version.rb
|
131
131
|
- nested_validator.gemspec
|
132
|
+
- spec/lib/nested_validator/nested_validator_context.rb
|
132
133
|
- spec/lib/nested_validator/nested_validator_spec.rb
|
133
134
|
- spec/lib/nested_validator/usage_spec.rb
|
134
135
|
- spec/lib/nested_validator/validate_nested_matcher_spec.rb
|
@@ -158,6 +159,7 @@ signing_key:
|
|
158
159
|
specification_version: 4
|
159
160
|
summary: A validator that supports nesting.
|
160
161
|
test_files:
|
162
|
+
- spec/lib/nested_validator/nested_validator_context.rb
|
161
163
|
- spec/lib/nested_validator/nested_validator_spec.rb
|
162
164
|
- spec/lib/nested_validator/usage_spec.rb
|
163
165
|
- spec/lib/nested_validator/validate_nested_matcher_spec.rb
|