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.
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