Easy Gem Configuration Variables with Defaults
Eli Fatsi, Former Development Director
Article Category:
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!