dry-initializer 3.0.4 → 3.1.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/LICENSE +1 -1
  4. data/README.md +4 -3
  5. data/dry-initializer.gemspec +15 -13
  6. data/lib/dry/initializer/builders/attribute.rb +92 -82
  7. data/lib/dry/initializer/builders/initializer.rb +56 -54
  8. data/lib/dry/initializer/builders/reader.rb +55 -49
  9. data/lib/dry/initializer/builders/signature.rb +29 -23
  10. data/lib/dry/initializer/builders.rb +9 -5
  11. data/lib/dry/initializer/config.rb +160 -158
  12. data/lib/dry/initializer/definition.rb +58 -54
  13. data/lib/dry/initializer/dispatchers/build_nested_type.rb +54 -40
  14. data/lib/dry/initializer/dispatchers/check_type.rb +45 -39
  15. data/lib/dry/initializer/dispatchers/prepare_default.rb +32 -25
  16. data/lib/dry/initializer/dispatchers/prepare_ivar.rb +13 -6
  17. data/lib/dry/initializer/dispatchers/prepare_optional.rb +14 -7
  18. data/lib/dry/initializer/dispatchers/prepare_reader.rb +29 -22
  19. data/lib/dry/initializer/dispatchers/prepare_source.rb +12 -5
  20. data/lib/dry/initializer/dispatchers/prepare_target.rb +44 -37
  21. data/lib/dry/initializer/dispatchers/unwrap_type.rb +21 -10
  22. data/lib/dry/initializer/dispatchers/wrap_type.rb +24 -17
  23. data/lib/dry/initializer/dispatchers.rb +48 -43
  24. data/lib/dry/initializer/dsl.rb +42 -34
  25. data/lib/dry/initializer/errors.rb +22 -0
  26. data/lib/dry/initializer/mixin/local.rb +19 -13
  27. data/lib/dry/initializer/mixin/root.rb +12 -7
  28. data/lib/dry/initializer/mixin.rb +17 -12
  29. data/lib/dry/initializer/struct.rb +34 -29
  30. data/lib/dry/initializer/undefined.rb +7 -1
  31. data/lib/dry/initializer/version.rb +3 -1
  32. data/lib/dry/initializer.rb +12 -9
  33. data/lib/dry-initializer.rb +3 -1
  34. data/lib/tasks/benchmark.rake +15 -13
  35. data/lib/tasks/profile.rake +20 -16
  36. metadata +5 -4
@@ -1,43 +1,49 @@
1
- #
1
+ # frozen_string_literal: true
2
+
2
3
  # Checks whether an unwrapped type is valid
3
4
  #
4
- module Dry::Initializer::Dispatchers::CheckType
5
- extend self
6
-
7
- def call(source:, type: nil, wrap: 0, **options)
8
- check_if_callable! source, type
9
- check_arity! source, type, wrap
10
-
11
- { source: source, type: type, wrap: wrap, **options }
12
- end
13
-
14
- private
15
-
16
- def check_if_callable!(source, type)
17
- return if type.nil?
18
- return if type.respond_to?(:call)
19
-
20
- raise ArgumentError,
21
- "The type of the argument '#{source}' should be callable"
22
- end
23
-
24
- def check_arity!(_source, type, wrap)
25
- return if type.nil?
26
- return if wrap.zero?
27
- return if type.method(:call).arity.abs == 1
28
-
29
- raise ArgumentError, <<~MESSAGE
30
- The dry_intitializer supports wrapped types with one argument only.
31
- You cannot use array types with element coercers having several arguments.
32
-
33
- For example, this definitions are correct:
34
- option :foo, [proc(&:to_s)]
35
- option :bar, type: [[]]
36
- option :baz, ->(a, b) { [a, b] }
37
-
38
- While this is not:
39
- option :foo, [->(a, b) { [a, b] }]
40
- MESSAGE
5
+ module Dry
6
+ module Initializer
7
+ module Dispatchers
8
+ module CheckType
9
+ extend self
10
+
11
+ def call(source:, type: nil, wrap: 0, **options)
12
+ check_if_callable! source, type
13
+ check_arity! source, type, wrap
14
+
15
+ {source: source, type: type, wrap: wrap, **options}
16
+ end
17
+
18
+ private
19
+
20
+ def check_if_callable!(source, type)
21
+ return if type.nil?
22
+ return if type.respond_to?(:call)
23
+
24
+ raise ArgumentError,
25
+ "The type of the argument '#{source}' should be callable"
26
+ end
27
+
28
+ def check_arity!(_source, type, wrap)
29
+ return if type.nil?
30
+ return if wrap.zero?
31
+ return if type.method(:call).arity.abs == 1
32
+
33
+ raise ArgumentError, <<~MESSAGE
34
+ The dry_intitializer supports wrapped types with one argument only.
35
+ You cannot use array types with element coercers having several arguments.
36
+
37
+ For example, this definitions are correct:
38
+ option :foo, [proc(&:to_s)]
39
+ option :bar, type: [[]]
40
+ option :baz, ->(a, b) { [a, b] }
41
+
42
+ While this is not:
43
+ option :foo, [->(a, b) { [a, b] }]
44
+ MESSAGE
45
+ end
46
+ end
47
+ end
41
48
  end
