dry-initializer 3.0.2
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 +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,111 @@
|
|
1
|
+
describe "definition" do
|
2
|
+
shared_examples :initializer do |in_context|
|
3
|
+
subject { Test::Foo.new(1, bar: 2) }
|
4
|
+
|
5
|
+
it "sets variables when defined via `#{in_context}`" do
|
6
|
+
expect(subject.instance_variable_get(:@foo)).to eql 1
|
7
|
+
expect(subject.instance_variable_get(:@bar)).to eql 2
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it_behaves_like :initializer, "extend Dry::Initializer" do
|
12
|
+
before do
|
13
|
+
class Test::Foo
|
14
|
+
extend Dry::Initializer
|
15
|
+
param :foo
|
16
|
+
option :bar
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "preservers definition params" do
|
21
|
+
params = Test::Foo.dry_initializer.params.map do |definition|
|
22
|
+
[definition.source, definition.options]
|
23
|
+
end
|
24
|
+
|
25
|
+
expect(params).to eq [
|
26
|
+
[:foo, { as: :foo, reader: :public, optional: false }]
|
27
|
+
]
|
28
|
+
end
|
29
|
+
|
30
|
+
it "preservers definition options" do
|
31
|
+
options = Test::Foo.dry_initializer.options.map do |definition|
|
32
|
+
[definition.source, definition.options]
|
33
|
+
end
|
34
|
+
|
35
|
+
expect(options).to eq [
|
36
|
+
[:bar, { as: :bar, reader: :public, optional: false }]
|
37
|
+
]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it_behaves_like :initializer, "extend Dry::Initializer" do
|
42
|
+
before do
|
43
|
+
class Test::Foo
|
44
|
+
extend Dry::Initializer
|
45
|
+
param :foo
|
46
|
+
option :bar
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it_behaves_like :initializer, "extend Dry::Initializer[undefined: false]" do
|
52
|
+
before do
|
53
|
+
class Test::Foo
|
54
|
+
extend Dry::Initializer[undefined: false]
|
55
|
+
param :foo
|
56
|
+
option :bar
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
it_behaves_like :initializer, "include Dry::Initializer with block" do
|
62
|
+
before do
|
63
|
+
class Test::Foo
|
64
|
+
include(
|
65
|
+
Dry::Initializer.define do
|
66
|
+
param :foo
|
67
|
+
option :bar
|
68
|
+
end
|
69
|
+
)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it_behaves_like :initializer, "include Dry::Initializer with lambda" do
|
75
|
+
before do
|
76
|
+
class Test::Foo
|
77
|
+
include Dry::Initializer.define -> do
|
78
|
+
param :foo
|
79
|
+
option :bar
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it_behaves_like :initializer, "include Dry::Initializer[undefined: false]" do
|
86
|
+
before do
|
87
|
+
class Test::Foo
|
88
|
+
include(
|
89
|
+
Dry::Initializer[undefined: false].define do
|
90
|
+
param :foo
|
91
|
+
option :bar
|
92
|
+
end
|
93
|
+
)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# @deprecated
|
99
|
+
it_behaves_like :initializer, "include Dry::Initializer::Mixin" do
|
100
|
+
before do
|
101
|
+
class Test::Foo
|
102
|
+
include(
|
103
|
+
Dry::Initializer::Mixin.define do
|
104
|
+
param :foo
|
105
|
+
option :bar
|
106
|
+
end
|
107
|
+
)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "dry-types"
|
2
|
+
|
3
|
+
describe "list type argument" do
|
4
|
+
before do
|
5
|
+
class Test::Foo
|
6
|
+
extend Dry::Initializer
|
7
|
+
param :foo, [proc(&:to_s)]
|
8
|
+
option :bar, [Dry::Types["strict.string"]]
|
9
|
+
option :baz, []
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "with single items" do
|
14
|
+
subject { Test::Foo.new(1, bar: "2", baz: { qux: :QUX }) }
|
15
|
+
|
16
|
+
it "coerces and wraps them to arrays" do
|
17
|
+
expect(subject.foo).to eq %w[1]
|
18
|
+
expect(subject.bar).to eq %w[2]
|
19
|
+
expect(subject.baz).to eq [{ qux: :QUX }]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "with arrays" do
|
24
|
+
subject { Test::Foo.new([1], bar: %w[2], baz: [{ qux: :QUX }]) }
|
25
|
+
|
26
|
+
it "coerces elements" do
|
27
|
+
expect(subject.foo).to eq %w[1]
|
28
|
+
expect(subject.bar).to eq %w[2]
|
29
|
+
expect(subject.baz).to eq [{ qux: :QUX }]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
describe "missed default values" do
|
2
|
+
subject do
|
3
|
+
class Test::Foo
|
4
|
+
extend Dry::Initializer
|
5
|
+
|
6
|
+
param :foo, default: proc { :FOO }
|
7
|
+
param :bar, required: true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it "raises SyntaxError" do
|
12
|
+
expect { subject }.to raise_error SyntaxError, /bar/
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
describe "nested type argument" do
|
2
|
+
subject { Test::Xyz.new("bar" => { "baz" => 42 }) }
|
3
|
+
|
4
|
+
context "with nested definition only" do
|
5
|
+
before do
|
6
|
+
class Test::Xyz
|
7
|
+
extend Dry::Initializer
|
8
|
+
|
9
|
+
param :foo, as: :x do
|
10
|
+
option :bar, as: :y do
|
11
|
+
option :baz, proc(&:to_s), as: :z
|
12
|
+
option :qux, as: :w, optional: true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "builds the type" do
|
19
|
+
expect(subject.x.y.z).to eq "42"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "converts the nested type to hash" do
|
23
|
+
expect(subject.x.to_h).to eq("y" => { "z" => "42" })
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with nested and wrapped definitions" do
|
28
|
+
before do
|
29
|
+
class Test::Xyz
|
30
|
+
extend Dry::Initializer
|
31
|
+
|
32
|
+
param :foo, [], as: :x do
|
33
|
+
option :bar, as: :y do
|
34
|
+
option :baz, proc(&:to_s), as: :z
|
35
|
+
option :qux, as: :w, optional: true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it "builds the type" do
|
42
|
+
x = subject.x
|
43
|
+
expect(x).to be_instance_of Array
|
44
|
+
|
45
|
+
expect(x.first.y.z).to eq "42"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
describe "optional value" do
|
2
|
+
context "when has no default value" do
|
3
|
+
before do
|
4
|
+
class Test::Foo
|
5
|
+
extend Dry::Initializer
|
6
|
+
|
7
|
+
param :foo
|
8
|
+
param :bar, optional: true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "quacks like nil" do
|
13
|
+
subject = Test::Foo.new(1)
|
14
|
+
|
15
|
+
expect(subject.bar).to eq nil
|
16
|
+
end
|
17
|
+
|
18
|
+
it "keeps info about been UNDEFINED" do
|
19
|
+
subject = Test::Foo.new(1)
|
20
|
+
|
21
|
+
expect(subject.instance_variable_get(:@bar))
|
22
|
+
.to eq Dry::Initializer::UNDEFINED
|
23
|
+
end
|
24
|
+
|
25
|
+
it "can be set explicitly" do
|
26
|
+
subject = Test::Foo.new(1, "qux")
|
27
|
+
|
28
|
+
expect(subject.bar).to eq "qux"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "with undefined: false" do
|
33
|
+
before do
|
34
|
+
class Test::Foo
|
35
|
+
extend Dry::Initializer[undefined: false]
|
36
|
+
|
37
|
+
param :foo
|
38
|
+
param :bar, optional: true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it "sets undefined values to nil" do
|
43
|
+
subject = Test::Foo.new(1)
|
44
|
+
|
45
|
+
expect(subject.instance_variable_get(:@bar)).to be_nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when has a default value" do
|
50
|
+
before do
|
51
|
+
class Test::Foo
|
52
|
+
extend Dry::Initializer
|
53
|
+
|
54
|
+
param :foo
|
55
|
+
param :bar, optional: true, default: proc { "baz" }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it "is takes default value" do
|
60
|
+
subject = Test::Foo.new(1)
|
61
|
+
|
62
|
+
expect(subject.bar).to eq "baz"
|
63
|
+
end
|
64
|
+
|
65
|
+
it "can be set explicitly" do
|
66
|
+
subject = Test::Foo.new(1, "qux")
|
67
|
+
|
68
|
+
expect(subject.bar).to eq "qux"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
describe Dry::Initializer, ".dry_initializer.public_attributes" do
|
2
|
+
subject { instance.class.dry_initializer.public_attributes(instance) }
|
3
|
+
|
4
|
+
context "when class has params" do
|
5
|
+
before do
|
6
|
+
class Test::Foo
|
7
|
+
extend Dry::Initializer
|
8
|
+
param :foo, proc(&:to_s), desc: "a weird parameter"
|
9
|
+
option :moo, optional: true
|
10
|
+
option :bar, default: proc { 1 }, reader: false
|
11
|
+
option :baz, optional: true, reader: :protected
|
12
|
+
option :qux, proc(&:to_s), as: :quxx, reader: :private
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:instance) { Test::Foo.new(:FOO, bar: :BAR, baz: :BAZ, qux: :QUX) }
|
17
|
+
|
18
|
+
it "collects public options only" do
|
19
|
+
expect(subject).to eq({ foo: "FOO", moo: nil })
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/spec/reader_spec.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
describe "reader" do
|
2
|
+
shared_examples "it has no public attr_reader" do
|
3
|
+
it "does not define a public attr_reader" do
|
4
|
+
expect(subject).not_to respond_to :foo
|
5
|
+
expect(subject).not_to respond_to :bar
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
context "with reader: :public or no reader: option" do
|
10
|
+
subject do
|
11
|
+
class Test::Foo
|
12
|
+
extend Dry::Initializer
|
13
|
+
|
14
|
+
param :foo
|
15
|
+
param :foo2, reader: :public
|
16
|
+
option :bar
|
17
|
+
option :bar2, reader: :public
|
18
|
+
end
|
19
|
+
|
20
|
+
Test::Foo.new 1, 2, bar: 3, bar2: 4
|
21
|
+
end
|
22
|
+
|
23
|
+
it "defines a public attr_reader by default" do
|
24
|
+
expect(subject).to respond_to(:foo, :foo2)
|
25
|
+
expect(subject).to respond_to :bar
|
26
|
+
expect(subject).to respond_to :bar2
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "with reader: false" do
|
31
|
+
before do
|
32
|
+
class Test::Foo
|
33
|
+
extend Dry::Initializer
|
34
|
+
|
35
|
+
param :foo, reader: false
|
36
|
+
option :bar, reader: false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
subject { Test::Foo.new 1, bar: 2 }
|
41
|
+
|
42
|
+
it_behaves_like "it has no public attr_reader"
|
43
|
+
|
44
|
+
it "keeps assigning variables" do
|
45
|
+
expect(subject.instance_variable_get(:@foo)).to eql 1
|
46
|
+
expect(subject.instance_variable_get(:@bar)).to eql 2
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "with reader: :private" do
|
51
|
+
before do
|
52
|
+
class Test::Foo
|
53
|
+
extend Dry::Initializer
|
54
|
+
|
55
|
+
param :foo, reader: :private
|
56
|
+
option :bar, reader: :private
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
subject { Test::Foo.new 1, bar: 2 }
|
61
|
+
|
62
|
+
it_behaves_like "it has no public attr_reader"
|
63
|
+
|
64
|
+
it "adds a private attr_reader" do
|
65
|
+
expect(subject.send(:foo)).to eql 1
|
66
|
+
expect(subject.send(:bar)).to eql 2
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "with reader: :protected" do
|
71
|
+
subject do
|
72
|
+
class Test::Foo
|
73
|
+
extend Dry::Initializer
|
74
|
+
|
75
|
+
param :foo, reader: :protected
|
76
|
+
option :bar, reader: :protected
|
77
|
+
end
|
78
|
+
|
79
|
+
Test::Foo.new 1, bar: 2
|
80
|
+
end
|
81
|
+
|
82
|
+
it "adds a protected attr_reader" do
|
83
|
+
protected_instance_methods = subject.class.protected_instance_methods
|
84
|
+
expect(protected_instance_methods).to match_array(%i[foo bar])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
describe "repetitive definitions" do
|
2
|
+
subject { Test::Foo.new }
|
3
|
+
|
4
|
+
context "of params" do
|
5
|
+
before do
|
6
|
+
class Test::Foo
|
7
|
+
extend Dry::Initializer
|
8
|
+
|
9
|
+
param :foo, default: proc { 0 }
|
10
|
+
param :bar, default: proc { 1 }
|
11
|
+
param :foo, default: proc { 2 }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "reloads the attribute" do
|
16
|
+
expect(subject.foo).to eq 2
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "of options" do
|
21
|
+
before do
|
22
|
+
class Test::Foo
|
23
|
+
extend Dry::Initializer
|
24
|
+
|
25
|
+
option :foo, default: proc { 0 }
|
26
|
+
option :bar, default: proc { 1 }
|
27
|
+
option :foo, default: proc { 2 }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "reloads the attribute" do
|
32
|
+
expect(subject.foo).to eq 2
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "of param and option" do
|
37
|
+
before do
|
38
|
+
class Test::Foo
|
39
|
+
extend Dry::Initializer
|
40
|
+
|
41
|
+
param :foo, default: proc { 0 }
|
42
|
+
option :bar, default: proc { 1 }
|
43
|
+
option :foo, default: proc { 2 }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "reloads the attribute" do
|
48
|
+
expect(subject.foo).to eq 2
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "of optional param and option" do
|
53
|
+
before do
|
54
|
+
class Test::Foo
|
55
|
+
extend Dry::Initializer
|
56
|
+
|
57
|
+
param :baz, optional: true, as: :foo
|
58
|
+
option :bar, optional: true
|
59
|
+
option :foo, optional: true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "allows various assignments" do
|
64
|
+
expect(Test::Foo.new(1).foo).to eq 1
|
65
|
+
expect(Test::Foo.new(foo: 2).foo).to eq 2
|
66
|
+
expect(Test::Foo.new(1, foo: 2).foo).to eq 2
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|