lukeck / config_mapper

Maps config data onto plain old Ruby objects

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ConfigMapper

ConfigMapper maps configuration data onto Ruby objects.

Usage

Imagine you have some Ruby objects:

class Position

  attr_reader :x
  attr_reader :y

  def x=(arg); @x = Integer(arg); end
  def y=(arg); @y = Integer(arg); end

end

class State

  def initialize
    @position = Position.new
  end

  attr_reader :position
  attr_accessor :orientation

end

state = State.new

and wish to populate/modify it, based on plain data:

config_data = {
  "orientation" => "North",
  "position" => {
    "x" => 2,
    "y" => 4
  }
}

ConfigMapper will help you out:

require 'config_mapper'

errors = ConfigMapper.configure_with(config_data, state)
state.orientation              #=> "North"
state.position.x               #=> 2

It can even populate Hashes of objects, e.g.

positions = Hash.new { |h,k| h[k] = Position.new }

config_data = {
  "fred" => { "x" => 2, "y" => 4 },
  "mary" => { "x" => 3, "y" => 5 }
}

ConfigMapper.configure_with(config_data, positions)
positions["fred"].x            #=> 2
positions["mary"].y            #=> 5

Target object

Given

ConfigMapper.configure_with(config_data, target)

the target object is expected provide accessor-methods corresponding to the attributes that you want to make configurable. For example, with:

config_data = {
  "orientation" => "North",
  "position" => { "x" => 2, "y" => 4 }
}

it should have a orientiation= method, and a position method that returns a Position object, which should in turn have x= and y= methods.

ConfigMapper cannot and will not create objects for you.

Errors

ConfigMapper.configure_with returns a Hash of errors encountered while mapping data onto objects. The errors are Exceptions (typically ArgumentError or NoMethodError), keyed by the path to the offending data. e.g.

config_data = {
  "position" => {
    "bogus" => "flibble"
  }
}

errors = ConfigMapper.configure_with(config_data, state)
errors    #=> { ".position.bogus" => #<NoMethodError> }

ConfigStruct

ConfigMapper works pretty well with plain old Ruby objects, but we provide a base-class, ConfigMapper::ConfigStruct, with a DSL that makes it even easier to declare configuration data-structures.

require "config_mapper/config_struct"

class State < ConfigMapper::ConfigStruct

  component :position do
    attribute(:x) { |arg| Integer(arg) }
    attribute(:y) { |arg| Integer(arg) }
  end

  attribute :orientation

end

By default, declared attributes are assumed to be mandatory. The ConfigStruct#config_errors method returns errors for each unset mandatory attribute.

state = State.new
state.position.x = 3
state.position.y = 4
state.config_errors
#=> { ".orientation" => "no value provided" }

#config_errors can be overridden to provide custom semantic validation.

Attributes can be given default values. Provide an explicit nil default to mark an attribute as optional, e.g.

class Address < ConfigMapper::ConfigStruct

  attribute :host
  attribute :port, :default => 80
  attribute :path, :default => nil

end

ConfigStruct#configure_with maps data into the object, and combines mapping errors and semantic errors (returned by #config_errors) into a single Hash:

data = {
  "position" => { "x" => 3, "y" => "fore" },
  "bogus" => "foobar"
}
state.configure_with(data)
#=> {
#=>   ".orientation" => "no value provided",
#=>   ".position.y" => #<ArgumentError: invalid value for Integer(): "fore">,
#=>   ".bogus" => #<NoMethodError: undefined method `bogus=' for #<State:0x007fc8e9b12a60>>
#=> }

License

The gem is available as open source under the terms of the MIT License.

Contributing

It's on GitHub; you know the drill.

See also

  • ConfigHound is a great way to load raw config-data, before throwing it to ConfigMapper.

About

Maps config data onto plain old Ruby objects

License:MIT License


Languages

Language:Ruby 100.0%