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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- N2Q3NDk5NTYwYjY5YWQwNGIzZGJiYzM2YzIyOTNlMzUyMmE1MzQzOA==
4
+ OTc4N2MxNWMwNGRjZGRlNjA0MzBkNzZlZjRlZjczMGU5M2I4YjE3OQ==
5
5
  data.tar.gz: !binary |-
6
- NmUxMGU2NWEyMDllNjhhNDZhMWQxZWI3ZjE1ZWQ0YmYzODFiNjUzZQ==
6
+ YmNhNWI2YzdjYmE1YjNhNGE0ZDdiMzY4Yzg0YWYwZmRiODUzNjk2Yg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- OTI0ZmY3MGQzZmUyNGEzNzAxOWJjZTQ3N2M5ZGQ3MTNjZTEzYTgxZTI2NTBh
10
- ZmY3MmJjOTgzMGFhNWIzOWJkMDU3MWRkODVkNzhiNWFkOGFlODEwZWZkZDZi
11
- ODkxYThkOGE4MjRlNzQ4YTJhMzkyYmNhYTRlNDQxODhkZTA0Yjk=
9
+ NWY5NmU2ZjA4NmY4OWI3ZGMwMjI0NzU0ZDM4YWVjZjRlNzYzZDc5MTE4ZDM4
10
+ NjgxZWQyNTc3ODZhMDhlYzc4OGQyYTk4MTI1Y2Q1N2NhYmFiNmMxMTgwYzBl
11
+ NTEzYjhhMDM2MjMzMTI0MzQ3ZGZkY2EyNmRiNzAxZDJjZThhMTE=
12
12
  data.tar.gz: !binary |-
13
- ZjJhMTQxZmY2NDM0M2IzZTg1YjJiZDI1YTY2ODhmODQ4OGJjNWJlNzkwNTNl
14
- ODY3NTVhOWNmMzUzMzM2ZWFmNzE2M2E3Y2NmODkxN2FkNjg5Nzg5NzE0NDgy
15
- MmU5OWIyOTcxOWI0NmUzYTdjZmE2MDdiMmI5NmE2ODQ3MmZlNTI=
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)
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.0)
12
- activesupport (= 4.1.0)
11
+ activemodel (4.1.1)
12
+ activesupport (= 4.1.1)
13
13
  builder (~> 3.1)
