25
JUN2008
The One Method Config
I've used this technique a couple of times now for dirt-simple configurations. The idea is to provide a trivial way to read and write configuration values with just a single method. Let me show you what I mean:
module Configurable
module_function
def config(new_config = nil)
if new_config.nil?
@config ||= { }
else
config.merge!(new_config)
end
end
end
include Configurable
config # => {}
config :a => 1, :b => 2
config # => {:a=>1, :b=>2}
config[:a] # => 1
config :a => -1, :c => 3
config # => {:a=>-1, :b=>2, :c=>3}
config.clear
config # => {}
There's no deep magic here, obviously. The method has two function: read and write for the configuration. Read is handled with what I like refer to as Ruby's "caching operator" (||=
). The first time that line is triggered, it will cache an empty Hash
in the variable. Thereafter, the same call is just a cache hit to get the same Hash
back.
We could stop there, of course. Access to the Hash
allows us to use any methods we need on it. However, one more nicety really pays off, in my opinion. Give an optional argument and, when it's available, use it as a shortcut for one of the write methods. In this case, I chose merge!()
just because it's a well rounded tool for adding and/or editing multiple Hash
entries at once.
The examples show how this plays out. Note that I mix both styles of the method calls with some standard Hash
tools ([]
and clear()
). I feel like that gives a whole lot of cool interface with barely any effort.
One more example, just to get your brain spinning in other directions:
module Sortable
module_function
def sortable_fields(*fields)
if fields.empty?
@sortable_fields ||= [ ]
else
sortable_fields.push(*fields.flatten)
end
end
end
include Sortable
sortable_fields # => []
sortable_fields :a, :b, :c
sortable_fields # => [:a, :b, :c]
sortable_fields %w[d e f]
sortable_fields # => [:a, :b, :c, "d", "e", "f"]
This method wraps an Array
instead of a Hash
, as you can seen. With that there's one other trick I sometimes like to throw in that should be called something like "slurp-flatten()
-splat." By taking any number of arguments, calling flatten()
to avoid worrying about whether the caller used an Array
(say for the convenient syntax used in the examples), and splitting the fields back out, we again get a smooth interface out of minimal code.
As I said earlier there's no deep magic here, but don't underestimate how nice the simple tricks can be in the right circumstances. Of course, purists who strongly hold to the "a method should only do one thing" philosophy will need to comfort themselves to sleep by repeating, "It just manages configuration!"
Comments (2)
-
Luke Hartman June 25th, 2008 Reply Link
Thanks for sharing James and helping expand my mind. I can already see some applications of this module in some projects I'm doing.
-
def config(options = {}) (@config ||= {}).update(options) end