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.
Files changed (60) hide show
  1. checksums.yaml +7 -7
  2. data/.travis.yml +4 -2
  3. data/License.txt +1 -1
  4. data/README.md +23 -7
  5. data/Rakefile +1 -1
  6. data/configurations.gemspec +7 -3
  7. data/lib/configurations.rb +8 -4
  8. data/lib/configurations/arbitrary.rb +124 -0
  9. data/lib/configurations/blank_object.rb +60 -0
  10. data/lib/configurations/configurable.rb +153 -42
  11. data/lib/configurations/configuration.rb +96 -213
  12. data/lib/configurations/strict.rb +170 -0
  13. data/test/configurations/arbitrary/test.rb +23 -0
  14. data/test/configurations/arbitrary/test_defaults.rb +5 -0
  15. data/test/configurations/arbitrary/test_hash_methods.rb +11 -0
  16. data/test/configurations/arbitrary/test_methods.rb +5 -0
  17. data/test/configurations/arbitrary/test_not_configured.rb +5 -0
  18. data/test/configurations/arbitrary/test_not_configured_default.rb +5 -0
  19. data/test/configurations/shared/defaults.rb +43 -0
  20. data/test/configurations/shared/hash_methods.rb +40 -0
  21. data/test/configurations/shared/kernel_methods.rb +9 -0
  22. data/test/configurations/shared/methods.rb +31 -0
  23. data/test/configurations/shared/not_configured_callbacks.rb +42 -0
  24. data/test/configurations/shared/not_configured_default_callback.rb +42 -0
  25. data/test/configurations/shared/properties.rb +46 -0
  26. data/test/configurations/shared/properties_outside_block.rb +40 -0
  27. data/test/configurations/shared/strict_hash_methods.rb +43 -0
  28. data/test/configurations/strict/test.rb +20 -0
  29. data/test/configurations/strict/test_defaults.rb +6 -0
  30. data/test/configurations/strict/test_hash_methods.rb +11 -0
  31. data/test/configurations/strict/test_methods.rb +6 -0
  32. data/test/configurations/strict/test_not_configured.rb +6 -0
  33. data/test/configurations/strict/test_not_configured_default.rb +6 -0
  34. data/test/configurations/strict_types/test.rb +18 -0
  35. data/test/configurations/strict_types/test_defaults.rb +6 -0
  36. data/test/configurations/strict_types/test_hash_methods.rb +11 -0
  37. data/test/configurations/strict_types/test_methods.rb +6 -0
  38. data/test/configurations/strict_types/test_not_configured.rb +6 -0
  39. data/test/configurations/strict_types/test_not_configured_default.rb +6 -0
  40. data/test/configurations/strict_types_with_blocks/test.rb +22 -0
  41. data/test/configurations/strict_types_with_blocks/test_defaults.rb +6 -0
  42. data/test/configurations/strict_types_with_blocks/test_hash_methods.rb +14 -0
  43. data/test/configurations/strict_types_with_blocks/test_methods.rb +6 -0
  44. data/test/configurations/strict_types_with_blocks/test_not_configured.rb +6 -0
  45. data/test/configurations/strict_types_with_blocks/test_not_configured_default.rb +6 -0
  46. data/test/configurations/strict_with_blocks/test.rb +16 -0
  47. data/test/configurations/strict_with_blocks/test_defaults.rb +6 -0
  48. data/test/configurations/strict_with_blocks/test_hash_methods.rb +14 -0
  49. data/test/configurations/strict_with_blocks/test_methods.rb +6 -0
  50. data/test/configurations/strict_with_blocks/test_not_configured.rb +6 -0
  51. data/test/configurations/strict_with_blocks/test_not_configured_default.rb +6 -0
  52. data/test/support/setup.rb +173 -0
  53. data/test/support/shared.rb +11 -0
  54. data/test/test_helper.rb +6 -5
  55. metadata +148 -65
  56. data/test/configurations/test_configurable_with_blocks_configuration.rb +0 -54
  57. data/test/configurations/test_configuration.rb +0 -182
  58. data/test/configurations/test_configuration_methods.rb +0 -85
  59. data/test/configurations/test_stricter_configuration.rb +0 -173
  60. 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 of various properties including keywords
2
+ # Configuration is a blank object in order to allow configuration
3
+ # of various properties including keywords
4
4
  #
5
- class Configuration < BasicObject
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 not_configured properties
43
- # @param [Proc] block a block to configure this configuration with
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
- @_writeable = true
53
- @configuration = _configuration_hash
12
+ def initialize(options = {}, &block)
13
+ @__methods__ = options.fetch(:methods) { ::Hash.new }
14
+ @__not_configured__ = options.fetch(:not_configured) { ::Hash.new }
54
15
 
55
- _evaluate_configurable!
16
+ @data = __configuration_hash__
56
17
 
