configurations 2.0.0.pre → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|