I've been following the discussion on abstract method declaration with 
some interest.  It seems to me that the ideal implementation should not 
only throw an exception on unimplemented methods, but also must pass 
Matz's example (implementation provided by base classes) and the 
method_missing example.

With that in mind, I started to develope an implementation of 
abstract_method in a test first way, where you write a test case and 
then only implement enough code to make the test pass.  Then you write 
another test case and only add enough code to make that test pass. 
Repeat as needed.

With that in mind, I came up with 5 test cases:

-- BEGIN UNIT TESTS ------------------------------------------------
require 'test/unit'
require 'abstract_method'

class TestAbstractMethod < Test::Unit::TestCase

  # This is the basic use case for abstract methods where the abstract
  # method is declared in the parent class and redefined in the child
  # class.
  def test_can_override_abstract_method
    parent = Class.new {
      abstract_method :foo
    }
    child = Class.new(parent) {
      def foo
        :foo_result
      end
    }
    assert_equal :foo_result, child.new.foo
  end

  # Here we make sure that not implementing the abstract method in the
  # child class will cause an exception when the method is invoked on
  # the child object.  We don't particularly care what exception is
  # thrown, but the exception message must mention the missing method
  # by name.
  def test_unimplemented_abstract_method_throws_exception
    parent = Class.new {
      abstract_method :foo
    }
    child = Class.new(parent) {
    }
    begin
      child.new.foo
      fail "Oops"
    rescue Exception => ex
      assert_match /\bfoo\b/, ex.message
    end
  end

  # Now we make sure that our implementation passes Matz's example
  # where an abstract method in a mixin is actually implemented in the
  # base class.  We need to make sure that the mixin doesn't hide the
  # implemented behavior.
  def test_abstract_method_in_mixin_may_be_implemented_in_base_class
    abstract_mixin = Module.new {
      abstract_method :foo
    }
    parent = Class.new {
      def foo
        :foo_result
      end
    }
    child = Class.new(parent) {
      include abstract_mixin
    }
    assert_equal :foo_result, child.new.foo
  end

  # This is a similar scenario to the previous test case where we make
  # sure the abstract declaration doesn't interfer with implemented
  # behavior.  This time the implemented behavior is provided by the
  # method missing technique.
  def test_abstract_method_may_be_implemented_by_method_missing
    parent = Class.new {
      abstract_method :foo
    }
    child = Class.new(parent) {
      def method_missing(sym, *args, &block)
        :foo_result
      end
    }
    assert_equal :foo_result, child.new.foo
  end

  # Finally we want to ensure that +abstract_method+ can take multiple
  # method names, and that the method names may be either strings or
  # symbols.
  def test_abstract_method_may_take_multiple_string_or_symbol_arguments
    parent = Class.new {
      abstract_method :foo, "bar", "baz"
    }
    child = Class.new(parent) {
      def foo
        :foo_result
      end
      def bar
        :bar_result
      end
    }
    assert_equal :foo_result, child.new.foo
    assert_equal :bar_result, child.new.bar
    begin
      child.new.baz
      fail "Oops"
    rescue Exception => ex
      assert_match /\bbaz\b/, ex.message
    end
  end
end
-- END UNIT TESTS -------------------------------------------------

And here is the implementation that came of that exercise:

-- BEGIN ABSTRACT METHOD IMPLEMENTATION ---------------------------

class Module
  def abstract_method(*method_names)
  end
end

-- END ABSTRACT METHOD IMPLEMENTATION -----------------------------

--
-- Jim Weirich

-- 
Posted via http://www.ruby-forum.com/.