Search code examples
rubyabstract-class

How to implement an abstract class in Ruby


I know there is no concept of an abstract class in Ruby. But if it needs to be implemented, how do I go about it? I tried something like this:

class A
  def self.new
    raise 'Doh! You are trying to write Java in Ruby!'
  end
end

class B < A
  ...
  ...
end

But, when I try to instantiate B, it is internally going to call A.new which is going to raise the exception.

Also, modules cannot be instantiated, but they cannot be inherited too. Making the new method private will also not work.

Does anyone have any pointers?


Solution

  • I don't like using abstract classes in Ruby (there's almost always a better way). If you really think it's the best technique for the situation though, you can use the following snippet to be more declarative about which methods are abstract:

    module Abstract
      def abstract_methods(*args)
        args.each do |name|
          class_eval(<<-END, __FILE__, __LINE__)
            def #{name}(*args)
              raise NotImplementedError.new("You must implement #{name}.")
            end
          END
          # important that this END is capitalized, since it marks the end of <<-END
        end
      end
    end
    
    require 'rubygems'
    require 'rspec'
    
    describe "abstract methods" do
      before(:each) do
        @klass = Class.new do
          extend Abstract
    
          abstract_methods :foo, :bar
        end
      end
    
      it "raises NoMethodError" do
        proc {
          @klass.new.foo
        }.should raise_error(NoMethodError)
      end
    
      it "can be overridden" do
        subclass = Class.new(@klass) do
          def foo
            :overridden
          end
        end
    
        subclass.new.foo.should == :overridden
      end
    end
    

    Basically, you just call abstract_methods with the list of methods that are abstract, and when they get called by an instance of the abstract class, a NotImplementedError exception will be raised.