nested_validator 1.0.4 → 1.0.5

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.
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