dry-initializer 1.4.1 → 2.0.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -8
- data/.travis.yml +0 -3
- data/CHANGELOG.md +339 -196
- data/LICENSE.txt +1 -1
- data/README.md +3 -3
- data/Rakefile +2 -47
- data/benchmarks/{several_defaults.rb → compare_several_defaults.rb} +4 -4
- data/benchmarks/{without_options.rb → plain_options.rb} +20 -9
- data/benchmarks/{params.rb → plain_params.rb} +20 -9
- data/benchmarks/{with_types.rb → with_coercion.rb} +20 -9
- data/benchmarks/with_defaults.rb +19 -8
- data/benchmarks/{with_types_and_defaults.rb → with_defaults_and_coercion.rb} +21 -10
- data/dry-initializer.gemspec +3 -3
- data/lib/dry/initializer/builders/attribute.rb +76 -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/builders.rb +7 -0
- data/lib/dry/initializer/config.rb +161 -0
- data/lib/dry/initializer/definition.rb +93 -0
- data/lib/dry/initializer/dsl.rb +43 -0
- data/lib/dry/initializer/mixin/local.rb +19 -0
- data/lib/dry/initializer/mixin/root.rb +10 -0
- data/lib/dry/initializer/mixin.rb +15 -0
- data/lib/dry/initializer.rb +45 -41
- data/lib/tasks/benchmark.rake +41 -0
- data/lib/tasks/profile.rake +78 -0
- data/spec/{options_var_spec.rb → attributes_spec.rb} +9 -9
- data/spec/custom_initializer_spec.rb +1 -1
- data/spec/default_values_spec.rb +6 -6
- data/spec/definition_spec.rb +21 -14
- data/spec/invalid_default_spec.rb +2 -2
- data/spec/missed_default_spec.rb +2 -2
- data/spec/optional_spec.rb +2 -2
- data/spec/options_tolerance_spec.rb +1 -1
- data/spec/public_attributes_utility_spec.rb +22 -0
- data/spec/reader_spec.rb +11 -11
- data/spec/repetitive_definitions_spec.rb +5 -5
- data/spec/several_assignments_spec.rb +1 -1
- data/spec/spec_helper.rb +5 -0
- data/spec/subclassing_spec.rb +7 -3
- data/spec/type_argument_spec.rb +1 -1
- data/spec/type_constraint_spec.rb +2 -2
- data/spec/value_coercion_via_dry_types_spec.rb +1 -1
- metadata +27 -27
- data/benchmarks/options.rb +0 -54
- data/benchmarks/params_vs_options.rb +0 -35
- data/benchmarks/profiler.rb +0 -28
- data/lib/dry/initializer/attribute.rb +0 -123
- data/lib/dry/initializer/builder.rb +0 -127
- data/lib/dry/initializer/class_dsl.rb +0 -37
- data/lib/dry/initializer/exceptions/default_value_error.rb +0 -8
- data/lib/dry/initializer/exceptions/params_order_error.rb +0 -8
- data/lib/dry/initializer/exceptions/type_constraint_error.rb +0 -7
- data/lib/dry/initializer/instance_dsl.rb +0 -15
- data/lib/dry/initializer/option.rb +0 -61
- data/lib/dry/initializer/param.rb +0 -52
- data/spec/gem_enhancement_spec.rb +0 -18
@@ -0,0 +1,161 @@
|
|
1
|
+
module Dry::Initializer
|
2
|
+
#
|
3
|
+
# Gem-related configuration of some class
|
4
|
+
#
|
5
|
+
class Config
|
6
|
+
# @!attribute [r] null
|
7
|
+
# @return [Dry::Initializer::UNDEFINED, nil] value of unassigned variable
|
8
|
+
|
9
|
+
# @!attribute [r] extended_class
|
10
|
+
# @return [Class] the class whose config collected by current object
|
11
|
+
|
12
|
+
# @!attribute [r] parent
|
13
|
+
# @return [Dry::Initializer::Config] parent configuration
|
14
|
+
|
15
|
+
# @!attribute [r] definitions
|
16
|
+
# @return [Hash<Symbol, Dry::Initializer::Definition>]
|
17
|
+
# hash of attribute definitions with their source names
|
18
|
+
|
19
|
+
attr_reader :null, :extended_class, :parent, :definitions
|
20
|
+
|
21
|
+
# @!attribute [r] mixin
|
22
|
+
# @return [Module] reference to the module to be included into class
|
23
|
+
def mixin
|
24
|
+
@mixin ||= Module.new.tap do |mod|
|
25
|
+
__dry_initializer__ = self
|
26
|
+
mod.extend(Mixin::Local)
|
27
|
+
mod.send :define_method, :__dry_initializer_config__ do
|
28
|
+
__dry_initializer__
|
29
|
+
end
|
30
|
+
mod.send :private, :__dry_initializer_config__
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# List of configs of all subclasses of the [#extended_class]
|
35
|
+
# @return [Array<Dry::Initializer::Config>]
|
36
|
+
def children
|
37
|
+
@children ||= Set.new
|
38
|
+
end
|
39
|
+
|
40
|
+
# List of definitions for initializer params
|
41
|
+
# @return [Array<Dry::Initializer::Definition>]
|
42
|
+
def params
|
43
|
+
definitions.values.reject(&:option)
|
44
|
+
end
|
45
|
+
|
46
|
+
# List of definitions for initializer options
|
47
|
+
# @return [Array<Dry::Initializer::Definition>]
|
48
|
+
def options
|
49
|
+
definitions.values.select(&:option)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Adds or redefines a parameter
|
53
|
+
# @param [Symbol] name
|
54
|
+
# @param [#call, nil] coercer (nil)
|
55
|
+
# @option opts [#call] :type
|
56
|
+
# @option opts [Proc] :default
|
57
|
+
# @option opts [Boolean] :optional
|
58
|
+
# @option opts [Symbol] :as
|
59
|
+
# @option opts [true, false, :protected, :public, :private] :reader
|
60
|
+
# @return [self] itself
|
61
|
+
def param(name, type = nil, **opts)
|
62
|
+
add_definition(false, name, type, opts)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Adds or redefines an option of [#dry_initializer]
|
66
|
+
#
|
67
|
+
# @param (see #param)
|
68
|
+
# @option (see #param)
|
69
|
+
# @return (see #param)
|
70
|
+
#
|
71
|
+
def option(name, type = nil, **opts)
|
72
|
+
add_definition(true, name, type, opts)
|
73
|
+
end
|
74
|
+
|
75
|
+
# The hash of public attributes for an instance of the [#extended_class]
|
76
|
+
# @param [Dry::Initializer::Instance] instance
|
77
|
+
# @return [Hash<Symbol, Object>]
|
78
|
+
def public_attributes(instance)
|
79
|
+
definitions.values.each_with_object({}) do |item, obj|
|
80
|
+
key = item.target
|
81
|
+
next unless instance.respond_to? key
|
82
|
+
val = instance.send(key)
|
83
|
+
obj[key] = val unless val == null
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# The hash of assigned attributes for an instance of the [#extended_class]
|
88
|
+
# @param [Dry::Initializer::Instance] instance
|
89
|
+
# @return [Hash<Symbol, Object>]
|
90
|
+
def attributes(instance)
|
91
|
+
definitions.values.each_with_object({}) do |item, obj|
|
92
|
+
key = item.target
|
93
|
+
val = instance.send(:instance_variable_get, item.ivar)
|
94
|
+
obj[key] = val unless val == null
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Code of the `#__initialize__` method
|
99
|
+
# @return [String]
|
100
|
+
def code
|
101
|
+
Builders::Initializer[self]
|
102
|
+
end
|
103
|
+
|
104
|
+
# Finalizes config
|
105
|
+
# @return [self]
|
106
|
+
def finalize
|
107
|
+
@definitions = final_definitions
|
108
|
+
check_order_of_params
|
109
|
+
mixin.class_eval(code)
|
110
|
+
children.each(&:finalize)
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def initialize(extended_class = nil, null: UNDEFINED)
|
117
|
+
@extended_class = extended_class.tap { |klass| klass&.include mixin }
|
118
|
+
sklass = extended_class&.superclass
|
119
|
+
@parent = sklass.dry_initializer if sklass.is_a? Dry::Initializer
|
120
|
+
@null = null || parent&.null
|
121
|
+
@definitions = {}
|
122
|
+
finalize
|
123
|
+
end
|
124
|
+
|
125
|
+
def add_definition(option, name, type, opts)
|
126
|
+
definition = Definition.new(option, null, name, type, opts)
|
127
|
+
definitions[definition.source] = definition
|
128
|
+
finalize
|
129
|
+
|
130
|
+
mixin.class_eval definition.code
|
131
|
+
end
|
132
|
+
|
133
|
+
def final_definitions
|
134
|
+
parent_definitions = Hash(parent&.definitions&.dup)
|
135
|
+
definitions.each_with_object(parent_definitions) do |(key, val), obj|
|
136
|
+
obj[key] = check_type(obj[key], val)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def check_type(previous, current)
|
141
|
+
return current unless previous
|
142
|
+
return current if previous.option == current.option
|
143
|
+
raise SyntaxError,
|
144
|
+
"cannot reload #{previous} of #{extended_class.superclass}" \
|
145
|
+
" by #{current} of its subclass #{extended_class}"
|
146
|
+
end
|
147
|
+
|
148
|
+
def check_order_of_params
|
149
|
+
params.inject(nil) do |optional, current|
|
150
|
+
if current.optional
|
151
|
+
current
|
152
|
+
elsif optional
|
153
|
+
raise SyntaxError, "#{extended_class}: required #{current}" \
|
154
|
+
" goes after optional #{optional}"
|
155
|
+
else
|
156
|
+
optional
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Dry::Initializer
|
2
|
+
#
|
3
|
+
# @private
|
4
|
+
# @abstract
|
5
|
+
#
|
6
|
+
# Base class for parameter or option definitions
|
7
|
+
# Defines methods to add corresponding reader to the class,
|
8
|
+
# and build value of instance attribute.
|
9
|
+
#
|
10
|
+
class Definition
|
11
|
+
attr_reader :option, :null, :source, :target, :ivar,
|
12
|
+
:type, :optional, :default, :reader
|
13
|
+
|
14
|
+
def name
|
15
|
+
@name ||= (option ? "option" : "parameter") << " '#{source}'"
|
16
|
+
end
|
17
|
+
alias to_s name
|
18
|
+
alias to_str name
|
19
|
+
alias inspect name
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
other.instance_of?(self.class) && (other.source == source)
|
23
|
+
end
|
24
|
+
|
25
|
+
def code
|
26
|
+
Builders::Reader[self]
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def initialize(option, null, source, coercer = nil, **options)
|
32
|
+
@option = !!option
|
33
|
+
@null = null
|
34
|
+
@source = source.to_sym
|
35
|
+
@target = check_target options.fetch(:as, source).to_sym
|
36
|
+
@ivar = :"@#{target}"
|
37
|
+
@type = check_type(coercer || options[:type])
|
38
|
+
@reader = prepare_reader options.fetch(:reader, true)
|
39
|
+
@default = check_default options[:default]
|
40
|
+
@optional = options.fetch(:optional, @default)
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_source(value)
|
44
|
+
if RESERVED.include? value
|
45
|
+
raise ArgumentError, "Name #{value} is reserved by dry-initializer gem"
|
46
|
+
end
|
47
|
+
|
48
|
+
unless option || value[ATTRIBUTE]
|
49
|
+
raise ArgumentError, "Invalid parameter name :'#{value}'"
|
50
|
+
end
|
51
|
+
|
52
|
+
value
|
53
|
+
end
|
54
|
+
|
55
|
+
def check_target(value)
|
56
|
+
return value if value[ATTRIBUTE]
|
57
|
+
raise ArgumentError, "Invalid variable name :'#{value}'"
|
58
|
+
end
|
59
|
+
|
60
|
+
def check_type(value)
|
61
|
+
return if value.nil?
|
62
|
+
arity = value.respond_to?(:call) ? value.method(:call).arity : 0
|
63
|
+
return value unless arity.zero? || arity > 1
|
64
|
+
raise TypeError,
|
65
|
+
"type of #{inspect} should respond to #call with one argument"
|
66
|
+
end
|
67
|
+
|
68
|
+
def check_default(value)
|
69
|
+
return if value.nil?
|
70
|
+
return value if value.is_a?(Proc) && value.arity < 1
|
71
|
+
raise TypeError,
|
72
|
+
"default value of #{inspect} should be a proc without params"
|
73
|
+
end
|
74
|
+
|
75
|
+
def prepare_reader(value)
|
76
|
+
case value.to_s
|
77
|
+
when "", "false" then false
|
78
|
+
when "private" then :private
|
79
|
+
when "protected" then :protected
|
80
|
+
else :public
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
ATTRIBUTE = /\A\w+\z/
|
85
|
+
RESERVED = %i[
|
86
|
+
__dry_initializer_options__
|
87
|
+
__dry_initializer_config__
|
88
|
+
__dry_initializer_value__
|
89
|
+
__dry_initializer_definition__
|
90
|
+
__dry_initializer_initializer__
|
91
|
+
].freeze
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,43 @@
|
|
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
|
7
|
+
|
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)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
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
|
30
|
+
|
31
|
+
private
|
32
|
+
|
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
|
38
|
+
|
39
|
+
def self.extended(mod)
|
40
|
+
mod.instance_variable_set :@null, UNDEFINED
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Dry::Initializer::Mixin
|
2
|
+
# @private
|
3
|
+
module Local
|
4
|
+
attr_reader :klass
|
5
|
+
|
6
|
+
def inspect
|
7
|
+
"Dry::Initializer::Mixin::Local[#{klass}]"
|
8
|
+
end
|
9
|
+
alias to_s inspect
|
10
|
+
alias to_str inspect
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def included(klass)
|
15
|
+
@klass = klass
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
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
|
11
|
+
|
12
|
+
require_relative "mixin/root"
|
13
|
+
require_relative "mixin/local"
|
14
|
+
end
|
15
|
+
end
|
data/lib/dry/initializer.rb
CHANGED
@@ -1,55 +1,59 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
# Namespace for gems in a dry-rb community
|
1
4
|
module Dry
|
5
|
+
#
|
6
|
+
# DSL for declaring params and options of class initializers
|
7
|
+
#
|
2
8
|
module Initializer
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
require_relative "initializer/
|
8
|
-
require_relative "initializer/
|
9
|
-
require_relative "initializer/
|
10
|
-
require_relative "initializer/
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
obj.define_singleton_method(:inspect) { "Dry::Initializer::UNDEFINED" }
|
20
|
-
end.freeze
|
21
|
-
|
22
|
-
extend Dry::Initializer::ClassDSL
|
23
|
-
|
24
|
-
def param(*args)
|
25
|
-
__initializer_builder__.param(*args).call(__initializer_mixin__)
|
9
|
+
# Singleton for unassigned values
|
10
|
+
UNDEFINED = Object.new.freeze
|
11
|
+
|
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
|
+
|
18
|
+
# Adds methods [.[]] and [.define]
|
19
|
+
extend DSL
|
20
|
+
|
21
|
+
# Gem-related configuration
|
22
|
+
# @return [Dry::Initializer::Config]
|
23
|
+
def dry_initializer
|
24
|
+
@dry_initializer ||= Config.new(self)
|
26
25
|
end
|
27
26
|
|
28
|
-
|
29
|
-
|
27
|
+
# Adds or redefines a parameter of [#dry_initializer]
|
28
|
+
# @param [Symbol] name
|
29
|
+
# @param [#call, nil] coercer (nil)
|
30
|
+
# @option opts [#call] :type
|
31
|
+
# @option opts [Proc] :default
|
32
|
+
# @option opts [Boolean] :optional
|
33
|
+
# @option opts [Symbol] :as
|
34
|
+
# @option opts [true, false, :protected, :public, :private] :reader
|
35
|
+
# @return [self] itself
|
36
|
+
def param(name, type = nil, **opts)
|
37
|
+
dry_initializer.param(name, type, opts)
|
38
|
+
self
|
30
39
|
end
|
31
40
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
41
|
+
# Adds or redefines an option of [#dry_initializer]
|
42
|
+
# @param (see #param)
|
43
|
+
# @option (see #param)
|
44
|
+
# @return (see #param)
|
45
|
+
def option(name, type = nil, **opts)
|
46
|
+
dry_initializer.option(name, type, opts)
|
47
|
+
self
|
36
48
|
end
|
37
49
|
|
38
|
-
|
39
|
-
@__initializer_builder__ ||= Dry::Initializer::Builder.new(settings)
|
40
|
-
end
|
50
|
+
private
|
41
51
|
|
42
52
|
def inherited(klass)
|
43
|
-
builder = @__initializer_builder__.dup
|
44
|
-
mixin = Module.new
|
45
|
-
|
46
|
-
klass.instance_variable_set :@__initializer_builder__, builder
|
47
|
-
klass.instance_variable_set :@__initializer_mixin__, mixin
|
48
|
-
|
49
|
-
builder.call(mixin)
|
50
|
-
klass.include mixin
|
51
|
-
|
52
53
|
super
|
54
|
+
config = Config.new(klass, null: dry_initializer.null)
|
55
|
+
klass.send(:instance_variable_set, :@dry_initializer, config)
|
56
|
+
dry_initializer.children << config
|
53
57
|
end
|
54
58
|
end
|
55
59
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
namespace :benchmark do
|
2
|
+
desc "Runs benchmarks for plain params"
|
3
|
+
task :plain_params do
|
4
|
+
system "ruby benchmarks/plain_params.rb"
|
5
|
+
end
|
6
|
+
|
7
|
+
desc "Runs benchmarks for plain options"
|
8
|
+
task :plain_options do
|
9
|
+
system "ruby benchmarks/plain_options.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Runs benchmarks for value coercion"
|
13
|
+
task :with_coercion do
|
14
|
+
system "ruby benchmarks/with_coercion.rb"
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Runs benchmarks with defaults"
|
18
|
+
task :with_defaults do
|
19
|
+
system "ruby benchmarks/with_defaults.rb"
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Runs benchmarks with defaults and coercion"
|
23
|
+
task :with_defaults_and_coercion do
|
24
|
+
system "ruby benchmarks/with_defaults_and_coercion.rb"
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Runs benchmarks for several defaults"
|
28
|
+
task :compare_several_defaults do
|
29
|
+
system "ruby benchmarks/with_several_defaults.rb"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Runs all benchmarks"
|
34
|
+
task benchmark: %i[
|
35
|
+
benchmark:plain_params
|
36
|
+
benchmark:plain_options
|
37
|
+
benchmark:with_coercion
|
38
|
+
benchmark:with_defaults
|
39
|
+
benchmark:with_defaults_and_coercion
|
40
|
+
benchmark:compare_several_defaults
|
41
|
+
]
|
@@ -0,0 +1,78 @@
|
|
1
|
+
namespace :profile do
|
2
|
+
def profile(name, execution, &definition)
|
3
|
+
require "dry-initializer"
|
4
|
+
require "ruby-prof"
|
5
|
+
require "fileutils"
|
6
|
+
|
7
|
+
definition.call
|
8
|
+
result = RubyProf.profile do
|
9
|
+
1_000.times { execution.call }
|
10
|
+
end
|
11
|
+
|
12
|
+
FileUtils.mkdir_p "./tmp"
|
13
|
+
|
14
|
+
FileUtils.touch "./tmp/#{name}.dot"
|
15
|
+
File.open("./tmp/#{name}.dot", "w+") do |output|
|
16
|
+
RubyProf::DotPrinter.new(result).print(output, min_percent: 0)
|
17
|
+
end
|
18
|
+
|
19
|
+
FileUtils.touch "./tmp/#{name}.html"
|
20
|
+
File.open("./tmp/#{name}.html", "w+") do |output|
|
21
|
+
RubyProf::CallStackPrinter.new(result).print(output, min_percent: 0)
|
22
|
+
end
|
23
|
+
|
24
|
+
system "dot -Tpng ./tmp/#{name}.dot > ./tmp/#{name}.png"
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Profiles initialization with required param and option"
|
28
|
+
task :required do
|
29
|
+
profile("required", -> { User.new :Andy, email: "andy@example.com" }) do
|
30
|
+
class User
|
31
|
+
extend Dry::Initializer
|
32
|
+
param :name
|
33
|
+
option :email
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "Profiles initialization with default param and option"
|
39
|
+
task :defaults do
|
40
|
+
profile("defaults", -> { User.new }) do
|
41
|
+
class User
|
42
|
+
extend Dry::Initializer
|
43
|
+
param :name, default: -> { :Andy }
|
44
|
+
option :email, default: -> { "andy@example.com" }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "Profiles initialization with coerced param and option"
|
50
|
+
task :coercion do
|
51
|
+
profile("coercion", -> { User.new :Andy, email: :"andy@example.com" }) do
|
52
|
+
class User
|
53
|
+
extend Dry::Initializer
|
54
|
+
param :name, proc(&:to_s)
|
55
|
+
option :email, proc(&:to_s)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "Profiles initialization with coerced defaults of param and option"
|
61
|
+
task :default_coercion do
|
62
|
+
profile("default_coercion", -> { User.new }) do
|
63
|
+
class User
|
64
|
+
extend Dry::Initializer
|
65
|
+
param :name, proc(&:to_s), default: -> { :Andy }
|
66
|
+
option :email, proc(&:to_s), default: -> { :"andy@example.com" }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
desc "Makes all profiling at once"
|
73
|
+
task profile: %i[
|
74
|
+
profile:required
|
75
|
+
profile:defaults
|
76
|
+
profile:coercion
|
77
|
+
profile:default_coercion
|
78
|
+
]
|
@@ -1,4 +1,6 @@
|
|
1
|
-
describe "
|
1
|
+
describe Dry::Initializer, "dry_initializer.attributes" do
|
2
|
+
subject { instance.class.dry_initializer.attributes(instance) }
|
3
|
+
|
2
4
|
context "when class has params" do
|
3
5
|
before do
|
4
6
|
class Test::Foo
|
@@ -9,11 +11,10 @@ describe "@__options__" do
|
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
12
|
-
|
13
|
-
subject = Test::Foo.new(:FOO)
|
14
|
+
let(:instance) { Test::Foo.new(:FOO) }
|
14
15
|
|
15
|
-
|
16
|
-
|
16
|
+
it "collects coerced params with default values" do
|
17
|
+
expect(subject).to eq({ foo: "FOO", bar: 1 })
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
@@ -28,11 +29,10 @@ describe "@__options__" do
|
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
31
|
-
|
32
|
-
subject = Test::Foo.new(foo: :FOO, qux: :QUX)
|
32
|
+
let(:instance) { Test::Foo.new(foo: :FOO, qux: :QUX) }
|
33
33
|
|
34
|
-
|
35
|
-
|
34
|
+
it "collects coerced and renamed options with default values" do
|
35
|
+
expect(subject).to eq({ foo: :FOO, bar: 1, quxx: "QUX" })
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
data/spec/default_values_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
describe "default values" do
|
2
2
|
before do
|
3
3
|
class Test::Foo
|
4
|
-
extend Dry::Initializer
|
4
|
+
extend Dry::Initializer
|
5
5
|
|
6
6
|
param :foo, default: proc { :FOO }
|
7
7
|
param :bar, default: proc { :BAR }
|
@@ -52,14 +52,14 @@ describe "default values" do
|
|
52
52
|
describe "when the last param has a default and there are no options" do
|
53
53
|
before do
|
54
54
|
class Test::Bar
|
55
|
-
extend Dry::Initializer
|
55
|
+
extend Dry::Initializer
|
56
56
|
|
57
|
-
param
|
58
|
-
param
|
57
|
+
param :foo
|
58
|
+
param :bar, default: proc { {} }
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
it "
|
62
|
+
it "instantiates arguments" do
|
63
63
|
subject = Test::Bar.new(1, 2)
|
64
64
|
|
65
65
|
expect(subject.foo).to eql 1
|
@@ -73,7 +73,7 @@ describe "default values" do
|
|
73
73
|
expect(subject.bar).to eql({})
|
74
74
|
end
|
75
75
|
|
76
|
-
it "
|
76
|
+
it "instantiates arguments also if the last is an hash" do
|
77
77
|
subject = Test::Bar.new(1, { baz: 2, qux: 3 })
|
78
78
|
|
79
79
|
expect(subject.foo).to eql 1
|