42
- # rubocop: enable Metrics/MethodLength
43
49
  end
@@ -1,40 +1,47 @@
1
- #
1
+ # frozen_string_literal: true
2
+
2
3
  # Prepares the `:default` option
3
4
  #
4
5
  # It must respond to `.call` without arguments
5
6
  #
6
- module Dry::Initializer::Dispatchers::PrepareDefault
7
- extend self
7
+ module Dry
8
+ module Initializer
9
+ module Dispatchers
10
+ module PrepareDefault
11
+ extend self
8
12
 
9
- def call(default: nil, optional: nil, **options)
10
- default = callable! default
11
- check_arity! default
13
+ def call(default: nil, optional: nil, **options)
14
+ default = callable! default
15
+ check_arity! default
12
16
 
13
- { default: default, optional: (optional | default), **options }
14
- end
17
+ {default: default, optional: (optional | default), **options}
18
+ end
15
19
 
16
- private
20
+ private
17
21
 
18
- def callable!(default)
19
- return unless default
20
- return default if default.respond_to?(:call)
21
- return callable(default.to_proc) if default.respond_to?(:to_proc)
22
+ def callable!(default)
23
+ return unless default
24
+ return default if default.respond_to?(:call)
25
+ return callable(default.to_proc) if default.respond_to?(:to_proc)
22
26
 
23
- invalid!(default)
24
- end
27
+ invalid!(default)
28
+ end
25
29
 
26
- def check_arity!(default)
27
- return unless default
30
+ def check_arity!(default)
31
+ return unless default
28
32
 
29
- arity = default.method(:call).arity.to_i
30
- return unless arity.positive?
33
+ arity = default.method(:call).arity.to_i
34
+ return unless arity.positive?
31
35
 
32
- invalid!(default)
33
- end
36
+ invalid!(default)
37
+ end
34
38
 
35
- def invalid!(default)
36
- raise TypeError, "The #{default.inspect} should be" \
37
- ' either convertable to proc with no arguments,' \
38
- ' or respond to #call without arguments.'
39
+ def invalid!(default)
40
+ raise TypeError, "The #{default.inspect} should be" \
41
+ " either convertable to proc with no arguments," \
42
+ " or respond to #call without arguments."
43
+ end
44
+ end
45
+ end
39
46
  end
40
47
  end
@@ -1,12 +1,19 @@
1
- #
1
+ # frozen_string_literal: true
2
+
2
3
  # Prepares the variable name of a parameter or an option.
3
4
  #
4
- module Dry::Initializer::Dispatchers::PrepareIvar
5
- module_function
5
+ module Dry
6
+ module Initializer
7
+ module Dispatchers
8
+ module PrepareIvar
9
+ module_function
6
10
 
7
- def call(target:, **options)
8
- ivar = "@#{target}".delete('?').to_sym
11
+ def call(target:, **options)
12
+ ivar = "@#{target}".delete("?").to_sym
9
13
 
10
- { target: target, ivar: ivar, **options }
14
+ {target: target, ivar: ivar, **options}
15
+ end
16
+ end
17
+ end
11
18
  end
12
19
  end
@@ -1,13 +1,20 @@
1
- #
1
+ # frozen_string_literal: true
2
+
2
3
  # Defines whether an argument is optional
3
4
  #
4
- module Dry::Initializer::Dispatchers::PrepareOptional
5
- module_function
5
+ module Dry
6
+ module Initializer
7
+ module Dispatchers
8
+ module PrepareOptional
9
+ module_function
6
10
 
