1

I have a question about super that I wanted confirmed. Consider the following code example:

class InFasionHello
  def hello person
    greet person.name
  end

  def greet name
   p 'Dude, hey ' + name
  end 
end

class OldFasionedHello < InFasionHello
  def hello person
    greet person.name if person.old_fashioned
    super(person) if !person.old_fashioned
  end

  def greet name
   p 'Good Day to you ' + name + '!'
  end
end

My question is, if I was using OldFasionedHello, would infasionHello use the local greet to it self or the one from the class that called it via super?

Phrogz
  • 296,393
  • 112
  • 651
  • 745
Thermatix
  • 2,757
  • 21
  • 51
  • 8
    If you run the code, you can see it in action. Why are you asking ? – Arup Rakshit May 19 '14 at 13:52
  • Because this is a simplified example of a more complex implementation, I wanted to try drying it up so I'm using super. I'm asking in-case there are caveats that I might miss or other information that might be useful to me as well. – Thermatix May 19 '14 at 13:54
  • Your this code would not work. As your method old_fashioned is not avaiable in this perticular code. So, Please add some more code for person class as well. We need more details if you want to be clear, please add more details – Arpit Vaishnav May 19 '14 at 14:08
  • When you have questions like this you might find it useful to insert statements such as `puts "self in #{__method___}() = #{self}"` in different parts of your code. – Cary Swoveland May 20 '14 at 02:24
  • Though this may have been easy to test yourself, +1 for what is nonetheless good question for others. I've reworded the title to make it easier to find. – Phrogz May 20 '14 at 17:42
  • Thanks, that does make more sense than what I had. – Thermatix May 21 '14 at 19:09

2 Answers2

4

The proof of the pudding is in the eating.

class Parent
  def foo; p self; bar; end        # This calls bar on the current object
  def bar; puts "parent bar"; end
end

class Child < Parent
  def foo; super; end              # Removing this line changes nothing
  def bar; puts "child bar"; end
end

Child.new.foo
#=> #<Child:0x007f980b051f40>
#=> child bar                      # NOTE! Not "parent bar"

Calling super doesn't change the self, as seen above. As such, methods you call on self (explicitly or implicitly, by not providing a receiver) still act upon the original instance, and use it for method lookup.


Calling super() is equivalent to calling:

self.class.superclass.instance_method(__method__).bind(self).call

…which helps to illustrate that you are calling the implementation of the method as though it is on the current instance. Note also that super is not the same as super(), since the former will magically pass along whatever parameters were supplied to the current method.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
  • I see, so calling super sort of pulls the super class method into the scope of the child class and any function calls made within the super class will be made using the child class's scope, so it will be the child's method that get called rather then the super class. `super` is explicit to the method that called it and Only that method. – Thermatix May 19 '14 at 14:37
  • 1
    `super` in this case is equivalent to `self.class.ancestors[1].instance_method(:foo).bind(self).call` (except for [the way it handles passing of parameters](http://stackoverflow.com/a/22369711/405017)). – Phrogz May 19 '14 at 15:49
  • heh I actually understood that, if `super` is equivalent to all that then that really is a shortcut, a very nice one too. EDIT: so that means I don't have to use super() I can just call super and the arguments get passed along from the current method to the superclass method, interesting – Thermatix May 19 '14 at 15:53
  • Even more generic: `self.class.ancestors[1].instance_method(__method__).bind(self).call` – Phrogz May 19 '14 at 15:54
  • 1
    Now your just showing off ;), but thanks, helped a lot. Essentially: call superclass version of this method but within this current objects scope. – Thermatix May 19 '14 at 15:59
  • Even more generic: `self.class.ancestors[l].instance_method(__method__).bind(self).call`. You may have to look carefully to see how this differs from the above). – Cary Swoveland May 20 '14 at 00:17
  • Hint: which ancestor? – Cary Swoveland May 20 '14 at 15:37
  • Go home @CarySwoveland, [you're drunk](http://knowyourmeme.com/memes/go-home-you-are-drunk). :) – Phrogz May 20 '14 at 16:40
  • I guess I need to spell it out. My "even more generic" was to change `ancestors`' argument from `1` to a variable, `l`. E.g., change `Child#foo` to `def foo; puts "child foo"; end` and add `class Child2 < Child; def foo; self.class.ancestors[2].instance_method(__method__).bind(self).call;end; def bar; puts "child2 bar"; end; end`. Then `Child2.new.foo #=> "child2 bar"`. How did you know I'm drunk? – Cary Swoveland May 20 '14 at 16:46
  • :) Yes, I saw that it was an `l`. Not only does that not work (without predeclaring an `l` variable), there's no need to do it. Asking for the `instance_method` on a class that does not define it will search its ancestors to find the first one. In short, finding your immediate ancestor, as `[1]` does, is also what `super` does. – Phrogz May 20 '14 at 16:51
  • But, unlike `super`, `self.class.ancestors[i]...` allows you to "jump over" ancestors that have the same method, as in the example in my previous comment (not that I'm saying that would be useful). – Cary Swoveland May 20 '14 at 16:56
  • I just happened to notice that I never upvoted your answer. +1 for "The proof of the pudding is in the eating.", rather than the more common, "The proof is in the pudding". The latter only makes sense in limited circumstances, such as a woman being tried for attempting to smuggle a weapon to her incarcerated husband. – Cary Swoveland Apr 24 '16 at 19:45
1

All the method calls inside given method are executed against self. self within an instance method is an instance itself and and it is an instance who is a receiver of this method. Hence it is initiating standard method lookup for given object so it will always execute the very top method with given name.

An extreme good example is class method:

class A
  def foo
    self.class
  end
end

class B < A
end

B.new.foo    #=> B even though foo comes from A   
BroiSatse
  • 44,031
  • 8
  • 61
  • 86