Calling method in parent class from subclass methods in Ruby - ruby

I've used super to initialize parent class but I cannot see any way of calling parent class from subclass methods.
I know PHP and other languages do have this feature but cannot find a good way to do this in Ruby.
What would one do in this situation?

If the method is the same name, i.e. you're overriding a method you can simply use super. Otherwise you can use an alias_method or a binding.
class Parent
def method
end
end
class Child < Parent
alias_method :parent_method, :method
def method
super
end
def other_method
parent_method
#OR
Parent.instance_method(:method).bind(self).call
end
end

The super keyword calls a method of the same name in the super class:
class Foo
def foo
"#{self.class}#foo"
end
end
class Bar < Foo
def foo
"Super says: #{super}"
end
end
Foo.new.foo # => "Foo#foo"
Bar.new.foo # => "Super says: Bar#foo"

The super keyword in Ruby actually calls a method of the same name in the parent class. (source)
class Foo
def foo
# Do something
end
end
class Bar < Foo
def foo
super # Calls foo() method in parent class
end
end

Others have said it already well. Just one additional note of caution:
The syntax super.foo to call method foo in the super class is not supported. Rather it will call the super-method and on the returned result, try to call foo.
class A
def a
"A::a"
end
end
class B < A
def a
"B::a is calling #{super.a}" # -> undefined method `a` for StringClass
end
end

class Parent
def self.parent_method
"#{self} called parent method"
end
def parent_method
"#{self} called parent method"
end
end
class Child < Parent
def parent_method
# call parent_method as
Parent.parent_method # self.parent_method gets invoked
# call parent_method as
self.class.superclass.parent_method # self.parent_method gets invoked
super # parent_method gets invoked
"#{self} called parent method" # returns "#<Child:0x00556c435773f8> called parent method"
end
end
Child.new.parent_method #This will produce following output
Parent called parent method
Parent called parent method
#<Child:0x00556c435773f8> called parent method
#=> "#<Child:0x00556c435773f8> called parent method"
self.class.superclass == Parent #=> true
Parent.parent_method and self.class.superclass will call self.parent_method(class method) of Parent while
super calls the parent_method(instance method) of Parent.

As of Ruby 2.2, super_method can also be used to call super of method a from method b.
method(:a).super_method.call

Related

How to call any instance method in ruby object without instantiating it?

