1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# Helper class that enables easy use of default values with minimal fuss. When a `ConfigHash`
# is created, it can be passed a Hash of default values to use when a requested value is not
# found.
# @!attribute [r]
# Default values for this hash.
# @return [Hash]
attr_reader :defaults
# These methods have been made private since their semantics conflict with the purpose of
# ConfigHash.
private(:default, :default=)
private(:default_proc=) if RUBY_VERSION >=
o = super()
o.instance_variable_set(:@defaults, @defaults.clone)
o
end
# Initialize a new `ConfigHash` instance.
#
# @param [ConfigHash,Hash,nil] defaults Hash containing default values to use when a given
# setting cannot be found, or `nil`.
#
# @param [Proc,#clone] default_default A proc to call or value to use as a
# default in the new ConfigHash when no default value is available in
# `defaults`.
_defaults = {}
default_proc = block if block_given?
if _options.nil? and _values.has_key?(:defaults)
_options = _values
_values = {}
end
unless _options.nil?
_defaults = _options[:defaults]
end
@defaults = ( _defaults.nil? ? {} : _defaults )
@defaults.freeze
update(_values)
end
_defaults = @defaults[key]
_klass = ConfigHash
if _defaults.kind_of?(ConfigHash)
# Allow for subclasses of ConfigHash in the defaults.
_klass = _defaults.class
_defaults = _defaults.defaults if _defaults.empty?
end
_klass.new(value, defaults: _defaults)
end
# Setter method override. This method allows setting values within a (sub) hash with
# defaults, without losing access to the other default values.
#
# @param [Object] k Search key.
# @param [Object] v Value.
old_value = begin
fetch(key)
rescue
nil
end
new_value =
if value.kind_of?(Hash)
if has_key?(key) and old_value.kind_of?(Hash)
old_value.update(value)
else
store(key, subhash_with_defaults(key, value))
end
else
store(key, value)
end
new_value
end
# Getter method override. Attempts to use default values when a missing key is encountered,
# and does some magic to enable assignment to sublevels of a (previously) entirely-default
# ConfigHash tree.
#
# Assignment to sublevels is made possible by creation of an empty ConfigHash for each
# non-existent key whose default value is a Hash instance. (If instead the default value was
# returned, any assignment would change the _default_ value!)
#
# @param [Object] s Search key.
#
# @return [Object] The value associated with `s`, or the default value if there is no such
# association in this hash. If no default values have been set, returns `nil`.
if explicit_key?(key)
super(key)
else
if (result = @defaults[key]).kind_of?(Hash)
# We need to create the empty storage in case the client assigns an entry of the
# returned hash -- otherwise, we'd get a modified _default_!
store(key, subhash_with_defaults(key, result))
else
result
end
end
end
if explicit_key?(key)
super
elsif @defaults.key?(key)
@defaults[key]
elsif ! rest.empty?
rest.shift
else
raise KeyError.new( % key.inspect)
end
end
# Recursively update from a source hash. This allows e.g. missing values in the
# `:directories' sub-hash of a Config.
#
# @param [Hash] from Source hash (to update with).
#
# @return [Hash] @c tgt
from.each_pair do
old_value = begin
fetch(key)
rescue
nil
end
if value.kind_of?(Hash)
if has_key?(key) and old_value.kind_of?(ConfigHash)
old_value.update(value)
else
store(key, subhash_with_defaults(key, value))
end
else
store(key, value)
end
end
self
end
# # Proxy to allow dotted-object-style access to configuration values.
self[sym]
end
self.key?(sym) && args.empty?
end
alias_method(:explicit_key?, :key?)
explicit_key?(name) or @defaults.key?(name)
end
o = {}
all_keys.each {
v = self[k]
o[k] = v.kind_of?(Hash) ? v.to_hash : v
}
o
end
self === other or self.to_hash == other.to_hash
end
other.kind_of?(ConfigHash) and @defaults === other.defaults
end
end
(@defaults.respond_to?(:all_keys) ? @defaults.all_keys : @defaults.keys) | keys
end
end