How to find any methods ancestor chain? - ruby

class Taco
# . . .
end
Get ancestor chain:
Taco.ancestors
#=> [Taco, Object, Kernel, BasicObject]
Say I want to find the "parent" class and it's ancestor chain for a ruby defined method. How would I go about doing that?
E.g. method_missing.parent.ancestors
And if everything is supposed to inherit from BasicObject why doesn't Kernel?
Object.ancestors
#=> [Object, Kernel, BasicObject]
Kernel.ancestors
#=> [Kernel]
BasicObject.ancestors
#=> [BasicObject]
Also Class inherits from Class and Module but why does my Taco class ancestor's chain not inherit from them and instead inherits directly from Object forward?
Class.ancestors
#=> [Class, Module, Object, Kernel, BasicObject]

You are looking for owner.
method(:puts).owner
#=> Kernel
method(:puts).owner.ancestors
#=> [Kernel]
Back to your taco example:
class Taco
def self.eat
"YUM"
end
end
Taco.method(:eat).owner
#=> #<Class:Taco>
Taco.method(:eat).owner.ancestors
#=> [Class, Module, Object, PP::ObjectMixin, Kernel, BasicObject]
Kernel is an instance of module. Check this out:
Kernel.class.ancestors
#=> [Module, Object, PP::ObjectMixin, Kernel, BasicObject]
Here is some further reading on the ruby object model if you're interested. Also, here's an image stolen from google images that may help solidify those concepts.

Specifically regarding your question: "Also Class inherits from Class and Module but why does my Taco class ancestor's chain not inherit from them and instead inherits directly from Object forward?"
Taco is an instance of Class, but its superclass is Object. An instance relationship and a superclass relationship are 2 completely different things.
For example: imagine you create a class Animal with a subclass Dog. Animal.new will give you an object which is an instance of Animal. The Dog class is not an instance of Animal (it is not itself an animal -- rather, it defines a type of animal). Its superclass is Animal.
Every object is an instance of some class -- call class to find out which one. But not all objects have a superclass -- only instances of Class do.
Additionally, Class does not inherit from Class. That is impossible. All classes appear at the beginning of their own ancestors, but that doesn't mean they inherit from themselves. That is just how the ancestors method works.

Related

Ruby: Are these two methods of mixing-in module methods equivalent?

I have a module and a class.
module Dog
def speak
"woof"
end
end
class Daschund; end
I create two separate instances of the class.
sausage = Daschund.new
saveloy = Daschund.new
If I want to add Dog#woof as an instance method of my two new objects, I can do it in two ways:
class << sausage
include Dog
end
> sausage.speak
=> "woof"
saveloy.extend Dog
> saveloy.speak
=> "woof"
Are the two methods equivalent? I know the first adds the module's method to the object's meta-class. Does object#extend do the same thing? Or is it doing something slightly different? Is there any way to prove this?
According to the docs for Object, extending an object with a module means including that module to the object's singleton class:
extend: Includes the given modules in the singleton class of self.
Inspecting the ancestors of both objects' singleton classes confirms this:
sausage.singleton_class.ancestors
#=> [#<Class:#<Daschund:0x00007fa6af92e868>>, Dog, Daschund, Object, Kernel, BasicObject]
saveloy.singleton_class.ancestors
#=> [#<Class:#<Daschund:0x00007fa6af92e778>>, Dog, Daschund, Object, Kernel, BasicObject]
The actual implementation details of course depend on the Ruby implementation and version you are using. For MRI/YARV, you have rb_extend_object defined as:
void
rb_extend_object(VALUE obj, VALUE module)
{
rb_include_module(rb_singleton_class(obj), module);
}

How does Object know about the const_get method?

