dry-configurable 0.11.6 → 1.0.1
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/CHANGELOG.md +187 -2
- data/LICENSE +1 -1
- data/README.md +5 -4
- data/dry-configurable.gemspec +16 -16
- data/lib/dry/configurable/class_methods.rb +40 -31
- data/lib/dry/configurable/compiler.rb +5 -12
- data/lib/dry/configurable/config.rb +133 -47
- data/lib/dry/configurable/constants.rb +0 -2
- data/lib/dry/configurable/dsl.rb +35 -23
- data/lib/dry/configurable/errors.rb +3 -2
- data/lib/dry/configurable/extension.rb +48 -0
- data/lib/dry/configurable/instance_methods.rb +20 -12
- data/lib/dry/configurable/methods.rb +3 -7
- data/lib/dry/configurable/setting.rb +44 -91
- data/lib/dry/configurable/settings.rb +30 -37
- data/lib/dry/configurable/test_interface.rb +1 -1
- data/lib/dry/configurable/version.rb +1 -1
- data/lib/dry/configurable.rb +27 -19
- data/lib/dry-configurable.rb +1 -1
- metadata +15 -29
- data/lib/dry/configurable/dsl/args.rb +0 -58
@@ -1,11 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
|
5
|
-
require 'dry/equalizer'
|
6
|
-
|
7
|
-
require 'dry/configurable/constants'
|
8
|
-
require 'dry/configurable/errors'
|
3
|
+
require "set"
|
9
4
|
|
10
5
|
module Dry
|
11
6
|
module Configurable
|
@@ -19,12 +14,29 @@ module Dry
|
|
19
14
|
attr_reader :_settings
|
20
15
|
|
21
16
|
# @api private
|
22
|
-
attr_reader :
|
17
|
+
attr_reader :_values
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
attr_reader :_configured
|
21
|
+
protected :_configured
|
23
22
|
|
24
23
|
# @api private
|
25
|
-
def initialize(settings)
|
26
|
-
@_settings = settings
|
27
|
-
@
|
24
|
+
def initialize(settings, values: {})
|
25
|
+
@_settings = settings
|
26
|
+
@_values = values
|
27
|
+
@_configured = Set.new
|
28
|
+
end
|
29
|
+
|
30
|
+
# @api private
|
31
|
+
private def initialize_copy(source)
|
32
|
+
super
|
33
|
+
@_values = source.__send__(:dup_values)
|
34
|
+
@_configured = source._configured.dup
|
35
|
+
end
|
36
|
+
|
37
|
+
# @api private
|
38
|
+
def dup_for_settings(settings)
|
39
|
+
dup.tap { |config| config.instance_variable_set(:@_settings, settings) }
|
28
40
|
end
|
29
41
|
|
30
42
|
# Get config value by a key
|
@@ -34,9 +46,19 @@ module Dry
|
|
34
46
|
# @return Config value
|
35
47
|
def [](name)
|
36
48
|
name = name.to_sym
|
37
|
-
raise ArgumentError, "+#{name}+ is not a setting name" unless _settings.key?(name)
|
38
49
|
|
39
|
-
_settings[name]
|
50
|
+
unless (setting = _settings[name])
|
51
|
+
raise ArgumentError, "+#{name}+ is not a setting name"
|
52
|
+
end
|
53
|
+
|
54
|
+
_values.fetch(name) {
|
55
|
+
# Mutable settings may be configured after read
|
56
|
+
_configured.add(name) if setting.cloneable?
|
57
|
+
|
58
|
+
setting.to_value.tap { |value|
|
59
|
+
_values[name] = value
|
60
|
+
}
|
61
|
+
}
|
40
62
|
end
|
41
63
|
|
42
64
|
# Set config value.
|
@@ -45,21 +67,34 @@ module Dry
|
|
45
67
|
# @param [String,Symbol] name
|
46
68
|
# @param [Object] value
|
47
69
|
def []=(name, value)
|
48
|
-
|
70
|
+
raise FrozenConfigError, "Cannot modify frozen config" if frozen?
|
71
|
+
|
72
|
+
name = name.to_sym
|
73
|
+
|
74
|
+
unless (setting = _settings[name])
|
75
|
+
raise ArgumentError, "+#{name}+ is not a setting name"
|
76
|
+
end
|
77
|
+
|
78
|
+
_configured.add(name)
|
79
|
+
|
80
|
+
_values[name] = setting.constructor.(value)
|
49
81
|
end
|
50
82
|
|
51
83
|
# Update config with new values
|
52
84
|
#
|
53
|
-
# @param values [Hash] A hash with new values
|
85
|
+
# @param values [Hash, #to_hash] A hash with new values
|
54
86
|
#
|
55
87
|
# @return [Config]
|
56
88
|
#
|
57
89
|
# @api public
|
58
90
|
def update(values)
|
59
91
|
values.each do |key, value|
|
60
|
-
|
61
|
-
|
62
|
-
|
92
|
+
if self[key].is_a?(self.class)
|
93
|
+
unless value.respond_to?(:to_hash)
|
94
|
+
raise ArgumentError, "#{value.inspect} is not a valid setting value"
|
95
|
+
end
|
96
|
+
|
97
|
+
self[key].update(value.to_hash)
|
63
98
|
else
|
64
99
|
self[key] = value
|
65
100
|
end
|
@@ -67,62 +102,113 @@ module Dry
|
|
67
102
|
self
|
68
103
|
end
|
69
104
|
|
70
|
-
#
|
105
|
+
# Returns true if the value for the given key has been set on this config.
|
106
|
+
#
|
107
|
+
# For simple values, this returns true if the value has been explicitly assigned.
|
108
|
+
#
|
109
|
+
# For cloneable (mutable) values, since these are captured on read, returns true if the value
|
110
|
+
# does not compare equally to its corresdponing default value. This relies on these objects
|
111
|
+
# having functioning `#==` checks.
|
112
|
+
#
|
113
|
+
# @return [Bool]
|
114
|
+
#
|
115
|
+
# @api public
|
116
|
+
def configured?(key)
|
117
|
+
if _configured.include?(key) && _settings[key].cloneable?
|
118
|
+
return _values[key] != _settings[key].to_value
|
119
|
+
end
|
120
|
+
|
121
|
+
_configured.include?(key)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns the current config values.
|
125
|
+
#
|
126
|
+
# Nested configs remain in their {Config} instances.
|
71
127
|
#
|
72
128
|
# @return [Hash]
|
73
129
|
#
|
74
130
|
# @api public
|
75
131
|
def values
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
132
|
+
# Ensure all settings are represented in values
|
133
|
+
_settings.each { |setting| self[setting.name] unless _values.key?(setting.name) }
|
134
|
+
|
135
|
+
_values
|
80
136
|
end
|
81
|
-
alias_method :to_h, :values
|
82
|
-
alias_method :to_hash, :values
|
83
137
|
|
84
|
-
#
|
85
|
-
|
86
|
-
|
87
|
-
|
138
|
+
# Returns config values as a hash, with nested values also converted from {Config} instances
|
139
|
+
# into hashes.
|
140
|
+
#
|
141
|
+
# @return [Hash]
|
142
|
+
#
|
143
|
+
# @api public
|
144
|
+
def to_h
|
145
|
+
values.to_h { |key, value| [key, value.is_a?(self.class) ? value.to_h : value] }
|
88
146
|
end
|
89
147
|
|
90
148
|
# @api private
|
91
|
-
|
92
|
-
|
149
|
+
alias_method :_dry_equalizer_hash, :hash
|
150
|
+
|
151
|
+
# @api public
|
152
|
+
def hash
|
153
|
+
return @__hash__ if instance_variable_defined?(:@__hash__)
|
154
|
+
|
155
|
+
_dry_equalizer_hash
|
156
|
+
end
|
157
|
+
|
158
|
+
# @api public
|
159
|
+
def finalize!(freeze_values: false)
|
160
|
+
return self if frozen?
|
161
|
+
|
162
|
+
values.each_value do |value|
|
163
|
+
if value.is_a?(self.class)
|
164
|
+
value.finalize!(freeze_values: freeze_values)
|
165
|
+
elsif freeze_values
|
166
|
+
value.freeze
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Memoize the hash for the object when finalizing (regardless of whether values themselves
|
171
|
+
# are to be frozen; the intention of finalization is that no further changes should be
|
172
|
+
# made). The benefit of freezing the hash at this point is that it saves repeated expensive
|
173
|
+
# computation (through Dry::Equalizer's hash implementation) if that hash is to be used
|
174
|
+
# later in performance-sensitive situations, such as when serving as a cache key or similar.
|
175
|
+
@__hash__ = _dry_equalizer_hash
|
176
|
+
|
177
|
+
freeze
|
93
178
|
end
|
94
179
|
|
95
180
|
# @api private
|
96
|
-
def
|
97
|
-
|
181
|
+
def pristine
|
182
|
+
self.class.new(_settings)
|
98
183
|
end
|
99
184
|
|
100
185
|
private
|
101
186
|
|
102
|
-
|
103
|
-
|
104
|
-
setting = _settings[
|
187
|
+
def method_missing(name, *args)
|
188
|
+
setting_name = setting_name_from_method(name)
|
189
|
+
setting = _settings[setting_name]
|
105
190
|
|
106
191
|
super unless setting
|
107
192
|
|
108
|
-
if
|
109
|
-
|
110
|
-
|
111
|
-
_settings << setting.with(input: args[0])
|
193
|
+
if name.end_with?("=")
|
194
|
+
self[setting_name] = args[0]
|
112
195
|
else
|
113
|
-
|
196
|
+
self[setting_name]
|
114
197
|
end
|
115
198
|
end
|
116
199
|
|
117
|
-
|
118
|
-
|
119
|
-
_resolved.fetch(meth) { _resolved[meth] = meth.to_s.tr('=', '').to_sym }
|
200
|
+
def respond_to_missing?(meth, include_private = false)
|
201
|
+
_settings.key?(setting_name_from_method(meth)) || super
|
120
202
|
end
|
121
203
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
204
|
+
def setting_name_from_method(method_name)
|
205
|
+
method_name.to_s.tr("=", "").to_sym
|
206
|
+
end
|
207
|
+
|
208
|
+
def dup_values
|
209
|
+
_values.each_with_object({}) { |(key, val), dup_hsh|
|
210
|
+
dup_hsh[key] = _settings[key].cloneable? ? val.dup : val
|
211
|
+
}
|
126
212
|
end
|
127
213
|
end
|
128
214
|
end
|
data/lib/dry/configurable/dsl.rb
CHANGED
@@ -1,11 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'dry/configurable/constants'
|
4
|
-
require 'dry/configurable/setting'
|
5
|
-
require 'dry/configurable/settings'
|
6
|
-
require 'dry/configurable/compiler'
|
7
|
-
require 'dry/configurable/dsl/args'
|
8
|
-
|
9
3
|
module Dry
|
10
4
|
module Configurable
|
11
5
|
# Setting DSL used by the class API
|
@@ -14,49 +8,67 @@ module Dry
|
|
14
8
|
class DSL
|
15
9
|
VALID_NAME = /\A[a-z_]\w*\z/i.freeze
|
16
10
|
|
17
|
-
# @api private
|
18
11
|
attr_reader :compiler
|
19
12
|
|
20
|
-
# @api private
|
21
13
|
attr_reader :ast
|
22
14
|
|
23
|
-
|
24
|
-
|
15
|
+
attr_reader :options
|
16
|
+
|
17
|
+
def initialize(**options, &block)
|
25
18
|
@compiler = Compiler.new
|
26
19
|
@ast = []
|
20
|
+
@options = options
|
27
21
|
instance_exec(&block) if block
|
28
22
|
end
|
29
23
|
|
30
|
-
#
|
24
|
+
# Registers a new setting node and compile it into a setting object
|
31
25
|
#
|
32
26
|
# @see ClassMethods.setting
|
33
|
-
# @api
|
27
|
+
# @api private
|
34
28
|
# @return Setting
|
35
|
-
def setting(name,
|
29
|
+
def setting(name, **options, &block) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
|
36
30
|
unless VALID_NAME.match?(name.to_s)
|
37
31
|
raise ArgumentError, "#{name} is not a valid setting name"
|
38
32
|
end
|
39
33
|
|
40
|
-
|
41
|
-
|
42
|
-
args.ensure_valid_options
|
34
|
+
ensure_valid_options(options)
|
43
35
|
|
44
|
-
default,
|
36
|
+
options = {default: default, config_class: config_class, **options}
|
45
37
|
|
46
|
-
node = [:setting, [name.to_sym,
|
38
|
+
node = [:setting, [name.to_sym, options]]
|
47
39
|
|
48
40
|
if block
|
49
|
-
|
50
|
-
ast << [:nested, [node, DSL.new(&block).ast]]
|
51
|
-
else
|
52
|
-
ast << [:constructor, [node, block]]
|
53
|
-
end
|
41
|
+
ast << [:nested, [node, DSL.new(&block).ast]]
|
54
42
|
else
|
55
43
|
ast << node
|
56
44
|
end
|
57
45
|
|
58
46
|
compiler.visit(ast.last)
|
59
47
|
end
|
48
|
+
|
49
|
+
def config_class
|
50
|
+
options[:config_class]
|
51
|
+
end
|
52
|
+
|
53
|
+
def default
|
54
|
+
options[:default_undefined] ? Undefined : nil
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def ensure_valid_options(options)
|
60
|
+
return if options.none?
|
61
|
+
|
62
|
+
invalid_keys = options.keys - Setting::OPTIONS
|
63
|
+
|
64
|
+
raise ArgumentError, "Invalid options: #{invalid_keys.inspect}" unless invalid_keys.empty?
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns a tuple of valid and invalid options hashes derived from the options hash
|
68
|
+
# given to the setting
|
69
|
+
def valid_and_invalid_options(options)
|
70
|
+
options.partition { |k, _| Setting::OPTIONS.include?(k) }.map(&:to_h)
|
71
|
+
end
|
60
72
|
end
|
61
73
|
end
|
62
74
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module Configurable
|
5
|
+
class Extension < Module
|
6
|
+
# @api private
|
7
|
+
attr_reader :config_class
|
8
|
+
|
9
|
+
# @api private
|
10
|
+
attr_reader :default_undefined
|
11
|
+
|
12
|
+
# @api private
|
13
|
+
def initialize(config_class: Configurable::Config, default_undefined: false)
|
14
|
+
super()
|
15
|
+
@config_class = config_class
|
16
|
+
@default_undefined = default_undefined
|
17
|
+
freeze
|
18
|
+
end
|
19
|
+
|
20
|
+
# @api private
|
21
|
+
def extended(klass)
|
22
|
+
super
|
23
|
+
klass.extend(ClassMethods)
|
24
|
+
klass.instance_variable_set(:@__config_extension__, self)
|
25
|
+
end
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
def included(klass)
|
29
|
+
raise AlreadyIncludedError if klass.include?(InstanceMethods)
|
30
|
+
|
31
|
+
super
|
32
|
+
|
33
|
+
klass.class_eval do
|
34
|
+
extend(ClassMethods)
|
35
|
+
include(InstanceMethods)
|
36
|
+
prepend(Initializer)
|
37
|
+
|
38
|
+
class << self
|
39
|
+
undef :config
|
40
|
+
undef :configure
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
klass.instance_variable_set(:@__config_extension__, self)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -1,10 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "dry/configurable/config"
|
4
|
+
require "dry/configurable/methods"
|
5
5
|
|
6
6
|
module Dry
|
7
7
|
module Configurable
|
8
|
+
# Initializer method which is prepended when `Dry::Configurable`
|
9
|
+
# is included in a class
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
module Initializer
|
13
|
+
# @api private
|
14
|
+
def initialize(*)
|
15
|
+
@__config__ = self.class.__config_build__(self.class.settings)
|
16
|
+
|
17
|
+
super
|
18
|
+
end
|
19
|
+
ruby2_keywords(:initialize) if respond_to?(:ruby2_keywords, true)
|
20
|
+
end
|
21
|
+
|
8
22
|
# Instance-level API when `Dry::Configurable` is included in a class
|
9
23
|
#
|
10
24
|
# @api public
|
@@ -16,20 +30,14 @@ module Dry
|
|
16
30
|
# @return [Config]
|
17
31
|
#
|
18
32
|
# @api public
|
19
|
-
|
20
|
-
|
21
|
-
# @api private
|
22
|
-
def initialize(*)
|
23
|
-
@config = Config.new(self.class._settings.dup)
|
24
|
-
super
|
33
|
+
def config
|
34
|
+
@__config__
|
25
35
|
end
|
26
36
|
|
27
37
|
# Finalize the config and freeze the object
|
28
38
|
#
|
29
39
|
# @api public
|
30
|
-
def finalize!
|
31
|
-
return self if frozen?
|
32
|
-
|
40
|
+
def finalize!(freeze_values: false)
|
33
41
|
super
|
34
42
|
freeze
|
35
43
|
end
|
@@ -39,7 +47,7 @@ module Dry
|
|
39
47
|
# @api public
|
40
48
|
def initialize_copy(source)
|
41
49
|
super
|
42
|
-
@
|
50
|
+
@__config__ = source.config.dup
|
43
51
|
end
|
44
52
|
end
|
45
53
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'dry/configurable/errors'
|
4
|
-
|
5
3
|
module Dry
|
6
4
|
module Configurable
|
7
5
|
# Common API for both classes and instances
|
@@ -10,7 +8,7 @@ module Dry
|
|
10
8
|
module Methods
|
11
9
|
# @api public
|
12
10
|
def configure(&block)
|
13
|
-
raise
|
11
|
+
raise FrozenConfigError, "Cannot modify frozen config" if config.frozen?
|
14
12
|
|
15
13
|
yield(config) if block
|
16
14
|
self
|
@@ -21,10 +19,8 @@ module Dry
|
|
21
19
|
# @return [Dry::Configurable::Config]
|
22
20
|
#
|
23
21
|
# @api public
|
24
|
-
def finalize!
|
25
|
-
|
26
|
-
|
27
|
-
config.finalize!
|
22
|
+
def finalize!(freeze_values: false)
|
23
|
+
config.finalize!(freeze_values: freeze_values)
|
28
24
|
self
|
29
25
|
end
|
30
26
|
end
|
@@ -1,102 +1,61 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
|
5
|
-
require 'dry/equalizer'
|
6
|
-
|
7
|
-
require 'dry/configurable/constants'
|
8
|
-
require 'dry/configurable/config'
|
3
|
+
require "set"
|
9
4
|
|
10
5
|
module Dry
|
11
6
|
module Configurable
|
12
|
-
#
|
7
|
+
# A defined setting.
|
13
8
|
#
|
14
|
-
# @api
|
9
|
+
# @api public
|
15
10
|
class Setting
|
16
|
-
include Dry::Equalizer(:name, :
|
11
|
+
include Dry::Equalizer(:name, :default, :constructor, :children, :options, inspect: false)
|
17
12
|
|
18
|
-
OPTIONS = %i[
|
13
|
+
OPTIONS = %i[default reader constructor mutable cloneable settings config_class].freeze
|
19
14
|
|
20
15
|
DEFAULT_CONSTRUCTOR = -> v { v }.freeze
|
21
16
|
|
22
|
-
|
17
|
+
MUTABLE_VALUE_TYPES = [Array, Hash, Set, Config].freeze
|
23
18
|
|
24
|
-
# @api
|
19
|
+
# @api public
|
25
20
|
attr_reader :name
|
26
21
|
|
27
|
-
# @api
|
28
|
-
attr_reader :
|
22
|
+
# @api public
|
23
|
+
attr_reader :default
|
29
24
|
|
30
|
-
# @api
|
31
|
-
attr_reader :
|
25
|
+
# @api public
|
26
|
+
attr_reader :mutable
|
32
27
|
|
33
|
-
# @api
|
34
|
-
attr_reader :
|
28
|
+
# @api public
|
29
|
+
attr_reader :constructor
|
35
30
|
|
36
|
-
# @api
|
31
|
+
# @api public
|
32
|
+
attr_reader :children
|
33
|
+
|
34
|
+
# @api public
|
37
35
|
attr_reader :options
|
38
36
|
|
39
|
-
# Specialized Setting which includes nested settings
|
40
|
-
#
|
41
37
|
# @api private
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
# @api private
|
46
|
-
def pristine
|
47
|
-
with(input: input.pristine)
|
48
|
-
end
|
49
|
-
|
50
|
-
# @api private
|
51
|
-
def constructor
|
52
|
-
CONSTRUCTOR
|
53
|
-
end
|
38
|
+
def self.mutable_value?(value)
|
39
|
+
MUTABLE_VALUE_TYPES.any? { |type| value.is_a?(type) }
|
54
40
|
end
|
55
41
|
|
56
42
|
# @api private
|
57
|
-
def initialize(
|
43
|
+
def initialize(
|
44
|
+
name,
|
45
|
+
default:,
|
46
|
+
constructor: DEFAULT_CONSTRUCTOR,
|
47
|
+
children: EMPTY_ARRAY,
|
48
|
+
**options
|
49
|
+
)
|
58
50
|
@name = name
|
59
|
-
@writer_name = :"#{name}="
|
60
|
-
@input = input.equal?(Undefined) ? default : input
|
61
51
|
@default = default
|
52
|
+
@mutable = children.any? || options.fetch(:mutable) {
|
53
|
+
# Allow `cloneable` as an option alias for `mutable`
|
54
|
+
options.fetch(:cloneable) { Setting.mutable_value?(default) }
|
55
|
+
}
|
56
|
+
@constructor = constructor
|
57
|
+
@children = children
|
62
58
|
@options = options
|
63
|
-
|
64
|
-
evaluate if input_defined?
|
65
|
-
end
|
66
|
-
|
67
|
-
# @api private
|
68
|
-
def input_defined?
|
69
|
-
!input.equal?(Undefined)
|
70
|
-
end
|
71
|
-
|
72
|
-
# @api private
|
73
|
-
def value
|
74
|
-
@value ||= evaluate
|
75
|
-
end
|
76
|
-
|
77
|
-
# @api private
|
78
|
-
def evaluated?
|
79
|
-
instance_variable_defined?(:@value)
|
80
|
-
end
|
81
|
-
|
82
|
-
# @api private
|
83
|
-
def nested(settings)
|
84
|
-
Nested.new(name, input: settings, **options)
|
85
|
-
end
|
86
|
-
|
87
|
-
# @api private
|
88
|
-
def pristine
|
89
|
-
with(input: Undefined)
|
90
|
-
end
|
91
|
-
|
92
|
-
# @api private
|
93
|
-
def with(new_opts)
|
94
|
-
self.class.new(name, input: input, default: default, **options, **new_opts)
|
95
|
-
end
|
96
|
-
|
97
|
-
# @api private
|
98
|
-
def constructor
|
99
|
-
options[:constructor] || DEFAULT_CONSTRUCTOR
|
100
59
|
end
|
101
60
|
|
102
61
|
# @api private
|
@@ -104,28 +63,22 @@ module Dry
|
|
104
63
|
options[:reader].equal?(true)
|
105
64
|
end
|
106
65
|
|
107
|
-
# @api
|
108
|
-
def
|
109
|
-
|
66
|
+
# @api public
|
67
|
+
def mutable?
|
68
|
+
mutable
|
110
69
|
end
|
70
|
+
alias_method :cloneable?, :mutable?
|
111
71
|
|
112
72
|
# @api private
|
113
|
-
def
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
73
|
+
def to_value
|
74
|
+
if children.any?
|
75
|
+
(options[:config_class] || Config).new(children)
|
76
|
+
else
|
77
|
+
value = default
|
78
|
+
value = constructor.(value) unless value.eql?(Undefined)
|
118
79
|
|
119
|
-
|
120
|
-
|
121
|
-
super
|
122
|
-
@value = source.value.dup if source.input_defined? && source.clonable_value?
|
123
|
-
@options = source.options.dup
|
124
|
-
end
|
125
|
-
|
126
|
-
# @api private
|
127
|
-
def evaluate
|
128
|
-
@value = constructor[input.equal?(Undefined) ? nil : input]
|
80
|
+
mutable? ? value.dup : value
|
81
|
+
end
|
129
82
|
end
|
130
83
|
end
|
131
84
|
end
|