I am creating a helper module to initialize the object before calling its methods
module Initialized
extend ActiveSupport::Concern
class_methods do
def run(*args)
new(*args).run
end
def call(*args)
new(*args).call
end
def execute(*args)
new(*args).create
end
end
end
So instead of defining run, call, and execute in my helper module I need to receive any method name and check if it exists on the main class after initializing it, then call the requested instance method if exists in the main class or raise an error if not
I would say my targeted code would be something like this
module Initialized
extend ActiveSupport::Concern
class_methods do
def _(*args, methodname)
new(*args).try(:send, "#{methodname}") || raise 'Method not exist'
end
end
end
Sample usage would be
class MyClass
include Initialized
def initialize(param1)
#param1 = param1
end
def call
puts "call '#{#param1}'"
end
end
then calling
MyClass.call('method param')
I found these links but couldn't find my answer yet:
meta-dynamic-generic-programming-in-ruby
ruby-module-that-delegates-methods-to-an-object
template-methods-in-ruby
Despite the fact method_missing would do the job, I'd probably avoid it in this case in favor of a more explicit delegation. Simple and dirty example:
module InstanceDelegator
def delegate_to_instance(*methods)
methods.each do |method_name|
define_singleton_method method_name do |*args|
new(*args).public_send(method_name)
end
end
end
end
class Foo
extend InstanceDelegator
delegate_to_instance :bar # <- define explicitly which instance methods
# should be mirrored by the class ones
def bar
puts "bar is called"
end
def baz
puts "baz is called"
end
end
# and then
Foo.bar # => bar is called
Foo.baz # NoMethodError ...
# reopening works too
class Foo
delegate_to_instance :baz
end
Foo.baz # baz is called
Pros:
you don't need to redefine method_missing (less magic -> less pain when you debug the code)
you control precisely which instance methods to be wrapped with the class level "shorthand" (fewer chances to delegate something you don't want to - more robust code)
(minor) no need to raise NoMethodError explicitly - you can fully rely on the core dispatching as it is...
I found another solution instead of using a module,
I can use the class method self.method_missing
def self.method_missing(method_name, *args, &block)
obj = new(*args)
raise NoMethodError, "undefined method `#{method_name}' for #{self}:Class" unless obj.respond_to?(method_name)
obj.send(method_name, &block)
end
But the limitation is that I have to copy it into every class whenever I need to use this feature

I need to know if is there a way to exec something everytime i define a class

Like i said in the title. I need to know if is there some way to execute something everytime i define a class
Like puts "fifi".
When i do this:
class Pipipupu
end
=> fifi
class Object
def self.inherited(obj)
puts "fifi" if obj == Pipipupu
end
end
class Pipipupu
end
diplays "fifi".
class Cat
end
displays nothing. See Class#inherited, which is referred to as a callback or hook method.
If you wish to display "fifi" whenever any class is created replace
puts "fifi" if obj == Pipipupu
with
puts "fifi" if obj.is_a? Class
Add an inherited hook to BasicObject:
class BasicObject
def self.inherited(subclass)
puts "fifi"
end
end
To prevent it from screwing up other hooks, instead of redefining it you can prepend a module that prints and calls super to invoke the already registered behavior:
module Hook
def inherited(subclass)
puts "fifi"
super
end
end
BasicObject.singleton_class.prepend(Hook)
Then:
irb> class Foo; end
fifi
=> nil
Alternatively, add the hook to Object. For Foo above the implicit superclass is Object. But BasicObject is the superclass of Object. Adding the hook to BasicObject will also print for classes that specify BasicObject as superclass.

Does calling super() cause further methods in the parent class to be used?

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

Why a module's singleton method is not visible in downstream eigenclasses where it gets mixed?

I understand the regular method lookup path i.e. class, superclass/module, all the way up to BasicObject. I thought it was true for singleton version of the chain also but doesn't seem the case when you mixin a module in the meta-chain. I'd appreciate if someone can explain why in the following example Automobile module's banner method is called instead of its singleton version when I have included this module in Vehicle's eigenclass.
module Automobile
def banner
"I am a regular method of Automobile"
end
class << self
def banner
"I am a class method of Automobile"
end
end
end
class Vehicle
def banner
"I am an instance method of Vehicle"
end
class << self
include Automobile
def banner
puts "I am a class method of Vehicle"
super
end
end
end
class Car < Vehicle
def banner
"I am an instance method of Car"
end
class << self
def banner
puts "I am a class method of Car"
super
end
end
end
puts Car.banner
# I am a class method of Car
# I am a class method of Vehicle
# I am a regular method of Automobile
First of all, include does not include eigenclass methods as you might expect. Consider:
module Foo
class << self
def do_something
puts "Foo's eigenclass method does something"
end
end
end
module Bar
include Foo
end
puts Bar.do_something
# undefined method `do_something' for Bar:Module (NoMethodError)
Note that this is consistent with the behavior of classically defined class methods:
module Foo
def self.do_something
puts "Foo's class method does something"
end
end
module Bar
include Foo
end
puts Bar.do_something
# undefined method `do_something' for Bar:Module (NoMethodError)
A common idiom is to define the class methods in a submodule and then trigger a call to extend when the module is included:
module Foo
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def do_something
puts "Foo::ClassMethod's instance method does something"
end
end
end
module Bar
include Foo
end
puts Bar.do_something
# Foo::ClassMethod's instance method does something
The second thing to note is, that you are really including the instance methods of Automobile into the eigenclass of Vehicle, thus the instance methods of Automobile turn into (eigen)class methods of Vehicle.
Your Car class basically has nothing to do with all this. The only thing to note here is, that class inheritance also makes class methods available, whereas include does not. Example:
class Foo
def self.do_something
puts "Foo's class method does something"
end
end
class Bar < Foo
end
puts Bar.do_something
# "Foo's class method does something"
First of all, a class is an object, just like the other objects it also has its own superclass;
second of all, Eigenclass itself is a normal class, only anonymous and sorta invisible;
third, the eigenclass's superclass of the derived class is the eigenclass of the base class;
Fourth, include includes instance methods (not singleton methods) of the included module, make them instance methods of the receiver class object.
There're two parallel inheritance chains in your example
Car < Vehicle < ...
Car's eigenclass < Vehicle's eigenclass < Automobile < ...
Do the following test on irb:
class Object
def eigenclass
class << self
self
end
end
end
Car.ancestors # => [Car, Vehicle, Object, Kernel, BasicObject]
Car.eigenclass.ancestors # => [Automobile, Class, Module, Object, Kernel, BasicObject]
Vehicle.eigenclass.ancestors # => [Automobile, Class, Module, Object, Kernel, BasicObject]
Car.eigenclass.superclass.equal? Vehicle.eigenclass # => true
You see, Automobile is in the eigenclass inheritance chain. But regretably, the ancestor method doesn't return invisible eigenclasses, nonetheless they are actually in the second chain.

Super keyword in Ruby

What is the super for in this code?
def initialize options = {}, &block
#filter = options.delete(:filter) || 1
super
end
As far as I know it's like calling the function recursively, right?
no... super calls the method of the parent class, if it exists. Also, as #EnabrenTane pointed out, it passes all the arguments to the parent class method as well.
super calls a parent method of the same name, with the same arguments. It's very useful to use for inherited classes.
Here's an example:
class Foo
def baz(str)
p 'parent with ' + str
end
end
class Bar < Foo
def baz(str)
super
p 'child with ' + str
end
end
Bar.new.baz('test') # => 'parent with test' \ 'child with test'
There's no limit to how many times you can call super, so it's possible to use it with multiple inherited classes, like this:
class Foo
def gazonk(str)
p 'parent with ' + str
end
end
class Bar < Foo
def gazonk(str)
super
p 'child with ' + str
end
end
class Baz < Bar
def gazonk(str)
super
p 'grandchild with ' + str
end
end
Baz.new.gazonk('test') # => 'parent with test' \ 'child with test' \ 'grandchild with test'
If there's no parent method of the same name, however, Ruby raises an exception:
class Foo; end
class Bar < Foo
def baz(str)
super
p 'child with ' + str
end
end
Bar.new.baz('test') # => NoMethodError: super: no superclass method ‘baz’
The super keyword can be used to call a method of the same name in the superclass of the class making the call.
It passes all the arguments to parent class method.
super is not same as super()
class Foo
def show
puts "Foo#show"
end
end
class Bar < Foo
def show(text)
super
puts text
end
end
Bar.new.show("Hello Ruby")
ArgumentError: wrong number of arguments (1 for 0)
super(without parentheses) within subclass will call parent method with exactly same arguments that were passed to original method (so super inside Bar#show becomes super("Hello Ruby") and causing error because parent method does not takes any argument)
I know this is late but:
super method calls the parent class method.
for example:
class A
def a
# do stuff for A
end
end
class B < A
def a
# do some stuff specific to B
super
# or use super() if you don't want super to pass on any args that method a might have had
# super/super() can also be called first
# it should be noted that some design patterns call for avoiding this construct
# as it creates a tight coupling between the classes. If you control both
# classes, it's not as big a deal, but if the superclass is outside your control
# it could change, w/o you knowing. This is pretty much composition vs inheritance
end
end
If it is not enough then you can study further from here
Bonus:
module Bar
def self.included base
base.extend ClassMethods
end
module ClassMethods
def bar
"bar in Bar"
end
end
end
class Foo
include Bar
class << self
def bar
super
end
end
end
puts Foo.bar # => "bar in Bar"
Super in a method of a class , say test_method, is used to call another method with same name i.e test_method of a parent class.
The code written above and below the super keyword will be executed normally and the whole bunch of code action of the method of super class will be included at the place of super keyword.

Resources