I was reading another question with an answer that mentions using the Module#const_get instance method to find a class in a module. For example:
module M
class C
end
end
p M.const_get 'C'
#=> M::C
I was curious about the const_get method so I used ri and found:
ri Module#const_get
...
This method will recursively look up constant names if a namespaced
class name is provided. For example:
module Foo; class Bar; end end
Object.const_get 'Foo::Bar'
...
It seems like Object::const_get is a singleton method. Using it in our context works:
module M
class C
end
end
p Object.const_get 'M::C'
#=> M::C
But there's nothing documented about that singleton method:
ri Object::const_get
Nothing known about Object::const_get
ri Object.const_get
Nothing known about Object.const_get
This confused me because I know a Module is an Object but an Object is not a Module:
Module.ancestors
#=> [Module, Object, Kernel, BasicObject]
Object.ancestors
#=> [Object, Kernel, BasicObject]
Except then I used the Object#is_a? instance method to check and saw that I was wrong about that:
Module.is_a? Object
#=> true
Object.is_a? Module
#=> true
What started as an innocent ri query has led me to be confused about the entire Ruby object model.
Why does Object.is_a? Module #=> true if Module is not in Objects ancestor chain?
How does Object know about the const_get method?
This is an artifact of the ill-understood separation between an object's class and an object's singleton class, a sort of shadow class that each class uses for things like this.
You can access this easily in Ruby 2.5+ with the singleton_class method:
Object.singleton_class.ancestors
# => [#<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
Where Module makes an appearance here, so that's how those methods get mixed in and are callable via Object.
Object itself has a comparatively dull inheritance chain:
Object.ancestors
#=> [Object, Kernel, BasicObject]
Every object in Ruby has a class, even Class is an Object, which also has an associated Class.
I think your confusion stems from looking at Object from two directions at once:
Object is a class so Object.ancestors can be used to look at the inheritance hierarchy. This tells you that Object < Kernel is true and Object < Module is false.
Classes in Ruby are also objects, specifically they're instances of the Class class. This tells you that Object.is_a? Class and Object.is_a? Module in the same way that 'pancakes'.is_a? String. And that Object.const_get is a method call in the same way that 'pancakes'.upcase is a method call.
You can think of some_obj.is_a? SomeClass as a shorter way of saying some_obj.class.ancestors.include? SomeClass.
Answering your specific questions:
Why does Object.is_a? Module #=> true if Module is not in Object's ancestor chain?
Because is_a? and ancestors are looking at different things.
How does Object know about the const_get method?
Because Object is an instance of the Class class and Class includes Module in its ancestors. Similarly to how 'pancakes' is an instance of the String class which has Kernel in its ancestors so 'pancakes' has an object_id method.
Object.is_a? Module #=> true
is_a? is asking whether Object is an instance (or specialised instance) of Module. Class is a subclass of Module so all instances of Class are really just specialised instances of Module. Since Object is an instance of Class, it follows that Object is also an instance of Module.
Module#const_get
const_get is an instance method defined within the Module class. Since Object is an instance of Module (for reasons discussed above), it has access to Module#const_get.

Ruby: Not able to understand why class method is accessible in child class

As per Ruby method lookup law whenever we invoke any method on an object then ruby finds the method using formula object.class.ancestors. If this is true then I should not be able to access parent method defined in parent class using Child class constant as Child.parent because the ancestors of Child class are [Class, Module, Object, Kernel, BasicObject]. But I can access it. Can anyone tell me why is so?
class Parent
def self.parent
puts "i am parent"
end
end
class Child < Parent
end
Child.parent # i am parent
my jruby version is jruby 1.7.16 (1.9.3p392) 2014-09-25 575b395 on Java HotSpot(TM) 64-Bit Server VM 1.8.0_20-b26 +jit [Windows 8.1-amd64]
Your understanding is not entirely correct. object.class.ancestors does not give you the full list of classes/modules where methods will be searched for. Here is a counter example:
foo = Object.new
def foo.bar
puts "I'm inside the singleton class"
end
foo.class.ancestors # => [Object, Kernel, BasicObject]
foo.bar # "I'm inside the singleton class"
Instead you have to start with the singleton class of the object:
foo.singleton_class.ancestors # => [#<Class:#<Object:0x007fa92c33c610>>, Object, Kernel, BasicObject]
foo.singleton_class.instance_methods.include?(:bar) # => true
Knowing that when invoking object.method_name, #method_name is searched for somewhere in
object.singleton_class.ancestors
and that Ruby classes are regular objects, it only stands that with Child.parent, #parent will be looked for somewhere in
Child.singleton_class.ancestors
The magic is that class methods in Ruby aren't special in any way. They are just defined in the singleton class of the class. In your example:
Child.singleton_class.ancestors # => [#<Class:Child>, #<Class:Parent>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
As you can see, the Child's singleton class ancestors chain includes the Parent's singleton class. That is why when you invoke Child.parent you actually call Parent.parent.
the parents of Child class are [Class, Module, Object, Kernel, BasicObject]
No, they're not:
Child.ancestors
#=> [Child, Parent, Object, Kernel, BasicObject]
You can access Child.parent because Child inherits from Parent.
In order to gain a full picture of what methods are available to instances of a class, however, you need to look at the singleton_class:
Child.singleton_class.ancestors
#=> [#<Class:Child>, #<Class:Parent>, #<Class:Object>,
#<Class:BasicObject>, Class, Module, Object,
Kernel, BasicObject]

