Class method aliased in superclass bypasses subclass overrides

M

Marcos

This seems like it should work:

class Parent
class << self
def real_method() "Parent" end
alias :fake_method :real_method
end
end

class Child < Parent
def real_method() "Child" end
end

But it doesn't:

Child.fake_method # ==> returns "Parent" instead of "Child"

If I replace the alias with a real method definition, e.g.

def fake_method() real_method end

then it works the way I expect (Child.fake_method returns "Child").
So what is it about "alias" that bypasses the override? Do I have to
replace my aliases with extra wrapper methods to get the behavior I'm
looking for?
 
Y

Yossef Mendelssohn

So what is it about "alias" that bypasses the override? =A0 Do I have to
replace my aliases with extra wrapper methods to get the behavior I'm
looking for?

As far as I can tell (or explain), alias and alias_method work by
creating a new copy of the method. One way to think about it is if you
had the block stored in a variable because you created the first
method like so

class Blah
meth =3D Proc.new { puts 'hi' }
define_method :first, &meth
end

Then aliasing the method like `alias :copy :first` is the same as
`define_method :copy, &meth`.

I ran into problems with the same sort of behavior when I was trying
to test-drive an addition of a simple alias for a complicated method,
and I assumed that I could simply test that calling the alias called
the original method with the same arguments. Nope! Calling the alias
went through the same complicated steps as the original. So I changed
the alias_method line to a wrapper function and went on my merry way.
 
R

Robert Dober

This seems like it should work:

=A0 =A0class Parent
=A0 =A0 =A0 =A0class << self
=A0 =A0 =A0 =A0 =A0 =A0def real_method() "Parent" end
=A0 =A0 =A0 =A0 =A0 =A0alias :fake_method :real_method
=A0 =A0 =A0 =A0end
=A0 =A0end

=A0 =A0class Child < Parent
=A0 =A0 =A0 =A0def real_method() "Child" end
=A0 =A0end

But it doesn't:

=A0 =A0 =A0 Child.fake_method # =3D=3D> returns "Parent" instead of "Chil= d"

If I replace the alias with a real method definition, e.g.

=A0 =A0 =A0 =A0def fake_method() real_method end

then it works the way I expect =A0(Child.fake_method returns "Child").
So what is it about "alias" that bypasses the override? =A0 Do I have to
replace my aliases with extra wrapper methods to get the behavior I'm
looking for?

I do not see anything surprising here, you aliased a method in parent
and not in child. When calling child's fake method there is none and
the lookup finds the parent's method.
When however you wrap the parent's methods after having found parent's
#fake it calls
real which is found in the child. Note that parent's #real is never
called in none of the two cases!

HTH
Robert
--=20
module Kernel
alias_method :=EB, :lambda
end
 
D

David A. Black

Hi --

This seems like it should work:

class Parent
class << self
def real_method() "Parent" end
alias :fake_method :real_method

You don't have to use symbols there; you can do:

alias fake_method real_method

because alias is a keyword, not a method, so it can take "raw"
identifiers.
end
end

class Child < Parent
def real_method() "Child" end

You mean def self.real_method, I think.
end

But it doesn't:

Child.fake_method # ==> returns "Parent" instead of "Child"

If I replace the alias with a real method definition, e.g.

def fake_method() real_method end

then it works the way I expect (Child.fake_method returns "Child").
So what is it about "alias" that bypasses the override? Do I have to
replace my aliases with extra wrapper methods to get the behavior I'm
looking for?

The aliasing just means that there's now a method called
Parent.fake_method. It doesn't add any methods to any class other than
the class where it's executed (in this case, the singleton class of
Parent). Basically, creating an alias has the same footprint as
writing a method definition.


David

--
David A. Black / Ruby Power and Light, LLC / http://www.rubypal.com
Q: What's the best way to get a really solid knowledge of Ruby?
A: Come to our Ruby training in Edison, New Jersey, September 14-17!
Instructors: David A. Black and Erik Kastner
More info and registration: http://rubyurl.com/vmzN
 
M

Marcos

This seems like it should work:

    class Parent
        class << self
            def real_method() "Parent" end
            alias :fake_method :real_method
        end
    end

    class Child < Parent
        def real_method() "Child" end
    end

Small correction - that should be "def self.real_method". All class
methods, not instance methods...
 
G

Gary Wright

