class B < A
def a
super
end
end
class C < B
def other
A#a() # no cigar... A's methods cannot be called
end
end
----
Ok, this has been discussed in the past, but... just want to verify
that ruby is still so poor in this area.
I want to be able to call functions like in C++ or in python, where
super is pretty much not restricted to just calling the same method nor
a single level in the inheritance chain.
I can work around the issue using an alias somewhere in B, but this is
a pretty horrible idea both in terms of design and mantainabilty of the
code in the long run.
Is this the best that ruby can do?
Basically, in python you can do:
class A:
def a(self):
print "A::a()"
class B(A):
def a(self):
print "B::a()"
A.a(self) # calls A.a()
class C(B):
def b(self):
print "C::b()"
B.a(self)
or even (in newer pythons 2.3+):
class A:
def a(self):
print "A::a()"
class B(A):
def a(self):
print "B::a()"
super(B, self).a() # call's B's ancestor's a() method, whatever
it may be
class C(B):
def b(self):
print "C::b()"
B.a(self)
---------------------------------
It seems to me that ruby should allow for just doing the logical:
class B < A
def a()
super#a() or A#b()
end
end
where I use # as rdoc/ri uses it, not as a comment (not too crazy about
that syntax, thou).
> class A
> def a
> puts "A::a()"
> end
> end
>
> class B < A
> def a
> super
> end
> end
>
> class C < B
> def other
> A#a() # no cigar... A's methods cannot be called
> end
> end
Use A.instance_method(:a).bind(self).call().
Note that this has a longish syntax which means it is an usual thing to
do in Ruby.
IMHO this is an indication of poor class design. "super" is usually
sufficient and in a scenario like this, you can easily refactor (if you
have access to the super class, and in Ruby's case you can do it even if
you don't by runtime changes).
# refactored
class A
def a() a_impl() end
protected
def a_impl() puts "A::a()" end
end
class B < A
def a() super end
end
class C < B
def other() a_impl() end
end
Regards
robert
However, I admit not being too crazy about that as a general solution,
as that can easily lead to needing tons of, relatively speaking,
"useless" a_impl()-like methods on complex inheritance chains. Not
that it has been a problem for me yet, but... makes me go ...hmm...
Glad to know there is at least some obscure way of going around it,
too.
Also, I'm not sure I understand your comment about runtime changes.
How would you work around the issue if A was not ruby code. Would you
create an additional intermediate bridge class in ruby and have B
inherit from it?
I'm not 100% sure but this should work - even if A was not completely
implemented in Ruby:
class A
alias :a_impl :a
end
Then all sub classes can access a_impl - unless it's overridden of course.
:-)
An additional point of clarification: I do not mandate to have a and
a_impl for each public method. Instead I suggest to proper distribute
functionality across methods (with appropriate names). I know that this
is not always simple but it's good to keep that in mind when designing
base classes. Writing methods that do too much yields usually bad
modularity.
An example from a recent thread: someone suggested a method to cat input
to output. The suggested solution opened two (or more) files and copied
data from one file to the other. IMHO it's better to have separate
methods that do the file handling and the copying, with the copying method
working on open streams (IO). That way the copying can be reused in a
much wider range of applications.
# monolithic
def file_copy(out, *in)
File.open(out, "wb") do |o|
in.each do |fin|
File.open(fin,"rb") do |i|
while ( b = i.read(1024) )
o.write b
end
end
end
end
end
# modular
def stream_copy(in, out, size=1024)
while ( b = in.read(size) )
out.write b
end
end
def file_copy(out, *in)
File.open(out, "wb") do |o|
in.each do |fin|
File.open(fin, "rb") do |i|
stream_copy(i, o)
end
end
end
end
Kind regards
robert
> Note that this has a longish syntax which means it is an usual thing to
^^^^^
> do in Ruby.
This should have been "unusual".