Ruby Voodoo

Deep dives into random corners of my favorite programming language.

25

JUN
2008

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)
  1. Luke Hartman
    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.

    1. Reply (using GitHub Flavored Markdown)

      Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

      Ajax loader
  2. Stephen Touset
    Stephen Touset June 25th, 2008 Reply Link
    def config(options = {})
      (@config ||= {}).update(options)
    end
    
    1. Reply (using GitHub Flavored Markdown)

      Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

      Ajax loader
Leave a Comment (using GitHub Flavored Markdown)

Comments on this blog are moderated. Spam is removed, formatting is fixed, and there's a zero tolerance policy on intolerance.

Ajax loader