Confused on hierarchy in Ruby - ruby

If we can do this:
class Child
def child_method
puts "hi world"
end
end
child = Child.new
child.child_method
Which calls the method puts which is defined in Kernel and mixed in Object.
Why can't we do this:
class Parent
def test
puts "hi from parent"
end
end
class Child
def child_method
test
end
end
child = Child.new
child.child_method
Why can't we call the method on a Parent object, as we do with "puts"?

When you wirte as below :
class Child
def child_method
puts "hi world"
end
end
By defaults Object becomes super class of Child. Object mixed in the Kernel. So Kernel also in the ancestor chain of Child. Thus you are able to call the puts private method of Kernel.
child = Child.new
child.class.ancestors # =>[Child, Object, Kernel, BasicObject]
Now coming to your below class definitions:
class Parent
def test
puts "hi from parent"
end
end
p Parent.ancestors # => [Parent, Object, Kernel, BasicObject]
class Child
def child_method
test
end
end
p Parent.ancestors # => [Parent, Object, Kernel, BasicObject]
As you didn't inherit Parent to Child class, again Object becomes the default super class of Child. So the test method will not be available to the Child class.If you want it do as below :
class Parent
def test
puts "hi from parent"
end
end
class Child < Parent
def child_method
test
end
end
child = Child.new
child.child_method # => "hi from parent"
Now it is working as the Parent is now in the ancestor chain of Class.
child.class.ancestors # => [Child, Parent, Object, Kernel, BasicObject]

You forgot to inherit. class Child < Parent and it works. Just because you call the classes Child and Parent, they don't become so. :P

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.

Delegating to a method defined in an included module

Is there a way in Ruby (2.5) to call a method defined in an included module in the same way super works for inheritance?
I mean, supposing I have the following
class Parent
def name
"parent"
end
end
class Child < Parent
def name
"my parent is #{super}"
end
end
When I call Child.new.name, I get my parent is parent.
How can I achieve the same in the following situation?
module Parent
def name
"parent"
end
end
class Child
include Parent
def name
"my parent is #{???}"
end
end
What should I replace ??? with?
Thank you
You can still use super:
module Parent
def name
"parent"
end
end
class Child
include Parent
def name
"my parent is #{super}"
end
end
Child.new.name # => "my parent is parent"
When including a module, ruby will add this module to inheritance chain.
Child.ancestors # => [Child, Parent, Object, Kernel, BasicObject]
Thus, calling super in Child will call the corresponding methods in its ancestors.
We are given the module Parent:
module Parent
def name
"parent"
end
end
and the class Child:
class Child
include Parent
def name
"my parent is #{super}"
end
end
As #Tai points out, we can use super to obtain:
Child.new.name
#=> "my parent is parent"
because
Child.ancestors
#=> [Child, Parent, Object, Kernel, BasicObject]
In what sense is the module Parent the "parent" of the class Child? Yes, those are just words, but it is misleading.
Now let's create another module and have Child include it.
module A
def name
"A"
end
end
Child.include A
Child.new.name
#=> "my parent is A"
because
Child.ancestors
#=> [Child, A, Parent, Object, Kernel, BasicObject]
We see that it would been clearer to define Class#name as follows:
def name
"the last module included is #{super}"
end
Let's try one more thing: using Module#prepend.
module B
def name
"B"
end
end
Child.prepend B
Child.new.name
#=> "B"
not "my parent is B", because
Child.ancestors
#=> [B, Child, A, Parent, Object, Kernel, BasicObject]
meaning that B#name (which does not call super) was executed (not Child#name). This means the results are meaningless if any modules have been prepended.

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

Ruby execute code when inherited class part initialised

I know it's probably a long shot, but I thought I'd ask:
Since Ruby does not execute the initialize method of a parent class unless you explicitly call super in the inheriting class's initialize method (or unless you don't overload it in the inheriting class), I was wondering if there's some other way to execute code as part of the parent context (maybe a hook) when instantiating a new instance of an inheriting class...
When implementing B's initialization method, this is currently the behaviour:
class A
def initialize
puts "Inside A's constructor"
end
end
class B < A
def initialize
puts "Inside B's constructor"
end
end
A.new
B.new
# Output
# => Inside A's constructor
# => Inside B's constructor
I was wondering if the output could somehow be:
A.new
# => Inside A's constructor
B.new
# => Inside A's constructor
# => Inside B's constructor
Of course you can, simply call super in the subclass initialize method
class A
def initialize
puts "Inside A's constructor"
end
end
class B < A
def initialize
super
puts "Inside B's constructor"
end
end
A.new
B.new
Output:
Inside A's constructor
Inside A's constructor
Inside B's constructor
class A
def initialize
puts "Inside A's constructor"
end
end
class B < A
def initialize
super
puts "Inside B's constructor"
end
end
A.new
B.new
Output:
Inside A's constructor
Inside A's constructor
Inside B's constructor

Ruby Metaprogramming: Check if called by parent class?

Given the following classes
class Parent
def hello
puts "I am the parent class"
end
def call_parent_hello
hello
end
end
class Child < Parent
def hello
puts "I am the child class"
end
end
When I do the following:
c = Child.new
c.hello # => Outputs: "I am the child class"
c.call_parent_hello # => Outputs: "I am the child class"
Is it possible to make Child#call_parent_hello access the Parent#hello, but without altering the Parent class?
I am looking for some kind of called_by_parent_class? implementation like this:
def hello
if called_by_parent_class?
super
else
puts "I am the child class"
end
end
You can use the super keyword:
class Child < Parent
def hello
super
end
end
I think you're looking to do something like this:
class Parent
def hello( opts = '' )
"Who's talking? The #{self.class} class is via the Parent class!"
end
end
class Child < Parent
def hello( opts = '' )
if opts == 'super'
super
else
"I have a parent and an independent voice"
end
end
def call_mom
hello( 'super' )
end
end
c1 = Child.new
puts c1.hello => "I have a parent and an independent voice"
puts c1.call_mom => "Who's talking? The Child class is via the Parent class!"
However (and I'm not trolling here) I also think you're kind of missing the point of subclassing. Generally you would subclass to get this automatic scoping of methods. If you break out of that I think you would want to instantiate an instance of Parent. But to each his own.
Good luck!
After rereading your question I see your real question is:
Is it possible to make Child#call_parent_hello access the Parent#hello, but without altering the Parent class?
Changing your child class to be:
class Child < Parent
alias_method :call_parent_hello, :hello
def hello
puts "I am the child class"
end
end
solves the problem just as you ask
Use super. super calls the same method in the parent class
class Child < Parent
def call_parent_hello
super
end
end
Use direct hierarchy call. Class#ancestors gives you the hierarchy of the inheritance.
class Child < Parent
def call_parent_hello
self.class.ancestors[1].new.hello
end
end

Resources