7
- def call(optional: nil, default: nil, required: nil, **options)
8
- optional ||= default
9
- optional &&= !required
11
+ def call(optional: nil, default: nil, required: nil, **options)
12
+ optional ||= default
13
+ optional &&= !required
10
14
 
11
- { optional: !!optional, default: default, **options }
15
+ {optional: !!optional, default: default, **options}
16
+ end
17
+ end
18
+ end
12
19
  end
13
20
  end
@@ -1,30 +1,37 @@
1
- #
1
+ # frozen_string_literal: true
2
+
2
3
  # Checks the reader privacy
3
4
  #
4
- module Dry::Initializer::Dispatchers::PrepareReader
5
- extend self
5
+ module Dry
6
+ module Initializer
7
+ module Dispatchers
8
+ module PrepareReader
9
+ extend self
6
10
 
7
- def call(target: nil, reader: :public, **options)
8
- reader = case reader.to_s
9
- when 'false', '' then nil
10
- when 'true' then :public
11
- when 'public', 'private', 'protected' then reader.to_sym
12
- else invalid_reader!(target, reader)
13
- end
11
+ def call(target: nil, reader: :public, **options)
12
+ reader = case reader.to_s
13
+ when "false", "" then nil
14
+ when "true" then :public
15
+ when "public", "private", "protected" then reader.to_sym
16
+ else invalid_reader!(target, reader)
17
+ end
14
18
 
15
- { target: target, reader: reader, **options }
16
- end
19
+ {target: target, reader: reader, **options}
20
+ end
17
21
 
18
- private
22
+ private
19
23
 
20
- def invalid_reader!(target, _reader)
21
- raise ArgumentError, <<~MESSAGE
22
- Invalid setting for the ##{target} reader's privacy.
23
- Use the one of the following values for the `:reader` option:
24
- - 'public' (true) for the public reader (default)
25
- - 'private' for the private reader
26
- - 'protected' for the protected reader
27
- - nil (false) if no reader should be defined
28
- MESSAGE
24
+ def invalid_reader!(target, _reader)
25
+ raise ArgumentError, <<~MESSAGE
26
+ Invalid setting for the ##{target} reader's privacy.
27
+ Use the one of the following values for the `:reader` option:
28
+ - 'public' (true) for the public reader (default)
29
+ - 'private' for the private reader
30
+ - 'protected' for the protected reader
31
+ - nil (false) if no reader should be defined
32
+ MESSAGE
33
+ end
34
+ end
35
+ end
29
36
  end
30
37
  end
@@ -1,4 +1,5 @@
1
- #
1
+ # frozen_string_literal: true
2
+
2
3
  # The dispatcher verifies a correctness of the source name
3
4
  # of param or option, taken as a `:source` option.
4
5
  #
@@ -19,10 +20,16 @@
19
20
  # foo.second # => 666
20
21
  # ```
21
22
  #
22
- module Dry::Initializer::Dispatchers::PrepareSource
23
- module_function
23
+ module Dry
24
+ module Initializer
25
+ module Dispatchers
26
+ module PrepareSource
27
+ module_function
24
28
 
25
- def call(source:, **options)
26
- { source: source.to_s.to_sym, **options }
29
+ def call(source:, **options)
30
+ {source: source.to_s.to_sym, **options}
31
+ end
32
+ end
33
+ end
27
34
  end
28
35
  end
@@ -1,44 +1,51 @@
1
- #
1
+ # frozen_string_literal: true
2
+
2
3
  # Prepares the target name of a parameter or an option.
3
4
  #
4
5
  # Unlike source, the target must satisfy requirements for Ruby variable names.
5
6
  # It also shouldn't be in conflict with names used by the gem.
6
7
  #
