[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Abstract -- what's the Ruby way?

Tim Rowe

12/13/2007 12:11:00 AM

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!


5 Answers

rbaldwin

12/13/2007 12:50:00 AM

0

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)...

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


Tim Connor

12/13/2007 1:41:00 AM

0

> 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?

Brian Marick

12/13/2007 2:50:00 AM

0


> 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.
>>

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.)

=================================

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



-----
Brian Marick, independent consultant
Mostly on agile methods with a testing slant
www.exampler.com, www.exampler.com/blog, www.twitter.com/marick


Charles Oliver Nutter

12/13/2007 6:58:00 AM

0

rbaldwin wrote:
> 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.

- Charlie

Robert Klemme

12/16/2007 12:38:00 PM

0

On 13.12.2007 07:58, Charles Oliver Nutter wrote:
> rbaldwin wrote:
>> 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
from :0
irb(main):014:0> Dog.new
=> #<Dog:0x7ff71960>
irb(main):015:0>

Kind regards

robert