dry-configurable 0.11.6 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|