Here is one way. Its not very satisfying, as you have no way to access
the initialize method on any parent classes of the Animal class (such
as ActiveRecord)...
class Animal
def initialize
raise 'Invalid instantiation of class'
end
def talk
puts 'hi'
end
end
class Dog < Animal
def initialize
# this can be empty
# don't use super here as will cause exception from Animal
end
end
# this will fail (as it should) with an exception
# a = Animal.new
# this succeeds
d = Dog.new
d.talk
···
On Dec 12, 5:10 pm, "Tim Rowe" <n...@digitig.co.uk> wrote:
What's the Ruby way to define a class that shouldn't be instantiated, but
with descendants that can be? Other than a comment in the documentation, of
course.
Here is one way. Its not very satisfying, as you have no way to access
the initialize method on any parent classes of the Animal class (such
as ActiveRecord)...
Is it possible to pull of maybe using some clever passing back and
forth up through the super chain?
raise 'Invalid instantiation of class' unless
child_class_name.constantize <= Animal
or maybe detecting caller and raising unless it's a child class?
I missed the beginning, so hope this response is to the point. See the code below. (What I do is name the class AbstractSomething and trust that's warning enough.)
···
On Dec 12, 5:10 pm, "Tim Rowe" <n...@digitig.co.uk> wrote:
What's the Ruby way to define a class that shouldn't be instantiated, but
with descendants that can be? Other than a comment in the documentation, of
course.
=================================
class Abstract
attr_reader :abstract_class_initialize_args
def initialize(*args)
raise "Initialized abstract class #{self.class}" if self.class == Abstract @abstract_class_initialize_args = args
end
end
class SubclassWithInitializer < Abstract
attr_reader :subclass_initialize_arg
def initialize(arg)
super @subclass_initialize_arg = arg
end
end
class SubclassWithoutInitializer < Abstract
end
require 'test/unit'
class TestCase < Test::Unit::TestCase
def test_abstract_class_raises_exception
assert_raises(RuntimeError) { Abstract.new }
end
def test_a_subclass_can_call_super
sub = SubclassWithInitializer.new('arg')
assert_equal(['arg'], sub.abstract_class_initialize_args)
assert_equal('arg', sub.subclass_initialize_arg)
end
def test_a_subclass_need_not_define_its_own_initialize
sub = SubclassWithoutInitializer.new('arg')
assert_equal(['arg'], sub.abstract_class_initialize_args)
end
end
What's the Ruby way to define a class that shouldn't be instantiated, but
with descendants that can be? Other than a comment in the documentation, of
course.
Thanks!
Here is one way. Its not very satisfying, as you have no way to access
the initialize method on any parent classes of the Animal class (such
as ActiveRecord)...
You could privatize new and define a new new for inheriting classes that calls the old new:
class Animal
class << self
private :new
def inherited(cls)
cls.instance_eval "def new(*args); super; end"
end
end
end
class Dog < Animal
puts Dog.new # => #<Dog:0x2506c>
pust Animal.new => NoMethodError: private method 'new' called for Animal...
I use instance_eval here because I don't like the semantics of define_method (or I dislike them more than eval magic); but it would probably work about the same.
The downside of this approach is subclasses that want to also define their own 'new' would have to know to call super. I'd expect that's pretty rare.
- Charlie
···
On Dec 12, 5:10 pm, "Tim Rowe" <n...@digitig.co.uk> wrote:
What's the Ruby way to define a class that shouldn't be instantiated, but
with descendants that can be? Other than a comment in the documentation, of
course.
Thanks!
Here is one way. Its not very satisfying, as you have no way to access
the initialize method on any parent classes of the Animal class (such
as ActiveRecord)...
You could privatize new and define a new new for inheriting classes that calls the old new:
class Animal
class << self
private :new
def inherited(cls)
cls.instance_eval "def new(*args); super; end"
end
end
end
class Dog < Animal
puts Dog.new # => #<Dog:0x2506c>
pust Animal.new => NoMethodError: private method 'new' called for Animal...
I use instance_eval here because I don't like the semantics of define_method (or I dislike them more than eval magic); but it would probably work about the same.
The downside of this approach is subclasses that want to also define their own 'new' would have to know to call super. I'd expect that's pretty rare.
This downside can be easily avoided: simply change #inherited to make new public again instead of redefining it.
irb(main):001:0> class Animal
irb(main):002:1> class <<self
irb(main):003:2> private :new
irb(main):004:2> def inherited(cl)
irb(main):005:3> class <<cl
irb(main):006:4> public :new
irb(main):007:4> end
irb(main):008:3> end
irb(main):009:2> end
irb(main):010:1> end
=> nil
irb(main):011:0> class Dog < Animal
irb(main):012:1> end
=> nil
irb(main):013:0> Animal.new
NoMethodError: private method `new' called for Animal:Class
from (irb):13
irb(main):014:0> Dog.new
=> #<Dog:0x7ff71960>
irb(main):015:0>
Kind regards
robert
···
On 13.12.2007 07:58, Charles Oliver Nutter wrote:
On Dec 12, 5:10 pm, "Tim Rowe" <n...@digitig.co.uk> wrote: