dry-initializer 3.0.3 → 3.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +234 -242
- data/dry-initializer.gemspec +28 -15
- data/lib/dry/initializer/builders/attribute.rb +1 -1
- data/lib/dry/initializer/version.rb +1 -1
- metadata +24 -104
- data/.codeclimate.yml +0 -12
- data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
- data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -30
- data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
- data/.github/workflows/custom_ci.yml +0 -58
- data/.github/workflows/docsite.yml +0 -34
- data/.github/workflows/sync_configs.yml +0 -56
- data/.gitignore +0 -12
- data/.rspec +0 -4
- data/.rubocop.yml +0 -102
- data/CODE_OF_CONDUCT.md +0 -13
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -36
- data/Gemfile.devtools +0 -16
- data/Guardfile +0 -5
- data/LICENSE.txt +0 -21
- data/Rakefile +0 -8
- data/benchmarks/compare_several_defaults.rb +0 -82
- data/benchmarks/plain_options.rb +0 -63
- data/benchmarks/plain_params.rb +0 -84
- data/benchmarks/with_coercion.rb +0 -71
- data/benchmarks/with_defaults.rb +0 -66
- data/benchmarks/with_defaults_and_coercion.rb +0 -59
- data/bin/.gitkeep +0 -0
- data/docsite/source/attributes.html.md +0 -106
- data/docsite/source/container-version.html.md +0 -39
- data/docsite/source/index.html.md +0 -43
- data/docsite/source/inheritance.html.md +0 -43
- data/docsite/source/optionals-and-defaults.html.md +0 -130
- data/docsite/source/options-tolerance.html.md +0 -27
- data/docsite/source/params-and-options.html.md +0 -74
- data/docsite/source/rails-support.html.md +0 -101
- data/docsite/source/readers.html.md +0 -43
- data/docsite/source/skip-undefined.html.md +0 -59
- data/docsite/source/type-constraints.html.md +0 -160
- data/project.yml +0 -2
- data/spec/attributes_spec.rb +0 -38
- data/spec/coercion_of_nil_spec.rb +0 -25
- data/spec/custom_dispatchers_spec.rb +0 -35
- data/spec/custom_initializer_spec.rb +0 -30
- data/spec/default_values_spec.rb +0 -83
- data/spec/definition_spec.rb +0 -111
- data/spec/invalid_default_spec.rb +0 -13
- data/spec/list_type_spec.rb +0 -32
- data/spec/missed_default_spec.rb +0 -14
- data/spec/nested_type_spec.rb +0 -48
- data/spec/optional_spec.rb +0 -71
- data/spec/options_tolerance_spec.rb +0 -11
- data/spec/public_attributes_utility_spec.rb +0 -22
- data/spec/reader_spec.rb +0 -87
- data/spec/repetitive_definitions_spec.rb +0 -69
- data/spec/several_assignments_spec.rb +0 -41
- data/spec/spec_helper.rb +0 -24
- data/spec/subclassing_spec.rb +0 -49
- data/spec/support/coverage.rb +0 -7
- data/spec/support/warnings.rb +0 -7
- data/spec/type_argument_spec.rb +0 -35
- data/spec/type_constraint_spec.rb +0 -96
- data/spec/value_coercion_via_dry_types_spec.rb +0 -29
@@ -1,27 +0,0 @@
|
|
1
|
-
---
|
2
|
-
title: Tolerance to Unknown Options
|
3
|
-
layout: gem-single
|
4
|
-
name: dry-initializer
|
5
|
-
---
|
6
|
-
|
7
|
-
By default the initializer is strict for params (positional arguments), expecting them to be defined explicitly.
|
8
|
-
|
9
|
-
```ruby
|
10
|
-
require 'dry-initializer'
|
11
|
-
|
12
|
-
class User
|
13
|
-
extend Dry::Initializer
|
14
|
-
end
|
15
|
-
|
16
|
-
user = User.new 'Joe' # raises ArgumentError
|
17
|
-
```
|
18
|
-
|
19
|
-
At the same time it is tolerant to unknown options. All unknown options are accepted, but ignored:
|
20
|
-
|
21
|
-
```ruby
|
22
|
-
# It accepts undefined options...
|
23
|
-
user = User.new name: 'Joe'
|
24
|
-
|
25
|
-
# ...but ignores them
|
26
|
-
user.respond_to? :name # => false
|
27
|
-
```
|
@@ -1,74 +0,0 @@
|
|
1
|
-
---
|
2
|
-
title: Params and Options
|
3
|
-
layout: gem-single
|
4
|
-
name: dry-initializer
|
5
|
-
---
|
6
|
-
|
7
|
-
Use `param` to define plain argument:
|
8
|
-
|
9
|
-
```ruby
|
10
|
-
require 'dry-initializer'
|
11
|
-
|
12
|
-
class User
|
13
|
-
extend Dry::Initializer
|
14
|
-
|
15
|
-
param :name
|
16
|
-
param :email
|
17
|
-
end
|
18
|
-
|
19
|
-
user = User.new 'Andrew', 'andrew@email.com'
|
20
|
-
user.name # => 'Andrew'
|
21
|
-
user.email # => 'andrew@email.com'
|
22
|
-
```
|
23
|
-
|
24
|
-
Use `option` to define named (hash) argument:
|
25
|
-
|
26
|
-
```ruby
|
27
|
-
require 'dry-initializer'
|
28
|
-
|
29
|
-
class User
|
30
|
-
extend Dry::Initializer
|
31
|
-
|
32
|
-
option :name
|
33
|
-
option :email
|
34
|
-
end
|
35
|
-
|
36
|
-
user = User.new email: 'andrew@email.com', name: 'Andrew'
|
37
|
-
user.name # => 'Andrew'
|
38
|
-
user.email # => 'andrew@email.com'
|
39
|
-
```
|
40
|
-
|
41
|
-
Options can be renamed using `:as` key:
|
42
|
-
|
43
|
-
```ruby
|
44
|
-
require 'dry-initializer'
|
45
|
-
|
46
|
-
class User
|
47
|
-
extend Dry::Initializer
|
48
|
-
|
49
|
-
option :name, as: :username
|
50
|
-
end
|
51
|
-
|
52
|
-
user = User.new name: "Joe"
|
53
|
-
user.username # => "Joe"
|
54
|
-
user.instance_variable_get :@username # => "Joe"
|
55
|
-
user.instance_variable_get :@name # => nil
|
56
|
-
user.respond_to? :name # => false
|
57
|
-
```
|
58
|
-
|
59
|
-
You can also define several ways of initializing the same argument via different options:
|
60
|
-
|
61
|
-
```ruby
|
62
|
-
require 'dry-initializer'
|
63
|
-
|
64
|
-
class User
|
65
|
-
extend Dry::Initializer
|
66
|
-
|
67
|
-
option :phone
|
68
|
-
option :telephone, as: :phone
|
69
|
-
option :name, optional: true
|
70
|
-
end
|
71
|
-
|
72
|
-
User.new(phone: '1234567890').phone # => '1234567890'
|
73
|
-
User.new(telephone: '1234567890').phone # => '1234567890'
|
74
|
-
```
|
@@ -1,101 +0,0 @@
|
|
1
|
-
---
|
2
|
-
title: Rails Support
|
3
|
-
layout: gem-single
|
4
|
-
name: dry-initializer
|
5
|
-
---
|
6
|
-
|
7
|
-
Rails plugin is implemented in a separate [dry-initializer-rails](https://github.com/nepalez/dry-initializer-rails) gem.
|
8
|
-
|
9
|
-
It provides coercion of assigned values to corresponding ActiveRecord instances.
|
10
|
-
|
11
|
-
### Base Example
|
12
|
-
|
13
|
-
Add the `:model` setting to `param` or `option`:
|
14
|
-
|
15
|
-
```ruby
|
16
|
-
require 'dry-initializer-rails'
|
17
|
-
|
18
|
-
class CreateOrder
|
19
|
-
extend Dry::Initializer
|
20
|
-
|
21
|
-
# Params and options
|
22
|
-
param :customer, model: 'Customer' # use either a name
|
23
|
-
option :product, model: Product # or a class
|
24
|
-
|
25
|
-
def call
|
26
|
-
Order.create customer: customer, product: product
|
27
|
-
end
|
28
|
-
end
|
29
|
-
```
|
30
|
-
|
31
|
-
Now you can assign values as pre-initialized model instances:
|
32
|
-
|
33
|
-
```ruby
|
34
|
-
customer = Customer.find(1)
|
35
|
-
product = Product.find(2)
|
36
|
-
|
37
|
-
order = CreateOrder.new(customer, product: product).call
|
38
|
-
order.customer # => <Customer @id=1 ...>
|
39
|
-
order.product # => <Product @id=2 ...>
|
40
|
-
```
|
41
|
-
|
42
|
-
...or their ids:
|
43
|
-
|
44
|
-
```ruby
|
45
|
-
order = CreateOrder.new(1, product: 2).call
|
46
|
-
order.customer # => <Customer @id=1 ...>
|
47
|
-
order.product # => <Product @id=2 ...>
|
48
|
-
```
|
49
|
-
|
50
|
-
The instance is envoked using method `find_by(id: ...)`.
|
51
|
-
With wrong ids `nil` values are assigned to corresponding params and options:
|
52
|
-
|
53
|
-
```ruby
|
54
|
-
order = CreateOrder.new(0, product: 0).call
|
55
|
-
order.customer # => nil
|
56
|
-
order.product # => nil
|
57
|
-
```
|
58
|
-
|
59
|
-
### Custom Keys
|
60
|
-
|
61
|
-
You can specify custom `key` for searching model instance:
|
62
|
-
|
63
|
-
```ruby
|
64
|
-
require 'dry-initializer-rails'
|
65
|
-
|
66
|
-
class CreateOrder
|
67
|
-
extend Dry::Initializer
|
68
|
-
|
69
|
-
param :customer, model: 'User', find_by: 'name'
|
70
|
-
option :product, model: Item, find_by: :name
|
71
|
-
end
|
72
|
-
```
|
73
|
-
|
74
|
-
This time you can send names (not ids) to the initializer:
|
75
|
-
|
76
|
-
```ruby
|
77
|
-
order = CreateOrder.new('Andrew', product: 'the_thing_no_123').call
|
78
|
-
|
79
|
-
order.customer # => <User @name='Andrew' ...>
|
80
|
-
order.product # => <Item @name='the_thing_no_123' ...>
|
81
|
-
```
|
82
|
-
|
83
|
-
### Container Syntax
|
84
|
-
|
85
|
-
If you prefer [container syntax](docs::container-version), extend plugin inside the block:
|
86
|
-
|
87
|
-
```ruby
|
88
|
-
require 'dry-initializer-rails'
|
89
|
-
|
90
|
-
class CreateOrder
|
91
|
-
include Dry::Initializer.define -> do
|
92
|
-
# ... params/options declarations
|
93
|
-
end
|
94
|
-
end
|
95
|
-
```
|
96
|
-
|
97
|
-
### Types vs Models
|
98
|
-
|
99
|
-
[Type constraints](docs::type-constraints) are checked before the coercion.
|
100
|
-
|
101
|
-
When mixing `:type` and `:model` settings for the same param/option, you should use [sum types](/gems/dry-types/1.2/sum) that accept both model instances and their attributes.
|
@@ -1,43 +0,0 @@
|
|
1
|
-
---
|
2
|
-
title: Readers
|
3
|
-
layout: gem-single
|
4
|
-
name: dry-initializer
|
5
|
-
---
|
6
|
-
|
7
|
-
By default public attribute reader is defined for every param and option.
|
8
|
-
|
9
|
-
You can define private or protected reader instead:
|
10
|
-
|
11
|
-
```ruby
|
12
|
-
require 'dry-initializer'
|
13
|
-
|
14
|
-
class User
|
15
|
-
extend Dry::Initializer
|
16
|
-
|
17
|
-
param :name, reader: :private # the same as adding `private :name`
|
18
|
-
param :email, reader: :protected # the same as adding `protected :email`
|
19
|
-
end
|
20
|
-
```
|
21
|
-
|
22
|
-
To skip any reader, use `reader: false`:
|
23
|
-
|
24
|
-
```ruby
|
25
|
-
require 'dry-initializer'
|
26
|
-
|
27
|
-
class User
|
28
|
-
extend Dry::Initializer
|
29
|
-
|
30
|
-
param :name
|
31
|
-
param :email, reader: false
|
32
|
-
end
|
33
|
-
|
34
|
-
user = User.new 'Luke', 'luke@example.com'
|
35
|
-
user.name # => 'Luke'
|
36
|
-
|
37
|
-
user.email # => #<NoMethodError ...>
|
38
|
-
user.instance_variable_get :@email # => 'luke@example.com'
|
39
|
-
```
|
40
|
-
|
41
|
-
Notice that any other value except for `false`, `:protected` and `:private` provides a public reader.
|
42
|
-
|
43
|
-
No writers are defined. Define them using pure ruby `attr_writer` when necessary.
|
@@ -1,59 +0,0 @@
|
|
1
|
-
---
|
2
|
-
title: Skip Undefined
|
3
|
-
layout: gem-single
|
4
|
-
name: dry-initializer
|
5
|
-
---
|
6
|
-
|
7
|
-
The initializer uses special constant `Dry::Initializer::UNDEFINED` to distinguish variables that are set to `nil` from those that are not set at all.
|
8
|
-
|
9
|
-
When no value was provided, the constant is assigned to a variable, but hidden in a reader.
|
10
|
-
|
11
|
-
```ruby
|
12
|
-
require 'dry-initializer'
|
13
|
-
|
14
|
-
class User
|
15
|
-
extend Dry::Initializer
|
16
|
-
option :email, optional: true
|
17
|
-
end
|
18
|
-
|
19
|
-
user = User.new
|
20
|
-
|
21
|
-
user.email
|
22
|
-
# => nil
|
23
|
-
|
24
|
-
user.instance_variable_get :@email
|
25
|
-
# => Dry::Initializer::UNDEFINED
|
26
|
-
```
|
27
|
-
|
28
|
-
This gives you full control of the real state of the attributes. However, all that checks cost about >30% of instantiation time, and make attribute readers 2 times slower.
|
29
|
-
|
30
|
-
To avoid the overhead in cases you don't care about the differences between `nil` and undefined, you can use a light version of the module. Add `[undefined: false]` config to either `extend` or `include` line of code:
|
31
|
-
|
32
|
-
```ruby
|
33
|
-
extend Dry::Initializer[undefined: false]
|
34
|
-
```
|
35
|
-
|
36
|
-
```ruby
|
37
|
-
include Dry::Initializer[undefined: false] -> do
|
38
|
-
# ...
|
39
|
-
end
|
40
|
-
```
|
41
|
-
|
42
|
-
This time you should expect `nil` every time no value was given to an optional attribute:
|
43
|
-
|
44
|
-
```ruby
|
45
|
-
require 'dry-initializer'
|
46
|
-
|
47
|
-
class User
|
48
|
-
extend Dry::Initializer[undefined: false]
|
49
|
-
option :email, optional: true
|
50
|
-
end
|
51
|
-
|
52
|
-
user = User.new
|
53
|
-
|
54
|
-
user.email
|
55
|
-
# => nil
|
56
|
-
|
57
|
-
user.instance_variable_get :@email
|
58
|
-
# => nil
|
59
|
-
```
|
@@ -1,160 +0,0 @@
|
|
1
|
-
---
|
2
|
-
title: Type Constraints
|
3
|
-
layout: gem-single
|
4
|
-
name: dry-initializer
|
5
|
-
---
|
6
|
-
|
7
|
-
## Base Syntax
|
8
|
-
|
9
|
-
Use `:type` key in a `param` or `option` declarations to add type coercer.
|
10
|
-
|
11
|
-
```ruby
|
12
|
-
require 'dry-initializer'
|
13
|
-
|
14
|
-
class User
|
15
|
-
extend Dry::Initializer
|
16
|
-
param :name, type: proc(&:to_s)
|
17
|
-
end
|
18
|
-
|
19
|
-
user = User.new :Andrew
|
20
|
-
user.name # => "Andrew"
|
21
|
-
```
|
22
|
-
|
23
|
-
Any object that responds to `#call` with 1 argument can be used as a type. Common examples are `proc(&:to_s)` for strings, `method(:Array)` (for arrays) or `Array.method(:wrap)` in Rails, `->(v) { !!v }` (for booleans), etc.
|
24
|
-
|
25
|
-
## Dry Types as coercers
|
26
|
-
|
27
|
-
Another important example is the usage of `dry-types` as type constraints:
|
28
|
-
|
29
|
-
```ruby
|
30
|
-
require 'dry-initializer'
|
31
|
-
require 'dry-types'
|
32
|
-
|
33
|
-
class User
|
34
|
-
extend Dry::Initializer
|
35
|
-
param :name, type: Dry::Types['strict.string']
|
36
|
-
end
|
37
|
-
|
38
|
-
user = User.new :Andrew # => #<TypeError ...>
|
39
|
-
```
|
40
|
-
|
41
|
-
## Positional Argument
|
42
|
-
|
43
|
-
Instead of `:type` option you can send a constraint/coercer as the second argument:
|
44
|
-
|
45
|
-
```ruby
|
46
|
-
require 'dry-initializer'
|
47
|
-
require 'dry-types'
|
48
|
-
|
49
|
-
class User
|
50
|
-
extend Dry::Initializer
|
51
|
-
param :name, Dry::Types['coercible.string']
|
52
|
-
param :email, proc(&:to_s)
|
53
|
-
end
|
54
|
-
```
|
55
|
-
|
56
|
-
## Array Types
|
57
|
-
|
58
|
-
As mentioned above, the `:type` option takes a callable object... with one important exception.
|
59
|
-
|
60
|
-
You can use arrays for values that should be wrapped to array:
|
61
|
-
|
62
|
-
```ruby
|
63
|
-
class User
|
64
|
-
extend Dry::Initializer
|
65
|
-
|
66
|
-
option :name, proc(&:to_s)
|
67
|
-
option :emails, [proc(&:to_s)]
|
68
|
-
end
|
69
|
-
|
70
|
-
user = User.new name: "joe", emails: :"joe@example.com"
|
71
|
-
user.emails # => ["joe@example.com"]
|
72
|
-
|
73
|
-
user = User.new name: "jane", emails: [:"jane@example.com", :"jane@example.org"]
|
74
|
-
user.emails # => ["jane@example.com", "jane@example.org"]
|
75
|
-
```
|
76
|
-
|
77
|
-
You can wrap the coercer into several arrays as well:
|
78
|
-
|
79
|
-
```ruby
|
80
|
-
class User
|
81
|
-
extend Dry::Initializer
|
82
|
-
|
83
|
-
option :emails, [[proc(&:to_s)]]
|
84
|
-
end
|
85
|
-
|
86
|
-
user = User.new name: "joe", emails: "joe@example.com"
|
87
|
-
user.emails # => [["joe@example.com"]]
|
88
|
-
```
|
89
|
-
|
90
|
-
Eventually, you can use an empty array as a coercer. In that case we just wrap the source value(s) into array, not modifying the items:
|
91
|
-
|
92
|
-
```ruby
|
93
|
-
class Article
|
94
|
-
extend Dry::Initializer
|
95
|
-
|
96
|
-
option :tags, []
|
97
|
-
end
|
98
|
-
|
99
|
-
article = Article.new(tags: 1)
|
100
|
-
article.tags # => [1]
|
101
|
-
```
|
102
|
-
|
103
|
-
## Nested Options
|
104
|
-
|
105
|
-
Sometimes you need to describe a structure with nested options. In this case you can use a block with `options` inside.
|
106
|
-
|
107
|
-
```ruby
|
108
|
-
class User
|
109
|
-
extend Dry::Initializer
|
110
|
-
|
111
|
-
option :name, proc(&:to_s)
|
112
|
-
|
113
|
-
option :emails, [] do
|
114
|
-
option :address, proc(&:to_s)
|
115
|
-
option :description, proc(&:to_s)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
user = User.new name: "joe",
|
120
|
-
emails: { address: "joe@example.com", description: "Job email" }
|
121
|
-
|
122
|
-
user.emails.class # => Array
|
123
|
-
user.emails.first.class # => User::Emails
|
124
|
-
user.emails.first.address # => "joe@example.com"
|
125
|
-
|
126
|
-
user.emails.to_h # => [{ address: "joe@example.com", description: "Job email" }]
|
127
|
-
```
|
128
|
-
|
129
|
-
Notice how we mixed array wrapper with a nested type.
|
130
|
-
|
131
|
-
The only syntax restriction here is that you cannot use a positional `param` _inside_ the block.
|
132
|
-
|
133
|
-
## Back References
|
134
|
-
|
135
|
-
Sometimes you need to refer back to the initialized instance. In this case use a second argument to explicitly give the instance to a coercer:
|
136
|
-
|
137
|
-
```ruby
|
138
|
-
class Location < String
|
139
|
-
attr_reader :parameter # refers back to its parameter
|
140
|
-
|
141
|
-
def initialize(name, parameter)
|
142
|
-
super(name)
|
143
|
-
@parameter = parameter
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
class Parameter
|
148
|
-
extend Dry::Initializer
|
149
|
-
param :name
|
150
|
-
param :location, ->(value, param) { Location.new(value, param) }
|
151
|
-
end
|
152
|
-
|
153
|
-
offset = Parameter.new "offset", location: "query"
|
154
|
-
offset.name # => "offset"
|
155
|
-
offset.location # => "query"
|
156
|
-
offset.location.parameter == offset # true
|
157
|
-
```
|
158
|
-
|
159
|
-
[dry-types]: https://github.com/dry-rb/dry-types
|
160
|
-
[dry-types-docs]: http://dry-rb.org/gems/dry-types/
|