/canfig

Dead simple canned configuration for gems or whatever

Primary LanguageRuby

Canfig

Build Status Coverage Status Code Climate Gem Version Dependency Status

Dead simple canned configuration for gems or whatever. It allows users of your gems (or whatever) to configure behavior using a familiar syntax (maybe in a rails config initializer, for example).

MyGem.configure do |config|
  config.foo = 'bar'
  config.enable_thingy!
end

Getting Started

Add the gem to your Gemfile:

gem 'canfig'

Then run bundle install.

Or if you want to use canfig to configure one of your gems, add it as a dependency to your gemspec and require 'canfig' somewhere in your gem.

Usage

You can create a new configuration object by passing in a list of allowed options and/or a hash with defaults.

conf = Canfig::Config.new(:foo, :bar) # new config with nil values for provided keys
conf = Canfig::Config.new(foo: 'abc', bar: 123) # new config with default values
conf = Canfig::Config.new(:foo, :bar, baz: 123) # combination of the two

Then you can use that object to allow further configuration of the allowed option keys with a hash or block.

conf = Canfig.new(:foo, :bar, :baz) # shortcut for Canfig::Config.new

conf.configure(foo: 'abc', bar: 123) # with a hash

conf.configure do |config| # with a block
  config.bar = 123
  config.baz = Struct.new(:a, :b).new(1, 2)
end

conf.configure foo: 'abc' do |config| # or with both
  config.bar = 123
  config.baz = Struct.new(:a, :b).new(1, 2)
end

Only the option keys provided when initializing the configuration object can be accessed, and attempting to get or set any other keys will result in a NoMethodError (similar to a Struct).

If you want your configuration object to be more flexible, you can use Canfig::OpenConfig which allows dynamically setting and getting option keys (similar to an OpenStruct).

conf = Canfig.open # or Canfig::OpenConfig.new

conf.configure foo: 'abc' do |config| # you can set whatever you want
  config.bar = 123
  config.baz = Struct.new(:a, :b).new(1, 2)
end

Getting Changes

The current state is saved everytime the options are altered, which allows you to grab a hash of changed attributes.

conf = Canfig.new(:foo, :bar, baz: 'abc')

conf.configure foo: 'abc' do |config| # or with both
  config.baz = 123
end

conf.changed # {foo: [nil, 'abc'], baz: ['abc', 123]}

Configuring Gems

This pattern isn't all that powerful on it's own, and you might be thinking "why wouldn't I just use a Hash or an OpenStruct?"

Canfig becomes particularly useful when you need to allow configuration of a class or module that will be used in different environments and scenarios (like a ruby gem).

module MyGem
  mattr_reader :configuration
  @@configuration = Canfig.new(:foo, :bar, :baz)

  def self.configure(&block)
    @@configuration.configure &block
  end
end

Users of your gem could then configure it with MyGem.configure.

MyGem.configure do |config|
  config.foo = 'abc'
  config.bar = 123
end

And you could check the configured options to control behavior within your gem.

module MyGem
  class MyClass
    def do_foo?
      MyGem.configuration.foo == true
    end
  end
end

Quick Mixins

Canfig also comes with some easy-to-use mixins for modules, classes and instances that provide a configuration module/class/instance variable and a configure module/class/instance method (similar to the gem example above).

module MyModule
  include Canfig::Module
end

MyModule.configuration # <Canfig::OpenConfig>
MyModule.configure { |config| config.foo = 'abc' }

class MyClass
  include Canfig::Class
end

MyClass.configuration # <Canfig::OpenConfig>
MyClass.configure { |config| config.foo = 'abc' }

class MyObject
  include Canfig::Instance
end

object = MyObject.new
object.configuration # <Canfig::OpenConfig>
object.configure { |config| config.foo = 'abc' }

Custom Configs

You can create your own custom configuration classes by extending either Canfig::Config or Canfig::OpenConfig as appropriate, and defining/overriding any additional functionality you need. One handy trick is to override initialize to set default keys/values without requiring them to be passed in every time.

class CustomConfig < Canfig::Config
  def initialize(*args, &block)
    # ignore the arguments that are passed in and set some custom keys and defaults
    super(:foo, :bar, baz: 'abc', &block)
  end
end

conf = CustomConfig.new # :foo => nil, :bar => nil, :baz => 'abc'

You can also define custom helpers to set groups of configuration options (or do anything else, really).

class DeveloperConfig < Canfig::OpenConfig
  def enter_beast_mode!
    set :headphones, :on
    set :status, 'do_not_disturb'
  end
end

conf = DeveloperConfig.new
conf.configure do |config|
  config.enter_beast_mode!
end

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request