Ruby Metaprogramming: Check if called by parent class? - ruby

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

Related

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

ruby mixing and inheritance injection

I need to inject a callbacks in every child class of a Parent class. So, method with callbacks must be called first, and all present chain later:
it is possible to achive thought alias_method (or alias_method_chain):
module ChildMod1
def save
puts "save ChildMod1"
super
end
end
module ChildMod2
def save
puts "save ChildMod2"
super
end
end
class Parent
def save
puts "save Parent"
end
end
class Child < Parent
include ChildMod1
include ChildMod2
def save
puts "save Child"
super
end
alias_method :old_save, :save
module_eval <<-R
def save
puts "save Callback"
old_save
end
R
end
c = Child.new
c.save
output
save Callback
save Child
save ChildMod2
save ChildMod1
save Parent
but is it possible to achieve this via inheritance? like in ChildMod1 or ChildMod2. I whant to execute a code inside a module space to get all benefits from inheritance
module ChildMod1
def save
puts "save ChildMod1"
super
end
end
module ChildMod2
def save
puts "save ChildMod2"
super
end
end
class Parent
def save
puts "save Parent"
end
end
class Child < Parent
include ChildMod1
include ChildMod2
def save
puts "save Child"
super
end
module_eval <<-R
def save
puts "save Callback"
super
end
R
end
c = Child.new
c.save
Output
save Callback
save ChildMod2
save ChildMod1
save Parent
as you see, it just overwrite Child
UPDATE
wdebeaum solution is good, but what if i need to create a lot of methods dynamically thought module_eval or analog and redefine them inside a class? I cannot create a separate module for them.
class TestEval
def redefine_me
puts "Test method"
super # I expect that it will call Eval method, but module_eval just overwrite it
end
module_eval <<-R
def redefine_me
puts "Eval method"
end
R
end
UPDATE2
using a singleton class i'll got wrong chain Eval => Test instead of Test => Eval
class TestEval
def initialize
class << self
def redefine_me
puts "Eval method"
super
end
end
end
def redefine_me
puts "Test method"
end
end
TestEval.new.redefine_me
Let assume, that I have a class method "field", that add some instance methods to a Datastream (for ex it'll add setter and getter methods) and I whant to redefine one of this methods, like this:
class Datastream
field :name
def name=(value)
puts "redefined!"
super
end
end
You could put the callback method in its own module, and rewrite the Parent's initialize method to extend that module (using alias_method if necessary). This will put the callback method before the Child's method, by linking it to each Child instance's singleton class. Just remove the module_eval part from your second code example, and add this before c = Child.new:
module Callback
def save
puts "save Callback"
super
end
end
class Parent
alias_method :old_initialize, :initialize
def initialize
old_initialize
extend Callback
end
end
Output:
save Callback
save Child
save ChildMod2
save ChildMod1
save Parent

setup settings send to its parent class in ruby

how can i write this (child) class like so:
class child < parent
create_columns :name, :address
end
so that:
class parent
# Can access the create_columns set by the child class?
end
Thanks.
You can solve this by using the inherited hook method in Ruby, so you can track all the children.
class Parent
self.inherited(base)
self.children << base
end
end
class Child < Parent
def initialize
##instances << self
end
def self.instances
##instances
end
Now you can do things like Parent.children.each { |child| child.instances.collect(:&name) }. If name is accessable :-)
Hope that helps!

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