Calling super, super class method with parameters in Ruby - ruby

I found the below working solutions (link 1, link 2) which call the grandparent method but without any parameters. Does anyone know how to call the grandparent method with parameters?
class GrandParent
def fun(word)
puts word
end
end
class Parent < GrandParent
def fun(word)
puts word + 'end'
end
end
class Child < Parent
def fun(word)
GrandParent.instance_method(:fun).bind(self).call
end
end

You can pass parameters directly to call like this:
class Child < Parent
def fun(word)
GrandParent.instance_method(:fun).bind(self).call(param1, param2)
end
end

call accepts parameters
GrandParent.instance_method(:fun).bind(self).call word
I don't know your use case, but this is a Really Bad Idea™. It creates unnecessary dependencies directly between Child and GrandParent such that all kinds of normally reasonable refactoring would result in a crash e.g. moving fun to only be implemented in Parent, changing Parent to subclass a different but similar parent, etc.

Related

Ruby - extending method with super using splat

In "Comprehensive Ruby programming course" e-book I have a case when the child class method extends parents method. I am not completely aware how it works:
class Parent
def initialize(foo:, bar:)
#foo = foo
#bar = bar
end
end
class Child < Parent
def initialize(buzz:,**args)
super(**args)
#buzz = buzz
end
end
I cant completely understand why we use splat here - **args.
In here def initialize(buzz:,**args) we are just telling initialize to take unknown number of key-value arguments, right? But what exactly this means super(**args). To tell method to take those key-value arguments from the superclass method? Why not just like this:
class Child < Parent
def initialize(buzz:)
super
#buzz = buzz
end
end
After all, super tells to extend method with whatever there is in the parent, so why these splat args needed?
**args in the parameter list simply means "get all extra keyword arguments and put them in a hash, called args".
Conversely, **args when calling a method does the opposite - "get this hash called args and pass keyword arguments with the corresponding names and values from that hash".
super without arguments will try to pass all arguments that the child method received. Hence if you have extra that the parent didn't expect, you will get an ArgumentError.
In your example, the parent expects only foo: and bar:, while the child also has buzz:.

Ruby Inheritance Get Caller Class Name