14
- activesupport (4.1.0)
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.6.11)
30
- json (1.8.1)
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.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 options[:only]
51
- only.any?{|k| key =~ /^#{k}/}
52
- elsif options[:except]
53
- except.none?{|k| key =~ /^#{k}/}
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 ||= Array.wrap(options[:only])
67
+ @only ||= prepare_options(:only)
61
68
  end
62
69
 
63
70
  def except
64
- @except ||= Array.wrap(options[: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
- # has_one :child
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 # inputs
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.send child_name
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 = only_keys.present? ? only_keys : [TEST_KEY]
72
- unique_except_keys = except_keys - only_keys
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
@@ -1,3 +1,3 @@
1
1
  module NestedValidator
2
- VERSION = '1.0.4'
2
+ VERSION = '1.0.5'
3
3
  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
- require 'spec_helper'
2
- require 'nested_validator'
1
+ require_relative 'nested_validator_context'
3
2
 
4
- describe NestedValidator do
5
- let(:base_class) do
6
- Class.new {
7
- include ActiveModel::Validations
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
- let(:parent_class) do
17
- Class.new(base_class) {
18
- attr_accessor :child1, :child2
19
- }
9
+ def description
10
+ "detect an error for '#{child_attribute}'"
20
11
  end
21
12
 
22
- let(:child_class) do
23
- Class.new(base_class) {
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
- let(:child1) { child_class.new }
42
- let(:child2) { child_class.new }
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 with_nested_options(options)
52
- parent_with { validates :child1, nested: options }
21
+ def error
22
+ subject.errors[child_attribute].first
53
23
  end
54
24
 
55
- shared_examples 'valid:' do |child_name, *attributes|
56
- attributes.each do |attribute|
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
- shared_examples 'invalid:' do |child_name, *attributes|
65
- attributes.each do |attribute|
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
- describe 'with "nested: true"' do
74
- subject { with_nested_options true }
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
- describe 'error messages' do
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
- its('errors.messages') { should eq :'child1 attribute1' => ["can't be blank"] }
87
- end
40
+ include_context 'nested validator'
88
41
 
89
- describe 'with "prefix: "OMG"' do
90
- subject { with_nested_options prefix: 'OMG' }
42
+ let(:options) { 'true' }
43
+ let(:parent) { parent_class.new child }
44
+ let(:child) { child_class.new }
91
45
 
92
- its('errors.messages') { puts subject.errors.messages; should eq :'OMG attribute1' => ["can't be blank"] }
93
- end
46
+ subject { parent }
94
47
 
95
- describe 'with "prefix: ""' do
96
- subject { with_nested_options prefix: '' }
48
+ let(:options) { self.class.description }
97
49
 
98
- its('errors.messages') { should eq :'attribute1' => ["can't be blank"] }
99
- end
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
- context 'with an array of values' do
103
- let(:child1) { [child_class.new, child_class.new] }
104
-
105
- before { child1[0].attribute1 = nil;subject.valid? }
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
- describe 'with single invalid value' do
109
- its('errors.messages') { should eq :'child1[0] attribute1' => ["can't be blank"] }
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
- describe 'with multiple invalid values' do
113
- before { child1[1].attribute1 = nil;subject.valid? }
114
- its('errors.messages') { should include :'child1[0] attribute1' => ["can't be blank"] }
115
- its('errors.messages') { should include :'child1[1] attribute1' => ["can't be blank"] }
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
- context 'with a prefix' do
119
- subject { with_nested_options prefix: 'OMG' }
120
-
121
- its('errors.messages') { should eq :'OMG[0] attribute1' => ["can't be blank"] }
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
- context 'with a hash of values' do
127
- let(:child1) { { first: child_class.new, second: child_class.new } }
128
-
129
- before { child1[:first].attribute1 = nil;subject.valid? }
130
- subject { with_nested_options true }
131
-
132
- describe 'with single invalid value' do
133
- its('errors.messages') { should eq :'child1[first] attribute1' => ["can't be blank"] }
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
- describe 'with multiple invalid values' do
137
- before { child1[:second].attribute1 = nil;subject.valid? }
138
- its('errors.messages') { should include :'child1[first] attribute1' => ["can't be blank"] }
139
- its('errors.messages') { should include :'child1[second] attribute1' => ["can't be blank"] }
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
- context 'with a prefix' do
143
- subject { with_nested_options prefix: 'OMG' }
144
-
145
- its('errors.messages') { should eq :'OMG[first] attribute1' => ["can't be blank"] }
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 '"validates :child1, nested: {only: ...}"' do
150
-
151
- describe 'with "only: :attribute1"' do
152
-
153
- subject { with_nested_options only: :attribute1 }
154
-
155
- it_should_validate_nested 'invalid:', :child1, :attribute1
156
- it_should_validate_nested 'valid:', :child1, :attribute2, :attribute3
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
- describe 'with "only: [:atnested_validator_spec.rbtribute1, :attribute2]"' do
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
- subject { parent_with { validates :child1, nested: { only: [:attribute1, :attribute2] } } }
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
- it_should_validate_nested 'invalid:', :child1, :attribute1, :attribute2
164
- it_should_validate_nested 'valid:', :child1, :attribute3
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 '"validates :child1, nested: {except: ...}"' do
169
- describe 'with "except: :attribute1"' do
170
- subject { with_nested_options except: :attribute1 }
196
+ describe 'error message keys' do
197
+ before { parent.valid? }
198
+ subject { parent.errors.messages.keys.first }
171
199
 
172
- it_should_validate_nested 'invalid:', :child1, :attribute2, :attribute3
173
- it_should_validate_nested 'valid:', :child1, :attribute1
174
- end
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
- describe 'with "except: [:attribute1, :attribute2"' do
177
- subject { with_nested_options except: [:attribute1, :attribute2] }
205
+ context 'with arrays' do
206
+ let(:child) { [child_class.new] }
178
207
 
179
- it_should_validate_nested 'invalid:', :child1, :attribute3
180
- it_should_validate_nested 'valid:', :child1, :attribute1, :attribute2
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
- describe 'attributes in "only" option should take precedence over "except"' do
185
- describe '"validates :child1, nested: {only: attribute1, except: :attribute1}"' do
186
- subject { with_nested_options only: :attribute1, except: :attribute1 }
214
+ context('with hashes') do
215
+ let(:child) { {key: child_class.new} }
187
216
 
188
- it_should_validate_nested 'invalid:', :child1, :attribute1
189
- it_should_validate_nested 'valid:', :child1, :attribute2, :attribute3
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 'validates_nested' do
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
- describe 'validates_nested :child1' do
196
- subject { parent_with { validates_nested :child1 } }
230
+ attr_accessor :parent
197
231
 
198
- it_should_validate_nested 'invalid:', :child1, :attribute1, :attribute2, :attribute3
199
- it_should_validate_nested 'valid:', :child2, :attribute1, :attribute2, :attribute3
200
- end
232
+ instance_eval "validates :parent, nested: #{opts}"
201
233
 
202
- describe 'validates_nested :child1, :child2' do
203
- subject { parent_with { validates_nested :child1, :child2 } }
234
+ def initialize(parent=nil)
235
+ self.parent = parent
236
+ end
204
237
 
205
- it_should_validate_nested 'invalid:', :child1, :attribute1, :attribute2, :attribute3
206
- it_should_validate_nested 'invalid:', :child2, :attribute1, :attribute2, :attribute3
238
+ def to_s
239
+ 'grand_parent'
240
+ end
241
+ }
207
242
  end
208
243
 
209
- describe 'validates_nested :child1, :child2, only: :attribute1' do
210
- subject { parent_with { validates_nested :child1, :child2, only: :attribute1 } }
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
- it_should_validate_nested 'invalid:', :child1, :attribute1
213
- it_should_validate_nested 'invalid:', :child2, :attribute1
214
- it_should_validate_nested 'valid:', :child1, :attribute2, :attribute3
215
- it_should_validate_nested 'valid:', :child2, :attribute2, :attribute3
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
- require 'spec_helper'
2
- require 'nested_validator'
1
+ require_relative 'nested_validator_context'
3
2
 
4
- describe 'validates_nested with [parent class with "validates, child1]"' do
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
- attr_accessor :child1, :child2
5
+ include_context 'nested validator'
10
6
 
11
- def initialize(child1=nil, child2=nil)
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 'nested: true' do
53
- it { should validate_nested(:child1) }
54
- it { should validate_nested('child1') }
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 'nested: {prefix: "OMG"}' do
61
- it { should validate_nested(:child1).with_prefix('OMG') }
62
- it { should validate_nested(:child1).with_prefix(:OMG) }
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(:child1) }
65
- it { should_not validate_nested(:child1).with_prefix('WTF') }
66
- it { should_not validate_nested(:child1).with_prefix(:WTF) }
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 'nested: {only: :attribute1}' do
70
- it { should validate_nested(:child1).only(:attribute1) }
71
- it { should validate_nested(:child1).only('attribute1') }
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(:child1).only(:attribute2) }
74
- it { should_not validate_nested(:child1).only('attribute2') }
75
- it { should_not validate_nested(:child1).only(:invalid_attribute_name) }
76
- it { should_not validate_nested(:child1).only(:attribute1, :attribute2) }
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 'nested: {only: [:attribute1, :attribute2]}' do
80
- it { should validate_nested(:child1).only(:attribute1) }
81
- it { should validate_nested(:child1).only(:attribute2) }
82
- it { should validate_nested(:child1).only(:attribute1, :attribute2) }
83
- it { should validate_nested(:child1).only('attribute1', 'attribute2') }
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(:child1).only(:attribute2, :attribute3) }
86
- it { should_not validate_nested(:child1).only(:invalid_attribute_name) }
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 'nested: {except: :attribute1}' do
90
- it { should validate_nested(:child1).except(:attribute1) }
91
- it { should validate_nested(:child1).except('attribute1') }
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(:child1).except(:attribute2) }
58
+ it { should_not validate_nested(:child).except(:attribute2) }
94
59
  end
95
60
 
96
- context 'nested: {except: [:attribute1, :attribute2]}' do
97
- it { should validate_nested(:child1).except(:attribute1, :attribute2) }
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(:child1)') { it { should eq 'validate nested :child1' } }
105
- context('validate_nested(:child1).only(:attribute1)') { it { should eq 'validate nested :child1 with only: :attribute1' } }
106
- context('validate_nested(:child1).except(:attribute1)') { it { should eq 'validate nested :child1 except: :attribute1' } }
107
- context('validate_nested(:child1).with_prefix(:OMG)') { it { should eq 'validate nested :child1 with prefix :OMG' } }
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 'failures for should and should_not' do
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 'nested: true' do
121
- describe('validate_nested(:invalid_child_name)') { it { should eq "parent doesn't respond to :invalid_child_name" } }
122
- describe('validate_nested(:child1).only(:invalid_attribute_name)') { it { should eq "child1 doesn't respond to :invalid_attribute_name" } }
123
- describe('validate_nested(:child1).except(:invalid_attribute_name)') { it { should eq "child1 doesn't respond to :invalid_attribute_name" } }
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 'should failure messages for' do
102
+ describe 'failure_message' do
129
103
  subject { validator.failure_message }
130
104
 
131
- context 'nested: true' do
132
- describe('validate_nested(:child2)') { it { should eq "parent doesn't nest validations for :child2" } }
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 'nested: {prefix: :OMG}' do
136
- describe('validate_nested(:child1)') { it { should eq "parent has a prefix of :OMG. Are you missing '.with_prefix(:OMG)'?" } }
137
- describe('validate_nested(:child1).with_prefix(:WTF)') { it { should eq 'parent uses a prefix of :OMG rather than :WTF' } }
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 'nested: {only: :attribute1}' do
141
- describe('validate_nested(:child1).only(:attribute2)') { it { should eq "parent doesn't nest validations for: :attribute2" } }
142
- describe('validate_nested(:child1).only(:attribute1, :attribute2)') { it { should eq "parent doesn't nest validations for: :attribute2" } }
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 'nested: {except: :attribute1}' do
146
- describe('validate_nested(:child1).except(:attribute2)') { it { should eq 'parent does nest validations for: :attribute2' } }
147
- describe('validate_nested(:child1).except(:attribute1, :attribute2)') { it { should eq 'parent does nest validations for: :attribute2' } }
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 'should_not failure messages for' do
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 'nested: true' do
157
- describe('validate_nested(:child1)') { it { should eq 'parent does nest validations for: :child1' } }
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 'nested: {prefix: :OMG}' do
161
- describe('validate_nested(:child1).with_prefix(:OMG)') { it { should eq 'parent does nest validations for: :child1 with a prefix of :OMG' } }
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 'nested: {only: :attribute1}' do
165
- describe('validate_nested(:child1).only(:attribute1)') { it { should eq 'parent does nest :child1 validations for: :attribute1' } }
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 'nested: {only: [:attribute1, :attribute2]}' do
169
- describe('validate_nested(:child1).only(:attribute1)') { it { should eq 'parent does nest :child1 validations for: :attribute1' } }
170
- describe('validate_nested(:child1).only(:attribute1, :attribute2)') { it { should eq 'parent does nest :child1 validations for: :attribute1, :attribute2' } }
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 'nested: {except: :attribute1}' do
174
- describe('validate_nested(:child1).except(:attribute1)') { it { should eq "parent doesn't nest :child1 validations for: :attribute1" } }
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 'nested: {except: [:attribute1, :attribute2]}' do
178
- describe('validate_nested(:child1).except(:attribute1)') { it { should eq "parent doesn't nest :child1 validations for: :attribute1" } }
179
- describe('validate_nested(:child1).except(:attribute1, :attribute2)') { it { should eq "parent doesn't nest :child1 validations for: :attribute1, :attribute2" } }
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
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: 2014-12-26 00:00:00.000000000 Z
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