Delegating to a method defined in an included module - ruby

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.

Related

Confused on hierarchy in 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

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

Best practice to connect objects between them in ruby

I cannot manage to find a nice way to implement the rails behavior of objects navigation rails has in a pure ruby script.
Say I have an object of class Parent, with an accessor to an Array of objects of class Child, I'd like, when I manipulate a Child object, to be able to get the Parent easily, like that :
class Parent
attr_accessor :children
def initialize
#children = Array.new
end
end
class Child
end
parent = Parent.new
first_child = Child.new
second_child = Child.new
parent.children << [ first_child, second_child ]
a_child = parent.children.first
get_parent_from_child = a.child.parent
The part I'm interested in is, of course, the last line in which I attempt to get the "parent" object from one of its children.
How could I implement it easily and cleanly ?
I was thinking of adding an accessor to the child object, but I'm not sure how to make sure this value is set everytime I attach a child object to a parent object.
Is there an easy and clean way of doing that in ruby ?
Thanks in advance.
You don't have to fully expose your children you know, there's nothing wrong with fully controlling access to your data.
class Parent
def initialize
#children = [ ]
end
# You get a shallow copy of the children, you can
# change the individual children but not the tree
# structure.
def children
#children.dup
end
# We take ownership of the children when you add them.
def add_children(*kids)
kids.each { |k| k.parent = self }
#children += kids
end
def remove_children(&block)
#children.delete_if(&block)
end
end
# This would probably have some sort of payload and
# a sensible "==" implementation.
class Child
attr_reader :parent
def parent=(p)
raise StandardError => 'Not allowed to change parents!' if #parent
#parent = p
end
end
Then child.parent works fine and if you want to remove children you'd tell the parent which ones to remove:
# Remove the children with even payloads.
parent.remove_children { |c| c.payload % 2 == 0 }
# Remove the children with odd payloads and copy them.
odds = [ ]
parent.remove_children do |c|
kill_it = c.payload % 2 != 0
odds << Child.new(c.payload) if(kill_it)
kill_it
end
You can achieve the has_many behavior from Rails by using a custom container class, like Rails does.
class Parent
attr_reader :children
def initialize
#children = ChildCollection.new(self)
end
end
class Child
attr_accessor :parent
end
class ChildCollection
def initialize parent
#children = []
#parent = parent
end
def new
child = Child.new
child.parent = #parent
#children << child
child
end
def << child
child.parent = #parent
#children << child
self
end
end
And some example code to add children to the parent:
parent = Parent.new
child1 = parent.children.new
child2 = Child.new
child3 = Child.new
parent.children << child2 << child3
puts "Parent is: #{parent.object_id}"
puts "Child1's parent is: #{child1.parent.object_id}"
puts "Child2's parent is: #{child2.parent.object_id}"
puts "Child3's parent is: #{child3.parent.object_id}"
You may want to throw on some other helpful methods like remove and each (so you can have ChildCollection include the Enumerable module and get all the cool stuff that comes with that) but this should be enough to get you started.
Write an own accessor for children in the class Parent in which you set a parent attribute for each child added to the array. Do the reverse when removing children from the array. This is how I would do it.

Resources