easy_params 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +8 -32
- data/README.md +30 -30
- data/easy_params.gemspec +1 -5
- data/lib/easy_params/base.rb +64 -73
- data/lib/easy_params/types/collection.rb +50 -0
- data/lib/easy_params/types/generic.rb +44 -0
- data/lib/easy_params/types/struct.rb +18 -0
- data/lib/easy_params/types.rb +31 -12
- data/lib/easy_params/validation.rb +51 -0
- data/lib/easy_params/version.rb +1 -1
- data/lib/easy_params.rb +5 -2
- metadata +6 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e5797272eae09e65c24f46558c801f19126e71e4ca940bdfaeecfaab478fe7d
|
4
|
+
data.tar.gz: 95d1a80d4f0635013658713c6dccc45058e707cae9e90ec88036313c673819ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e76c8c64f7568d0ef3633eb3ca86a1cf1f03f331d528aa7a941c8197a2030dbbeb3fc77149b7dd0416b4776b3a8b547a6e0c451a6e1c3af92835b25e2d6815c
|
7
|
+
data.tar.gz: 82f361cf4ebce4db3ab3bbc9fd7ca196a3dc57a91c3156df1b2e18d443922a900998f58c43b5a12571346de047c1f9e803ad45e109dcb32c028840c44ebf0681
|
data/Gemfile.lock
CHANGED
@@ -1,17 +1,15 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
easy_params (0.
|
5
|
-
activemodel (>= 3.2
|
6
|
-
dry-struct (~> 1.4)
|
7
|
-
dry-types (~> 1.5)
|
4
|
+
easy_params (0.5.0)
|
5
|
+
activemodel (>= 3.2)
|
8
6
|
|
9
7
|
GEM
|
10
8
|
remote: https://rubygems.org/
|
11
9
|
specs:
|
12
|
-
activemodel (7.1.
|
13
|
-
activesupport (= 7.1.
|
14
|
-
activesupport (7.1.
|
10
|
+
activemodel (7.1.3)
|
11
|
+
activesupport (= 7.1.3)
|
12
|
+
activesupport (7.1.3)
|
15
13
|
base64
|
16
14
|
bigdecimal
|
17
15
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
@@ -23,40 +21,19 @@ GEM
|
|
23
21
|
tzinfo (~> 2.0)
|
24
22
|
ast (2.4.2)
|
25
23
|
base64 (0.2.0)
|
26
|
-
bigdecimal (3.1.
|
24
|
+
bigdecimal (3.1.6)
|
27
25
|
coderay (1.1.3)
|
28
|
-
concurrent-ruby (1.2.
|
26
|
+
concurrent-ruby (1.2.3)
|
29
27
|
connection_pool (2.4.1)
|
30
28
|
diff-lcs (1.4.4)
|
31
29
|
docile (1.3.5)
|
32
30
|
drb (2.2.0)
|
33
31
|
ruby2_keywords
|
34
|
-
dry-core (1.0.1)
|
35
|
-
concurrent-ruby (~> 1.0)
|
36
|
-
zeitwerk (~> 2.6)
|
37
|
-
dry-inflector (1.0.0)
|
38
|
-
dry-logic (1.5.0)
|
39
|
-
concurrent-ruby (~> 1.0)
|
40
|
-
dry-core (~> 1.0, < 2)
|
41
|
-
zeitwerk (~> 2.6)
|
42
|
-
dry-struct (1.6.0)
|
43
|
-
dry-core (~> 1.0, < 2)
|
44
|
-
dry-types (>= 1.7, < 2)
|
45
|
-
ice_nine (~> 0.11)
|
46
|
-
zeitwerk (~> 2.6)
|
47
|
-
dry-types (1.7.2)
|
48
|
-
bigdecimal (~> 3.0)
|
49
|
-
concurrent-ruby (~> 1.0)
|
50
|
-
dry-core (~> 1.0)
|
51
|
-
dry-inflector (~> 1.0)
|
52
|
-
dry-logic (~> 1.4)
|
53
|
-
zeitwerk (~> 2.6)
|
54
32
|
i18n (1.14.1)
|
55
33
|
concurrent-ruby (~> 1.0)
|
56
|
-
ice_nine (0.11.2)
|
57
34
|
json (2.6.3)
|
58
35
|
method_source (1.0.0)
|
59
|
-
minitest (5.21.
|
36
|
+
minitest (5.21.2)
|
60
37
|
mutex_m (0.2.0)
|
61
38
|
parallel (1.23.0)
|
62
39
|
parser (3.2.2.3)
|
@@ -108,7 +85,6 @@ GEM
|
|
108
85
|
tzinfo (2.0.6)
|
109
86
|
concurrent-ruby (~> 1.0)
|
110
87
|
unicode-display_width (2.4.2)
|
111
|
-
zeitwerk (2.6.12)
|
112
88
|
|
113
89
|
PLATFORMS
|
114
90
|
ruby
|
data/README.md
CHANGED
@@ -4,11 +4,11 @@
|
|
4
4
|
[![Maintainability](https://api.codeclimate.com/v1/badges/17872804ce576b8b0df2/maintainability)](https://codeclimate.com/github/andriy-baran/easy_params/maintainability)
|
5
5
|
[![Test Coverage](https://api.codeclimate.com/v1/badges/17872804ce576b8b0df2/test_coverage)](https://codeclimate.com/github/andriy-baran/easy_params/test_coverage)
|
6
6
|
|
7
|
-
Provides an easy way define structure, validation rules, type coercion and set default values for any hash-like structure. It's built on top of `
|
7
|
+
Provides an easy way define structure, validation rules, type coercion and set default values for any hash-like structure. It's built on top of `active_model`.
|
8
8
|
|
9
9
|
## Types
|
10
10
|
|
11
|
-
Dry types are wrapped by class methods. Avaliable types: `integer`, `decimal`, `float`, `bool`, `string`, `array`, `date`, `datetime`, `time
|
11
|
+
Dry types are wrapped by class methods. Avaliable types: `integer`, `decimal`, `float`, `bool`, `string`, `array`, `date`, `datetime`, `time`
|
12
12
|
|
13
13
|
## Installation
|
14
14
|
|
@@ -28,24 +28,39 @@ Or install it yourself as:
|
|
28
28
|
|
29
29
|
## Usage
|
30
30
|
|
31
|
+
To define attribute we have a set of methods which match types list. Ex.
|
32
|
+
```ruby
|
33
|
+
integer(param_name, default: nil, normalize: nil, **validations)
|
34
|
+
```
|
35
|
+
* `:default` provides a value to return if got `nil` as input or there were errors during coersion.
|
36
|
+
* `normalize` a proc or lambda that accepts single argument and changes it in some way. It's get called before coercion.
|
37
|
+
* `validations` mimics `activemodel/validation` can be any supported validation `presence: true, numericality: { only_integer: true, greater_than: 0 }`
|
38
|
+
|
39
|
+
In addition we have special option for an `array` type
|
40
|
+
* `:of` accepts `:integer`, `:decimal`, `:float`, `:bool`, `:string`, `:date`, `:datetime`, `:time` (`:array` is not supported)
|
41
|
+
|
42
|
+
There are two special types:
|
43
|
+
|
44
|
+
| type | method to define | default |
|
45
|
+
|-------------------|------------------|---------|
|
46
|
+
| :struct | has | {} |
|
47
|
+
| :array_of_structs | each | [] |
|
48
|
+
|
31
49
|
```ruby
|
32
50
|
# app/params/api/v2/icqa/move_params.rb
|
33
51
|
class Api::V1::Carts::MoveParams < EasyParams::Base
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
52
|
+
integer :receive_cart_id, presence: { message: "can't be blank" }
|
53
|
+
bool :i_am_sure
|
54
|
+
string :location_code, default: '', presence: { message: "can't be blank" }
|
55
|
+
has :section do
|
56
|
+
string :from
|
57
|
+
string :to
|
40
58
|
end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
validates :option_type_count, :option_type_value, presence: { message: "can't be blank" }
|
59
|
+
array :variant_ids, of: :integer
|
60
|
+
each :options do
|
61
|
+
integer :option_type_count, presence: { message: "can't be blank" }
|
62
|
+
integer :option_type_value, presence: { message: "can't be blank" }
|
46
63
|
end
|
47
|
-
|
48
|
-
validates :receive_cart_id, :location_code, presence: { message: "can't be blank" }
|
49
64
|
end
|
50
65
|
```
|
51
66
|
Validation messages for nested attributes will look like this.
|
@@ -57,21 +72,6 @@ Validation messages for nested attributes will look like this.
|
|
57
72
|
:"post.sections[0].id"=>an_instance_of(Array)
|
58
73
|
}
|
59
74
|
```
|
60
|
-
Optionally you can use more compact form
|
61
|
-
```ruby
|
62
|
-
class MyParams < EasyParams::Base
|
63
|
-
extend EasyParams::DSL
|
64
|
-
|
65
|
-
quantity integer.default(1)
|
66
|
-
posts each do
|
67
|
-
content string.default('')
|
68
|
-
end
|
69
|
-
user has do
|
70
|
-
role string
|
71
|
-
end
|
72
|
-
end
|
73
|
-
```
|
74
|
-
This hovewer has some limitations: for attributes have name like `integer`, `decimal`, `float`, `bool`, `string`, `array`, `date`, `datetime`, `time`, `struct` it won't work as expected.
|
75
75
|
|
76
76
|
More examples here [params_spec.rb](https://github.com/andriy-baran/easy_params/blob/master/spec/easy_params_spec.rb)
|
77
77
|
|
data/easy_params.gemspec
CHANGED
@@ -29,11 +29,7 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
30
|
spec.require_paths = ['lib']
|
31
31
|
|
32
|
-
version_string = ['>= 3.2'
|
32
|
+
version_string = ['>= 3.2']
|
33
33
|
|
34
34
|
spec.add_runtime_dependency 'activemodel', version_string
|
35
|
-
|
36
|
-
# spec.add_dependency 'dry-logic', '~> 0.4.2'
|
37
|
-
spec.add_dependency 'dry-struct', '~> 1.4'
|
38
|
-
spec.add_dependency 'dry-types', '~> 1.5'
|
39
35
|
end
|
data/lib/easy_params/base.rb
CHANGED
@@ -2,99 +2,90 @@
|
|
2
2
|
|
3
3
|
module EasyParams
|
4
4
|
# Implements validations logic and nesting structures
|
5
|
-
class Base
|
6
|
-
include ActiveModel::
|
5
|
+
class Base
|
6
|
+
include ActiveModel::Model
|
7
|
+
include EasyParams::Types::Struct
|
8
|
+
include EasyParams::Validation
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
def initialize(params = {})
|
11
|
+
self.class.schema.each do |attr, type|
|
12
|
+
public_send("#{attr}=", type.coerce(params[attr])) if params
|
13
|
+
end
|
12
14
|
end
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
class << self
|
17
|
+
def name
|
18
|
+
'EasyParams::Base'
|
19
|
+
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
def attribute(param_name, type)
|
22
|
+
attr_accessor param_name
|
23
|
+
|
24
|
+
schema[param_name] = type
|
25
|
+
end
|
26
|
+
|
27
|
+
def schema
|
28
|
+
@schema ||= {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def each(param_name, normalize: nil, **validations, &block)
|
26
32
|
validates param_name, **validations if validations.any?
|
33
|
+
type = EasyParams::Types::Each.with_type(&block)
|
34
|
+
type = customize_type(type, nil, &normalize)
|
27
35
|
public_send(:attribute, param_name, type)
|
28
36
|
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.each(param_name, normalize: nil, optional: nil, **validations, &block)
|
32
|
-
validates param_name, **validations if validations.any?
|
33
|
-
type = EasyParams::Types::Array.of(EasyParams::Types::Struct)
|
34
|
-
type = type.meta(omittable: true) if optional
|
35
|
-
type = type.constructor { |value| value == Dry::Types::Undefined ? value : normalize.call(value) } if normalize
|
36
|
-
public_send(:attribute, param_name, type, &block)
|
37
|
-
end
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
38
|
+
def has(param_name, normalize: nil, **validations, &block)
|
39
|
+
validates param_name, **validations if validations.any?
|
40
|
+
type = Class.new(EasyParams::Types::Struct.class).tap { |c| c.class_eval(&block) }.new
|
41
|
+
type = customize_type(type, nil, &normalize)
|
42
|
+
public_send(:attribute, param_name, type)
|
43
|
+
end
|
46
44
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
public_send(:attribute, param_name, type.of(of_type), &block)
|
54
|
-
end
|
45
|
+
def array(param_name, of:, normalize: nil, **validations)
|
46
|
+
validates param_name, **validations if validations.any?
|
47
|
+
type = EasyParams::Types::Array.of(EasyParams::Types.const_get(of.to_s.camelcase))
|
48
|
+
type = customize_type(type, nil, &normalize)
|
49
|
+
public_send(:attribute, param_name, type)
|
50
|
+
end
|
55
51
|
|
56
|
-
|
52
|
+
private
|
57
53
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
value.each(&:valid?)
|
63
|
-
when *EasyParams::Types::STRUCT_TYPES_LIST
|
64
|
-
value.valid?
|
65
|
-
end
|
54
|
+
def customize_type(type, default, &normalize)
|
55
|
+
type = type.default(default) if default
|
56
|
+
type = type.normalize(&normalize) if normalize
|
57
|
+
type
|
66
58
|
end
|
67
|
-
attributes.each(&aggregate_nested_errors)
|
68
59
|
end
|
69
60
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
when *EasyParams::Types::STRUCT_TYPES_LIST
|
78
|
-
handle_nested_errors(value, error_key_prefix, attr_name, array_index)
|
79
|
-
end
|
61
|
+
%w[Integer Decimal Float Bool String Date DateTime Time].each do |type_name|
|
62
|
+
send(:define_singleton_method,
|
63
|
+
type_name.underscore) do |param_name, default: nil, normalize: nil, **validations|
|
64
|
+
validates param_name, **validations if validations.any?
|
65
|
+
type = EasyParams::Types.const_get(type_name)
|
66
|
+
type = customize_type(type, default, &normalize)
|
67
|
+
public_send(:attribute, param_name, type)
|
80
68
|
end
|
81
69
|
end
|
82
70
|
|
83
|
-
def
|
84
|
-
|
71
|
+
def attributes
|
72
|
+
self.class.schema.to_h { |k, type| [k, type.array? ? send(k).to_a : send(k)] }
|
73
|
+
end
|
85
74
|
|
86
|
-
|
87
|
-
|
88
|
-
add_errors_on_top_level(value, attr_error_key_prefix)
|
75
|
+
validate do
|
76
|
+
validate_nested
|
89
77
|
end
|
90
78
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
79
|
+
def to_h
|
80
|
+
attributes.each.with_object({}) do |(key, value), result|
|
81
|
+
result[key] = case value
|
82
|
+
when EasyParams::Types::StructsCollection
|
83
|
+
value.map(&:to_h)
|
84
|
+
when EasyParams::Types::Struct.class
|
85
|
+
value.to_h
|
86
|
+
else
|
87
|
+
value
|
88
|
+
end
|
98
89
|
end
|
99
90
|
end
|
100
91
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EasyParams
|
4
|
+
module Types
|
5
|
+
# base interface for array type
|
6
|
+
class Collection < Generic
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
def initialize(*attrs, of: nil)
|
10
|
+
super(*attrs)
|
11
|
+
@of_type = of
|
12
|
+
end
|
13
|
+
|
14
|
+
def of(of_type)
|
15
|
+
self.class.new(@title, @default, @normalize_proc, of: of_type, &@coerce_proc)
|
16
|
+
end
|
17
|
+
|
18
|
+
def coerce(value)
|
19
|
+
value = @normalize_proc.call(Array(value)) if @normalize_proc
|
20
|
+
self.class.new(@title, Array(value).map do |v|
|
21
|
+
@of_type.coerce(v)
|
22
|
+
end, @normalize_proc, of: @of_type, &@coerce_proc)
|
23
|
+
end
|
24
|
+
|
25
|
+
def normalize(&block)
|
26
|
+
self.class.new(@title, @default, block, of: @of_type, &@coerce_proc)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.optional
|
30
|
+
self.class.new(@title, @default, @normalize_proc, of: @of_type, &@coerce_proc)
|
31
|
+
end
|
32
|
+
|
33
|
+
def default(value)
|
34
|
+
self.class.new(@title, value, @normalize_proc, of: @of_type, &@coerce_proc)
|
35
|
+
end
|
36
|
+
|
37
|
+
def each(&block)
|
38
|
+
@default.each(&block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# base interface for array of structs type
|
43
|
+
class StructsCollection < Collection
|
44
|
+
def with_type(&block)
|
45
|
+
of_type = Class.new(EasyParams::Types::Struct.class).tap { |c| c.class_eval(&block) }.new
|
46
|
+
self.class.new(@title, @default, @normalize_proc, of: of_type)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EasyParams
|
4
|
+
module Types
|
5
|
+
# base interface for simple types
|
6
|
+
class Generic
|
7
|
+
def array?
|
8
|
+
@title == :array
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(title, default = nil, normalize_proc = nil, &coerce_proc)
|
12
|
+
@title = title
|
13
|
+
@default = default
|
14
|
+
@coerce_proc = coerce_proc
|
15
|
+
@normalize_proc = normalize_proc
|
16
|
+
end
|
17
|
+
|
18
|
+
def default(value)
|
19
|
+
self.class.new(@title, value, @normalize_proc, &@coerce_proc)
|
20
|
+
end
|
21
|
+
|
22
|
+
def optional
|
23
|
+
self.class.new(@title, @default, @normalize_proc, &@coerce_proc)
|
24
|
+
end
|
25
|
+
|
26
|
+
def optional?
|
27
|
+
@optional
|
28
|
+
end
|
29
|
+
|
30
|
+
def normalize(&block)
|
31
|
+
self.class.new(@title, @default, block, &@coerce_proc)
|
32
|
+
end
|
33
|
+
|
34
|
+
def coerce(value)
|
35
|
+
value = @normalize_proc.call(value) if @normalize_proc
|
36
|
+
return @default if value.nil?
|
37
|
+
|
38
|
+
@coerce_proc.call(value)
|
39
|
+
rescue StandardError
|
40
|
+
@default
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EasyParams
|
4
|
+
module Types
|
5
|
+
# base interface for struct type
|
6
|
+
module Struct
|
7
|
+
def array?
|
8
|
+
false
|
9
|
+
end
|
10
|
+
|
11
|
+
def coerce(input)
|
12
|
+
return if input.blank?
|
13
|
+
|
14
|
+
self.class.new(input)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/easy_params/types.rb
CHANGED
@@ -2,18 +2,37 @@
|
|
2
2
|
|
3
3
|
module EasyParams
|
4
4
|
module Types
|
5
|
-
|
6
|
-
Integer = Dry::Types['params.integer'].optional.default(nil)
|
7
|
-
Decimal = Dry::Types['params.decimal'].optional.default(nil)
|
8
|
-
Float = Dry::Types['params.float'].optional.default(nil)
|
9
|
-
Bool = Dry::Types['params.bool'].optional.default(nil)
|
10
|
-
String = Dry::Types['coercible.string'].optional.default(nil)
|
11
|
-
Array = Dry::Types['array'].default([].freeze)
|
12
|
-
Date = Dry::Types['params.date'].optional.default(nil)
|
13
|
-
DateTime = Dry::Types['params.date_time'].optional.default(nil)
|
14
|
-
Time = Dry::Types['params.time'].optional.default(nil)
|
5
|
+
class CoercionError < StandardError; end
|
15
6
|
|
16
|
-
|
17
|
-
|
7
|
+
BOOLEAN_MAP =
|
8
|
+
{ '1' => true, 't' => true, 'true' => true, 'True' => true, 'TRUE' => true, 'T' => true }.merge(
|
9
|
+
{ '0' => false, 'f' => false, 'false' => false, 'False' => false, 'FALSE' => false, 'F' => false }
|
10
|
+
).freeze
|
11
|
+
|
12
|
+
Struct = Class.new(EasyParams::Base).new
|
13
|
+
Array = Collection.new(:array)
|
14
|
+
Each = StructsCollection.new(:array_of_structs)
|
15
|
+
Integer = Generic.new(:integer, &:to_i)
|
16
|
+
Float = Generic.new(:float, &:to_f)
|
17
|
+
String = Generic.new(:string, &:to_s)
|
18
|
+
Decimal = Generic.new(:decimal) { |v| v.to_f.to_d }
|
19
|
+
Bool = Generic.new(:bool) do |v|
|
20
|
+
BOOLEAN_MAP.fetch(v.to_s) { raise CoercionError }
|
21
|
+
end
|
22
|
+
Date = Generic.new(:date) do |v|
|
23
|
+
::Date.parse(v)
|
24
|
+
rescue ArgumentError, RangeError
|
25
|
+
raise CoercionError, 'cannot be coerced'
|
26
|
+
end
|
27
|
+
DateTime = Generic.new(:datetime) do |v|
|
28
|
+
::DateTime.parse(v)
|
29
|
+
rescue ArgumentError
|
30
|
+
raise CoercionError, 'cannot be coerced'
|
31
|
+
end
|
32
|
+
Time = Generic.new(:time) do |v|
|
33
|
+
::Time.parse(v)
|
34
|
+
rescue ArgumentError
|
35
|
+
raise CoercionError, 'cannot be coerced'
|
36
|
+
end
|
18
37
|
end
|
19
38
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EasyParams
|
4
|
+
# helpers for recursive validation
|
5
|
+
module Validation
|
6
|
+
private
|
7
|
+
|
8
|
+
def validate_nested
|
9
|
+
attributes.each_value do |value|
|
10
|
+
case value
|
11
|
+
when EasyParams::Types::StructsCollection
|
12
|
+
value.each(&:valid?)
|
13
|
+
when EasyParams::Types::Struct.class
|
14
|
+
value.valid?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
attributes.each(&aggregate_nested_errors)
|
18
|
+
end
|
19
|
+
|
20
|
+
def aggregate_nested_errors
|
21
|
+
proc do |attr_name, value, array_index, error_key_prefix|
|
22
|
+
case value
|
23
|
+
when EasyParams::Types::StructsCollection
|
24
|
+
value.each.with_index do |element, i|
|
25
|
+
aggregate_nested_errors[attr_name, element, "[#{i}]", error_key_prefix]
|
26
|
+
end
|
27
|
+
when EasyParams::Types::Struct.class
|
28
|
+
handle_nested_errors(value, error_key_prefix, attr_name, array_index)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def handle_nested_errors(value, error_key_prefix, attr_name, array_index)
|
34
|
+
return if value.errors.blank?
|
35
|
+
|
36
|
+
error_key_components = [error_key_prefix, attr_name, array_index]
|
37
|
+
attr_error_key_prefix = error_key_components.compact.join('.').gsub(/\.\[(\d+)\]/, '[\1]')
|
38
|
+
add_errors_on_top_level(value, attr_error_key_prefix)
|
39
|
+
end
|
40
|
+
|
41
|
+
if defined? ActiveModel::Error
|
42
|
+
def add_errors_on_top_level(value, attr_error_key_prefix)
|
43
|
+
value.errors.each { |error| errors.add("#{attr_error_key_prefix}.#{error.attribute}", error.message) }
|
44
|
+
end
|
45
|
+
else
|
46
|
+
def add_errors_on_top_level(value, attr_error_key_prefix)
|
47
|
+
value.errors.each { |key, message| errors.add("#{attr_error_key_prefix}.#{key}", message) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/easy_params/version.rb
CHANGED
data/lib/easy_params.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'dry-struct'
|
4
|
-
require 'dry-types'
|
5
3
|
require 'active_model'
|
4
|
+
require 'bigdecimal/util'
|
5
|
+
require 'easy_params/types/generic'
|
6
|
+
require 'easy_params/types/collection'
|
7
|
+
require 'easy_params/types/struct'
|
8
|
+
require 'easy_params/validation'
|
6
9
|
require 'easy_params/base'
|
7
10
|
require 'easy_params/types'
|
8
11
|
require 'easy_params/version'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: easy_params
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrii Baran
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-01-
|
11
|
+
date: 2024-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -17,9 +17,6 @@ dependencies:
|
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '3.2'
|
20
|
-
- - "<"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '8'
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,37 +24,6 @@ dependencies:
|
|
27
24
|
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '3.2'
|
30
|
-
- - "<"
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '8'
|
33
|
-
- !ruby/object:Gem::Dependency
|
34
|
-
name: dry-struct
|
35
|
-
requirement: !ruby/object:Gem::Requirement
|
36
|
-
requirements:
|
37
|
-
- - "~>"
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: '1.4'
|
40
|
-
type: :runtime
|
41
|
-
prerelease: false
|
42
|
-
version_requirements: !ruby/object:Gem::Requirement
|
43
|
-
requirements:
|
44
|
-
- - "~>"
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '1.4'
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: dry-types
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
50
|
-
requirements:
|
51
|
-
- - "~>"
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: '1.5'
|
54
|
-
type: :runtime
|
55
|
-
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
requirements:
|
58
|
-
- - "~>"
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
version: '1.5'
|
61
27
|
description: Dessribe structure, validate and coerce values. Powered by dry-types
|
62
28
|
and dry-struct
|
63
29
|
email:
|
@@ -83,6 +49,10 @@ files:
|
|
83
49
|
- lib/easy_params.rb
|
84
50
|
- lib/easy_params/base.rb
|
85
51
|
- lib/easy_params/types.rb
|
52
|
+
- lib/easy_params/types/collection.rb
|
53
|
+
- lib/easy_params/types/generic.rb
|
54
|
+
- lib/easy_params/types/struct.rb
|
55
|
+
- lib/easy_params/validation.rb
|
86
56
|
- lib/easy_params/version.rb
|
87
57
|
homepage: https://github.com/andriy-baran/easy_params
|
88
58
|
licenses:
|