7
- module Dry::Initializer::Dispatchers::PrepareTarget
8
- extend self
9
-
10
- # List of variable names reserved by the gem
11
- RESERVED = %i[
12
- __dry_initializer_options__
13
- __dry_initializer_config__
14
- __dry_initializer_value__
15
- __dry_initializer_definition__
16
- __dry_initializer_initializer__
17
- ].freeze
18
-
19
- def call(source:, target: nil, as: nil, **options)
20
- target ||= as || source
21
- target = target.to_s.to_sym.downcase
22
-
23
- check_ruby_name!(target)
24
- check_reserved_names!(target)
25
-
26
- { source: source, target: target, **options }
27
- end
28
-
29
- private
30
-
31
- def check_ruby_name!(target)
32
- return if target[/\A[[:alpha:]_][[:alnum:]_]*\??\z/u]
33
-
34
- raise ArgumentError,
35
- "The name `#{target}` is not allowed for Ruby methods"
36
- end
37
-
38
- def check_reserved_names!(target)
39
- return unless RESERVED.include?(target)
40
-
41
- raise ArgumentError,
42
- "The method name `#{target}` is reserved by the dry-initializer gem"
8
+ module Dry
9
+ module Initializer
10
+ module Dispatchers
11
+ module PrepareTarget
12
+ extend self
13
+
14
+ # List of variable names reserved by the gem
15
+ RESERVED = %i[
16
+ __dry_initializer_options__
17
+ __dry_initializer_config__
18
+ __dry_initializer_value__
19
+ __dry_initializer_definition__
20
+ __dry_initializer_initializer__
21
+ ].freeze
22
+
23
+ def call(source:, target: nil, as: nil, **options)
24
+ target ||= as || source
25
+ target = target.to_s.to_sym.downcase
26
+
27
+ check_ruby_name!(target)
28
+ check_reserved_names!(target)
29
+
30
+ {source: source, target: target, **options}
31
+ end
32
+
33
+ private
34
+
35
+ def check_ruby_name!(target)
36
+ return if target[/\A[[:alpha:]_][[:alnum:]_]*\??\z/u]
37
+
38
+ raise ArgumentError,
39
+ "The name `#{target}` is not allowed for Ruby methods"
40
+ end
41
+
42
+ def check_reserved_names!(target)
43
+ return unless RESERVED.include?(target)
44
+
45
+ raise ArgumentError,
46
+ "The method name `#{target}` is reserved by the dry-initializer gem"
47
+ end
48
+ end
49
+ end
43
50
  end
44
51
  end
@@ -1,22 +1,33 @@
1
- #
1
+ # frozen_string_literal: true
2
+
2
3
  # Looks at the `:type` option and counts how many nested arrays
3
4
  # it contains around either nil or a callable value.
4
5
  #
5
6
  # The counted number is preserved in the `:wrap` virtual option
6
7
  # used by the [WrapType] dispatcher.
7
8
  #
8
- module Dry::Initializer::Dispatchers::UnwrapType
9
- extend self
9
+ module Dry
10
+ module Initializer
11
+ module Dispatchers
12
+ module UnwrapType
13
+ extend self
10
14
 
11
- def call(type: nil, wrap: 0, **options)
12
- type, wrap = unwrap(type, 0)
15
+ def call(type: nil, wrap: 0, **options)
16
+ type, count = unwrap(type, wrap)
13
17
 
14
- { type: type, wrap: wrap, **options }
15
- end
18
+ {type: type, wrap: count, **options}
19
+ end
16
20
 
17
- private
21
+ private
18
22
 
19
- def unwrap(type, count)
20
- type.is_a?(Array) ? unwrap(type.first, count + 1) : [type, count]
23
+ def unwrap(type, count)
24
+ if type.is_a?(::Array)
25
+ unwrap(type.first, count + 1)
26
+ else
27
+ [type, count]
28
+ end
29
+ end
30
+ end
31
+ end
21
32
  end
22
33
  end
@@ -1,28 +1,35 @@
1
- #
1
+ # frozen_string_literal: true
2
+
2
3
  # Takes `:type` and `:wrap` to construct the final value coercer
3
4
  #
4
- module Dry::Initializer::Dispatchers::WrapType
5
- extend self
5
+ module Dry
6
+ module Initializer
7
+ module Dispatchers
8
+ module WrapType
9
+ extend self
6
10
 
7
- def call(type: nil, wrap: 0, **options)
8
- { type: wrapped_type(type, wrap), **options }
9
- end
11
+ def call(type: nil, wrap: 0, **options)
12
+ {type: wrapped_type(type, wrap), **options}
13
+ end
10
14
 
11
- private
15
+ private
12
16
 
13
- def wrapped_type(type, count)
14
- return type if count.zero?
17
+ def wrapped_type(type, count)
18
+ return type if count.zero?
15
19
 
16
- ->(value) { wrap_value(value, count, type) }
17
- end
20
+ ->(value) { wrap_value(value, count, type) }
21
+ end
18
22
 
19
- def wrap_value(value, count, type)
20
- if count.zero?
21
- type ? type.call(value) : value
22
- else
23
- return [wrap_value(value, count - 1, type)] unless value.is_a?(Array)
23
+ def wrap_value(value, count, type)
24
+ if count.zero?
25
+ type ? type.call(value) : value
26
+ else
27
+ return [wrap_value(value, count - 1, type)] unless value.is_a?(Array)
24
28
 