I'm so lost. I know how to use caller to get the caller method, but what do you use the get the caller class?
For example:
class Testing
def return_caller_class
return caller_class
end
end
class Parent
attr_accessor :test_me
def initialize
self.test_me = Testing.new
end
end
class Child < Parent
end
class GrandChild < Child
end
test_Parent = Parent.new
test_Child = Child.new
test_GrandChild = GrandChild.new
puts test_Parent.test_me.return_caller_class => Parent
puts test_Child.test_me.return_caller_class => Child
puts test_GrandChild.test_me.return_caller_class => GrandChild
Thank you!!!
Edit:
I've tried to do the following
class Testing
def return_caller_class
return caller[0][/`.*'/][1..-2]
end
end
And the output is:
{"
"=>Parent}
{"
"=>Child}
{"
"=>GrandChild}
To explain better about my question.
I would the output to display this instead
Parent
Child
GrandChild
I'm a bit out of my depth with this question, but I think you have made a few mistakes unrelated to the problem of getting the caller's class name. If I can help you with those things, at least you might be a step closer (if a solution is even possible)!
Firstly, it seems to me that you're calling return_caller_class from the main program object, not from one of those three objects you created. You have an object of class Testing inside an object of class Parent, but the method call is outside of both.
Secondly, the only reason you seem to be getting anything close to what you want (when you get output like "=>Parent} has nothing to do with the return_caller_class method. It seems as though you inadvertently created little hashes in the last three lines of your program (when you added => Parent, etc), which are being output with puts. (Confirmed here: Has #puts created a new hash?) If these are meant to be comments, they need a # before them.
PS. I found a link to this gem on another thread: https://github.com/asher-/sender. Might be worth checking out.
class Testing
def return_caller_class
self.class.name
end
end
class ChildOne < Testing
end
class ChildTwo < Testing
end
result:
------------------------------------------------
>ChildOne.new.return_caller_class
=> "ChildOne"
>ChildTwo.new.return_caller_class
=> "ChildTwo"
>Testing.new.return_caller_class
=> "Testing"

Calling a parent's method from the child

Some code
class Parent
def print
p "Hi I'm the parent"
end
end
class Child < Parent
def initialize(num)
#num = num
end
def print
child_print
end
def child_print
if #num == 1
#call parent.print
else
p "I'm the child"
end
end
end
c1 = Child.new(1)
c2 = Child.new(2)
c1.print
c2.print
Child is an instance of Parent. Print is the method exposed in the interface, and both classes define them. Child decides to do other things in a (possibly really complex) method, but will invoke its parent's method under some condition.
I could just write
def print
if #num == 1
super
else
p "I'm the child"
end
end
And that works, but what if it's not just a simple one-liner comparison but instead is doing lots of complicated things that deserve to be separated into another method? It may have to do some calculations before deciding that the parent's method should be called.
Or perhaps there is a different, better way to design it.
Parent.instance_method(:print).bind(self).call
This is already pretty readable, but here's an explanation.
Get the #print method of the Parent class
Bind it to your current object
Call it
PS: You can even give arguments to #call and they will be relayed to the called method.
PPS: That said, such code almost always hints at an issue in your class design. You should try to avoid it whenever possible.

Calling super class' super class method?

How can I get child to ignore what its parent thinks is fun and go straight to the grandparent's idea of fun?
Child still inherits from the parent, but it just doesn't agree with a couple methods.
Calling the method of the super class of the super class?
Also, is it considered poor design if I'm in a situation where the child doesn't agree with its parents but agrees with the parent's parents?
class Grandparent
def fun
#do stuff
end
end
class Parent < Grandparent
def fun
super
#parent does some stuff
end
def new_business
#unrelated to my parent
end
end
class Child < Parent
def fun
super
#child also does some stuff
end
def inherit_new_business
new_business
#stuff
end
end
It's generally easier in Ruby to get this kind of behavior through composition rather than inheritance. To accomplish that Modules are included that contain the specific behaviors you wish a class to have.
But if you absolutely have to use inheritance you can do this:
class Child < Parent
def fun
GrandParent.instance_method(:fun).bind(self).call
# fun child stuff
end
end
This will do exactly what it says. Grab the instance method fun from the GrandParent class, attach it to the current instance object self and call it.

Calling super's super method

Is is possible to do something like super.super in the overriden method? That is, to bypass the direct parent's super and call "grandparent's" super?
This is not recommended, but what you want is possible like this:
grandparent = self.class.superclass.superclass
meth = grandparent.instance_method(:the_method)
meth.bind(self).call
This works by first getting the grandparent class, then calling instance_method on it to get an UnboundMethod representing the grandparent's version of the_method. It then uses UnboundMethod#bind and Method#call to call the grandparent's method on the current object.
you could modify the method's arguments to allow some kind of optional 'pass to parent' parameter. in the super class of your child, check for this parameter and if so, call super from that method and return, otherwise allow execution to continue.
class Grandparent; def method_name(opts={}); puts "Grandparent called."; end; end
class Parent < Grandparent
def method_name(opts={})
return super if opts[:grandparent]
# do stuff otherwise...
puts "Parent called."
end
end
class Child < Parent
def method_name(opts={})
super(:grandparent=>true)
end
end
ruby-1.9.2-p0 > Child.new.method_name
Grandparent called.
=> nil
otherwise i agree with #Femaref, just b/c something is possible doesn't mean it's a good idea. reconsider your design if you think this is necessary.
Considering this is breaking one of the principles of OOP (encapsulation), I dearly hope it isn't possible. Even the case of you trying to do this speaks of a problem with your design.

Resources