On Sunday, November 28, 2010 04:15:19 am Diego Virasoro wrote:
> Hello,
> for some code I am writing, I'd like to have a couple of abstract
> classes. However I am not very sure how to go about it in Ruby.

Just do it.

> Ideally all I would need is:
> 1) The class cannot be instantiated
> and/or
> 2) The class is not visible outside of the module it comes in

The canonical Ruby solution is either to

1) Document the class as abstract, and warn people not to instantiate it
or
2) Document the class as a private implementation detail

The simplest way to do this would be to put "Abstract" in the name of your 
class, but you'll want to go a bit farther than that.

> What would be the canonical way to go about this in Ruby? Should I use
> a mix-in module?

No, if it's a class, you should use a class. Modules are for collections of 
behavior you might mix in to a class, but sometimes, a superclass actually 
does make sense.

So you should do whichever actually makes sense to you. Do what makes it 
easiest to do things the right way. Don't even consider how to prevent people 
from doing things the wrong way.

> Ideally I would prefer to use a class since all the subclasses have a
> similar instantiation so I'd like to code it only once and use
> super(),

If you call super, while the behavior is a bit weird, it will hit module 
methods.

> but then the user himself may try to instantiate it breaking
> the whole "magic". :)

This is Ruby. The user may try to do ANYTHING, and you CANNOT stop them.

Let's say you use a module. How do you stop the user from inheriting from that 
module? Or say you use a class -- even if you stop the user from instantiating 
it, how do you stop them from simply inheriting from that class and 
instantiating anyway?

So stop trying. Document the proper way to use your code, and don't try to 
prevent users from doing it the wrong way.

Just in case I haven't convinced you, here's a few examples of how you might 
do what you're accomplishing, and why they don't work.

You might do this:

class Abstract
  def self.new
    raise "Can't initialize abstract class!"
  end
end

But then you'll have a hard time instantiating it yourself in subclasses, and 
the user can still do this:

Class.instance_method(:new).bind(Abstract).call

That's assuming they can't figure out (and duplicate) exactly how you manage 
to instantiate subclasses. You could do something fancier:

class Abstract
  def self.new
    if self == Abstract
      raise "Can't instantiate abstract class!"
    else
      super
    end
  end
end

But then they can do this:

Class.new(Abstract).new

Which is basically an anonymous version of this:

class Foo < Abstract; end
Foo.new

You could try to hide the class away by making it anonymous:

module MyModule
  k = Class.new do
    def some_method
      "Don't call me without super!"
    end
  end

  class Foo < k
    def some_method
      super
      "some_method called in child"
    end
  end
end

Now no one outside that block can see k, right?

Wrong.

MyModule::Foo.ancestors[1].new

I suppose you could override ancestors, but that's just an arms race that 
you're very likely to lose. Just for fun, let's add that in:

class MyModule::Foo
  def self.ancestors
    a = super
    [a.first] + a[2...a.length]
  end
end

So now I just do this:

Class.instance_method(:ancestors).bind(MyModule::Foo).call[1].new

You'd pretty much have to dig into all the core classes like Class and Object 
to really prevent me from doing this kind of trick. I don't think that's worth 
the time and effort, and I think it's very likely I could still find a way 
around it.

If you really want, you could do something like one of those raise-in-
the-'new'-method approaches to at least force your users to know what they're 
doing, but I don't really see the point. If your users want to do things 
properly, they'll follow the docs. If they don't, you can't stop them.