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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7fe4e4a0e2cf6d27dd73e985d29bceb8e6aff0657304639964eff597d6d47cde
4
- data.tar.gz: eb28f4cf0c70b38f97f89e5a582a4c5bb985f1af8242c37ec085502a4b346b7d
3
+ metadata.gz: 76ca8c12dfb8eac58738773cab5d49cfad6d1b1e58728873fac9f740b72fbc0b
4
+ data.tar.gz: a9c10806fb84f110dd5a13b07aea6f036d295c6eb81810ea8ab788aacb859cd0
5
5
  SHA512:
6
- metadata.gz: 3ee9b4c10050311661e8955c6ee0e5015062473885a793fa0946283469c2e97b2352ddee92c058f1bd2d01b1e20ee99698f5e6d2715c99b198e572b8458c1864
7
- data.tar.gz: 40c4eb08fbba1f0b0cdb28f4534899d9241553623a358e96dd455b1f0e941e10a62fb7a2e144a50616ddda9da2b17fe81b0159684d017acd92f5c376226c8a24
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 = init_validator(value)
13
- cache_validator(record, attr_name, validator)
5
+ validator = value
14
6
  return if validator_valid?(validator)
15
7
 
16
- record.errors.add(error_key(attr_name), error_text)
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 error_key(attr_name)
42
- # Create a key with a dot to tell a framework that an error happened in
43
- # a nested object
44
- "#{attr_name}.attributes".to_sym
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 error_text
48
- I18n.t('errors.messages.invalid')
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, validator_class)
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, validator_class)
26
+ def self.bind_validator(nested_name)
30
27
  validates_with BetterValidations::NestedValidator,
31
- attributes: [nested_name],
32
- validator_class: validator_class
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
- # Override to define a start value and getter by name
76
- def nested_object_validators(name = nil)
77
- return nested_object_validators[name.to_sym] unless name.nil?
78
-
79
- @nested_object_validators ||= {}
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
@@ -1,3 +1,3 @@
1
1
  module BetterValidations
2
- VERSION = '0.1.4'.freeze
2
+ VERSION = '1.0.0'.freeze
3
3
  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.1.4
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-04-20 00:00:00.000000000 Z
11
+ date: 2020-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails