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,51 @@
1
- module Dry::Initializer
2
- # Module-level DSL
3
- module DSL
4
- # Setting for null (undefined value)
5
- # @return [nil, Dry::Initializer::UNDEFINED]
6
- attr_reader :null
1
+ # frozen_string_literal: true
7
2
 
8
- # Returns a version of the module with custom settings
9
- # @option settings [Boolean] :undefined
10
- # If unassigned params and options should be treated different from nil
11
- # @return [Dry::Initializer]
12
- def [](undefined: true, **)
13
- null = (undefined == false) ? nil : UNDEFINED
14
- Module.new.tap do |mod|
15
- mod.extend DSL
16
- mod.include self
17
- mod.send(:instance_variable_set, :@null, null)
3
+ module Dry
4
+ module Initializer
5
+ # Module-level DSL
6
+ module DSL
7
+ # Setting for null (undefined value)
8
+ # @return [nil, Dry::Initializer::UNDEFINED]
9
+ attr_reader :null
10
+
11
+ # Returns a version of the module with custom settings
12
+ # @option settings [Boolean] :undefined
13
+ # If unassigned params and options should be treated different from nil
14
+ # @return [Dry::Initializer]
15
+ def [](undefined: true, **)
16
+ null = undefined == false ? nil : UNDEFINED
17
+ Module.new.tap do |mod|
18
+ mod.extend DSL
19
+ mod.include self
20
+ mod.send(:instance_variable_set, :@null, null)
21
+ end
18
22
  end
19
- end
20
23
 
21
- # Returns mixin module to be included to target class by hand
22
- # @return [Module]
23
- # @yield proc defining params and options
24
- def define(procedure = nil, &block)
25
- config = Config.new(null: null)
26
- config.instance_exec(&(procedure || block))
27
- config.mixin.include Mixin::Root
28
- config.mixin
29
- end
24
+ # Returns mixin module to be included to target class by hand
25
+ # @return [Module]
26
+ # @yield proc defining params and options
27
+ def define(procedure = nil, &block)
28
+ config = Config.new(null: null)
29
+ config.instance_exec(&(procedure || block))
30
+ config.mixin.include Mixin::Root
31
+ config.mixin
32
+ end
30
33
 
31
- private
34
+ private
32
35
 
33
- def extended(klass)
34
- config = Config.new(klass, null: null)
35
- klass.send :instance_variable_set, :@dry_initializer, config
36
- klass.include Mixin::Root
37
- end
36
+ def extended(klass)
37
+ config = Config.new(klass, null: null)
38
+ klass.send :instance_variable_set, :@dry_initializer, config
39
+ klass.include Mixin::Root
40
+ end
38
41
 
39
- def self.extended(mod)
40
- mod.instance_variable_set :@null, UNDEFINED
42
+ class << self
43
+ private
44
+
45
+ def extended(mod)
46
+ mod.instance_variable_set :@null, UNDEFINED
47
+ end
48
+ end
41
49
  end
42
50
  end
43
51
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Initializer
5
+ class CoercionError < ::StandardError
6
+ def initialize(constraint_error, field)
7
+ @constraint_error = constraint_error
8
+ @field = field
9
+ super(message)
10
+ end
11
+
12
+ # Ensure that the field name is in the error message
13
+ def message
14
+ if @constraint_error.message =~ /#{@field}/
15
+ @constraint_error.message
16
+ else
17
+ "#{@constraint_error.message} for field :#{@field}"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,19 +1,25 @@
1
- module Dry::Initializer::Mixin
2
- # @private
3
- module Local
4
- attr_reader :klass
1
+ # frozen_string_literal: true
5
2
 
6
- def inspect
7
- "Dry::Initializer::Mixin::Local[#{klass}]"
8
- end
9
- alias to_s inspect
10
- alias to_str inspect
3
+ module Dry
4
+ module Initializer
5
+ module Mixin
6
+ # @private
7
+ module Local
8
+ attr_reader :klass
9
+
10
+ def inspect
11
+ "Dry::Initializer::Mixin::Local[#{klass}]"
12
+ end
13
+ alias_method :to_s, :inspect
14
+ alias_method :to_str, :inspect
11
15
 
12
- private
16
+ private
13
17
 
14
- def included(klass)
15
- @klass = klass
16
- super
18
+ def included(klass)
19
+ @klass = klass
20
+ super
21
+ end
22
+ end
17
23
  end
18
24
  end
19
25
  end
@@ -1,11 +1,16 @@
1
- module Dry::Initializer::Mixin
2
- # @private
3
- module Root
4
- private
1
+ # frozen_string_literal: true
5
2
 
6
- def initialize(*args)
7
- __dry_initializer_initialize__(*args)
3
+ module Dry
4
+ module Initializer
5
+ module Mixin
6
+ # @private
7
+ module Root
8
+ private
9
+
10
+ def initialize(...)
11
+ __dry_initializer_initialize__(...)
12
+ end
13
+ end
8
14
  end
9
- ruby2_keywords(:initialize) if respond_to?(:ruby2_keywords, true)
10
15
  end
11
16
  end
@@ -1,15 +1,20 @@
1
- module Dry::Initializer
2
- # @private
3
- module Mixin
4
- extend DSL # @deprecated
5
- include Dry::Initializer # @deprecated
6
- def self.extended(klass) # @deprecated
7
- warn '[DEPRECATED] Use Dry::Initializer instead of its alias' \
8
- ' Dry::Initializer::Mixin. The later will be removed in v2.1.0'
9
- super
10
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Initializer
5
+ # @private
6
+ module Mixin
7
+ extend DSL # @deprecated
8
+ include Dry::Initializer # @deprecated
9
+ # @deprecated
10
+ def self.extended(klass)
11
+ warn "[DEPRECATED] Use Dry::Initializer instead of its alias" \
12
+ " Dry::Initializer::Mixin. The later will be removed in v2.1.0"
13
+ super
14
+ end
11
15
 
12
- require_relative 'mixin/root'
13
- require_relative 'mixin/local'
16
+ require_relative "mixin/root"
17
+ require_relative "mixin/local"
18
+ end
14
19
  end
15
20
  end
@@ -1,39 +1,44 @@
1
- #
1
+ # frozen_string_literal: true
2
+
2
3
  # The nested structure that takes nested hashes with indifferent access
3
4
  #
4
- class Dry::Initializer::Struct
5
- extend Dry::Initializer
5
+ module Dry
6
+ module Initializer
7
+ class Struct
8
+ extend ::Dry::Initializer
6
9
 
7
- class << self
8
- undef_method :param
10
+ class << self
11
+ undef_method :param
9
12
 
10
- def new(options)
11
- super(**Hash(options).each_with_object({}) { |(k, v), h| h[k.to_sym] = v })
12
- end
13
- alias call new
14
- end
13
+ def new(options)
14
+ super(**Hash(options).each_with_object({}) { |(k, v), h| h[k.to_sym] = v })
15
+ end
16
+ alias_method :call, :new
17
+ end
15
18
 
16
- #
17
- # Represents event data as a nested hash with deeply stringified keys
18
- # @return [Hash<String, ...>]
19
- #
20
- def to_h
21
- self
22
- .class
23
- .dry_initializer
24
- .attributes(self)
25
- .each_with_object({}) { |(k, v), h| h[k.to_s] = __hashify(v) }
26
- end
19
+ #
20
+ # Represents event data as a nested hash with deeply stringified keys
21
+ # @return [Hash<String, ...>]
22
+ #
23
+ def to_h
24
+ self
25
+ .class
26
+ .dry_initializer
27
+ .attributes(self)
28
+ .each_with_object({}) { |(k, v), h| h[k.to_s] = __hashify(v) }
29
+ end
27
30
 
28
- private
31
+ private
29
32
 
30
- def __hashify(value)
31
- case value
32
- when Hash
33
- value.each_with_object({}) { |(k, v), obj| obj[k.to_s] = __hashify(v) }
34
- when Array then value.map { |v| __hashify(v) }
35
- when Dry::Initializer::Struct then value.to_h
36
- else value
33
+ def __hashify(value)
34
+ case value
35
+ when Hash
36
+ value.each_with_object({}) { |(k, v), obj| obj[k.to_s] = __hashify(v) }
37
+ when Array then value.map { |v| __hashify(v) }
38
+ when Dry::Initializer::Struct then value.to_h
39
+ else value
40
+ end
41
+ end
37
42
  end
38
43
  end
39
44
  end
@@ -1,2 +1,8 @@
1
- module Dry::Initializer::UNDEFINED
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Initializer
5
+ module UNDEFINED
6
+ end
7
+ end
2
8
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Initializer
3
- VERSION = '3.0.4'.freeze
5
+ VERSION = "3.1.0"
4
6
  end
5
7
  end
@@ -1,4 +1,6 @@
1
- require 'set'
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
2
4
 
3
5
  # Namespace for gems in a dry-rb community
4
6
  module Dry
@@ -6,13 +8,14 @@ module Dry
6
8
  # DSL for declaring params and options of class initializers
7
9
  #
8
10
  module Initializer
9
- require_relative 'initializer/undefined'
10
- require_relative 'initializer/dsl'
11
- require_relative 'initializer/definition'
12
- require_relative 'initializer/builders'
13
- require_relative 'initializer/config'
14
- require_relative 'initializer/mixin'
15
- require_relative 'initializer/dispatchers'
11
+ require_relative "initializer/undefined"
12
+ require_relative "initializer/dsl"
13
+ require_relative "initializer/definition"
14
+ require_relative "initializer/builders"
15
+ require_relative "initializer/config"
16
+ require_relative "initializer/mixin"
17
+ require_relative "initializer/dispatchers"
18
+ require_relative "initializer/errors"
16
19
 
17
20
  # Adds methods [.[]] and [.define]
18
21
  extend DSL
@@ -56,6 +59,6 @@ module Dry
56
59
  dry_initializer.children << config
57
60
  end
58
61
 
59
- require_relative 'initializer/struct'
62
+ require_relative "initializer/struct"
60
63
  end
61
64
  end
@@ -1 +1,3 @@
1
- require_relative 'dry/initializer'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "dry/initializer"
@@ -1,36 +1,38 @@
1
+ # frozen_string_literal: true
2
+
1
3
  namespace :benchmark do
2
- desc 'Runs benchmarks for plain params'
4
+ desc "Runs benchmarks for plain params"
3
5
  task :plain_params do
4
- system 'ruby benchmarks/plain_params.rb'
6
+ system "ruby benchmarks/plain_params.rb"
5
7
  end
6
8
 
7
- desc 'Runs benchmarks for plain options'
9
+ desc "Runs benchmarks for plain options"
8
10
  task :plain_options do
9
- system 'ruby benchmarks/plain_options.rb'
11
+ system "ruby benchmarks/plain_options.rb"
10
12
  end
11
13
 
12
- desc 'Runs benchmarks for value coercion'
14
+ desc "Runs benchmarks for value coercion"
13
15
  task :with_coercion do
14
- system 'ruby benchmarks/with_coercion.rb'
16
+ system "ruby benchmarks/with_coercion.rb"
15
17
  end
16
18
 
17
- desc 'Runs benchmarks with defaults'
19
+ desc "Runs benchmarks with defaults"
18
20
  task :with_defaults do
19
- system 'ruby benchmarks/with_defaults.rb'
21
+ system "ruby benchmarks/with_defaults.rb"
20
22
  end
21
23
 
22
- desc 'Runs benchmarks with defaults and coercion'
24
+ desc "Runs benchmarks with defaults and coercion"
23
25
  task :with_defaults_and_coercion do
24
- system 'ruby benchmarks/with_defaults_and_coercion.rb'
26
+ system "ruby benchmarks/with_defaults_and_coercion.rb"
25
27
  end
26
28
 
27
- desc 'Runs benchmarks for several defaults'
29
+ desc "Runs benchmarks for several defaults"
28
30
  task :compare_several_defaults do
29
- system 'ruby benchmarks/with_several_defaults.rb'
31
+ system "ruby benchmarks/with_several_defaults.rb"
30
32
  end
31
33
  end
32
34
 