25
- value.map { |item| wrap_value(item, count - 1, type) }
29
+ value.map { |item| wrap_value(item, count - 1, type) }
30
+ end
31
+ end
32
+ end
26
33
  end
27
34
  end
28
35
  end
@@ -1,4 +1,5 @@
1
- #
1
+ # frozen_string_literal: true
2
+
2
3
  # The module is responsible for __normalizing__ arguments
3
4
  # of `.param` and `.option`.
4
5
  #
@@ -60,53 +61,57 @@
60
61
  # param :id, integer: true
61
62
  # end
62
63
  #
63
- module Dry::Initializer::Dispatchers
64
- extend self
64
+ module Dry
65
+ module Initializer
66
+ module Dispatchers
67
+ extend self
65
68
 
66
- # @!attribute [rw] null Defines a value to be set to unassigned attributes
67
- # @return [Object]
68
- attr_accessor :null
69
+ # @!attribute [rw] null Defines a value to be set to unassigned attributes
70
+ # @return [Object]
71
+ attr_accessor :null
69
72
 
70
- #
71
- # Registers a new dispatcher
72
- #
73
- # @param [#call] dispatcher
74
- # @return [self] itself
75
- #
76
- def <<(dispatcher)
77
- @pipeline = [dispatcher] + pipeline
78
- self
79
- end
73
+ #
74
+ # Registers a new dispatcher
75
+ #
76
+ # @param [#call] dispatcher
77
+ # @return [self] itself
78
+ #
79
+ def <<(dispatcher)
80
+ @pipeline = [dispatcher] + pipeline
81
+ self
82
+ end
80
83
 
81
- #
82
- # Normalizes the source set of options
83
- #
84
- # @param [Hash<Symbol, Object>] options
85
- # @return [Hash<Symbol, Objct>] normalized set of options
86
- #
87
- def call(**options)
88
- options = { null: null, **options }
89
- pipeline.reduce(options) { |opts, dispatcher| dispatcher.call(**opts) }
90
- end
84
+ #
85
+ # Normalizes the source set of options
86
+ #
87
+ # @param [Hash<Symbol, Object>] options
88
+ # @return [Hash<Symbol, Objct>] normalized set of options
89
+ #
90
+ def call(**options)
91
+ options = {null: null, **options}
92
+ pipeline.reduce(options) { |opts, dispatcher| dispatcher.call(**opts) }
93
+ end
91
94
 
92
- private
95
+ private
93
96
 
94
- require_relative 'dispatchers/build_nested_type'
95
- require_relative 'dispatchers/check_type'
96
- require_relative 'dispatchers/prepare_default'
97
- require_relative 'dispatchers/prepare_ivar'
98
- require_relative 'dispatchers/prepare_optional'
99
- require_relative 'dispatchers/prepare_reader'
100
- require_relative 'dispatchers/prepare_source'
101
- require_relative 'dispatchers/prepare_target'
102
- require_relative 'dispatchers/unwrap_type'
103
- require_relative 'dispatchers/wrap_type'
97
+ require_relative "dispatchers/build_nested_type"
98
+ require_relative "dispatchers/check_type"
99
+ require_relative "dispatchers/prepare_default"
100
+ require_relative "dispatchers/prepare_ivar"
101
+ require_relative "dispatchers/prepare_optional"
102
+ require_relative "dispatchers/prepare_reader"
103
+ require_relative "dispatchers/prepare_source"
104
+ require_relative "dispatchers/prepare_target"
105
+ require_relative "dispatchers/unwrap_type"
106
+ require_relative "dispatchers/wrap_type"
104
107
 
105
- def pipeline
106
- @pipeline ||= [
107
- PrepareSource, PrepareTarget, PrepareIvar, PrepareReader,
108
- PrepareDefault, PrepareOptional,
109
- UnwrapType, CheckType, BuildNestedType, WrapType
110
- ]
108
+ def pipeline
109
+ @pipeline ||= [
110
+ PrepareSource, PrepareTarget, PrepareIvar, PrepareReader,
111
+ PrepareDefault, PrepareOptional,
112
+ UnwrapType, CheckType, BuildNestedType, WrapType
113
+ ]
114
+ end
115
+ end
111
116
  end
112
117
  end