Skip to content

A simple and flexible means of configuration for Ruby applications.

License

Notifications You must be signed in to change notification settings

saclark/accessible

Repository files navigation

Accessible

Gem Version Build Status Coverage Status

A simple and flexible means of setting up configuration for Ruby applications.

Usage

MyConfig = Accessible.create do |config|
  confg.load   'config/defaults.yml'
  config.merge 'config/environments.yml', ENV['APP_ENV']
  config.merge { :foo => 'bar' }
end

MyConfig.wake_me_up.before.you_go.go?
# => true

Documentation

Creating an "Accessible" class

Assign the result of calling Accessible's create method, passing it a block in which you may load/merge any number of data sources.

MyConfig = Accessible.create do |config|
  confg.load 'config/defaults.yml'
  config.merge 'config/environments/dev.yml'
end

Alternatively, you can create a class, include the Accessible module, and load/merge your data.

class MyConfig
  include Accessible

  load 'config/defaults.yml'
  merge 'config/environments/dev.yml'
end

Methods

The following class methods are available on any class created with/including Accessible.

Accessor methods

When you load data into your class, getter and setter methods are defined on the class and recursively through the loaded data for each key. This way you can easily walk through your data:

MyConfig.my_deeply.nested.data_is.accessible

The main difference between accessor methods and [] and []= is that the accessor methods raise an error if a matching key does not exist.

Examples:

MyConfig = Accessible.create do |config|
  config.load({
    :characters => {
      :calvin => {
        :alter_egos => [
          { :name => 'Spaceman Spiff' },
          { :name => 'Tracer Bullet' }
        ]
      }
    }
  })
end

MyConfig.characters.calvin.alter_egos[0].name
# => 'Spaceman Spiff'
MyConfig.characters[:calvin].alter_egos[0].name
# => 'Spaceman Spiff'

MyConfig[:characters].calvin.alter_egos[1].name = 'Stupendous Man'
# => 'Stupendous Man'
MyConfig.characters.calvin
# => {
#   :alter_egos => [
#     { :name => 'Spaceman Spiff' },
#     { :name => 'Stupendous Man' }
#   ]
# }

MyConfig.does_not_exist
# => NoMethodError
MyConfig[:does_not_exist]
# => nil

MyConfig.does_not_exist = 'foo'
# => NoMethodError
MyConfig[:new_key] = 'a new value'
# => 'a new value'
MyConfig.new_key
# => 'a new value'

Be careful with this one though. It is best to treat its return value as read-only due to surprising results when used in combination with []= (read why).

load

load(data_source, key = nil) -> data

Loads data into your class, wiping out any previously loaded data. It accepts a data source as well as an optional second parameter representing the name of a specific key within the data source from which data should be loaded.

A data source can be any of the following:

Hash
Loads the given hash:

MyConfig.load({ :names => ['Calvin', 'Hobbes'] })

String
The given string should represent a file path to an existing yaml file to be loaded. An error will be throw if the file cannot be found:

MyConfig.load('config/env_config.yml')

Symbol
The given symbol should represent the name of a .yml file located in a /config directory (relative to the working directory of the running process):

MyConfig.load(:env_config)

Therefore, the following are equivalent

# These are the same
MyConfig.load(:env_config)
MyConfig.load('config/env_config.yml')

merge

merge(data_source, namespace = nil) -> data

Equivalent to load with the exception that the data source is merged (i.e. entries with duplicate keys are overwritten) with previously loaded data.

[]

[key] -> value

Gets data from your class. Returns nil if the key does not exist, making this method useful for assigning default values in the absence of a key.

MyConfig.load({ :calvin => 'Spaceman Spiff' })

MyConfig[:calvin]
# => 'Spaceman Spiff'

MyConfig[:susie]
# => nil

person = MyConfig[:susie] || 'Hobbes'
# => 'Hobbes'

[]=

[key] = value -> value

Sets data on your class.

MyConfig.load({})

MyConfig[:calvin] = 'Spaceman Spiff'
MyConfig[:calvin]
# => 'Spaceman Spiff'

MyConfig[:calvin] = 'Stupendous Man'
MyConfig[:calvin]
# => 'Stupendous Man'

Note, however, this is not functionally equivalent to setting values on the result of calling to_h on your class or it's values. (e.g. MyConfig.to_h[:foo] = 'bar').

The subtle difference here is that using []= directly on the class ensures that the appropriate accessor methods are defined on the class, it's data, and the value being set. Thus, the following works:

MyConfig.load({ :calvin => { :superhero => 'Spaceman Spiff' } })

MyConfig[:calvin] = { :superhero => 'Stupendous Man' }

MyConfig.calvin
# => { :superhero => 'Stupendous Man' }

MyConfig.calvin.superhero
# => 'Stupendous Man'

MyConfig.calvin[:detective] = 'Tracer Bullet'
MyConfig.calvin.detective
# => 'Tracer Bullet'

MyConfig[:susie] = 'Derkins'
MyConfig.susie
# => 'Derkins'

Contrast this with the following behavior:

MyConfig.load({ :calvin => { :superhero => 'Spaceman Spiff' } })

MyConfig.to_h[:calvin] = { :superhero => 'Stupendous Man' }

# The following only works by coincidence because an accessor for :calvin
# was defined when the config data was initially loaded
MyConfig.calvin
# => { :superhero => 'Stupendous Man' }

MyConfig.calvin.superhero
# => NoMethodError: undefined method `superhero' for {:superhero=>"Stupendous Man"}:Hash

MyConfig.calvin.to_h[:detective] = 'Tracer Bullet'
MyConfig.calvin.detective
# => NoMethodError: undefined method `detective' for {:superhero=>"Stupendous Man", :detective=>"Tracer Bullet"}:Hash

MyConfig.to_h[:susie] = 'Derkins'
MyConfig.susie
# => NoMethodError: undefined method `susie' for MyConfig:Class

As you can see, being sure to only use []= on your class and it's values directly ensures the proper accessors are maintained. Setting values on the return value of to_h is a recipe for disaster.

to_h

to_h -> data

Returns all data loaded to your class as a hash.

MyConfig.load({ :names => ['Calvin', 'Hobbes'] })

MyConfig.to_h
# => { :names => ['Calvin', 'Hobbes'] }

About

A simple and flexible means of configuration for Ruby applications.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages