Call derived class instance method from parent class in ruby - ruby-on-rails

I have a parent class Parent and its child class Child. Child class contains a method child_method which I have to call from Parent class. I have tried a couple of approaches, one of them is below:
class Child < Parent
def child_method(params)
# ...
end
def some_other_method(params)
Parent.call_child_method(params, &method(:child_method))
end
end
class Parent
def self.call_child_method(params, &callback)
# Some common code which it's Child classes share
callback.call(params)
end
end
Below is the error that I get:
NameError Exception: undefined local variable or method `params'
for <Child:0x00000000154f53e8>
Did you mean? params
And in case you are wondering why I'm not directly calling child_method from Child class itself. Well, the reason beging that 2 different child classes duplicate that code which then call different methods with different params and the constraints are such that I can't return after calling the call_child_method from Child class and then make a call to child_method. I must call those methods(other child class has another method with different number of params) while I am inside 'call_child_method' only. Moreover, the old code was not written by me and due to time constraints I don't want to refactor the whole Design logic. So, what options do I have here ?

Your code already works, so I don't know what the question is.
However, one thing I will say is that there's a standard way to handle control flow like this, without resorting to method meta-programming: yield. You can do something like this:
class Parent
def common_logic(params)
# Some common code which it's Child classes share
yield
end
end
class Child < Parent
def child_method(params)
# ...
end
def some_other_method(params)
common_logic(params) { child_method(params) }
end
end

Your code almost works, but you forgot the def keyword when defining the call_child_method method.
The following works on my system:
class Parent
def self.call_child_method(params, &callback)
# Some common code which it's Child classes share
callback.call(params)
end
end
class Child < Parent
def child_method(params)
p "The params are", params
end
def some_other_method(params)
Parent.call_child_method(params, &method(:child_method))
end
end
Child.new.some_other_method("hello")
I get output:
"The params are"
"hello"

Related

How rails is handling the inheritance concept, parent accessing child

This raises an error "undefined method hi":
class A
def bla
hi
end
end
class B < A
def hi
puts "Hii"
end
end
a = A.new
a.bla
I have a Rails application that does this:
class BlaParentController < ApplicationController
before_filter :setting
def create
#random_obj = ParentRandom.create(permitted_params)
end
end
class BlaController < BlaParentController
private
def setting
end
def permitted_params
end
end
A method call BlaController#create goes to BlaParentController#create. But BlaParentController is accessing methods permitted_params and setting from its child BlaController. How this is possible?
For ordinary methods, what matters is whether it is defined for the receiver. Other kinds of contexts (such as, from which method it is called) does not matter. (The few exception to this are methods like Kernel#__dir__, Module#nesting, Exception#backtrace.)
In the first example, the receiver is an instance of A, but not B. Hence it can access methods in A, but not those in B.
In the second example, the receiver is an instance of BlaController, which is a subclass of BlaParentController. Hence it can access methods in BlaController as well as those in BlaParentController.

Rails 4 override a model attribute with parent attribute

What is the best way to achieve the following in Rails 4? Parent and child model have the same attribute. If child model hasn't set said attribute then it inherits from the parent, otherwise, it renders its own value.
I tried to create a method in the child method with the same name as the model attribute to do the logic, but that causes a stack level too deep error.
def age_of_seniority
age_of_seniority.present? ? age_of_seniority : borough.age_of_seniority
end
Update
I don't want to change the method name, I would like to be able to access it as a normal attribute
You can do this using read_attribute
def age_of_seniority
read_attribute(:age_of_seniority) || borough.age_of_seniority
end
Call super:
def seniority_age
super || borough.age_of_seniority
end
A simple example:
class Parent
attr_accessor :seniority_age
end
class Child < Parent
def seniority_age
super||'foo'
end
end
c = Child.new
puts c.seniority_age
c.seniority_age = "bar"
puts c.seniority_age
returns:
foo
bar
You are recursively calling the method from within the method, this of course will cause a stack overflow.
def age_of_seniority
age_of_seniority.present? ? age_of_seniority : borough.age_of_seniority
end
If the age_of_seniority is stored in the instance variable, you access it with:
#age_of_seniority
So in your code:
def age_of_seniority
#age_of_seniority.present? ? #age_of_seniority : borough.age_of_seniority
end
And to call the overrided method from parent, you can just use super.
not quite sure what's borough.age_of_seniority doing.

Use attribute of a derived class in parent

I'm trying to use define_method to create additional methods for classes inheriting from a superclass:
class Child < Parent
ADDITIONAL_METHODS += ['xyz', 'qwe']
end
class Parent
ADDITIONAL_METHODS = ['common']
ADDITIONAL_METHODS.each do |key|
define_method key do
...
end
end
end
This doesn't work because ADDITIONAL_METHODS is always taken from the Parent class and the only method created is common. Is there a way to access the attribute from the derived class?
The example code would not work, because you use Parent as ancestor of Child before declaring Parent.
This would produce this error :
uninitialized constant Parent (NameError)
If it actually works for you, it means that Parent has indeed be declared before Child. In that case, the #each loop on ADDITIONAL_METHODS is performed before Child even exists, since instructions you give in a class outside method definition are executed right away :
class Foo
def initialize
puts "second"
end
puts "first"
end
Foo.new
puts "third"
Outputs :
first
second
third
Solution
You may want to implement a class method and call it right away, to perform that.
class Parent
private
def self.add_my_methods( *methods )
( methods.empty? ? [ 'common' ] : methods ).each do |key|
define_method key do
p key
end
end
end
add_my_methods # will implement "common"
end
class Child < Parent
add_my_methods 'xyz', 'qwe'
end
c = Child.new
c.common # outputs "common"
c.xyz # outputs "xyz"
c.qwe # outputs #qwe"
This is an usual pattern for metaprogramming on descendants, like you probably already encountered it with methods like #has_many, #before_filter, etc.

Extending a controller in Rails

I have a controller which calls out to another class.
class BlahController < ActionController
def index
OtherClass.get_stuff
end
end
In this class I want to be able to write controller style code.
for instance:
class OtherClass
def self.get_stuff
#foo = bar
end
end
However, I would also like #foo to exist when inside my view, but as it's a separate class those variables aren't making it back through into the controller assigns - so question is, how I can make this so?
(Ignore why I'm having to call out to a separate class, I'm trying to get this code fitting in with a legacy codebase without too much butchery)
class BlahController < ActionController
def index
OtherClass.get_stuff(self)
end
end
class OtherClass
def self.get_stuff(that)
that.instance_variable_set(:#foo, bar)
end
end
Please note that I don't agree with this method. I am just answering the question as you stated it.
I would prefer to accomplish this functionality through mixins and thereby decrease parameter coupling that is present within the code above.
Code structured like this will be difficult to read and maintain. Whenever you can, let the controller directly set all of the variables that the view needs:
class BlahController < ActionController
def index
#foo = OtherClass.get_stuff
end
end
class OtherClass
def self.get_stuff
# return the value that should be assigned to #foo
end
end

How to I get the controller_name of a subclassed controller from its parent?

I'm using some namespaced controllers that also inherit from a parent controller. In each subclass I need to have (for anyone wondering why...):
class Fruits::ApplesController < FruitsController
# controller_name below is 'apples'
require_dependency "fruits/#{controller_name}"
...
end
So, since I'd rather have the require_dependency line once in my parent class I tried to move it to FruitsController, but the problem is that controller_name is now equal to "fruits"..
class FruitsController < ApplicationController
# controller_name is 'fruits' no matter which subclassed controller is called
require_dependency "fruits/#{controller_name}"
...
end
So how can I properly get the value of the subclassed controller name in FruitsController, so that I can keep that require_dependency line out of my subclasses? controller_path doesn't help either.
Thanks!
As written, your "require_dependency" statement is only executed once, when the parent loads.
You could potentially use the Class#inherited method to require your dependency, like this (code untested).
class FruitsController < ApplicationController
def self.inherited(subclass)
subclass.require_dependency subclass.to_s.underscore
end
end
The require statement above in the Fruits class is executed only once during the loading of the parent class, which means subclasses won't have it executed again. Check the following example:
class A
puts name
end
class B < A
end
#=> A
So, you have to execute a separate require per subclass and thus you can't refactor it that way you want.
As I mentioned above in my response to #ElliotNelson (thanks a lot btw!), here's the code that I've placed in my FruitsController that has allowed me to re-factor my original code:
def self.inherited(subclass)
subclass.require_dependency subclass.controller_name
super
end

Resources