Method missing magic can often be a code smell leading you to question your design. Sometimes, however, it can be a boon. For those of us who have trouble spelling or remembering the exact names of methods, we can make method missing help with method lookup. Since we’re good TDDers, we’ll start with some tests specifying the behavior we want …

Method missing magic can often be a code smell leading you to question your design. Sometimes, however, it can be a boon. For those of us who have trouble spelling or remembering the exact names of methods, we can make method missing help with method lookup. Since we’re good TDDers, we’ll start with some tests specifying the behavior we want:


  class TestMethodSortaMissing < Test::Unit::TestCase
    class TestClass
      def do_something; :do_something; end
      def nothing_to_do; :nothing_to_do; end
    end

    def test_do_something_can_be_invoked_exactly
      assert_equal :do_something, TestClass.new.do_something
    end

    def test_do_something_can_be_invoked_fuzzily
      assert_equal :do_something, TestClass.new.something_do_to
    end

    def test_nothing_to_do_can_be_invoked_exactly
      assert_equal :nothing_to_do, TestClass.new.nothing_to_do
    end

    def test_nothing_to_do_can_be_invoked_fuzzily
      assert_equal :nothing_to_do, TestClass.new.do_nothing
    end

    def test_simple_mispellings_can_be_invoked
      assert_equal :do_something, TestClass.new.do_somthing
      assert_equal :do_something, TestClass.new.do_someting
    end

    def test_a_dissimilar_message_raises_an_error
      assert_raises(NoMethodError) do
        TestClass.new.a_dissimilar_method
      end
    end
  end

To make the tests pass, we’ll employ the help of the Amatch gem, which implements a few string matching algorithms.


  sudo gem install amatch

And then we’ll override method_missing on Object to run a pattern match against the available methods


  class Object
    alias_method :__exact_method_missing, :method_missing

    def method_missing(sym, *args, &block)
      symbol_pattern = Amatch::PairDistance.new(sym.to_s)
      methods.each do |m|
        return send(m, *args, &block) if symbol_pattern.similar(m, /_/) > 0.75
      end
      __exact_method_missing(sym, *args, &block)
    end  
  end

Now when an object doesn’t understand the message you’ve sent it it will do it’s best to figure out what you mean. If it can’t figure it out based on string analysis, it will fall back to raising an error as usual. The best part is now I don’t have to improve my spelling!

Sorry, comments are closed for this article.