57
- instance_eval(&options[:defaults]) if options[:defaults]
18
+ __instance_eval__(&options[:defaults]) if options[:defaults]
19
+ __instance_eval__(&block) if block
58
20
 
59
- if block
60
- instance_eval(&block)
61
- self._writeable = false
62
- end
21
+ __install_configuration_methods__
63
22
  end
64
23
 
65
- # Method missing gives access for reading and writing to the underlying configuration hash via dot notation
24
+ # Method missing gives access to Kernel methods
66
25
  #
67
26
  def method_missing(method, *args, &block)
68
- property = method.to_s[0..-2].to_sym
69
- value = args.first
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 respond_to?(method, include_private = false)
89
- _respond_to_writer?(method) or _respond_to_property_for_write?(method) or _respond_to_property_for_read?(method) or _can_delegate_to_kernel?(method) or !!super
36
+ def respond_to_missing?(method, include_private = false)
37
+ __can_delegate_to_kernel?(method) || super
90
38
  end
91
39
 
92
- # Set the configuration to writeable or read only. Access to writer methods is only allowed within the
93
- # configure block, this method is used to invoke writability for subconfigurations.
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
- @configuration.inject({}) do |h, (k,v)|
108
- h[k] = v.is_a?(Configuration) ? v.to_h : v
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) && _nested?(property)
124
- @configuration[property].from_h(value)
125
- elsif _configurable?(property)
126
- _assign!(property, value)
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 _configurable?(property)
135
- _arbitrarily_configurable? or @_configurable.has_key?(property)
71
+ def __configurable?(_property)
72
+ fail NotImplementedError, 'must be implemented in subclass'
136
73
  end
137
74
 
138
-
139
- # @return [Boolean] whether this configuration is arbitrarily configurable
75
+ # @param [Symbol] property The property to test for
76
+ # @return [Boolean] whether the given property has been configured
140
77
  #
141
- def _arbitrarily_configurable?
142
- @_configurable.nil? or @_configurable.empty?
78
+ def __configured?(property)
79
+ @data.key?(property)
143
80
  end
144
81
 
145
- # @return [Hash] the configurations configurable hash
146
- def _configurable
147
- @_configurable
82
+ # @return [Boolean] whether this configuration is empty
83
+ def __empty?
84
+ @data.empty?
148
85
  end
149
86
 
150
- private
87
+ protected
151
88
 
152
- # @param [Symbol] property The property to test for
153
- # @return [Boolean] whether the given property has been configured
89
+ # Installs the given configuration methods for this configuration
90
+ # as singleton methods
154
91
  #
155
- def _configured?(property)
156
- @configuration.has_key?(property) or _not_configured_callback? or _arbitrarily_configurable?
157
- end
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
- # @return [Proc] The not_configured callback
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 _not_configured_callback
170
- @_not_configured || ::Proc.new { |key| nil }
171
- end
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
- # @return [Boolean] whether the not configured callback has been configured
174
- #
175
- def _not_configured_callback?
176
- !!@_not_configured
107
+ hash
177
108
  end
178
109
 
179
- # Evaluates configurable properties and passes eventual hashes down to subconfigurations
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 _evaluate_configurable!
182
- return if _arbitrarily_configurable?
114
+ def __not_configured_callback_for__(property)
115
+ not_configured = @__not_configured__[property] || ::Proc.new { nil }
183
116
 
184
- @_configurable.each do |k, assertion|
185
- if k.is_a?(::Hash)
186
- k.each do |property, nested|
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
- # Executes a configuration method
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, Hash, Array] property configurable properties, either single or nested
201
- # @param [Symbol, Hash, Array] value configurable properties, either single or nested
202
- # @param [Hash] assertion assertion if any
203
- # @return a hash with configurable values pointing to their types
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 _configurable_hash(property, value, assertion)
206
- value = [value] unless value.is_a?(::Array)
207
- hash = ::Hash[value.zip([assertion].flatten*value.size)]
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
- # Assigns a value after running the assertions
214
- # @param [Symbol] property the property to type test
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 _assert_type!(property, value)
230
- return unless _evaluable?(property, :type)
231
-
232
- assertion = @_configurable[property][:type]
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
- # Block assertion for configurable properties
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 _evaluate_block!(property, value)
241
- return value unless _evaluable?(property, :block)
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 _is_writer?(method)
157
+ def __is_writer?(method)
266
158
  method.to_s.end_with?('=')
267
159
  end
268
160
 
269
- # @param [Symbol] method the method to test for existence
270
- # @return [Boolean] whether the method exists as a configuration method
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 responds to the given method writer
166
+ # @return [Boolean] whether the configuration can delegate
167
+ # the given method to Kernel
278
168
  #
279
- def _respond_to_writer?(method)
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 _property_from_writer(method)
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