class ConfigMapper::ConfigStruct
A set of configurable attributes.
Public Class Methods
Defines reader and writer methods for the specified attribute.
A `:default` value may be specified; otherwise, the attribute is considered mandatory.
If a block is provided, it will invoked in the writer-method to validate the argument.
@param name [Symbol] attribute name @param default default value @yield type-coercion block
# File lib/config_mapper/config_struct.rb, line 39 def attribute(name, type = nil, default: :no_default, description: nil, &type_block) attribute = attribute!(name) attribute.description = description if default == :no_default attribute.required = true else attribute.default = default.freeze end attribute.validator = Validator.resolve(type || type_block) define_method("#{attribute.name}=") do |value| if value.nil? raise NoValueProvided if attribute.required else value = attribute.validator.call(value) if attribute.validator end instance_variable_set("@#{attribute.name}", value) end end
# File lib/config_mapper/config_struct.rb, line 116 def attributes attributes_by_name.values end
Defines a sub-component.
If a block is be provided, it will be `class_eval`ed to define the sub-components class.
@param name [Symbol] component name @param type [Class] component base-class
# File lib/config_mapper/config_struct.rb, line 71 def component(name, type: ConfigStruct, description: nil, &block) type = Class.new(type, &block) if block attribute = attribute!(name) attribute.description = description attribute.factory = type end
Defines an associative array of sub-components.
If a block is be provided, it will be `class_eval`ed to define the sub-components class.
@param name [Symbol] dictionary attribute name @param type [Class] base-class for component values @param key_type [Proc] function used to validate keys
# File lib/config_mapper/config_struct.rb, line 87 def component_dict(name, type: ConfigStruct, key_type: nil, description: nil, &block) type = Class.new(type, &block) if block component(name, type: ConfigDict::Factory.new(type, key_type), description: description) end
Defines an array of sub-components.
If a block is be provided, it will be `class_eval`ed to define the sub-components class.
@param name [Symbol] list attribute name @param type [Class] base-class for component values
# File lib/config_mapper/config_struct.rb, line 100 def component_list(name, type: ConfigStruct, description: nil, &block) type = Class.new(type, &block) if block component(name, type: ConfigList::Factory.new(type), description: description) end
Generate documentation, as Ruby data.
Returns an entry for each configurable path, detailing `description`, `type`, and `default`.
@return [Hash] documentation, keyed by path
# File lib/config_mapper/config_struct.rb, line 112 def config_doc each_attribute.sort_by(&:name).map(&:config_doc).inject({}, :merge) end
# File lib/config_mapper/config_struct.rb, line 120 def each_attribute(&action) return enum_for(:each_attribute) unless action ancestors.each do |klass| next unless klass.respond_to?(:attributes) klass.attributes.each(&action) end end
Instantiate from data.
@param attribute_values [Hash] attribute values @raise [ConfigMapper::MappingError] on error
# File lib/config_mapper/config_struct.rb, line 20 def from_data(attribute_values) new.tap do |instance| errors = instance.configure_with(attribute_values) raise MappingError, errors if errors.any? end end
# File lib/config_mapper/config_struct.rb, line 141 def initialize self.class.each_attribute do |attribute| instance_variable_set("@#{attribute.name}", attribute.initial_value) end end
Private Class Methods
# File lib/config_mapper/config_struct.rb, line 134 def attribute!(name) attr_reader(name) attributes_by_name[name] ||= Attribute.new(name) end
# File lib/config_mapper/config_struct.rb, line 130 def attributes_by_name @attributes_by_name ||= {} end
Public Instance Methods
# File lib/config_mapper/config_struct.rb, line 151 def config_errors immediate_config_errors.merge(component_config_errors) end
Configure with data.
@param attribute_values [Hash] attribute values @return [Hash] errors encountered, keyed by attribute path
# File lib/config_mapper/config_struct.rb, line 160 def configure_with(attribute_values) errors = ConfigMapper.configure_with(attribute_values, self) config_errors.merge(errors) end
# File lib/config_mapper/config_struct.rb, line 147 def immediate_config_errors missing_required_attribute_errors end
Return the configuration as a Hash.
@return [Hash] serializable config data
# File lib/config_mapper/config_struct.rb, line 169 def to_h {}.tap do |result| self.class.each_attribute do |attribute| value = send(attribute.name) if value && value.respond_to?(:to_h) && !value.is_a?(Array) value = value.to_h elsif value && value.respond_to?(:to_a) value = value.to_a end result[attribute.name.to_s] = value end end end
Private Instance Methods
# File lib/config_mapper/config_struct.rb, line 212 def component_config_errors {}.tap do |errors| components.each do |component_path, component_value| next unless component_value.respond_to?(:config_errors) component_value.config_errors.each do |path, value| errors["#{component_path}#{path}"] = value end end end end
# File lib/config_mapper/config_struct.rb, line 185 def components {}.tap do |result| self.class.each_attribute do |a| next unless a.factory result[".#{a.name}"] = instance_variable_get("@#{a.name}") end end end
# File lib/config_mapper/config_struct.rb, line 202 def missing_required_attribute_errors {}.tap do |errors| self.class.each_attribute do |a| if a.required && instance_variable_get("@#{a.name}").nil? errors[".#{a.name}"] = NoValueProvided.new end end end end