Class: ConfigMapper::ConfigStruct

Inherits:
Object
  • Object
show all
Defined in:
lib/config_mapper/config_struct.rb

Overview

A set of configurable attributes.

Defined Under Namespace

Classes: Attribute, NoValueProvided

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConfigStruct

Returns a new instance of ConfigStruct.



143
144
145
146
147
# File 'lib/config_mapper/config_struct.rb', line 143

def initialize
  self.class.each_attribute do |attribute|
    instance_variable_set("@#{attribute.name}", attribute.initial_value)
  end
end

Class Method Details

.attribute(name, type = nil, default: :no_default, description: nil) { ... } ⇒ Object

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.

Parameters:

  • name (Symbol)

    attribute name

  • default (defaults to: :no_default)

    default value

Yields:

  • type-coercion block



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/config_mapper/config_struct.rb', line 41

def attribute(name, type = nil, default: :no_default, description: nil, &type_block) # rubocop:disable Metrics/PerceivedComplexity
  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
    elsif attribute.validator
      value = attribute.validator.call(value)
    end
    instance_variable_set("@#{attribute.name}", value)
  end
end

.attributesObject



116
117
118
# File 'lib/config_mapper/config_struct.rb', line 116

def attributes
  attributes_by_name.values
end

.component(name, type: ConfigStruct, description: nil, &block) ⇒ Object

Defines a sub-component.

If a block is be provided, it will be ‘class_eval`ed to define the sub-components class.

Parameters:

  • name (Symbol)

    component name

  • type (Class) (defaults to: ConfigStruct)

    component base-class



71
72
73
74
75
76
# 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

.component_dict(name, type: ConfigStruct, key_type: nil, description: nil, &block) ⇒ Object

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.

Parameters:

  • name (Symbol)

    dictionary attribute name

  • type (Class) (defaults to: ConfigStruct)

    base-class for component values

  • key_type (Proc) (defaults to: nil)

    function used to validate keys



87
88
89
90
# 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

.component_list(name, type: ConfigStruct, description: nil, &block) ⇒ Object

Defines an array of sub-components.

If a block is be provided, it will be ‘class_eval`ed to define the sub-components class.

Parameters:

  • name (Symbol)

    list attribute name

  • type (Class) (defaults to: ConfigStruct)

    base-class for component values



100
101
102
103
# 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

.config_docHash

Generate documentation, as Ruby data.

Returns an entry for each configurable path, detailing ‘description`, `type`, and `default`.

Returns:

  • (Hash)

    documentation, keyed by path



112
113
114
# File 'lib/config_mapper/config_struct.rb', line 112

def config_doc
  each_attribute.sort_by(&:name).map(&:config_doc).inject({}, :merge)
end

.each_attribute(&action) ⇒ Object



120
121
122
123
124
125
126
127
128
# 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

.from_data(attribute_values) ⇒ Object

Instantiate from data.

Parameters:

  • attribute_values (Hash)

    attribute values

Raises:



22
23
24
25
26
27
# File 'lib/config_mapper/config_struct.rb', line 22

def from_data(attribute_values)
  new.tap do |instance|
    errors = instance.configure_with(attribute_values)
    raise MappingError, errors if errors.any?
  end
end

Instance Method Details

#config_errorsObject



153
154
155
# File 'lib/config_mapper/config_struct.rb', line 153

def config_errors
  immediate_config_errors.merge(component_config_errors)
end

#configure_with(attribute_values) ⇒ Hash

Configure with data.

Parameters:

  • attribute_values (Hash)

    attribute values

Returns:

  • (Hash)

    errors encountered, keyed by attribute path



162
163
164
165
# File 'lib/config_mapper/config_struct.rb', line 162

def configure_with(attribute_values)
  errors = ConfigMapper.configure_with(attribute_values, self)
  config_errors.merge(errors)
end

#immediate_config_errorsObject



149
150
151
# File 'lib/config_mapper/config_struct.rb', line 149

def immediate_config_errors
  missing_required_attribute_errors
end

#to_hHash

Return the configuration as a Hash.

Returns:

  • (Hash)

    serializable config data



171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/config_mapper/config_struct.rb', line 171

def to_h
  {}.tap do |result|
    self.class.each_attribute do |attribute|
      value = send(attribute.name)
      if value&.respond_to?(:to_h) && !value.is_a?(Array) && !value.is_a?(ConfigList)
        value = value.to_h
      elsif value&.respond_to?(:to_a)
        value = value.to_a
      end
      result[attribute.name.to_s] = value
    end
  end
end