Block Based Configuration
Block based configuration is a pattern you see quite a bit when using ruby. When I look at this website itself, there are 3 different gems that I am configuring using this approach. I think it provides a clean and encapsulated interface for configuration, and it is actually quite easy for you to use yourself in your own gems or projects.
Usage Examples
In the simple form gem, used for building forms, here is an example of its configuration.
SimpleForm.setup do |config|
# Default tag used for error notification helper.
config.error_notification_tag = :div
# CSS class to add for error notification helper.
config.error_notification_class = 'alert alert-error'
end
In the sorcery gem, used for authentication, here is what its configuration looks like. This one actually has a sub configuration inside of the main one.
Rails.application.config.sorcery.configure do |config|
# --- user config ---
config.user_config do |user|
user.reset_password_email_method_name = :reset_password_email
end
# This line must come after the 'user config' block.
# Define which model authenticates with sorcery.
config.user_class = "User"
end
Example
Here’s a simple example for configuring the settings of the Idioma gem. I’ll include all of the code here at the beginning, but I’ll break down some of the choices and how to use it below.
module Idioma
class Configuration
attr_writer :locales, :default_locale
attr_accessor :redis_backend
def initialize
self.default_locale = :en
self.locales = [self.default_locale]
end
def locales
proc_or_value(@locales)
end
def default_locale
proc_or_value(@default_locale)
end
private
def proc_or_value(var)
var.is_a?(Proc) ? var.call : var
end
end
# Return a single instance of the Configuration class
# @return [Idioma::Configuration] single instance
def self.conf
@configuration ||= Configuration.new
end
# Configure the settings for this module.
# @param [lambda] which will be passed instance of Configuration class
def self.configure
yield(conf)
end
end
Usage
We usually set this up in a file called idioma.rb inside of the config/initializers folder. One thing to keep in mind is that this code is only run once which happens when the project is initialized. Because I am pulling the setting values in this case from the database, I’ve chosen to send a lambda as the value instead of a straight value so that it will be evaluated every time the setting is needed. This is so when the DB value changes, it is reflected in the app immediately instead of needing to restart the application.
# config/initializers/idioma.rb
Idioma.configure do |configure|
configure.redis_backend = I18n.backend
configure.default_locale = -> {
Setting.get(:i18n_default_locale).to_sym
}
configure.locales = -> {
Setting.get(:i18n_available_locales).split(",").map { |locale| locale.strip.to_sym }
}
end
Explanation
This method is what handles receiving a lambda or a simple value. It checks to see if it is a Proc, and if so will call it, otherwise it just returns the value.
def proc_or_value(var)
var.is_a?(Proc) ? var.call : var
end
This method returns a memoized instance of the Configuration class… it treats it as a singleton because we don’t need multiple instances of it.
def self.conf
@configuration ||= Configuration.new
end
This is the method which is called when actually doing the configuration. It expects a block and an instance of the Configuration class is passed to it.
def self.configure
yield(conf)
end
Lastly, if you want to just grab a setting from the Idioma module somewhere in your code, say, which locales are available to it, you would grab it like this.
available_locales = Idioma.conf.locales