Ruby ancestors method for singleton class objects

The Ruby documentation has a simple definition for the Module#ancestors method that talks about what ancestors returns when it's called on modules, but does not say anything about when ancestors is called on classes.
Returns a list of modules included in mod (including mod itself).
I'm trying to decipher the purpose of the ancestors method when it's called on classes, especially singleton class object. Here's an example:
module InstanceMethods; end
module ClassMethods; end
class A; end
class B < A
include InstanceMethods
extend ClassMethods
end
B.ancestors # => [B, InstanceMethods, A, Object, Kernel, BasicObject]
B.singleton_class.ancestors # => [ClassMethods, Class, Module, Object, Kernel, BasicObject]
Questions:
Why is the receiver not included in the ancestors list when the method is called on singleton_class objects? I would have expected B.singleton_class.ancestors to equal [B, ClassMethods, Class, Module, Object, Kernel, BasicObject]
I think the ancestor path for B's singleton class should be more like this (this isn't perfect because it shouldn't be using the singleton class of modules):
B.singleton_class.ancestors.map(&:singleton_class) # => [#<Class:ClassMethods>, #<Class:Class>, #<Class:Module>, #<Class:Object>, #<Class:Kernel>, #<Class:BasicObject>]
I typically use ancestors view the inheritance chain and get a better idea of Ruby's method lookup. Perhaps the method is not well suited for this task. What task is the ancestors method well suited for?
I think you'll really like the ruby 2.1 then!
$ rbenv local 2.1.0
roman.brodetski#nb-rbrodetski []
$ irb
...
irb(main):008:0> B.singleton_class.ancestors
=> [#<Class:B>, ClassMethods, #<Class:A>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
irb(main):009:0>
I also think it's a great thing that your mind is synchronized with the language developers!
Here is the redmine issue: https://bugs.ruby-lang.org/issues/8035

Ruby Object Model - ancestors of a class

I'm studying the Ruby Object Model from the book "Metaprogramming Ruby" and I understand the notion of how classes are Objects as well.
class A
end
A.ancestors # => [A, Object, Kernel, BasicObject]
A.class.ancestors # => [Class, Module, Object, Kernel, BasicObject]
What I'm confused about is that when I initially tried A.ancestors in irb, I expected the results that I got in A.class.ancestors - my thought process was: since A is a class, and a class is an instance of class Class, it's ancestor is Class. But Class doesn't seem to be an ancestor of A.
Would someone be able to clear up my confusion here?
The class A is an instance of Class, and you can see that via A.class
The class of an instance of A is A, and you access that via a = A.new; a.class
The method ancestors is showing the class hierarchy that an object of that class has (or would have) as its inheritance.
There are two parallel class hierarchy models going on in your example, and they only impinge on each other because Ruby represents its classes as objects for you to inspect and modify.
There is no fundamental reason to need A.class.ancestors and A.ancestors to intersect at all - except Ruby also has a deep class model with simple roots, so in practice that is what you'll see.
In fact I couldn't find any counter-example, even nil does this:
NilClass.ancestors
=> [NilClass, Object, Kernel, BasicObject]
NilClass.class.ancestors
=> [Class, Module, Object, Kernel, BasicObject]
This one is more enlightening though:
BasicObject.ancestors
=> [BasicObject]
BasicObject.class.ancestors
=> [Class, Module, Object, Kernel, BasicObject]

Resources