easy_params 0.4.1 → 0.6.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/.ruby-version +1 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +68 -77
- data/README.md +67 -33
- data/easy_params.gemspec +2 -6
- data/lib/easy_params/base.rb +66 -73
- data/lib/easy_params/types/collection.rb +50 -0
- data/lib/easy_params/types/generic.rb +36 -0
- data/lib/easy_params/types/struct.rb +24 -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 +8 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c59e34205e7543bb5d58358d8e93b92ff911cacac58b4dbd55069b1398d10691
|
4
|
+
data.tar.gz: 7ba6653bb5b7ecd3e10d3e23be396f9482b0cf336f8807d5cc09a8d9fa1b9750
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b6b302946b9eb92cbcedf61a6eae529030154ec251ce4a98928d0cd31cffb069e43ad2fd98f4768ca208ea1f7f65e5cf0be89fa16bce1d3fd7f2c02d8d837c1
|
7
|
+
data.tar.gz: ee6bdb5edaf5a37230c631036936a3dc8a520aa0ed5a344ad69cdf03ff9d77d817f2c346d0b8a27ca0a421f6c25f16a80190262ead23877af6a7a4f7a706a8ad
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.4.1
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,120 +1,111 @@
|
|
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.6.0)
|
5
|
+
activemodel (>= 3.2)
|
8
6
|
|
9
7
|
GEM
|
10
8
|
remote: https://rubygems.org/
|
11
9
|
specs:
|
12
|
-
activemodel (
|
13
|
-
activesupport (=
|
14
|
-
activesupport (
|
10
|
+
activemodel (8.0.2)
|
11
|
+
activesupport (= 8.0.2)
|
12
|
+
activesupport (8.0.2)
|
15
13
|
base64
|
14
|
+
benchmark (>= 0.3)
|
16
15
|
bigdecimal
|
17
|
-
concurrent-ruby (~> 1.0, >= 1.
|
16
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
18
17
|
connection_pool (>= 2.2.5)
|
19
18
|
drb
|
20
19
|
i18n (>= 1.6, < 2)
|
20
|
+
logger (>= 1.4.2)
|
21
21
|
minitest (>= 5.1)
|
22
|
-
|
23
|
-
tzinfo (~> 2.0)
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
securerandom (>= 0.3)
|
23
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
24
|
+
uri (>= 0.13.1)
|
25
|
+
ast (2.4.3)
|
26
|
+
base64 (0.3.0)
|
27
|
+
benchmark (0.4.1)
|
28
|
+
bigdecimal (3.2.2)
|
27
29
|
coderay (1.1.3)
|
28
|
-
concurrent-ruby (1.
|
29
|
-
connection_pool (2.
|
30
|
-
diff-lcs (1.
|
31
|
-
docile (1.
|
32
|
-
drb (2.2.
|
33
|
-
|
34
|
-
dry-core (1.0.1)
|
30
|
+
concurrent-ruby (1.3.5)
|
31
|
+
connection_pool (2.5.3)
|
32
|
+
diff-lcs (1.6.2)
|
33
|
+
docile (1.4.1)
|
34
|
+
drb (2.2.3)
|
35
|
+
i18n (1.14.7)
|
35
36
|
concurrent-ruby (~> 1.0)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
i18n (1.14.1)
|
55
|
-
concurrent-ruby (~> 1.0)
|
56
|
-
ice_nine (0.11.2)
|
57
|
-
json (2.6.3)
|
58
|
-
method_source (1.0.0)
|
59
|
-
minitest (5.21.1)
|
60
|
-
mutex_m (0.2.0)
|
61
|
-
parallel (1.23.0)
|
62
|
-
parser (3.2.2.3)
|
37
|
+
json (2.13.2)
|
38
|
+
language_server-protocol (3.17.0.5)
|
39
|
+
lint_roller (1.1.0)
|
40
|
+
logger (1.7.0)
|
41
|
+
method_source (1.1.0)
|
42
|
+
minitest (5.25.5)
|
43
|
+
ostruct (0.6.2)
|
44
|
+
parallel (1.27.0)
|
45
|
+
parser (3.3.9.0)
|
63
46
|
ast (~> 2.4.1)
|
64
47
|
racc
|
65
|
-
|
48
|
+
prism (1.4.0)
|
49
|
+
pry (0.15.2)
|
66
50
|
coderay (~> 1.1)
|
67
51
|
method_source (~> 1.0)
|
68
|
-
racc (1.
|
52
|
+
racc (1.8.1)
|
69
53
|
rainbow (3.1.1)
|
70
54
|
rake (12.3.3)
|
71
|
-
regexp_parser (2.
|
72
|
-
|
73
|
-
|
74
|
-
rspec-
|
75
|
-
rspec-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
rspec-expectations (3.10.0)
|
55
|
+
regexp_parser (2.10.0)
|
56
|
+
rspec (3.13.1)
|
57
|
+
rspec-core (~> 3.13.0)
|
58
|
+
rspec-expectations (~> 3.13.0)
|
59
|
+
rspec-mocks (~> 3.13.0)
|
60
|
+
rspec-core (3.13.5)
|
61
|
+
rspec-support (~> 3.13.0)
|
62
|
+
rspec-expectations (3.13.5)
|
80
63
|
diff-lcs (>= 1.2.0, < 2.0)
|
81
|
-
rspec-support (~> 3.
|
82
|
-
rspec-mocks (3.
|
64
|
+
rspec-support (~> 3.13.0)
|
65
|
+
rspec-mocks (3.13.5)
|
83
66
|
diff-lcs (>= 1.2.0, < 2.0)
|
84
|
-
rspec-support (~> 3.
|
85
|
-
rspec-support (3.
|
67
|
+
rspec-support (~> 3.13.0)
|
68
|
+
rspec-support (3.13.4)
|
86
69
|
rspec_vars_helper (0.1.0)
|
87
70
|
rspec (>= 2.4)
|
88
|
-
rubocop (1.
|
71
|
+
rubocop (1.79.0)
|
89
72
|
json (~> 2.3)
|
73
|
+
language_server-protocol (~> 3.17.0.2)
|
74
|
+
lint_roller (~> 1.1.0)
|
90
75
|
parallel (~> 1.10)
|
91
|
-
parser (>= 3.
|
76
|
+
parser (>= 3.3.0.2)
|
92
77
|
rainbow (>= 2.2.2, < 4.0)
|
93
|
-
regexp_parser (>=
|
94
|
-
|
95
|
-
rubocop-ast (>= 1.28.0, < 2.0)
|
78
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
79
|
+
rubocop-ast (>= 1.46.0, < 2.0)
|
96
80
|
ruby-progressbar (~> 1.7)
|
97
|
-
|
98
|
-
|
99
|
-
|
81
|
+
tsort (>= 0.2.0)
|
82
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
83
|
+
rubocop-ast (1.46.0)
|
84
|
+
parser (>= 3.3.7.2)
|
85
|
+
prism (~> 1.4)
|
100
86
|
ruby-progressbar (1.13.0)
|
101
|
-
|
102
|
-
simplecov (0.
|
87
|
+
securerandom (0.4.1)
|
88
|
+
simplecov (0.22.0)
|
103
89
|
docile (~> 1.1)
|
104
90
|
simplecov-html (~> 0.11)
|
105
91
|
simplecov_json_formatter (~> 0.1)
|
106
|
-
simplecov-html (0.
|
107
|
-
simplecov_json_formatter (0.1.
|
92
|
+
simplecov-html (0.13.2)
|
93
|
+
simplecov_json_formatter (0.1.4)
|
94
|
+
tsort (0.2.0)
|
108
95
|
tzinfo (2.0.6)
|
109
96
|
concurrent-ruby (~> 1.0)
|
110
|
-
unicode-display_width (
|
111
|
-
|
97
|
+
unicode-display_width (3.1.4)
|
98
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
99
|
+
unicode-emoji (4.0.4)
|
100
|
+
uri (1.0.3)
|
112
101
|
|
113
102
|
PLATFORMS
|
103
|
+
arm64-darwin-24
|
114
104
|
ruby
|
115
105
|
|
116
106
|
DEPENDENCIES
|
117
107
|
easy_params!
|
108
|
+
ostruct
|
118
109
|
pry
|
119
110
|
rake (~> 12.0)
|
120
111
|
rspec (~> 3.0)
|
@@ -123,4 +114,4 @@ DEPENDENCIES
|
|
123
114
|
simplecov (~> 0.17)
|
124
115
|
|
125
116
|
BUNDLED WITH
|
126
|
-
2.
|
117
|
+
2.7.1
|
data/README.md
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# EasyParams
|
2
2
|
|
3
|
-
[](https://badge.fury.io/rb/easy_params)
|
4
4
|
[](https://codeclimate.com/github/andriy-baran/easy_params/maintainability)
|
5
5
|
[](https://codeclimate.com/github/andriy-baran/easy_params/test_coverage)
|
6
6
|
|
7
|
-
Provides an easy way define structure, validation rules, type coercion and
|
7
|
+
Provides an easy way to define structure, validation rules, type coercion, and default values for any hash-like structure. It's built on top of `ActiveModel`.
|
8
8
|
|
9
9
|
## Types
|
10
10
|
|
11
|
-
|
11
|
+
Available types: `integer`, `decimal`, `float`, `bool`, `string`, `array`, `date`, `datetime`, `time`
|
12
12
|
|
13
13
|
## Installation
|
14
14
|
|
@@ -28,24 +28,73 @@ 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.
|
31
32
|
```ruby
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
integer(param_name, default: nil, normalize: nil, **validations)
|
34
|
+
```
|
35
|
+
* `:default` provides a value to return if we get `nil` as input or there were errors during coercion.
|
36
|
+
* `:normalize` is a Proc or lambda that accepts a single argument and transforms it. It gets called before coercion.
|
37
|
+
* `validations` mimic ActiveModel validations; can be any supported validation, e.g., `presence: true, numericality: { only_integer: true, greater_than: 0 }`
|
38
|
+
|
39
|
+
In addition, there is a special option for the `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 | nil |
|
47
|
+
| :array_of_structs | each | [] |
|
48
|
+
|
49
|
+
### Defaults for nested types
|
50
|
+
|
51
|
+
- **has (struct)**: `default:` must be a Hash. When the input is `nil`, the nested struct is instantiated with that hash; otherwise the provided input is used. If no `default:` is given and the input is `nil`, the value will be `nil`.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
has :shipping_address, default: { country: 'US' } do
|
55
|
+
string :country, default: 'US'
|
56
|
+
string :city
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
- **each (array_of_structs)**: `default:` should be an Array (typically an array of hashes). When the input is `nil`, the collection defaults to an empty array `[]`. If you provide a default array, each element will be coerced into the nested struct.
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
each :items, default: [{ qty: 1 }] do
|
64
|
+
integer :qty, default: 1
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
- **Override precedence**: Container-level defaults override attribute-level defaults for the same keys. Attribute defaults apply only when the key is absent (or `nil`) in the provided default/input.
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
has :user, default: { role: 'admin' } do
|
72
|
+
string :role, default: 'guest'
|
73
|
+
string :name, default: 'Anonymous'
|
40
74
|
end
|
41
|
-
|
42
|
-
attribute :option_type_count, integer
|
43
|
-
attribute :option_type_value, integer
|
75
|
+
# input: nil => role: 'admin', name: 'Anonymous'
|
44
76
|
|
45
|
-
|
77
|
+
each :items, default: [{ qty: 2 }, {}] do
|
78
|
+
integer :qty, default: 1
|
46
79
|
end
|
80
|
+
# input: nil => items.map(&:qty) == [2, 1]
|
81
|
+
```
|
47
82
|
|
48
|
-
|
83
|
+
```ruby
|
84
|
+
# app/params/api/v2/icqa/move_params.rb
|
85
|
+
class Api::V1::Carts::MoveParams < EasyParams::Base
|
86
|
+
integer :receive_cart_id, presence: { message: "can't be blank" }
|
87
|
+
bool :i_am_sure
|
88
|
+
string :location_code, default: '', presence: { message: "can't be blank" }
|
89
|
+
has :section do
|
90
|
+
string :from
|
91
|
+
string :to
|
92
|
+
end
|
93
|
+
array :variant_ids, of: :integer
|
94
|
+
each :options do
|
95
|
+
integer :option_type_count, presence: { message: "can't be blank" }
|
96
|
+
integer :option_type_value, presence: { message: "can't be blank" }
|
97
|
+
end
|
49
98
|
end
|
50
99
|
```
|
51
100
|
Validation messages for nested attributes will look like this.
|
@@ -57,21 +106,6 @@ Validation messages for nested attributes will look like this.
|
|
57
106
|
:"post.sections[0].id"=>an_instance_of(Array)
|
58
107
|
}
|
59
108
|
```
|
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
109
|
|
76
110
|
More examples here [params_spec.rb](https://github.com/andriy-baran/easy_params/blob/master/spec/easy_params_spec.rb)
|
77
111
|
|
@@ -83,7 +117,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
83
117
|
|
84
118
|
## Contributing
|
85
119
|
|
86
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
120
|
+
Bug reports and pull requests are welcome on GitHub at [github.com/andriy-baran/easy_params](https://github.com/andriy-baran/easy_params). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/andriy-baran/easy_params/blob/master/CODE_OF_CONDUCT.md).
|
87
121
|
|
88
122
|
|
89
123
|
## License
|
@@ -92,4 +126,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
92
126
|
|
93
127
|
## Code of Conduct
|
94
128
|
|
95
|
-
Everyone interacting in the EasyParams project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
129
|
+
Everyone interacting in the EasyParams project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/andriy-baran/easy_params/blob/master/CODE_OF_CONDUCT.md).
|
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
|
-
spec.
|
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'
|
34
|
+
spec.add_dependency 'activemodel', version_string
|
39
35
|
end
|
data/lib/easy_params/base.rb
CHANGED
@@ -2,99 +2,92 @@
|
|
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
|
-
|
10
|
+
attr_writer :default
|
9
11
|
|
10
|
-
def
|
11
|
-
|
12
|
+
def initialize(params = {})
|
13
|
+
self.class.schema.each do |attr, type|
|
14
|
+
public_send("#{attr}=", type.coerce(params.to_h[attr]))
|
15
|
+
end
|
12
16
|
end
|
13
17
|
|
14
|
-
|
15
|
-
|
16
|
-
|
18
|
+
class << self
|
19
|
+
def name
|
20
|
+
'EasyParams::Base'
|
21
|
+
end
|
17
22
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
type = EasyParams::Types.const_get(type_name)
|
23
|
-
type = type.default(default) if default
|
24
|
-
type = type.meta(omittable: true) if optional
|
25
|
-
type = type.constructor { |value| value == Dry::Types::Undefined ? value : normalize.call(value) } if normalize
|
26
|
-
validates param_name, **validations if validations.any?
|
27
|
-
public_send(:attribute, param_name, type)
|
23
|
+
def attribute(param_name, type)
|
24
|
+
attr_accessor param_name
|
25
|
+
|
26
|
+
schema[param_name] = type
|
28
27
|
end
|
29
|
-
end
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
29
|
+
def schema
|
30
|
+
@schema ||= {}
|
31
|
+
end
|
38
32
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
33
|
+
def each(param_name, default: nil, normalize: nil, **validations, &block)
|
34
|
+
validates param_name, **validations if validations.any?
|
35
|
+
type = EasyParams::Types::Each.with_type(&block)
|
36
|
+
type = customize_type(type, default, &normalize)
|
37
|
+
attribute(param_name, type)
|
38
|
+
end
|
46
39
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
public_send(:attribute, param_name, type.of(of_type), &block)
|
54
|
-
end
|
40
|
+
def has(param_name, default: nil, normalize: nil, **validations, &block)
|
41
|
+
validates param_name, **validations if validations.any?
|
42
|
+
type = Class.new(EasyParams::Types::Struct.class).tap { |c| c.class_eval(&block) }.new
|
43
|
+
type = customize_type(type, default, &normalize)
|
44
|
+
attribute(param_name, type)
|
45
|
+
end
|
55
46
|
|
56
|
-
|
47
|
+
def array(param_name, of:, default: nil, normalize: nil, **validations)
|
48
|
+
validates param_name, **validations if validations.any?
|
49
|
+
type = EasyParams::Types::Array.of(EasyParams::Types.const_get(of.to_s.camelcase))
|
50
|
+
type = customize_type(type, default, &normalize)
|
51
|
+
attribute(param_name, type)
|
52
|
+
end
|
57
53
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
value.valid?
|
65
|
-
end
|
54
|
+
private
|
55
|
+
|
56
|
+
def customize_type(type, default, &normalize)
|
57
|
+
type = type.default(default) if default
|
58
|
+
type = type.normalize(&normalize) if normalize
|
59
|
+
type
|
66
60
|
end
|
67
|
-
attributes.each(&aggregate_nested_errors)
|
68
61
|
end
|
69
62
|
|
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
|
63
|
+
%w[Integer Decimal Float Bool String Date DateTime Time].each do |type_name|
|
64
|
+
send(:define_singleton_method,
|
65
|
+
type_name.underscore) do |param_name, default: nil, normalize: nil, **validations|
|
66
|
+
validates param_name, **validations if validations.any?
|
67
|
+
type = EasyParams::Types.const_get(type_name)
|
68
|
+
type = customize_type(type, default, &normalize)
|
69
|
+
attribute(param_name, type)
|
80
70
|
end
|
81
71
|
end
|
82
72
|
|
83
|
-
def
|
84
|
-
|
73
|
+
def attributes
|
74
|
+
self.class.schema.to_h { |k, type| [k, type.array? ? send(k).to_a : send(k)] }
|
75
|
+
end
|
85
76
|
|
86
|
-
|
87
|
-
|
88
|
-
add_errors_on_top_level(value, attr_error_key_prefix)
|
77
|
+
validate do
|
78
|
+
validate_nested
|
89
79
|
end
|
90
80
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
81
|
+
def to_h
|
82
|
+
attributes.each.with_object({}) do |(key, value), result|
|
83
|
+
result[key] = case value
|
84
|
+
when EasyParams::Types::StructsCollection
|
85
|
+
value.map(&:to_h)
|
86
|
+
when EasyParams::Types::Struct.class
|
87
|
+
value.to_h
|
88
|
+
else
|
89
|
+
value
|
90
|
+
end
|
98
91
|
end
|
99
92
|
end
|
100
93
|
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
|
+
input = value || @default
|
20
|
+
input = @normalize_proc.call(Array(input)) if @normalize_proc
|
21
|
+
coerced = Array(input).map { |v| @of_type.coerce(v) }
|
22
|
+
self.class.new(@title, coerced, @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,36 @@
|
|
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 normalize(&block)
|
23
|
+
self.class.new(@title, @default, block, &@coerce_proc)
|
24
|
+
end
|
25
|
+
|
26
|
+
def coerce(value)
|
27
|
+
value = @normalize_proc.call(value) if @normalize_proc
|
28
|
+
return @default if value.nil?
|
29
|
+
|
30
|
+
@coerce_proc.call(value)
|
31
|
+
rescue StandardError
|
32
|
+
@default
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,24 @@
|
|
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 default(value)
|
12
|
+
self.default = value
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def coerce(input)
|
17
|
+
return if input.nil? && @default.nil?
|
18
|
+
return self.class.new(@default) if input.nil? && @default.is_a?(Hash)
|
19
|
+
|
20
|
+
self.class.new(input)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
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,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: easy_params
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrii Baran
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: activemodel
|
@@ -17,9 +16,6 @@ dependencies:
|
|
17
16
|
- - ">="
|
18
17
|
- !ruby/object:Gem::Version
|
19
18
|
version: '3.2'
|
20
|
-
- - "<"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '8'
|
23
19
|
type: :runtime
|
24
20
|
prerelease: false
|
25
21
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,37 +23,6 @@ dependencies:
|
|
27
23
|
- - ">="
|
28
24
|
- !ruby/object:Gem::Version
|
29
25
|
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
26
|
description: Dessribe structure, validate and coerce values. Powered by dry-types
|
62
27
|
and dry-struct
|
63
28
|
email:
|
@@ -70,6 +35,7 @@ files:
|
|
70
35
|
- ".gitignore"
|
71
36
|
- ".rspec"
|
72
37
|
- ".rubocop.yml"
|
38
|
+
- ".ruby-version"
|
73
39
|
- ".travis.yml"
|
74
40
|
- CODE_OF_CONDUCT.md
|
75
41
|
- Gemfile
|
@@ -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:
|
@@ -91,7 +61,6 @@ metadata:
|
|
91
61
|
allowed_push_host: https://rubygems.org
|
92
62
|
homepage_uri: https://github.com/andriy-baran/easy_params
|
93
63
|
source_code_uri: https://github.com/andriy-baran/easy_params
|
94
|
-
post_install_message:
|
95
64
|
rdoc_options: []
|
96
65
|
require_paths:
|
97
66
|
- lib
|
@@ -106,8 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
75
|
- !ruby/object:Gem::Version
|
107
76
|
version: '0'
|
108
77
|
requirements: []
|
109
|
-
rubygems_version: 3.
|
110
|
-
signing_key:
|
78
|
+
rubygems_version: 3.7.1
|
111
79
|
specification_version: 4
|
112
80
|
summary: A tool that handles common tasks needed when working with params in Rails
|
113
81
|
test_files: []
|