configurations 2.0.0.pre → 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 +7 -7
- data/.travis.yml +4 -2
- data/License.txt +1 -1
- data/README.md +23 -7
- data/Rakefile +1 -1
- data/configurations.gemspec +7 -3
- data/lib/configurations.rb +8 -4
- data/lib/configurations/arbitrary.rb +124 -0
- data/lib/configurations/blank_object.rb +60 -0
- data/lib/configurations/configurable.rb +153 -42
- data/lib/configurations/configuration.rb +96 -213
- data/lib/configurations/strict.rb +170 -0
- data/test/configurations/arbitrary/test.rb +23 -0
- data/test/configurations/arbitrary/test_defaults.rb +5 -0
- data/test/configurations/arbitrary/test_hash_methods.rb +11 -0
- data/test/configurations/arbitrary/test_methods.rb +5 -0
- data/test/configurations/arbitrary/test_not_configured.rb +5 -0
- data/test/configurations/arbitrary/test_not_configured_default.rb +5 -0
- data/test/configurations/shared/defaults.rb +43 -0
- data/test/configurations/shared/hash_methods.rb +40 -0
- data/test/configurations/shared/kernel_methods.rb +9 -0
- data/test/configurations/shared/methods.rb +31 -0
- data/test/configurations/shared/not_configured_callbacks.rb +42 -0
- data/test/configurations/shared/not_configured_default_callback.rb +42 -0
- data/test/configurations/shared/properties.rb +46 -0
- data/test/configurations/shared/properties_outside_block.rb +40 -0
- data/test/configurations/shared/strict_hash_methods.rb +43 -0
- data/test/configurations/strict/test.rb +20 -0
- data/test/configurations/strict/test_defaults.rb +6 -0
- data/test/configurations/strict/test_hash_methods.rb +11 -0
- data/test/configurations/strict/test_methods.rb +6 -0
- data/test/configurations/strict/test_not_configured.rb +6 -0
- data/test/configurations/strict/test_not_configured_default.rb +6 -0
- data/test/configurations/strict_types/test.rb +18 -0
- data/test/configurations/strict_types/test_defaults.rb +6 -0
- data/test/configurations/strict_types/test_hash_methods.rb +11 -0
- data/test/configurations/strict_types/test_methods.rb +6 -0
- data/test/configurations/strict_types/test_not_configured.rb +6 -0
- data/test/configurations/strict_types/test_not_configured_default.rb +6 -0
- data/test/configurations/strict_types_with_blocks/test.rb +22 -0
- data/test/configurations/strict_types_with_blocks/test_defaults.rb +6 -0
- data/test/configurations/strict_types_with_blocks/test_hash_methods.rb +14 -0
- data/test/configurations/strict_types_with_blocks/test_methods.rb +6 -0
- data/test/configurations/strict_types_with_blocks/test_not_configured.rb +6 -0
- data/test/configurations/strict_types_with_blocks/test_not_configured_default.rb +6 -0
- data/test/configurations/strict_with_blocks/test.rb +16 -0
- data/test/configurations/strict_with_blocks/test_defaults.rb +6 -0
- data/test/configurations/strict_with_blocks/test_hash_methods.rb +14 -0
- data/test/configurations/strict_with_blocks/test_methods.rb +6 -0
- data/test/configurations/strict_with_blocks/test_not_configured.rb +6 -0
- data/test/configurations/strict_with_blocks/test_not_configured_default.rb +6 -0
- data/test/support/setup.rb +173 -0
- data/test/support/shared.rb +11 -0
- data/test/test_helper.rb +6 -5
- metadata +148 -65
- data/test/configurations/test_configurable_with_blocks_configuration.rb +0 -54
- data/test/configurations/test_configuration.rb +0 -182
- data/test/configurations/test_configuration_methods.rb +0 -85
- data/test/configurations/test_stricter_configuration.rb +0 -173
- data/test/support/testmodules.rb +0 -10
@@ -1,83 +1,31 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
1
|
module Configurations
|
3
|
-
# Configuration is a blank object in order to allow configuration
|
2
|
+
# Configuration is a blank object in order to allow configuration
|
3
|
+
# of various properties including keywords
|
4
4
|
#
|
5
|
-
class Configuration <
|
6
|
-
# 1.9 does not allow for method rebinding in another scope
|
7
|
-
#
|
8
|
-
if ::RUBY_VERSION < '2.0.0'
|
9
|
-
include ::Kernel
|
10
|
-
undef :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :initialize_dup,
|
11
|
-
:initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?,
|
12
|
-
:to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods,
|
13
|
-
:instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?,
|
14
|
-
:instance_of?, :kind_of?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend,
|
15
|
-
:display, :method, :public_method, :define_singleton_method, :to_enum, :enum_for
|
16
|
-
else
|
17
|
-
# @macro [attach] install_kernel_method
|
18
|
-
# @method $1
|
19
|
-
#
|
20
|
-
def self.install_kernel_method(method)
|
21
|
-
kernel_method = ::Kernel.instance_method(method)
|
22
|
-
|
23
|
-
define_method method do |*args, &block|
|
24
|
-
kernel_method.bind(self).call(*args, &block)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# Installs the type asserting is_a? method from Kernel
|
29
|
-
#
|
30
|
-
install_kernel_method(:is_a?)
|
31
|
-
|
32
|
-
# Installs the inspect method from Kernel
|
33
|
-
#
|
34
|
-
install_kernel_method(:inspect)
|
35
|
-
|
36
|
-
end
|
37
|
-
|
5
|
+
class Configuration < BlankObject
|
38
6
|
# Initialize a new configuration
|
39
7
|
# @param [Hash] options The options to initialize a configuration with
|
40
|
-
# @option options [Hash] configurable a hash of configurable properties and their asserted types if given
|
41
8
|
# @option options [Hash] methods a hash of method names pointing to procs
|
42
|
-
# @option options [Proc] not_configured a proc to evaluate for
|
43
|
-
#
|
44
|
-
# @yield [HostModule::Configuration] a configuration
|
45
|
-
# @return [HostModule::Configuration] a configuration
|
46
|
-
#
|
47
|
-
def initialize(options={}, &block)
|
48
|
-
@_configurable = options[:configurable]
|
49
|
-
@_methods = options[:methods]
|
50
|
-
@_not_configured = options[:not_configured]
|
9
|
+
# @option options [Proc] not_configured a proc to evaluate for
|
10
|
+
# not_configured properties
|
51
11
|
|
52
|
-
|
53
|
-
@
|
12
|
+
def initialize(options = {}, &block)
|
13
|
+
@__methods__ = options.fetch(:methods) { ::Hash.new }
|
14
|
+
@__not_configured__ = options.fetch(:not_configured) { ::Hash.new }
|
54
15
|
|
55
|
-
|
16
|
+
@data = __configuration_hash__
|
56
17
|
|
57
|
-
|
18
|
+
__instance_eval__(&options[:defaults]) if options[:defaults]
|
19
|
+
__instance_eval__(&block) if block
|
58
20
|
|
59
|
-
|
60
|
-
instance_eval(&block)
|
61
|
-
self._writeable = false
|
62
|
-
end
|
21
|
+
__install_configuration_methods__
|
63
22
|
end
|
64
23
|
|
65
|
-
# Method missing gives access
|
24
|
+
# Method missing gives access to Kernel methods
|
66
25
|
#
|
67
26
|
def method_missing(method, *args, &block)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
if _respond_to_writer?(method)
|
72
|
-
_assign!(property, value)
|
73
|
-
elsif _respond_to_property_for_write?(method)
|
74
|
-
@configuration[method]
|
75
|
-
elsif _respond_to_property_for_read?(method)
|
76
|
-
@configuration.fetch(method, &_not_configured_callback)
|
77
|
-
elsif _has_configuration_method?(method)
|
78
|
-
_exec_configuration_method!(method, *args, &block)
|
79
|
-
elsif _can_delegate_to_kernel?(method)
|
80
|
-
::Kernel.send(method, *args, &block)
|
27
|
+
if __can_delegate_to_kernel?(method)
|
28
|
+
::Kernel.__send__(method, *args, &block)
|
81
29
|
else
|
82
30
|
super
|
83
31
|
end
|
@@ -85,27 +33,17 @@ module Configurations
|
|
85
33
|
|
86
34
|
# Respond to missing according to the method_missing implementation
|
87
35
|
#
|
88
|
-
def
|
89
|
-
|
36
|
+
def respond_to_missing?(method, include_private = false)
|
37
|
+
__can_delegate_to_kernel?(method) || super
|
90
38
|
end
|
91
39
|
|
92
|
-
#
|
93
|
-
#
|
94
|
-
# @param [Boolean] data true if the configuration should be writeable, false otherwise
|
95
|
-
#
|
96
|
-
def _writeable=(data)
|
97
|
-
@_writeable = data
|
98
|
-
@configuration.each do |k,v|
|
99
|
-
v._writeable = data if v.is_a?(Configuration)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
# A convenience accessor to get a hash representation of the current state of the configuration
|
40
|
+
# A convenience accessor to get a hash representation of the
|
41
|
+
# current state of the configuration
|
104
42
|
# @return [Hash] the configuration in hash form
|
105
43
|
#
|
106
44
|
def to_h
|
107
|
-
@
|
108
|
-
h[k] = v.is_a?(
|
45
|
+
@data.reduce({}) do |h, (k, v)|
|
46
|
+
h[k] = v.is_a?(__class__) ? v.to_h : v
|
109
47
|
|
110
48
|
h
|
111
49
|
end
|
@@ -114,199 +52,144 @@ module Configurations
|
|
114
52
|
# A convenience accessor to instantiate a configuration from a hash
|
115
53
|
# @param [Hash] h the hash to read into the configuration
|
116
54
|
# @return [Configuration] the configuration with values assigned
|
117
|
-
# @note can only be accessed during writeable state (in configure block). Unassignable values are ignored
|
118
55
|
#
|
119
56
|
def from_h(h)
|
120
|
-
raise ArgumentError, 'can not dynamically assign values from a hash' unless @_writeable
|
121
|
-
|
122
57
|
h.each do |property, value|
|
123
|
-
if value.is_a?(::Hash) &&
|
124
|
-
@
|
125
|
-
elsif
|
126
|
-
|
58
|
+
if value.is_a?(::Hash) && __nested?(property)
|
59
|
+
@data[property].from_h(value)
|
60
|
+
elsif __configurable?(property)
|
61
|
+
__assign!(property, value)
|
127
62
|
end
|
128
63
|
end
|
64
|
+
|
65
|
+
self
|
129
66
|
end
|
130
67
|
|
131
68
|
# @param [Symbol] property The property to test for configurability
|
132
69
|
# @return [Boolean] whether the given property is configurable
|
133
70
|
#
|
134
|
-
def
|
135
|
-
|
71
|
+
def __configurable?(_property)
|
72
|
+
fail NotImplementedError, 'must be implemented in subclass'
|
136
73
|
end
|
137
74
|
|
138
|
-
|
139
|
-
# @return [Boolean] whether
|
75
|
+
# @param [Symbol] property The property to test for
|
76
|
+
# @return [Boolean] whether the given property has been configured
|
140
77
|
#
|
141
|
-
def
|
142
|
-
@
|
78
|
+
def __configured?(property)
|
79
|
+
@data.key?(property)
|
143
80
|
end
|
144
81
|
|
145
|
-
# @return [
|
146
|
-
def
|
147
|
-
@
|
82
|
+
# @return [Boolean] whether this configuration is empty
|
83
|
+
def __empty?
|
84
|
+
@data.empty?
|
148
85
|
end
|
149
86
|
|
150
|
-
|
87
|
+
protected
|
151
88
|
|
152
|
-
#
|
153
|
-
#
|
89
|
+
# Installs the given configuration methods for this configuration
|
90
|
+
# as singleton methods
|
154
91
|
#
|
155
|
-
def
|
156
|
-
@
|
157
|
-
|
158
|
-
|
159
|
-
# @return [Hash] A configuration hash instantiating subhashes if the key is configurable
|
160
|
-
#
|
161
|
-
def _configuration_hash
|
162
|
-
::Hash.new do |h, k|
|
163
|
-
h[k] = Configuration.new(configurable: @_configurable, not_configured: @_not_configured) if _configurable?(k)
|
92
|
+
def __install_configuration_methods__
|
93
|
+
@__methods__.each do |meth, block|
|
94
|
+
__define_singleton_method__(meth, &block) if block.is_a?(::Proc)
|
164
95
|
end
|
165
96
|
end
|
166
97
|
|
167
|
-
#
|
98
|
+
# Instantiates an options hash for a nested property
|
99
|
+
# @param [Symbol] property the nested property to instantiate the hash for
|
100
|
+
# @return [Hash] a hash to be used for configuration initialization
|
168
101
|
#
|
169
|
-
def
|
170
|
-
|
171
|
-
|
102
|
+
def __options_hash_for__(property)
|
103
|
+
hash = {}
|
104
|
+
hash[:not_configured] = __not_configured_hash_for__(property) if @__not_configured__[property]
|
105
|
+
hash[:methods] = @__methods__[property] if @__methods__.key?(property)
|
172
106
|
|
173
|
-
|
174
|
-
#
|
175
|
-
def _not_configured_callback?
|
176
|
-
!!@_not_configured
|
107
|
+
hash
|
177
108
|
end
|
178
109
|
|
179
|
-
#
|
110
|
+
# @param [Symbol] property the property to return the callback for
|
111
|
+
# @return [Proc] a block to use when property is called before
|
112
|
+
# configuration, defaults to a block yielding nil
|
180
113
|
#
|
181
|
-
def
|
182
|
-
|
114
|
+
def __not_configured_callback_for__(property)
|
115
|
+
not_configured = @__not_configured__[property] || ::Proc.new { nil }
|
183
116
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
@configuration[property] = Configuration.new(configurable: _configurable_hash(property, nested, assertion), not_configured: _not_configured_callback)
|
188
|
-
end
|
189
|
-
end
|
117
|
+
unless not_configured.is_a?(::Proc)
|
118
|
+
blocks = __collect_blocks__(not_configured)
|
119
|
+
not_configured = ->(p) { blocks.each { |b| b.call(p) } }
|
190
120
|
end
|
191
|
-
end
|
192
121
|
|
193
|
-
|
194
|
-
#
|
195
|
-
def _exec_configuration_method!(method, *args, &block)
|
196
|
-
args << block #rbx complains if block is separate in the arguments list
|
197
|
-
instance_exec(*args, &@_methods[method])
|
122
|
+
not_configured
|
198
123
|
end
|
199
124
|
|
200
|
-
# @param [Symbol
|
201
|
-
#
|
202
|
-
# @
|
203
|
-
#
|
125
|
+
# @param [Symbol] property the property to return the not
|
126
|
+
# configured hash option for
|
127
|
+
# @return [Hash] a hash which can be used as a not configured
|
128
|
+
# hash in options
|
204
129
|
#
|
205
|
-
def
|
206
|
-
|
207
|
-
hash
|
208
|
-
hash = @configuration[property]._configurable.merge(hash) if @configuration.has_key?(property)
|
130
|
+
def __not_configured_hash_for__(property)
|
131
|
+
hash = ::Hash.new(&@__not_configured__.default_proc)
|
132
|
+
hash.merge! @__not_configured__[property] if @__not_configured__[property].is_a?(::Hash)
|
209
133
|
|
210
134
|
hash
|
211
135
|
end
|
212
136
|
|
213
|
-
#
|
214
|
-
#
|
215
|
-
# @param [Any] value the given value
|
216
|
-
#
|
217
|
-
def _assign!(property, value)
|
218
|
-
_assert_type!(property, value)
|
219
|
-
v = _evaluate_block!(property, value)
|
220
|
-
value = v unless v.nil?
|
221
|
-
@configuration[property] = value
|
222
|
-
end
|
223
|
-
|
224
|
-
# Type assertion for configurable properties
|
225
|
-
# @param [Symbol] property the property to type test
|
226
|
-
# @param [Any] value the given value
|
227
|
-
# @raise [ConfigurationError] if the given value has the wrong type
|
137
|
+
# @return [Hash] A configuration hash instantiating subhashes
|
138
|
+
# if the key is configurable
|
228
139
|
#
|
229
|
-
def
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
::Kernel.raise ConfigurationError, "Expected #{property} to be configured with #{assertion}, but got #{value.class.inspect}", caller unless value.is_a?(assertion)
|
140
|
+
def __configuration_hash__
|
141
|
+
::Hash.new do |h, k|
|
142
|
+
h[k] = __class__.new(__options_hash_for__(k)) if __configurable?(k)
|
143
|
+
end
|
234
144
|
end
|
235
145
|
|
236
|
-
#
|
146
|
+
# Assigns a value after running the assertions
|
237
147
|
# @param [Symbol] property the property to type test
|
238
148
|
# @param [Any] value the given value
|
239
149
|
#
|
240
|
-
def
|
241
|
-
|
242
|
-
|
243
|
-
evaluation = @_configurable[property][:block]
|
244
|
-
evaluation.call(value)
|
245
|
-
end
|
246
|
-
|
247
|
-
# @param [Symbol] property The property to test for
|
248
|
-
# @param [Symbol] assertion_type The evaluation type type to test for
|
249
|
-
# @return [Boolean] whether the given property is assertable
|
250
|
-
#
|
251
|
-
def _evaluable?(property, evaluation)
|
252
|
-
@_configurable and @_configurable.has_key?(property) and @_configurable[property].is_a?(::Hash) and @_configurable[property].has_key?(evaluation)
|
253
|
-
end
|
254
|
-
|
255
|
-
# @param [Symbol] property The property to test for
|
256
|
-
# @return [Boolean] whether this property is nested
|
257
|
-
#
|
258
|
-
def _nested?(property)
|
259
|
-
_arbitrarily_configurable? or @configuration.has_key?(property) and @configuration[property].is_a?(Configuration)
|
150
|
+
def __assign!(property, value)
|
151
|
+
@data[property] = value
|
260
152
|
end
|
261
153
|
|
262
154
|
# @param [Symbol] method the method to test for
|
263
155
|
# @return [Boolean] whether the given method is a writer
|
264
156
|
#
|
265
|
-
def
|
157
|
+
def __is_writer?(method)
|
266
158
|
method.to_s.end_with?('=')
|
267
159
|
end
|
268
160
|
|
269
|
-
|
270
|
-
|
271
|
-
#
|
272
|
-
def _has_configuration_method?(method)
|
273
|
-
@_methods and @_methods.has_key?(method)
|
161
|
+
def __nested?(property)
|
162
|
+
@data[property].is_a?(__class__)
|
274
163
|
end
|
275
164
|
|
276
165
|
# @param [Symbol] method the method to test for
|
277
|
-
# @return [Boolean] whether the configuration
|
166
|
+
# @return [Boolean] whether the configuration can delegate
|
167
|
+
# the given method to Kernel
|
278
168
|
#
|
279
|
-
def
|
280
|
-
_is_writer?(method) and @_writeable and _configurable?(_property_from_writer(method))
|
281
|
-
end
|
282
|
-
|
283
|
-
# @param [Symbol] method the method to test for
|
284
|
-
# @return [Boolean] whether the configuration responds to the given property as a method during readable state
|
285
|
-
#
|
286
|
-
def _respond_to_property_for_read?(method)
|
287
|
-
not _is_writer?(method) and _configured?(method)
|
288
|
-
end
|
289
|
-
|
290
|
-
# @param [Symbol] method the method to test for
|
291
|
-
# @return [Boolean] whether the configuration responds to the given property as a method during writeable state
|
292
|
-
#
|
293
|
-
def _respond_to_property_for_write?(method)
|
294
|
-
not _is_writer?(method) and @_writeable
|
295
|
-
end
|
296
|
-
|
297
|
-
# @param [Symbol] method the method to test for
|
298
|
-
# @return [Boolean] whether the configuration can delegate the given method to Kernel
|
299
|
-
#
|
300
|
-
def _can_delegate_to_kernel?(method)
|
169
|
+
def __can_delegate_to_kernel?(method)
|
301
170
|
::Kernel.respond_to?(method, true)
|
302
171
|
end
|
303
172
|
|
304
173
|
# @param [Symbol] method the writer method to turn into a property
|
305
174
|
# @return [Symbol] the property derived from the writer method
|
306
175
|
#
|
307
|
-
def
|
176
|
+
def __property_from_writer__(method)
|
308
177
|
method.to_s[0..-2].to_sym
|
309
178
|
end
|
310
179
|
|
180
|
+
# @param [Hash] a hash to collect blocks from
|
181
|
+
# @return [Proc] a proc to call all the procs
|
182
|
+
#
|
183
|
+
def __collect_blocks__(hash)
|
184
|
+
hash.reduce([]) do |array, (k, v)|
|
185
|
+
array << if v.is_a?(::Hash)
|
186
|
+
__collect_blocks__(v)
|
187
|
+
else
|
188
|
+
v || k
|
189
|
+
end
|
190
|
+
|
191
|
+
array
|
192
|
+
end.flatten
|
193
|
+
end
|
311
194
|
end
|
312
195
|
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Configurations
|
3
|
+
# StrictConfiguration is a blank object with setters and getters defined
|
4
|
+
# according to the configurable settings given
|
5
|
+
#
|
6
|
+
class StrictConfiguration < Configuration
|
7
|
+
# Initialize a new configuration
|
8
|
+
# @param [Hash] options The options to initialize a configuration with
|
9
|
+
# @option options [Hash] configurable a hash of configurable properties
|
10
|
+
# and their asserted types if given
|
11
|
+
# @option options [Hash] methods a hash of method names pointing to procs
|
12
|
+
# @option options [Proc] not_configured a proc to evaluate for
|
13
|
+
# not_configured properties
|
14
|
+
# @param [Proc] block a block to configure this configuration with
|
15
|
+
# @yield [HostModule::Configuration] a configuration
|
16
|
+
# @return [HostModule::Configuration] a configuration
|
17
|
+
#
|
18
|
+
def initialize(options = {}, &block)
|
19
|
+
@__configurable__ = options.fetch(:configurable)
|
20
|
+
__evaluate_configurable!
|
21
|
+
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param [Symbol] property The property to test for configurability
|
26
|
+
# @return [Boolean] whether the given property is configurable
|
27
|
+
#
|
28
|
+
def __configurable?(property)
|
29
|
+
@__configurable__.key?(property) ||
|
30
|
+
@__nested_configurables__.key?(property)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Evaluates configurable properties and passes eventual hashes
|
36
|
+
# down to subconfigurations
|
37
|
+
#
|
38
|
+
def __evaluate_configurable!
|
39
|
+
@__configurable__.each do |k, assertion|
|
40
|
+
if k.is_a?(::Hash)
|
41
|
+
k.each do |property, nested|
|
42
|
+
__add_to_nested_configurables!(property, nested, assertion)
|
43
|
+
__install_nested_getter__(property)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
__install_property__(k)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def __add_to_nested_configurables!(property, nested, assertion)
|
52
|
+
@__nested_configurables__ ||= ::Hash.new { |h, k| h[k] = {} }
|
53
|
+
@__nested_configurables__[property].merge!(
|
54
|
+
__configurable_hash__(property, nested, assertion)
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
def __options_hash_for__(property)
|
59
|
+
super(property).merge(configurable: @__nested_configurables__[property])
|
60
|
+
end
|
61
|
+
|
62
|
+
# @param [Symbol, Hash, Array] property configurable properties,
|
63
|
+
# either single or nested
|
64
|
+
# @param [Symbol, Hash, Array] value configurable properties,
|
65
|
+
# either single or nested
|
66
|
+
# @param [Hash] assertion assertion if any
|
67
|
+
# @return a hash with configurable values pointing to their types
|
68
|
+
#
|
69
|
+
def __configurable_hash__(_property, value, assertion)
|
70
|
+
value = [value] unless value.is_a?(::Array)
|
71
|
+
hash = ::Hash[value.zip([assertion].flatten * value.size)]
|
72
|
+
|
73
|
+
hash
|
74
|
+
end
|
75
|
+
|
76
|
+
# @param [Symbol] property the property to test for
|
77
|
+
# @return [Boolean] whether this property is pointing to a
|
78
|
+
# nested configuration
|
79
|
+
#
|
80
|
+
def __nested?(property)
|
81
|
+
respond_to?(property) && __send__(property).is_a?(__class__)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Installs a property setter and getter as singleton methods
|
85
|
+
# @param [Symbol] property the property to install
|
86
|
+
#
|
87
|
+
def __install_property__(property)
|
88
|
+
__install_setter__(property)
|
89
|
+
__install_getter__(property)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Installs a property setter as a singleton method
|
93
|
+
# @param [Symbol] property the property to install the setter for
|
94
|
+
#
|
95
|
+
def __install_setter__(property)
|
96
|
+
__define_singleton_method__ :"#{property}=" do |value|
|
97
|
+
__assign!(property, value)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Installs a property getter as a singleton method
|
102
|
+
# @param [Symbol] property the property to install the getter for
|
103
|
+
#
|
104
|
+
def __install_getter__(property)
|
105
|
+
__define_singleton_method__ property do
|
106
|
+
@data.fetch(property, &__not_configured_callback_for__(property))
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Installs a property getter for a nested configuration as a
|
111
|
+
# singleton method
|
112
|
+
# @param [Symbol] property the property to install the getter for
|
113
|
+
#
|
114
|
+
def __install_nested_getter__(property)
|
115
|
+
__define_singleton_method__ property do
|
116
|
+
@data[property]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Assigns a value after running the assertions
|
121
|
+
# @param [Symbol] property the property to type test
|
122
|
+
# @param [Any] value the given value
|
123
|
+
#
|
124
|
+
def __assign!(property, value)
|
125
|
+
__assert_type!(property, value)
|
126
|
+
v = __evaluate_block!(property, value)
|
127
|
+
value = v unless v.nil?
|
128
|
+
super(property, value)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Type assertion for configurable properties
|
132
|
+
# @param [Symbol] property the property to type test
|
133
|
+
# @param [Any] value the given value
|
134
|
+
# @raise [ConfigurationError] if the given value has the wrong type
|
135
|
+
#
|
136
|
+
def __assert_type!(property, value)
|
137
|
+
return unless __evaluable?(property, :type)
|
138
|
+
|
139
|
+
assertion = @__configurable__[property][:type]
|
140
|
+
return if value.is_a?(assertion)
|
141
|
+
|
142
|
+
::Kernel.fail(
|
143
|
+
ConfigurationError,
|
144
|
+
"#{property} must be configured with #{assertion} (got #{value.class})",
|
145
|
+
caller
|
146
|
+
)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Block assertion for configurable properties
|
150
|
+
# @param [Symbol] property the property to type test
|
151
|
+
# @param [Any] value the given value
|
152
|
+
#
|
153
|
+
def __evaluate_block!(property, value)
|
154
|
+
return value unless __evaluable?(property, :block)
|
155
|
+
|
156
|
+
evaluation = @__configurable__[property][:block]
|
157
|
+
evaluation.call(value)
|
158
|
+
end
|
159
|
+
|
160
|
+
# @param [Symbol] property The property to test for
|
161
|
+
# @param [Symbol] assertion_type The evaluation type type to test for
|
162
|
+
# @return [Boolean] whether the given property is assertable
|
163
|
+
#
|
164
|
+
def __evaluable?(property, evaluation)
|
165
|
+
__configurable?(property) &&
|
166
|
+
@__configurable__[property].is_a?(::Hash) &&
|
167
|
+
@__configurable__[property].key?(evaluation)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|