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.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +12 -0
  3. data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +10 -0
  4. data/.github/ISSUE_TEMPLATE/---bug-report.md +34 -0
  5. data/.github/ISSUE_TEMPLATE/---feature-request.md +18 -0
  6. data/.github/workflows/custom_ci.yml +74 -0
  7. data/.github/workflows/docsite.yml +34 -0
  8. data/.github/workflows/sync_configs.yml +34 -0
  9. data/.gitignore +12 -0
  10. data/.rspec +4 -0
  11. data/.rubocop.yml +89 -0
  12. data/CHANGELOG.md +890 -0
  13. data/CODE_OF_CONDUCT.md +13 -0
  14. data/CONTRIBUTING.md +29 -0
  15. data/Gemfile +38 -0
  16. data/Guardfile +5 -0
  17. data/LICENSE +20 -0
  18. data/LICENSE.txt +21 -0
  19. data/README.md +89 -0
  20. data/Rakefile +8 -0
  21. data/benchmarks/compare_several_defaults.rb +82 -0
  22. data/benchmarks/plain_options.rb +63 -0
  23. data/benchmarks/plain_params.rb +84 -0
  24. data/benchmarks/with_coercion.rb +71 -0
  25. data/benchmarks/with_defaults.rb +66 -0
  26. data/benchmarks/with_defaults_and_coercion.rb +59 -0
  27. data/docsite/source/attributes.html.md +106 -0
  28. data/docsite/source/container-version.html.md +39 -0
  29. data/docsite/source/index.html.md +43 -0
  30. data/docsite/source/inheritance.html.md +43 -0
  31. data/docsite/source/optionals-and-defaults.html.md +130 -0
  32. data/docsite/source/options-tolerance.html.md +27 -0
  33. data/docsite/source/params-and-options.html.md +74 -0
  34. data/docsite/source/rails-support.html.md +101 -0
  35. data/docsite/source/readers.html.md +43 -0
  36. data/docsite/source/skip-undefined.html.md +59 -0
  37. data/docsite/source/type-constraints.html.md +160 -0
  38. data/dry-initializer.gemspec +20 -0
  39. data/lib/dry-initializer.rb +1 -0
  40. data/lib/dry/initializer.rb +61 -0
  41. data/lib/dry/initializer/builders.rb +7 -0
  42. data/lib/dry/initializer/builders/attribute.rb +81 -0
  43. data/lib/dry/initializer/builders/initializer.rb +61 -0
  44. data/lib/dry/initializer/builders/reader.rb +50 -0
  45. data/lib/dry/initializer/builders/signature.rb +32 -0
  46. data/lib/dry/initializer/config.rb +184 -0
  47. data/lib/dry/initializer/definition.rb +65 -0
  48. data/lib/dry/initializer/dispatchers.rb +112 -0
  49. data/lib/dry/initializer/dispatchers/build_nested_type.rb +59 -0
  50. data/lib/dry/initializer/dispatchers/check_type.rb +43 -0
  51. data/lib/dry/initializer/dispatchers/prepare_default.rb +40 -0
  52. data/lib/dry/initializer/dispatchers/prepare_ivar.rb +12 -0
  53. data/lib/dry/initializer/dispatchers/prepare_optional.rb +13 -0
  54. data/lib/dry/initializer/dispatchers/prepare_reader.rb +30 -0
  55. data/lib/dry/initializer/dispatchers/prepare_source.rb +28 -0
  56. data/lib/dry/initializer/dispatchers/prepare_target.rb +44 -0
  57. data/lib/dry/initializer/dispatchers/unwrap_type.rb +22 -0
  58. data/lib/dry/initializer/dispatchers/wrap_type.rb +27 -0
  59. data/lib/dry/initializer/dsl.rb +43 -0
  60. data/lib/dry/initializer/mixin.rb +15 -0
  61. data/lib/dry/initializer/mixin/local.rb +19 -0
  62. data/lib/dry/initializer/mixin/root.rb +11 -0
  63. data/lib/dry/initializer/struct.rb +39 -0
  64. data/lib/dry/initializer/undefined.rb +2 -0
  65. data/lib/tasks/benchmark.rake +41 -0
  66. data/lib/tasks/profile.rake +78 -0
  67. data/spec/attributes_spec.rb +38 -0
  68. data/spec/coercion_of_nil_spec.rb +25 -0
  69. data/spec/custom_dispatchers_spec.rb +35 -0
  70. data/spec/custom_initializer_spec.rb +30 -0
  71. data/spec/default_values_spec.rb +83 -0
  72. data/spec/definition_spec.rb +111 -0
  73. data/spec/invalid_default_spec.rb +13 -0
  74. data/spec/list_type_spec.rb +32 -0
  75. data/spec/missed_default_spec.rb +14 -0
  76. data/spec/nested_type_spec.rb +48 -0
  77. data/spec/optional_spec.rb +71 -0
  78. data/spec/options_tolerance_spec.rb +11 -0
  79. data/spec/public_attributes_utility_spec.rb +22 -0
  80. data/spec/reader_spec.rb +87 -0
  81. data/spec/repetitive_definitions_spec.rb +69 -0
  82. data/spec/several_assignments_spec.rb +41 -0
  83. data/spec/spec_helper.rb +29 -0
  84. data/spec/subclassing_spec.rb +49 -0
  85. data/spec/type_argument_spec.rb +35 -0
  86. data/spec/type_constraint_spec.rb +78 -0
  87. data/spec/value_coercion_via_dry_types_spec.rb +29 -0
  88. 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,13 @@
1
+ describe "invalid default value assignment" do
2
+ subject do
3
+ class Test::Foo
4
+ extend Dry::Initializer
5
+
6
+ param :foo, default: 1
7
+ end
8
+ end
9
+
10
+ it "raises TypeError" do
11
+ expect { subject }.to raise_error TypeError
12
+ end
13
+ 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,11 @@
1
+ describe "options tolerance" do
2
+ before do
3
+ class Test::Foo
4
+ extend Dry::Initializer
5
+ end
6
+ end
7
+
8
+ it "allows options before any definition" do
9
+ expect { Test::Foo.new bar: :baz }.not_to raise_error
10
+ end
11
+ 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
@@ -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