Get child constant in parent method - Ruby - ruby

In Ruby, is it possible to go about getting a child's constant when I've called a parent's method through the child?
Example:
class Tester
class Parent
def go
EWOCK
end
end
class Child < Parent
EWOCK = "EWOCKS rule"
end
end
Then call Tester::Child.new.go and desire "EWOCKS rule" to come back?
[Edit 3/31]
Wow I'm REALLY sorry guys. I completely screwed up the explanation.
Parent should have been Child and Child should have inherited from base.
The call should have been to Child and not Parent
Again, many apologies and thanks to those who replied attempting to understand my horrid writeup.
It is fixed now.

EDIT: this answer is correct, although Wayne's is the more ruby-ish way to approach the problem.
Yes it is.
Your implementation will not work, because the parent tries to resolve EWOK locally. Parent doesn't have EWOK defined. However, you can tell Ruby to look specifically at the class of the actual instance the method was called on, to get EWOK.
this will work:
class Parent
def go
self.class::EWOK
end
end
class Child < Parent
EWOK = "Ewoks Rule"
end
class Child2 < Parent
EWOK = "Ewoks are ok, I guess"
end
bob = Child.new
bob.go # => "Ewoks Rule"
joe = Child2.new
joe.go # => "Ewoks are ok, I guess"
what's going on here:
in Parent's 'go', "self" will refer to the instance of the object that 'go' is actually being called on. i.e., bob (a Child), or joe (a Child2). self.class gets the actual class of that instance - Child in the case of bob, or Child2 in the case of joe.
then, self.class::EWOK will retrieve EWOK from the correct class.

For a parent class to have access to a constant defined in a child class, wrap that constant in a method. Then the normal inheritance rules apply:
class Parent
def ewok
"Ewoks are lame"
end
end
class Child < Parent
def ewok
"Ewoks rule"
end
end
p Parent.new.ewok # Ewoks are lame
p Child.new.ewok # Ewoks rule
If the constant is expensive to initialize (a large hash, for example), the define it in a constant, but access it via a method:
class Parent
EWOK = {
# Enormous hash...
}
def ewok
EWOK
end
end

Related

Can I call a subclass' private method from its parent class?

After reading about Ruby's access controls, I understand that a private method may only be called, implicitly, from within a class and within that class' subclasses. I have an example, though, where a class seems to be calling a private method default_chain on its subclasses, and it still works. Check out the following code (adapted from Sandi Metz' Practical Object-Oriented Design in Ruby):
class Bicycle
attr_reader :chain
def initialize(args={})
#chain = args[:chain] || default_chain
end
def parts
{
chain: chain
}
end
end
class RoadBike < Bicycle
def parts
super.merge(
handlebar_tape_color: "red"
)
end
private
def default_chain
"21-speed"
end
end
class MountainBike < Bicycle
def parts
super.merge(
suspension: "Manitou Mezzer Pro"
)
end
private
def default_chain
"10-speed"
end
end
RoadBike.new.parts # {:chain=>"21-speed", :handlebar_tape_color=>"red"}
MountainBike.new.parts # {:chain=>"10-speed", :suspension=>"Manitou Mezzer Pro"}
What's going on?
You're getting it wrong - in your example, there is no such a thing as the parent class calling children methods.
Methods/constants name lookup in Ruby always works "bottom up": first we check if the method is defined in object's class, then in object's class's superclass and so on (this is a huge simplification because Ruby's object model is more complicated, more on this later). So, in your example things happen in roughly the following order:
When you call RoadBike.new runtime checks if there is an initialize methods defined for the class RoadBike. There is no, so we use the implementation defined for its parent class - Bycicle (but the execution context stays the same - it is still RoadBike instance)
When executing Bycicle#initialize runtime encounters another method call - default_chain. At this moment we start method name resolving in the very same manner - starting from the RoadBike context. Does RoadBike have its own implementation of default_chain? Yes, it does, so we simply call it.
The following baby example makes it crystal clear, hopefully:
class Parent
def initialize
puts "Parent Initializer is called"
a
b
end
def a
puts "Parent a is called"
end
def b
puts "Parent b is called"
end
end
class Child < Parent
def b
puts "Child b is called"
end
end
pry(main)> Child.new
Parent Initializer is called
Parent a is called
Child b is called
In reality the methods/constants resolution machinery is more complicated(includes so-called singleton classes). This is a bigger topic that will not fit nicely in SO answer, so I strongly recommend reading "Metaprogramming Ruby 2" by Paolo Perotta where this model is wery well explained in great details from the very practical point of view.

Calling super, super class method with parameters in Ruby

