Ruby Voodoo

Deep dives into random corners of my favorite programming language.
  • 26

    JUN
    2008

    Summoning Error Classes As Needed

    In the past, I've written a lot of code like this:

    class SomeThing
      class SomeError       < RuntimeError; end
      class AnotherError    < RuntimeError; end
      class YetAnotherError < RuntimeError; end
    
      # some methods that can raise the above errors...
    end
    

    I have a new strategy I've been using for code like this and it has really been working out well. Here is how I do the same thing today:

    class SmarterThing
      def self.const_missing(error_name)  # :nodoc:
        if error_name.to_s =~ /Error\z/
          const_set(error_name, Class.new(RuntimeError))
        else
          super
        end
      end
    
      # error raising methods here...
    end
    

    Let's discuss how this works. The const_missing() method is a hook in Ruby, much like the beloved method_missing(). Note that const_missing() is a class method though, instead of an instance method. When a constant is used and Ruby can't find it, a call to the hook will be triggered.

    In this version, I just check to see if the constant name ends in Error. If it does, I summon an Exception subclass on the fly. Other calls to this hook are forwarded on to Ruby's default error raising implementation via super.

    Read more…

  • 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.

    Read more…