lib/configurations/configuration.rb in configurations-1.4.0 vs lib/configurations/configuration.rb in configurations-2.0.0.pre

- old
+ new

@@ -1,11 +1,10 @@ # -*- coding: utf-8 -*- module Configurations # Configuration is a blank object in order to allow configuration of various properties including keywords # class Configuration < BasicObject - # 1.9 does not allow for method rebinding in another scope # if ::RUBY_VERSION < '2.0.0' include ::Kernel undef :nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :initialize_dup, @@ -31,29 +30,36 @@ install_kernel_method(:is_a?) # Installs the inspect method from Kernel # install_kernel_method(:inspect) + end # Initialize a new configuration - # @param [Proc] configuration_defaults A proc yielding to a default configuration - # @param [Hash] configurable a hash of configurable properties and their asserted types if given + # @param [Hash] options The options to initialize a configuration with + # @option options [Hash] configurable a hash of configurable properties and their asserted types if given + # @option options [Hash] methods a hash of method names pointing to procs + # @option options [Proc] not_configured a proc to evaluate for not_configured properties # @param [Proc] block a block to configure this configuration with + # @yield [HostModule::Configuration] a configuration # @return [HostModule::Configuration] a configuration # - def initialize(configuration_defaults, configurable, &block) - @_writeable = true - @configurable = configurable + def initialize(options={}, &block) + @_configurable = options[:configurable] + @_methods = options[:methods] + @_not_configured = options[:not_configured] + + @_writeable = true @configuration = _configuration_hash _evaluate_configurable! - self.instance_eval(&configuration_defaults) if configuration_defaults + instance_eval(&options[:defaults]) if options[:defaults] if block - self.instance_eval(&block) + instance_eval(&block) self._writeable = false end end # Method missing gives access for reading and writing to the underlying configuration hash via dot notation @@ -62,23 +68,27 @@ property = method.to_s[0..-2].to_sym value = args.first if _respond_to_writer?(method) _assign!(property, value) - elsif _respond_to_property?(method) + elsif _respond_to_property_for_write?(method) @configuration[method] + elsif _respond_to_property_for_read?(method) + @configuration.fetch(method, &_not_configured_callback) + elsif _has_configuration_method?(method) + _exec_configuration_method!(method, *args, &block) elsif _can_delegate_to_kernel?(method) ::Kernel.send(method, *args, &block) else super end end # Respond to missing according to the method_missing implementation # def respond_to?(method, include_private = false) - _respond_to_writer?(method) or _respond_to_property?(method) or _can_delegate_to_kernel?(method) or super + _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 end # Set the configuration to writeable or read only. Access to writer methods is only allowed within the # configure block, this method is used to invoke writability for subconfigurations. # @param [Boolean] data true if the configuration should be writeable, false otherwise @@ -120,56 +130,75 @@ # @param [Symbol] property The property to test for configurability # @return [Boolean] whether the given property is configurable # def _configurable?(property) - _arbitrarily_configurable? or @configurable.has_key?(property) + _arbitrarily_configurable? or @_configurable.has_key?(property) end # @return [Boolean] whether this configuration is arbitrarily configurable # def _arbitrarily_configurable? - @configurable.nil? or @configurable.empty? + @_configurable.nil? or @_configurable.empty? end # @return [Hash] the configurations configurable hash def _configurable - @configurable + @_configurable end private # @param [Symbol] property The property to test for # @return [Boolean] whether the given property has been configured # def _configured?(property) - @configuration.has_key?(property) + @configuration.has_key?(property) or _not_configured_callback? or _arbitrarily_configurable? end # @return [Hash] A configuration hash instantiating subhashes if the key is configurable # def _configuration_hash ::Hash.new do |h, k| - h[k] = Configuration.new(nil, @configurable) if _configurable?(k) + h[k] = Configuration.new(configurable: @_configurable, not_configured: @_not_configured) if _configurable?(k) end end + # @return [Proc] The not_configured callback + # + def _not_configured_callback + @_not_configured || ::Proc.new { |key| nil } + end + + # @return [Boolean] whether the not configured callback has been configured + # + def _not_configured_callback? + !!@_not_configured + end + # Evaluates configurable properties and passes eventual hashes down to subconfigurations # def _evaluate_configurable! return if _arbitrarily_configurable? - @configurable.each do |k, assertion| + @_configurable.each do |k, assertion| if k.is_a?(::Hash) k.each do |property, nested| - @configuration[property] = Configuration.new(nil, _configurable_hash(property, nested, assertion)) + @configuration[property] = Configuration.new(configurable: _configurable_hash(property, nested, assertion), not_configured: _not_configured_callback) end end end end + # Executes a configuration method + # + def _exec_configuration_method!(method, *args, &block) + args << block #rbx complains if block is separate in the arguments list + instance_exec(*args, &@_methods[method]) + end + # @param [Symbol, Hash, Array] property configurable properties, either single or nested # @param [Symbol, Hash, Array] value configurable properties, either single or nested # @param [Hash] assertion assertion if any # @return a hash with configurable values pointing to their types # @@ -198,31 +227,31 @@ # @raise [ConfigurationError] if the given value has the wrong type # def _assert_type!(property, value) return unless _evaluable?(property, :type) - assertion = @configurable[property][:type] + assertion = @_configurable[property][:type] ::Kernel.raise ConfigurationError, "Expected #{property} to be configured with #{assertion}, but got #{value.class.inspect}", caller unless value.is_a?(assertion) end # Block assertion for configurable properties # @param [Symbol] property the property to type test # @param [Any] value the given value # def _evaluate_block!(property, value) return value unless _evaluable?(property, :block) - evaluation = @configurable[property][:block] + evaluation = @_configurable[property][:block] evaluation.call(value) end # @param [Symbol] property The property to test for # @param [Symbol] assertion_type The evaluation type type to test for # @return [Boolean] whether the given property is assertable # def _evaluable?(property, evaluation) - @configurable and @configurable.has_key?(property) and @configurable[property].is_a?(::Hash) and @configurable[property].has_key?(evaluation) + @_configurable and @_configurable.has_key?(property) and @_configurable[property].is_a?(::Hash) and @_configurable[property].has_key?(evaluation) end # @param [Symbol] property The property to test for # @return [Boolean] whether this property is nested # @@ -235,21 +264,35 @@ # def _is_writer?(method) method.to_s.end_with?('=') end + # @param [Symbol] method the method to test for existence + # @return [Boolean] whether the method exists as a configuration method + # + def _has_configuration_method?(method) + @_methods and @_methods.has_key?(method) + end + # @param [Symbol] method the method to test for # @return [Boolean] whether the configuration responds to the given method writer # def _respond_to_writer?(method) _is_writer?(method) and @_writeable and _configurable?(_property_from_writer(method)) end # @param [Symbol] method the method to test for - # @return [Boolean] whether the configuration responds to the given property as a method + # @return [Boolean] whether the configuration responds to the given property as a method during readable state # - def _respond_to_property?(method) - not _is_writer?(method) and (@_writeable or _configured?(method)) + def _respond_to_property_for_read?(method) + not _is_writer?(method) and _configured?(method) + end + + # @param [Symbol] method the method to test for + # @return [Boolean] whether the configuration responds to the given property as a method during writeable state + # + def _respond_to_property_for_write?(method) + not _is_writer?(method) and @_writeable end # @param [Symbol] method the method to test for # @return [Boolean] whether the configuration can delegate the given method to Kernel #