I found the below working solutions (link 1, link 2) which call the grandparent method but without any parameters. Does anyone know how to call the grandparent method with parameters?
class GrandParent
def fun(word)
puts word
end
end
class Parent < GrandParent
def fun(word)
puts word + 'end'
end
end
class Child < Parent
def fun(word)
GrandParent.instance_method(:fun).bind(self).call
end
end
You can pass parameters directly to call like this:
class Child < Parent
def fun(word)
GrandParent.instance_method(:fun).bind(self).call(param1, param2)
end
end
call accepts parameters
GrandParent.instance_method(:fun).bind(self).call word
I don't know your use case, but this is a Really Bad Idea™. It creates unnecessary dependencies directly between Child and GrandParent such that all kinds of normally reasonable refactoring would result in a crash e.g. moving fun to only be implemented in Parent, changing Parent to subclass a different but similar parent, etc.

Accessing a child's constant from the parent's class

If a child inherits from the parent, it inherits the parents methods as well.
So how come an inherited method can't access the child's constants?
Example -
class Parent
def my_method
puts "Value of FOO is #{FOO}"
end
end
class Child < Parent
FOO = "bar"
end
Child.new.my_method #=> NameError: uninitialized constant Parent::FOO
Doesn't the inherited method run "inside" the child class?
How can I get around this?
No it doesn't. When you call Child.new.my_method it looks for the FOO constant in Parent and upwards.
I'm not going to question your approach and just provide you with a way to do what you want:
class Parent
def self.inherited(other)
other.define_singleton_method(:my_method) do
puts "Value of FOO is #{other.const_get(:FOO)}"
end
end
end
Now whenever you inherit from Parent the inherited hook will define a method my_method on the subclass. Notice that the scope of the block in the inherited method is still the class level scope of Parent. That's why I'm directly referencing the subclass (other) to get the constant.
A way around this would be to use other.instance_exec { ... }.
Update
#maxpleaner pointed out (with the explanation provided by #CarySwoveland) that you could also retrieve the class via self and then retrieve the constant from it.
I.e., just do
class Parent
def my_method
puts "Values of FOO is #{self.class.const_get(:FOO)}"
end
end
That works because when calling child = Child.new; child.my_method the value of self within my_method will be child. And the class of child is obviously Child which knows the constant FOO.

Ruby Inheritance Get Caller Class Name

I'm so lost. I know how to use caller to get the caller method, but what do you use the get the caller class?
For example:
class Testing
def return_caller_class
return caller_class
end
end
class Parent
attr_accessor :test_me
def initialize
self.test_me = Testing.new
end
end
class Child < Parent
end
class GrandChild < Child
end
test_Parent = Parent.new
test_Child = Child.new
test_GrandChild = GrandChild.new
puts test_Parent.test_me.return_caller_class => Parent
puts test_Child.test_me.return_caller_class => Child
puts test_GrandChild.test_me.return_caller_class => GrandChild
Thank you!!!
Edit:
I've tried to do the following
class Testing
def return_caller_class
return caller[0][/`.*'/][1..-2]
end
end
And the output is:
{"
"=>Parent}
{"
"=>Child}
{"
"=>GrandChild}
To explain better about my question.
I would the output to display this instead
Parent
Child
GrandChild
I'm a bit out of my depth with this question, but I think you have made a few mistakes unrelated to the problem of getting the caller's class name. If I can help you with those things, at least you might be a step closer (if a solution is even possible)!
Firstly, it seems to me that you're calling return_caller_class from the main program object, not from one of those three objects you created. You have an object of class Testing inside an object of class Parent, but the method call is outside of both.
Secondly, the only reason you seem to be getting anything close to what you want (when you get output like "=>Parent} has nothing to do with the return_caller_class method. It seems as though you inadvertently created little hashes in the last three lines of your program (when you added => Parent, etc), which are being output with puts. (Confirmed here: Has #puts created a new hash?) If these are meant to be comments, they need a # before them.
PS. I found a link to this gem on another thread: https://github.com/asher-/sender. Might be worth checking out.
class Testing
def return_caller_class
self.class.name
end
end
class ChildOne < Testing
end
class ChildTwo < Testing
end
result:
------------------------------------------------
>ChildOne.new.return_caller_class
=> "ChildOne"
>ChildTwo.new.return_caller_class
=> "ChildTwo"
>Testing.new.return_caller_class
=> "Testing"

Calling super class' super class method?

How can I get child to ignore what its parent thinks is fun and go straight to the grandparent's idea of fun?
Child still inherits from the parent, but it just doesn't agree with a couple methods.
Calling the method of the super class of the super class?
Also, is it considered poor design if I'm in a situation where the child doesn't agree with its parents but agrees with the parent's parents?
class Grandparent
def fun
#do stuff
end
end
class Parent < Grandparent
def fun
super
#parent does some stuff
end
def new_business
#unrelated to my parent
end
end
class Child < Parent
def fun
super
#child also does some stuff
end
def inherit_new_business
new_business
#stuff
end
end
It's generally easier in Ruby to get this kind of behavior through composition rather than inheritance. To accomplish that Modules are included that contain the specific behaviors you wish a class to have.
But if you absolutely have to use inheritance you can do this:
class Child < Parent
def fun
GrandParent.instance_method(:fun).bind(self).call
# fun child stuff
end
end
This will do exactly what it says. Grab the instance method fun from the GrandParent class, attach it to the current instance object self and call it.

Resources