Easy Gem Configuration Variables with Defaults

Eli Fatsi, Former Development Director

Article Category: #Code

Posted on

I find myself working on Ruby gems from time to time, and am often met with the following task:

  • Allow the gem user to configure gem-related variables
  • Provide defaults for configurable variables that are not assigned

I know this is a solved problem since countless gems offer (or require) configuration. Some big name examples - ActiveAdmin, Simple Form, any API wrapper ever. Despite the fact that so many gem developers have solved this problem, digging around the code reveals some fairly complicated logic typically involving a Configuration class (example), or a dependency on ActiveSupport and the mattr_accessor/@@class_variable combination (example). I didn’t want either of those things, so for that reason (and great justice), I’ve pieced together a simple module you can just include in your gem.

Throw the following bit of code in a file such as my_gem/lib/helpers/configuration.rb

module Configuration

 def configuration
 yield self
 end

 def define_setting(name, default = nil)
 class_variable_set("@@#{name}", default)

 define_class_method "#{name}=" do |value|
 class_variable_set("@@#{name}", value)
 end

 define_class_method name do
 class_variable_get("@@#{name}")
 end
 end

 private

 def define_class_method(name, &block)
 (class << self; self; end).instance_eval do
 define_method name, &block
 end
 end

end

This exposes two class methods on anything that extends this module - define_setting and configuration. I’ll go over each of these below.

define_setting: This allows you, as a gem developer, to define configurable gem variables and set defaults if you care to do so. The usage would look something like this:

# my_gem/lib/my_gem.rb

require 'helpers/configuration'

module MyGem
 extend Configuration

 define_setting :access_token
 define_setting :access_secret

 define_setting :favorite_liquid, "apple juice"
 define_setting :least_favorite_liquid, "seltzer water"
end

Comparing this to the executions of the previously mentioned gems, we’re not relying on ActiveSupport, can optionally define defaults, and don’t have to hide our configuration variable definitions behind some other object. I would also argue that this is one of the simplest DSLs for adding and managing configurable values.

configuration: This allows you, now as a gem user, to get real fancy when you’re setting these variables by using a configuration block.

# config/initializers/my_gem.rb

MyGem.configuration do |config|
 config.access_token = "token"

 config.favorite_liquid = "gluten free apple juice"
end

With this configuration set, you’ve got the following variables available to use within your gem and the app using the gem:

MyGem.access_token #=> "token"
MyGem.access_secret #=> nil (was never assigned so remains nil)

MyGem.favorite_liquid #=> "gluten free apple juice"
MyGem.least_favorite_liquid #=> "seltzer water"

Wrapup notes

This method is effectively the same as using mattr_accessor (available with ActiveSupport) to create your getter and setter, and defining @@favorite_liquid = "apple juice" for the default, but a recent gem I was working on did not have access to ActiveSupport, and this is cleaner anyway.

Have a better way to set up configurable variables with ease? Let me know in the comments below!

Related Articles