dry-initializer 3.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +12 -0
- data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +10 -0
- data/.github/ISSUE_TEMPLATE/---bug-report.md +34 -0
- data/.github/ISSUE_TEMPLATE/---feature-request.md +18 -0
- data/.github/workflows/custom_ci.yml +74 -0
- data/.github/workflows/docsite.yml +34 -0
- data/.github/workflows/sync_configs.yml +34 -0
- data/.gitignore +12 -0
- data/.rspec +4 -0
- data/.rubocop.yml +89 -0
- data/CHANGELOG.md +890 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/CONTRIBUTING.md +29 -0
- data/Gemfile +38 -0
- data/Guardfile +5 -0
- data/LICENSE +20 -0
- data/LICENSE.txt +21 -0
- data/README.md +89 -0
- data/Rakefile +8 -0
- data/benchmarks/compare_several_defaults.rb +82 -0
- data/benchmarks/plain_options.rb +63 -0
- data/benchmarks/plain_params.rb +84 -0
- data/benchmarks/with_coercion.rb +71 -0
- data/benchmarks/with_defaults.rb +66 -0
- data/benchmarks/with_defaults_and_coercion.rb +59 -0
- data/docsite/source/attributes.html.md +106 -0
- data/docsite/source/container-version.html.md +39 -0
- data/docsite/source/index.html.md +43 -0
- data/docsite/source/inheritance.html.md +43 -0
- data/docsite/source/optionals-and-defaults.html.md +130 -0
- data/docsite/source/options-tolerance.html.md +27 -0
- data/docsite/source/params-and-options.html.md +74 -0
- data/docsite/source/rails-support.html.md +101 -0
- data/docsite/source/readers.html.md +43 -0
- data/docsite/source/skip-undefined.html.md +59 -0
- data/docsite/source/type-constraints.html.md +160 -0
- data/dry-initializer.gemspec +20 -0
- data/lib/dry-initializer.rb +1 -0
- data/lib/dry/initializer.rb +61 -0
- data/lib/dry/initializer/builders.rb +7 -0
- data/lib/dry/initializer/builders/attribute.rb +81 -0
- data/lib/dry/initializer/builders/initializer.rb +61 -0
- data/lib/dry/initializer/builders/reader.rb +50 -0
- data/lib/dry/initializer/builders/signature.rb +32 -0
- data/lib/dry/initializer/config.rb +184 -0
- data/lib/dry/initializer/definition.rb +65 -0
- data/lib/dry/initializer/dispatchers.rb +112 -0
- data/lib/dry/initializer/dispatchers/build_nested_type.rb +59 -0
- data/lib/dry/initializer/dispatchers/check_type.rb +43 -0
- data/lib/dry/initializer/dispatchers/prepare_default.rb +40 -0
- data/lib/dry/initializer/dispatchers/prepare_ivar.rb +12 -0
- data/lib/dry/initializer/dispatchers/prepare_optional.rb +13 -0
- data/lib/dry/initializer/dispatchers/prepare_reader.rb +30 -0
- data/lib/dry/initializer/dispatchers/prepare_source.rb +28 -0
- data/lib/dry/initializer/dispatchers/prepare_target.rb +44 -0
- data/lib/dry/initializer/dispatchers/unwrap_type.rb +22 -0
- data/lib/dry/initializer/dispatchers/wrap_type.rb +27 -0
- data/lib/dry/initializer/dsl.rb +43 -0
- data/lib/dry/initializer/mixin.rb +15 -0
- data/lib/dry/initializer/mixin/local.rb +19 -0
- data/lib/dry/initializer/mixin/root.rb +11 -0
- data/lib/dry/initializer/struct.rb +39 -0
- data/lib/dry/initializer/undefined.rb +2 -0
- data/lib/tasks/benchmark.rake +41 -0
- data/lib/tasks/profile.rake +78 -0
- data/spec/attributes_spec.rb +38 -0
- data/spec/coercion_of_nil_spec.rb +25 -0
- data/spec/custom_dispatchers_spec.rb +35 -0
- data/spec/custom_initializer_spec.rb +30 -0
- data/spec/default_values_spec.rb +83 -0
- data/spec/definition_spec.rb +111 -0
- data/spec/invalid_default_spec.rb +13 -0
- data/spec/list_type_spec.rb +32 -0
- data/spec/missed_default_spec.rb +14 -0
- data/spec/nested_type_spec.rb +48 -0
- data/spec/optional_spec.rb +71 -0
- data/spec/options_tolerance_spec.rb +11 -0
- data/spec/public_attributes_utility_spec.rb +22 -0
- data/spec/reader_spec.rb +87 -0
- data/spec/repetitive_definitions_spec.rb +69 -0
- data/spec/several_assignments_spec.rb +41 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/subclassing_spec.rb +49 -0
- data/spec/type_argument_spec.rb +35 -0
- data/spec/type_constraint_spec.rb +78 -0
- data/spec/value_coercion_via_dry_types_spec.rb +29 -0
- metadata +209 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
Bundler.require(:benchmarks)
|
2
|
+
|
3
|
+
require "dry-initializer"
|
4
|
+
class DryTest
|
5
|
+
extend Dry::Initializer[undefined: false]
|
6
|
+
|
7
|
+
option :foo, proc(&:to_s)
|
8
|
+
option :bar, proc(&:to_s)
|
9
|
+
end
|
10
|
+
|
11
|
+
class DryTestUndefined
|
12
|
+
extend Dry::Initializer
|
13
|
+
|
14
|
+
option :foo, proc(&:to_s)
|
15
|
+
option :bar, proc(&:to_s)
|
16
|
+
end
|
17
|
+
|
18
|
+
class PlainRubyTest
|
19
|
+
attr_reader :foo, :bar
|
20
|
+
|
21
|
+
def initialize(options)
|
22
|
+
@foo = options[:foo].to_s
|
23
|
+
@bar = options[:bar].to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
require "virtus"
|
28
|
+
class VirtusTest
|
29
|
+
include Virtus.model
|
30
|
+
|
31
|
+
attribute :foo, String
|
32
|
+
attribute :bar, String
|
33
|
+
end
|
34
|
+
|
35
|
+
require "fast_attributes"
|
36
|
+
class FastAttributesTest
|
37
|
+
extend FastAttributes
|
38
|
+
|
39
|
+
define_attributes initialize: true do
|
40
|
+
attribute :foo, String
|
41
|
+
attribute :bar, String
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
puts "Benchmark for instantiation with coercion"
|
46
|
+
|
47
|
+
Benchmark.ips do |x|
|
48
|
+
x.config time: 15, warmup: 10
|
49
|
+
|
50
|
+
x.report("plain Ruby") do
|
51
|
+
PlainRubyTest.new foo: "FOO", bar: "BAR"
|
52
|
+
end
|
53
|
+
|
54
|
+
x.report("dry-initializer") do
|
55
|
+
DryTest.new foo: "FOO", bar: "BAR"
|
56
|
+
end
|
57
|
+
|
58
|
+
x.report("dry-initializer (with UNDEFINED)") do
|
59
|
+
DryTestUndefined.new foo: "FOO", bar: "BAR"
|
60
|
+
end
|
61
|
+
|
62
|
+
x.report("virtus") do
|
63
|
+
VirtusTest.new foo: "FOO", bar: "BAR"
|
64
|
+
end
|
65
|
+
|
66
|
+
x.report("fast_attributes") do
|
67
|
+
FastAttributesTest.new foo: "FOO", bar: "BAR"
|
68
|
+
end
|
69
|
+
|
70
|
+
x.compare!
|
71
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
Bundler.require(:benchmarks)
|
2
|
+
|
3
|
+
require "dry-initializer"
|
4
|
+
class DryTest
|
5
|
+
extend Dry::Initializer[undefined: false]
|
6
|
+
|
7
|
+
option :foo, default: -> { "FOO" }
|
8
|
+
option :bar, default: -> { "BAR" }
|
9
|
+
end
|
10
|
+
|
11
|
+
class DryTestUndefined
|
12
|
+
extend Dry::Initializer
|
13
|
+
|
14
|
+
option :foo, default: -> { "FOO" }
|
15
|
+
option :bar, default: -> { "BAR" }
|
16
|
+
end
|
17
|
+
|
18
|
+
class PlainRubyTest
|
19
|
+
attr_reader :foo, :bar
|
20
|
+
|
21
|
+
def initialize(foo: "FOO", bar: "BAR")
|
22
|
+
@foo = foo
|
23
|
+
@bar = bar
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
require "kwattr"
|
28
|
+
class KwattrTest
|
29
|
+
kwattr foo: "FOO", bar: "BAR"
|
30
|
+
end
|
31
|
+
|
32
|
+
require "active_attr"
|
33
|
+
class ActiveAttrTest
|
34
|
+
include ActiveAttr::AttributeDefaults
|
35
|
+
|
36
|
+
attribute :foo, default: "FOO"
|
37
|
+
attribute :bar, default: "BAR"
|
38
|
+
end
|
39
|
+
|
40
|
+
puts "Benchmark for instantiation with default values"
|
41
|
+
|
42
|
+
Benchmark.ips do |x|
|
43
|
+
x.config time: 15, warmup: 10
|
44
|
+
|
45
|
+
x.report("plain Ruby") do
|
46
|
+
PlainRubyTest.new
|
47
|
+
end
|
48
|
+
|
49
|
+
x.report("dry-initializer") do
|
50
|
+
DryTest.new
|
51
|
+
end
|
52
|
+
|
53
|
+
x.report("dry-initializer (with UNDEFINED)") do
|
54
|
+
DryTestUndefined.new
|
55
|
+
end
|
56
|
+
|
57
|
+
x.report("kwattr") do
|
58
|
+
KwattrTest.new
|
59
|
+
end
|
60
|
+
|
61
|
+
x.report("active_attr") do
|
62
|
+
ActiveAttrTest.new
|
63
|
+
end
|
64
|
+
|
65
|
+
x.compare!
|
66
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
Bundler.require(:benchmarks)
|
2
|
+
|
3
|
+
require "dry-initializer"
|
4
|
+
class DryTest
|
5
|
+
extend Dry::Initializer[undefined: false]
|
6
|
+
|
7
|
+
option :foo, proc(&:to_s), default: -> { "FOO" }
|
8
|
+
option :bar, proc(&:to_s), default: -> { "BAR" }
|
9
|
+
end
|
10
|
+
|
11
|
+
class DryTestUndefined
|
12
|
+
extend Dry::Initializer
|
13
|
+
|
14
|
+
option :foo, proc(&:to_s), default: -> { "FOO" }
|
15
|
+
option :bar, proc(&:to_s), default: -> { "BAR" }
|
16
|
+
end
|
17
|
+
|
18
|
+
class PlainRubyTest
|
19
|
+
attr_reader :foo, :bar
|
20
|
+
|
21
|
+
def initialize(foo: "FOO", bar: "BAR")
|
22
|
+
@foo = foo
|
23
|
+
@bar = bar
|
24
|
+
raise TypeError unless String === @foo
|
25
|
+
raise TypeError unless String === @bar
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
require "virtus"
|
30
|
+
class VirtusTest
|
31
|
+
include Virtus.model
|
32
|
+
|
33
|
+
attribute :foo, String, default: "FOO"
|
34
|
+
attribute :bar, String, default: "BAR"
|
35
|
+
end
|
36
|
+
|
37
|
+
puts "Benchmark for instantiation with type constraints and default values"
|
38
|
+
|
39
|
+
Benchmark.ips do |x|
|
40
|
+
x.config time: 15, warmup: 10
|
41
|
+
|
42
|
+
x.report("plain Ruby") do
|
43
|
+
PlainRubyTest.new
|
44
|
+
end
|
45
|
+
|
46
|
+
x.report("dry-initializer") do
|
47
|
+
DryTest.new
|
48
|
+
end
|
49
|
+
|
50
|
+
x.report("dry-initializer (with UNDEFINED)") do
|
51
|
+
DryTest.new
|
52
|
+
end
|
53
|
+
|
54
|
+
x.report("virtus") do
|
55
|
+
VirtusTest.new
|
56
|
+
end
|
57
|
+
|
58
|
+
x.compare!
|
59
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
---
|
2
|
+
title: Attributes
|
3
|
+
layout: gem-single
|
4
|
+
name: dry-initializer
|
5
|
+
---
|
6
|
+
|
7
|
+
Sometimes you need to access all attributes assigned via params and options of the object constructor.
|
8
|
+
|
9
|
+
We support 2 methods: `attributes` and `public_attributes` for this goal. Both methods are wrapped into container accessible via `.dry_types` container:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
require 'dry-initializer'
|
13
|
+
|
14
|
+
class User
|
15
|
+
extend Dry::Initializer
|
16
|
+
|
17
|
+
param :name
|
18
|
+
option :email, optional: true
|
19
|
+
option :telefon, optional: true, as: :phone
|
20
|
+
end
|
21
|
+
|
22
|
+
user = User.new "Andy", telefon: "71002003040"
|
23
|
+
|
24
|
+
User.dry_initializer.attributes(user)
|
25
|
+
# => { name: "Andy", phone: "71002003040" }
|
26
|
+
```
|
27
|
+
|
28
|
+
What the method does is extracts *variables assigned* to the object (and skips unassigned ones like the `email` above). It doesn't matter whether you send it via `params` or `option`; we look at the result of the instantiation, not at the interface.
|
29
|
+
|
30
|
+
Method `public_attributes` works different. Let's look at the following example to see the difference:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
require 'dry-initializer'
|
34
|
+
|
35
|
+
class User
|
36
|
+
extend Dry::Initializer
|
37
|
+
|
38
|
+
param :name
|
39
|
+
option :telefon, optional: true, as: :phone
|
40
|
+
option :email, optional: true
|
41
|
+
option :token, optional: true, reader: :private
|
42
|
+
option :password, optional: true, reader: false
|
43
|
+
end
|
44
|
+
|
45
|
+
user = User.new "Andy", telefon: "71002003040", token: "foo", password: "bar"
|
46
|
+
|
47
|
+
User.dry_initializer.attributes(user)
|
48
|
+
# => { name: "Andy", phone: "71002003040", token: "foo", password: "bar" }
|
49
|
+
|
50
|
+
User.dry_initializer.public_attributes(user)
|
51
|
+
# => { name: "Andy", phone: "71002003040", email: nil }
|
52
|
+
```
|
53
|
+
|
54
|
+
Notice that `public_attribute` reads *public reader methods*, not variables. That's why it skips both the private `token`, and the `password` whose reader hasn't been defined.
|
55
|
+
|
56
|
+
Another difference concerns unassigned values. Because the reader `user.email` returns `nil` (its `@email` variable contains `Dry::Initializer::UNDEFINED` constant), the `public_attributes` adds this value to the hash using the method.
|
57
|
+
|
58
|
+
The third thing to mention is that you can override the reader, and it is the overriden method which will be used by `public_attributes`:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
require 'dry-initializer'
|
62
|
+
|
63
|
+
class User
|
64
|
+
extend Dry::Initializer
|
65
|
+
|
66
|
+
param :name
|
67
|
+
option :password, optional: true
|
68
|
+
|
69
|
+
def password
|
70
|
+
super.hash.to_s
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
user = User.new "Joe", password: "foo"
|
75
|
+
|
76
|
+
User.dry_initializer.attributes(user)
|
77
|
+
# => { user: "Joe", password: "foo" }
|
78
|
+
|
79
|
+
User.dry_initializer.public_attributes(user)
|
80
|
+
# => { user: "Joe", password: "-1844874613000160009" }
|
81
|
+
```
|
82
|
+
|
83
|
+
This feature works for the "extend Dry::Initializer" syntax. But what about "include Dry::Initializer.define ..."? Now we don't pollute class namespace with new methods, that's why `.dry_initializer` is absent.
|
84
|
+
|
85
|
+
To access config you can use a hack. Under the hood we define private instance method `#__dry_initializer_config__` which refers to the same container. So you can write:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
require 'dry-initializer'
|
89
|
+
|
90
|
+
class User
|
91
|
+
extend Dry::Initializer
|
92
|
+
param :name
|
93
|
+
end
|
94
|
+
|
95
|
+
user = User.new "Joe"
|
96
|
+
|
97
|
+
user.send(:__dry_initializer_config__).attributes(user)
|
98
|
+
# => { user: "Joe" }
|
99
|
+
|
100
|
+
user.send(:__dry_initializer_config__).public_attributes(user)
|
101
|
+
# => { user: "Joe" }
|
102
|
+
```
|
103
|
+
|
104
|
+
This is a hack because the `__dry_initializer_config__` is not a part of the gem's public interface; there's a possibility it can be changed or removed in the later releases.
|
105
|
+
|
106
|
+
We'll try to be careful with it, and mark it as deprecated method in case of such a removal.
|
@@ -0,0 +1,39 @@
|
|
1
|
+
---
|
2
|
+
title: Container Version
|
3
|
+
layout: gem-single
|
4
|
+
name: dry-initializer
|
5
|
+
---
|
6
|
+
|
7
|
+
Instead of extending a class with the `Dry::Initializer`, you can include a container with the `initializer` method only. This method should be preferred when you don't need subclassing.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
require 'dry-initializer'
|
11
|
+
|
12
|
+
class User
|
13
|
+
# notice `-> do .. end` syntax
|
14
|
+
include Dry::Initializer.define -> do
|
15
|
+
param :name, proc(&:to_s)
|
16
|
+
param :role, default: proc { 'customer' }
|
17
|
+
option :admin, default: proc { false }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
```
|
21
|
+
|
22
|
+
If you still need the DSL (`param` and `option`) to be inherited, use the direct extension:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
require 'dry-initializer'
|
26
|
+
|
27
|
+
class BaseService
|
28
|
+
extend Dry::Initializer
|
29
|
+
alias_method :dependency, :param
|
30
|
+
end
|
31
|
+
|
32
|
+
class ShowUser < BaseService
|
33
|
+
dependency :user
|
34
|
+
|
35
|
+
def call
|
36
|
+
puts user&.name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
@@ -0,0 +1,43 @@
|
|
1
|
+
---
|
2
|
+
title: Introduction & Usage
|
3
|
+
description: DSL for defining initializer params and options
|
4
|
+
layout: gem-single
|
5
|
+
order: 8
|
6
|
+
type: gem
|
7
|
+
name: dry-initializer
|
8
|
+
sections:
|
9
|
+
- container-version
|
10
|
+
- params-and-options
|
11
|
+
- options-tolerance
|
12
|
+
- optionals-and-defaults
|
13
|
+
- type-constraints
|
14
|
+
- readers
|
15
|
+
- inheritance
|
16
|
+
- skip-undefined
|
17
|
+
- attributes
|
18
|
+
- rails-support
|
19
|
+
---
|
20
|
+
|
21
|
+
`dry-initializer` is a simple mixin of class methods `params` and `options` for instances.
|
22
|
+
|
23
|
+
## Synopsis
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require 'dry-initializer'
|
27
|
+
|
28
|
+
class User
|
29
|
+
extend Dry::Initializer
|
30
|
+
|
31
|
+
param :name, proc(&:to_s)
|
32
|
+
param :role, default: proc { 'customer' }
|
33
|
+
option :admin, default: proc { false }
|
34
|
+
option :phone, optional: true
|
35
|
+
end
|
36
|
+
|
37
|
+
user = User.new 'Vladimir', 'admin', admin: true
|
38
|
+
|
39
|
+
user.name # => 'Vladimir'
|
40
|
+
user.role # => 'admin'
|
41
|
+
user.admin # => true
|
42
|
+
user.phone # => nil
|
43
|
+
```
|
@@ -0,0 +1,43 @@
|
|
1
|
+
---
|
2
|
+
title: Inheritance
|
3
|
+
layout: gem-single
|
4
|
+
name: dry-initializer
|
5
|
+
---
|
6
|
+
|
7
|
+
Subclassing preserves all definitions being made inside a superclass.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
require 'dry-initializer'
|
11
|
+
|
12
|
+
class User
|
13
|
+
extend Dry::Initializer
|
14
|
+
|
15
|
+
param :name
|
16
|
+
end
|
17
|
+
|
18
|
+
class Employee < User
|
19
|
+
param :position
|
20
|
+
end
|
21
|
+
|
22
|
+
employee = Employee.new('John', 'supercargo')
|
23
|
+
employee.name # => 'John'
|
24
|
+
employee.position # => 'supercargo'
|
25
|
+
|
26
|
+
employee = Employee.new # => fails because type
|
27
|
+
```
|
28
|
+
|
29
|
+
You can override params and options.
|
30
|
+
Such overriding leaves initial order of params (positional arguments) unchanged:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
class Employee < User
|
34
|
+
param :position, optional: true
|
35
|
+
param :name, default: proc { 'Unknown' }
|
36
|
+
end
|
37
|
+
|
38
|
+
user = User.new # => Boom! because User#name is required
|
39
|
+
employee = Employee.new # passes because who cares on employee's name
|
40
|
+
|
41
|
+
employee.name
|
42
|
+
# => 'Unknown' because it is the name that positioned first like in User
|
43
|
+
```
|
@@ -0,0 +1,130 @@
|
|
1
|
+
---
|
2
|
+
title: Optional Attributes and Default Values
|
3
|
+
layout: gem-single
|
4
|
+
name: dry-initializer
|
5
|
+
---
|
6
|
+
|
7
|
+
By default both params and options are mandatory. Use `:default` key to make them optional:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
require 'dry-initializer'
|
11
|
+
|
12
|
+
class User
|
13
|
+
extend Dry::Initializer
|
14
|
+
|
15
|
+
param :name, default: proc { 'Unknown user' }
|
16
|
+
option :email, default: proc { 'unknown@example.com' }
|
17
|
+
option :phone, optional: true
|
18
|
+
end
|
19
|
+
|
20
|
+
user = User.new
|
21
|
+
user.name # => 'Unknown user'
|
22
|
+
user.email # => 'unknown@example.com'
|
23
|
+
user.phone # => Dry::Initializer::UNDEFINED
|
24
|
+
|
25
|
+
user = User.new 'Vladimir', email: 'vladimir@example.com', phone: '71234567788'
|
26
|
+
user.name # => 'Vladimir'
|
27
|
+
user.email # => 'vladimir@example.com'
|
28
|
+
user.phone # => '71234567788'
|
29
|
+
```
|
30
|
+
|
31
|
+
You cannot define required **parameter** after optional one. The following example raises `SyntaxError` exception:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
require 'dry-initializer'
|
35
|
+
|
36
|
+
class User
|
37
|
+
extend Dry::Initializer
|
38
|
+
|
39
|
+
param :name, default: proc { 'Unknown name' }
|
40
|
+
param :email # => #<SyntaxError ...>
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
You should assign `nil` value explicitly. Otherwise an instance variable it will be left undefined. In both cases attribute reader method will return `nil`.
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
require 'dry-initializer'
|
48
|
+
|
49
|
+
class User
|
50
|
+
extend Dry::Initializer
|
51
|
+
|
52
|
+
param :name
|
53
|
+
option :email, optional: true
|
54
|
+
end
|
55
|
+
|
56
|
+
user = User.new 'Andrew'
|
57
|
+
user.email # => nil
|
58
|
+
user.instance_variable_get :@email
|
59
|
+
# => Dry::Initializer::UNDEFINED
|
60
|
+
|
61
|
+
user = User.new 'Andrew', email: nil
|
62
|
+
user.email # => nil
|
63
|
+
user.instance_variable_get :@email
|
64
|
+
# => nil
|
65
|
+
```
|
66
|
+
|
67
|
+
You can also set `nil` as a default value:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
require 'dry-initializer'
|
71
|
+
|
72
|
+
class User
|
73
|
+
extend Dry::Initializer
|
74
|
+
|
75
|
+
param :name
|
76
|
+
option :email, default: proc { nil }
|
77
|
+
end
|
78
|
+
|
79
|
+
user = User.new 'Andrew'
|
80
|
+
user.email # => nil
|
81
|
+
user.instance_variable_get :@email
|
82
|
+
# => nil
|
83
|
+
```
|
84
|
+
|
85
|
+
You **must** wrap default values into procs.
|
86
|
+
|
87
|
+
If you need to **assign** proc as a default value, wrap it to another one:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
require 'dry-initializer'
|
91
|
+
|
92
|
+
class User
|
93
|
+
extend Dry::Initializer
|
94
|
+
|
95
|
+
param :name_proc, default: proc { proc { 'Unknown user' } }
|
96
|
+
end
|
97
|
+
|
98
|
+
user = User.new
|
99
|
+
user.name_proc.call # => 'Unknown user'
|
100
|
+
```
|
101
|
+
|
102
|
+
Proc will be executed in a scope of new instance. You can refer to other arguments:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
require 'dry-initializer'
|
106
|
+
|
107
|
+
class User
|
108
|
+
extend Dry::Initializer
|
109
|
+
|
110
|
+
param :name
|
111
|
+
param :email, default: proc { "#{name.downcase}@example.com" }
|
112
|
+
end
|
113
|
+
|
114
|
+
user = User.new 'Andrew'
|
115
|
+
user.email # => 'andrew@example.com'
|
116
|
+
```
|
117
|
+
|
118
|
+
**Warning**: when using lambdas instead of procs, don't forget an argument, required by [instance_eval][instance_eval] (you can skip in in a proc).
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
require 'dry-initializer'
|
122
|
+
|
123
|
+
class User
|
124
|
+
extend Dry::Initializer
|
125
|
+
|
126
|
+
param :name, default: -> (obj) { 'Dude' }
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
130
|
+
[instance_eval]: http://ruby-doc.org/core-2.2.0/BasicObject.html#method-i-instance_eval
|