The aliasing just means that there's now a method called
Parent.fake_method. It doesn't add any methods to any class other than
the class where it's executed (in this case, the singleton class of
Parent). Basically, creating an alias has the same footprint as
writing a method definition.


I find it mildly annoying that 'alias' doesn't actually alias the
method (i.e. multiple names for the same thing). Instead, alias
just replicates an existing method body and associates a new
name with the replica. Seems like 'replicate' would be a better
keyword for the current semantics.

I also have the inability to ever remember which argument to alias
is the new name and which argument is the old name.

Did 'alias' work differently in the early days of Ruby such that
the semantics changed but not the keyword?
 
J

Joel VanderWerf

Gary said:
I find it mildly annoying that 'alias' doesn't actually alias the
method (i.e. multiple names for the same thing). Instead, alias
just replicates an existing method body and associates a new
name with the replica. Seems like 'replicate' would be a better
keyword for the current semantics.

I also have the inability to ever remember which argument to alias
is the new name and which argument is the old name.

Did 'alias' work differently in the early days of Ruby such that
the semantics changed but not the keyword?

+1 to all of above, albeit mildly for me too.

In practice I usually end up with

def myalias(*a,&b); orig(*a,&b); end

even if it started out as an "alias". I don't mind not having a language
construct for this purpose, since the above is so compact and clear.
 
R

Robert Dober

+1 to all of above, albeit mildly for me too.

In practice I usually end up with

=C2=A0def myalias(*a,&b); orig(*a,&b); end

even if it started out as an "alias". I don't mind not having a language
construct for this purpose, since the above is so compact and clear.

It is strange that as a Unix guy I have never been bothered by this.
As an important use case for my aliases (although I prefer the
alias_method method) I am often using this pattern:

alias_method :__behavior__, :behavior
remove_method :behavior # for 1.9
define_method :behavior do ...
...
__behavior__
...

thus the current behavior became second nature.

Maybe it would be nice to have a synonym method in Module that does
what you two expected from alias, that is being dynamically redefined
with its target.

Cheers
Robert


--=20
module Kernel
alias_method :=CE=BB, :lambda
end
 
7

7stud --

I believe your alias version is equivalent to this:

==========
class Parent
Parent.real_method
"Parent"
end

Parent.fake_method #alias creates a copy of real_method
"Parent"
end

end
=========

and your wrapped version is this:

=========
class Parent
def Parent.real_method
"Parent"
end

def Parent.fake_method
real_method #<-----**BIG DIFFERENCE**
end
end
=========

Those Parent classes are clearly not the same. If you add the following
code to your wrapped version:

=========
class Child < Parent
def self.real_method
"Child"
end
end

puts Child.fake_method

--output:--
Child
=========

the message "fake_method" is sent to the Child object (=a class object).
The Child object has no method named "fake_method" defined on it, e.g.
def Child.fake_message, so lookup proceeds to the superclass class
object, i.e. Parent. The Parent object does have the method
"fake_method" defined on it, so Parent.fake_method is executed.
Parent.fake_method really looks like this:

def Parent.fake_method
self.real_method
end

In this case, self is the Child object--because when you write:

puts Child.fake_method

the fake_method message gets sent to the Child object--in other words
Child is calling fake_method, and the caller is self inside a method.
Therefore, calling self.real_method (inside fake_method) is equivalent
to calling Child.real_method. And calling Child.real_method sends the
message real_method to the Child object. As a consequence, a new lookup
begins starting with the Child object. Because Child has a method
called real_method defined on it, Child.real_method executes.

It's highly probable that the above description contains some factual
errors, but the esteemed members who previously posted above will surely
correct them below.
 
M

Marcos

I believe your alias version is equivalent to this:

==========
class Parent
  Parent.real_method
    "Parent"
  end

  Parent.fake_method  #alias creates a copy of real_method
    "Parent"
  end

end
=========

In that case, 'alias' wouldn't alias anything; it just makes a copy,
which is a different thing altogether.

It seems more likely to be something likc Yossef said:

block = Proc.new { "Parent" }
define_method :real_method, block
define_method :fake_method, block

Where the same body (rather than two identical copies) is used for
both methods; the traditional CS kind of "alias". In UNIX terms, it's
an ln, not a cp.

To stretch the analogy a bit, what I was expecting was more like "ln -
s". But clearly that's not correct, so I'll just stick with the
wrapper methods.

Thanks for all the replies.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,189
Latest member
CryptoTaxSoftware

Latest Threads

Top