better_validations 0.1.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +17 -19
- data/lib/better_validations/nested_validator.rb +35 -31
- data/lib/better_validations/validator.rb +51 -20
- data/lib/better_validations/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 76ca8c12dfb8eac58738773cab5d49cfad6d1b1e58728873fac9f740b72fbc0b
|
4
|
+
data.tar.gz: a9c10806fb84f110dd5a13b07aea6f036d295c6eb81810ea8ab788aacb859cd0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 06c42b8d5a217a8eaf63be99d335dc9df563285b54b0d8ee76f5a8c2841ee926bf40dfa6f123a1d2a486740c025643eb73d9c388c0d807439f1356ea8147e275
|
7
|
+
data.tar.gz: d68999c0de44a3a2705d4cb299ae7ec5ba3714cf80d1465173364584c85f232b2a271a3452cbc89f31a1f6aa55e66bce61ca5bbd827559adc02666e74edd9f4b
|
data/README.md
CHANGED
@@ -185,6 +185,23 @@ object = ActionController::Parameters.new(nested_object: { attribute_one: nil })
|
|
185
185
|
validator = SomeModelValidator.new(object)
|
186
186
|
```
|
187
187
|
|
188
|
+
Names of nested objects with the `_attributes` suffix is also acceptable:
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
validator = SomeModelValidator.new(
|
192
|
+
nested_object_attributes: { attribute_one: nil }
|
193
|
+
)
|
194
|
+
```
|
195
|
+
|
196
|
+
You can initialize fields by setters if needs:
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
validator = SomeModelValidator.new
|
200
|
+
validator.attribute_one = 'filled'
|
201
|
+
validator.nested_object = { attribute_one: 'filled' }
|
202
|
+
validator.valid?
|
203
|
+
```
|
204
|
+
|
188
205
|
### Merge error messages from multiple validators
|
189
206
|
|
190
207
|
Better Validations provides the ability to merge validators in order to get merged detailed errors.
|
@@ -212,25 +229,6 @@ class SomeModelValidator < ApplicationValidator
|
|
212
229
|
end
|
213
230
|
```
|
214
231
|
|
215
|
-
This will allow you to easily add common functionality to the validators. For example, add automatic parsing of keys with `_attributes` suffix to validator properties in order to support format of `accepts_nested_attributes_for` parameters:
|
216
|
-
|
217
|
-
```ruby
|
218
|
-
class ApplicationValidator
|
219
|
-
include BetterValidations::Validator
|
220
|
-
|
221
|
-
def self.validate_nested(nested_name, validator_class)
|
222
|
-
define_attributes_accessor(nested_name)
|
223
|
-
bind_validator(nested_name, validator_class)
|
224
|
-
end
|
225
|
-
|
226
|
-
def self.define_attributes_accessor(nested_name)
|
227
|
-
setter_name = "#{nested_name}_attributes="
|
228
|
-
define_method(setter_name) { |value| set_value(nested_name, value) }
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
```
|
233
|
-
|
234
232
|
## Development
|
235
233
|
|
236
234
|
1. Clone project.
|
@@ -1,50 +1,54 @@
|
|
1
1
|
class BetterValidations::NestedValidator < ActiveModel::EachValidator
|
2
|
-
attr_reader :validator_class
|
3
|
-
|
4
|
-
def initialize(options)
|
5
|
-
@validator_class = options.delete(:validator_class)
|
6
|
-
super
|
7
|
-
end
|
8
|
-
|
9
2
|
def validate_each(record, attr_name, value)
|
10
3
|
return if value.nil?
|
11
4
|
|
12
|
-
validator =
|
13
|
-
cache_validator(record, attr_name, validator)
|
5
|
+
validator = value
|
14
6
|
return if validator_valid?(validator)
|
15
7
|
|
16
|
-
|
8
|
+
add_errors(validator, record, attr_name)
|
17
9
|
end
|
18
10
|
|
19
11
|
protected
|
20
12
|
|
21
|
-
def init_validator(value)
|
22
|
-
# A value can be a single object or a list of objects
|
23
|
-
if value.is_a?(Hash) || value.is_a?(ActionController::Parameters)
|
24
|
-
validator_class.new(value)
|
25
|
-
elsif value.is_a?(Enumerable)
|
26
|
-
value.map { |object| validator_class.new(object) }
|
27
|
-
else
|
28
|
-
validator_class.new(value)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def cache_validator(record, attr_name, validator)
|
33
|
-
record.nested_object_validators[attr_name.to_sym] = validator
|
34
|
-
end
|
35
|
-
|
36
13
|
def validator_valid?(validator)
|
37
14
|
validators = validator.is_a?(Enumerable) ? validator : [validator]
|
38
15
|
validators.map(&:valid?).all?
|
39
16
|
end
|
40
17
|
|
41
|
-
def
|
42
|
-
#
|
43
|
-
#
|
44
|
-
|
18
|
+
def add_errors(validator, record, attr_name)
|
19
|
+
# Should copy all errors from nested object to the record
|
20
|
+
# in order to emulate active record nested errors.
|
21
|
+
# But in case of list should merge errors of all items together.
|
22
|
+
details = if validator.is_a?(Enumerable)
|
23
|
+
collect_nested_errors_details(validator)
|
24
|
+
else
|
25
|
+
validator.errors.details
|
26
|
+
end
|
27
|
+
|
28
|
+
add_errors_details(details, record, attr_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def add_errors_details(details, record, attr_name)
|
32
|
+
details.each do |field_name, errors|
|
33
|
+
errors.each do |error|
|
34
|
+
record.errors.add("#{attr_name}.#{field_name}",
|
35
|
+
error[:error],
|
36
|
+
error.except(:error))
|
37
|
+
end
|
38
|
+
end
|
45
39
|
end
|
46
40
|
|
47
|
-
def
|
48
|
-
|
41
|
+
def collect_nested_errors_details(validators)
|
42
|
+
result = {}
|
43
|
+
|
44
|
+
validators.each do |validator|
|
45
|
+
validator.errors.details.each do |field_name, errors|
|
46
|
+
# Use set to remove duplicates automalically
|
47
|
+
result_errors = result[field_name] || Set.new
|
48
|
+
result[field_name] = result_errors + errors
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
result.transform_values(&:to_a)
|
49
53
|
end
|
50
54
|
end
|
@@ -7,11 +7,6 @@ module BetterValidations::Validator
|
|
7
7
|
# A system accessors required by BetterValidations::Object
|
8
8
|
attr_accessor :id, :client_id
|
9
9
|
|
10
|
-
# A hash with cached instances of nested validators by field.
|
11
|
-
# Filled by instances of a BetterValidations::NestedValidator class
|
12
|
-
# in the process of validation.
|
13
|
-
attr_accessor :nested_object_validators
|
14
|
-
|
15
10
|
# A validation object as a possible data source
|
16
11
|
attr_reader :validation_object
|
17
12
|
|
@@ -21,15 +16,32 @@ module BetterValidations::Validator
|
|
21
16
|
# Example of usage in a User model for validating nested PersonalInfo:
|
22
17
|
# validate_nested :personal_info, PersonalInfoValidator
|
23
18
|
def self.validate_nested(nested_name, validator_class)
|
24
|
-
bind_validator(nested_name
|
19
|
+
bind_validator(nested_name)
|
20
|
+
define_nested_object_setter(nested_name, validator_class)
|
21
|
+
define_attributes_setter(nested_name)
|
25
22
|
end
|
26
23
|
|
27
24
|
# Calls a validates_with method to save information about
|
28
25
|
# the validating object to the validator and run validations.
|
29
|
-
def self.bind_validator(nested_name
|
26
|
+
def self.bind_validator(nested_name)
|
30
27
|
validates_with BetterValidations::NestedValidator,
|
31
|
-
attributes: [nested_name]
|
32
|
-
|
28
|
+
attributes: [nested_name]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Overriders the setter for nested object in order to create the
|
32
|
+
# instance of validator instead of hash/params/ActiveRecord.
|
33
|
+
def self.define_nested_object_setter(nested_name, validator_class)
|
34
|
+
define_method("#{nested_name}=".to_sym) do |value|
|
35
|
+
validator = init_nested_object_validator(validator_class, value)
|
36
|
+
instance_variable_set("@#{nested_name}".to_sym, validator)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Defines an _attributes setter in order to set a value by _attributes key
|
41
|
+
# instead of an original name.
|
42
|
+
def self.define_attributes_setter(nested_name)
|
43
|
+
setter_name = "#{nested_name}_attributes="
|
44
|
+
define_method(setter_name) { |value| set_value(nested_name, value) }
|
33
45
|
end
|
34
46
|
|
35
47
|
# Returns a structure of validators such as:
|
@@ -72,17 +84,11 @@ module BetterValidations::Validator
|
|
72
84
|
BetterValidations::ValidatorsList.new(*([self] + validators))
|
73
85
|
end
|
74
86
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
# The method is overriden for providing a validator object instead of
|
83
|
-
# an active record to collecting error messages.
|
84
|
-
def relation_for_nested_messages(relation)
|
85
|
-
nested_object_validators(relation.to_sym)
|
87
|
+
def read_attribute_for_validation(attr)
|
88
|
+
# Default implementation is 'send(attr)', but it fails if set
|
89
|
+
# an :blank error as a symbol to the nested object:
|
90
|
+
# errors.add(:'nested_name.nested_attribute', :blank)
|
91
|
+
try(attr)
|
86
92
|
end
|
87
93
|
|
88
94
|
protected
|
@@ -112,5 +118,30 @@ module BetterValidations::Validator
|
|
112
118
|
setter = "#{key}=".to_sym
|
113
119
|
public_send(setter, value) if respond_to?(setter)
|
114
120
|
end
|
121
|
+
|
122
|
+
def init_nested_object_validator(validator_class, value)
|
123
|
+
return nil if value.nil?
|
124
|
+
|
125
|
+
# A value can be a single object or a list of objects
|
126
|
+
if value.is_a?(Hash) || value.is_a?(ActionController::Parameters)
|
127
|
+
validator_class.new(value)
|
128
|
+
elsif value.is_a?(Enumerable)
|
129
|
+
init_nested_object_validators_list(value, validator_class)
|
130
|
+
elsif value.is_a?(BetterValidations::Validator)
|
131
|
+
value
|
132
|
+
else
|
133
|
+
validator_class.new(value)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def init_nested_object_validators_list(list, validator_class)
|
138
|
+
list.map do |object|
|
139
|
+
if object.is_a?(BetterValidations::Validator)
|
140
|
+
object
|
141
|
+
else
|
142
|
+
validator_class.new(object)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
115
146
|
end
|
116
147
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: better_validations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Petr Bazov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|