Super keyword in Ruby - 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.

Related

Child constructor arguments from the parent

How can a parent get the constructor arguments of a child ?
class A
include Parent
def initialize(foo, bar)
#foo = foo
#bar = bar
end
end
class B
include Parent
def initialize(foo)
#foo = foo
end
end
module Parent
def print_args
# here is the code for print args of child, this is not real code
puts Child.args # this is not real code
end
end
The expected behavior would be :
a = A.new('hello', 'world')
a.print_args
=> "hello world"
b = B.new('hello')
b.print_args
=> "hello"
The Parent module should not now the args names
One way is to have the "children" implement a method that returns their arguments:
class A
include Parent
def initialize(foo, bar)
#foo = foo
#bar = bar
end
def args
[#foo, #bar]
end
end
class B
include Parent
def initialize(foo)
#foo = foo
end
def args
[#foo]
end
end
The "parent" can than call that method without having to know its implementation:
module Parent
def print_args
puts args.join(' ')
end
end
If your module is included in many classes and you want to display instance variable values space separated, then you can do as follow,
using only ruby,
def print_args
instance_variables.map { |x| instance_variable_get(x) }.join(' ')
end
using rails,
def print_args
instance_values.values.join(' ')
end
You're asking how to get the "constructor arguments from the parent" and since almost everything is possible in Ruby: if you're really adventurous (read: don't do this), you can override the new method upon including Parent in order to intercept its arguments and define a singleton method on the instance which prints the argument:
module Parent
def self.included(mod)
def mod.new(*args)
super.tap do |instance|
instance.define_singleton_method(:print_args) do
puts args.join(' ')
end
end
end
end
end
Example usage:
class A
include Parent
def initialize(foo, bar)
end
end
A.new('hello', 'world').print_args
# prints "hello world"
The instance doesn't even have to store the arguments in instance variables.

Inheritance on different namespace

I have self-writed gem
module GemNamespace
class Foo; end
class Bar
def foo
#foo ||= Foo.new
end
end
end
Also I have application
module ApplicationNamespace
class Foo < GemNamespace::Foo; end
class Bar < GemNamespace::Bar; end
end
When I call foo method at my application it returned me instanceof GemNamespace object:
bar = ApplicationNamespace::Bar.new
puts bar.foo
=> #<GemNamespace::Foo:0x007f849d8169f0>
But I want get object of ApplicationNamespace how I can do this without redefine foo method
Your Problem is not, that you have several Namespaces, but that GemNamespace::Bar is tightly coupled to GemNamespace::Foo.
You could use something like this:
class Bar
def initialize(klass)
#klass = klass
end
def foo
#foo ||= #klass.new
end
end
So instead of only ever using GemNamespace::Foo within Bar, you could pass any class.
Your current version of the foo method will allways refer to GemNamespace::Foo because its context is set at definition (not at execution). Instead you could get the module of the current executing class dynamically. I don't think there is a build-in method that does this so you have to get it manually:
def foo
#foo ||= self.class.name.split("::")[0..-2].inject(Kernel) { |s, c| s.const_get c }.const_get("Foo").new
end
This will work for any number of nested modules.

super in the Ruby class

As I know 'super' calls the method of parent class. Here is a code from jekyll project:
def read_yaml(base, name)
super(base, name)
self.extracted_excerpt = extract_excerpt
end
And here is a class declaration:
class Post
There is no parent class. What is the 'super' on this context ?
Here is full class code.
super doesn't just call methods on the parent class but also those in included modules.
The general resolution order is
instance methods of the eigenclass (or singleton class, which is an alternative name for the same thing) of the object
instance methods of the object
methods in any included modules, starting from the last included module
if not yet found, do all previous steps for the parent class (and then up until a method is found)
In this case, its Convertible#read_yaml.
Including a module adds it as an ancestor of the class, allowing its methods to be called with super. Post includes both Comparable and Convertible, so the super method is in one of those classes.
For example:
module Foo; end
class Bar
include Foo
end
Bar.ancestors
# [Bar, Foo, Object, Kernel, BasicObject]
In Ruby, the super keyword calls a parent method of the same name using the same arguments.
It can also be used for inherited classes.
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'
Super can be called any number of times and can be used in multiple inherited classes.
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'
An exception will be raised if there is no parent method of the same name.
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’
Hope this helped.

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

Calling method in parent class from subclass methods in 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

Resources