33
- desc 'Runs all benchmarks'
35
+ desc "Runs all benchmarks"
34
36
  task benchmark: %i[
35
37
  benchmark:plain_params
36
38
  benchmark:plain_options
@@ -1,32 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop: disable Lint/ConstantDefinitionInBlock
1
4
  namespace :profile do
2
5
  def profile(name, execution, &definition)
3
- require 'dry-initializer'
4
- require 'ruby-prof'
5
- require 'fileutils'
6
+ require "dry-initializer"
7
+ require "ruby-prof"
8
+ require "fileutils"
6
9
 
7
10
  definition.call
8
11
  result = RubyProf.profile do
9
12
  1_000.times { execution.call }
10
13
  end
11
14
 
12
- FileUtils.mkdir_p './tmp'
15
+ FileUtils.mkdir_p "./tmp"
13
16
 
14
17
  FileUtils.touch "./tmp/#{name}.dot"
15
- File.open("./tmp/#{name}.dot", 'w+') do |output|
18
+ File.open("./tmp/#{name}.dot", "w+") do |output|
16
19
  RubyProf::DotPrinter.new(result).print(output, min_percent: 0)
17
20
  end
18
21
 
19
22
  FileUtils.touch "./tmp/#{name}.html"
20
- File.open("./tmp/#{name}.html", 'w+') do |output|
23
+ File.open("./tmp/#{name}.html", "w+") do |output|
21
24
  RubyProf::CallStackPrinter.new(result).print(output, min_percent: 0)
22
25
  end
23
26
 
24
27
  system "dot -Tpng ./tmp/#{name}.dot > ./tmp/#{name}.png"
25
28
  end
26
29
 
27
- desc 'Profiles initialization with required param and option'
30
+ desc "Profiles initialization with required param and option"
28
31
  task :required do
29
- profile('required', -> { User.new :Andy, email: 'andy@example.com' }) do
32
+ profile("required", -> { User.new :Andy, email: "andy@example.com" }) do
30
33
  class User
31
34
  extend Dry::Initializer
32
35
  param :name
@@ -35,20 +38,20 @@ namespace :profile do
35
38
  end
36
39
  end
37
40
 
38
- desc 'Profiles initialization with default param and option'
41
+ desc "Profiles initialization with default param and option"
39
42
  task :defaults do
40
- profile('defaults', -> { User.new }) do
43
+ profile("defaults", -> { User.new }) do
41
44
  class User
42
45
  extend Dry::Initializer
43
46
  param :name, default: -> { :Andy }
44
- option :email, default: -> { 'andy@example.com' }
47
+ option :email, default: -> { "andy@example.com" }
45
48
  end
46
49
  end
47
50
  end
48
51
 
49
- desc 'Profiles initialization with coerced param and option'
52
+ desc "Profiles initialization with coerced param and option"
50
53
  task :coercion do
51
- profile('coercion', -> { User.new :Andy, email: :"andy@example.com" }) do
54
+ profile("coercion", -> { User.new :Andy, email: :"andy@example.com" }) do
52
55
  class User
53
56
  extend Dry::Initializer
54
57
  param :name, proc(&:to_s)
@@ -57,9 +60,9 @@ namespace :profile do
57
60
  end
58
61
  end
59
62
 
60
- desc 'Profiles initialization with coerced defaults of param and option'
63
+ desc "Profiles initialization with coerced defaults of param and option"
61
64
  task :default_coercion do
62
- profile('default_coercion', -> { User.new }) do
65
+ profile("default_coercion", -> { User.new }) do
63
66
  class User
64
67
  extend Dry::Initializer
65
68
  param :name, proc(&:to_s), default: -> { :Andy }
@@ -69,10 +72,11 @@ namespace :profile do
69
72
  end
70
73
  end
71
74
 
72
- desc 'Makes all profiling at once'
75
+ desc "Makes all profiling at once"
73
76
  task profile: %i[
74
77
  profile:required
75
78
  profile:defaults
76
79
  profile:coercion
77
80
  profile:default_coercion
78
81
  ]
82
+ # rubocop: enable Lint/ConstantDefinitionInBlock
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-initializer
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.4
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Kochnev (marshall-lee)
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-09-29 00:00:00.000000000 Z
12
+ date: 2022-01-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -71,6 +71,7 @@ files:
71
71
  - lib/dry/initializer/dispatchers/unwrap_type.rb
72
72
  - lib/dry/initializer/dispatchers/wrap_type.rb
73
73
  - lib/dry/initializer/dsl.rb
74
+ - lib/dry/initializer/errors.rb
74
75
  - lib/dry/initializer/mixin.rb
75
76
  - lib/dry/initializer/mixin/local.rb
76
77
  - lib/dry/initializer/mixin/root.rb
@@ -95,14 +96,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
95
96
  requirements:
96
97
  - - ">="
97
98
  - !ruby/object:Gem::Version
98
- version: 2.4.0
99
+ version: 2.7.0
99
100
  required_rubygems_version: !ruby/object:Gem::Requirement
100
101
  requirements:
101
102
  - - ">="
102
103
  - !ruby/object:Gem::Version
103
104
  version: '0'
104
105
  requirements: []
105
- rubygems_version: 3.0.3
106
+ rubygems_version: 3.1.6
106
107
  signing_key:
107
108
  specification_version: 4
108
109
  summary: DSL for declaring params and options of the initializer