Understanding ruby .class and .ancestors methods - ruby

I have a class defined as below
class Order
end
puts Order.class #-> Class
puts Order.ancestors #-> [Order, Object, Kernel, BasicObject]
puts Order.class.ancestors #->[Class, Module, Object, Kernel, BasicObject]
My question is why is it that Order.ancestors doesn't show 'Class' or 'Module' in ancestors chain? Since Order is an object of the class Class, shouldn't Order show all the ancestors of Class?

For that you need to see how the Ruby object model looks.
That means the classes created using keyword class will always be the subclass of Object by default. Class is not the superclass of your class Order, rather it is an instance of class Class.Module#ancestors will include list of modules included in mod (including mod itself) and the superclass of your class Order.
class Order;end
Order.superclass # => Object
Order.superclass.superclass # => BasicObject
Order.superclass.included_modules # => [Kernel]
So if you look at the output and understand the above code,then the below should now be clear to you:
Order.ancestors #-> [Order, Object, Kernel, BasicObject]
Now see,
class Order;end
Order.class # => Class
Order.instance_of? Class # => true
Order.class.superclass # => Module
Order.class.superclass.superclass # => Object
Order.class.superclass.superclass.included_modules # => [Kernel]
So if you look at the output and understand the above code, then the below should now be clear to you:
Order.class.ancestors #->[Class, Module, Object, Kernel, BasicObject]
That said Order.ancestors is giving you the ancestors of the class Order,whereas Order.class.ancestors is giving you the ancestors of the Class.

Related

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

How to find any methods ancestor chain?

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.

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]

Constants in singleton classes

I have this code:
class A
def print
puts CONSTANT
end
end
module B
CONSTANT = "Don't Panic!"
end
Suppose a is an instance of class A.
So I need a definition of CONSTANT that should be able to be found to make a.print available.
I considered to include the module B into a's singleton class like:
a.singleton_class.send :include, B
a.print # error: CONSTANT isn't found
I assumed it should be okay now to call the method but actually not.
The constant should be successfully imported as the following code works as expectation:
a.singleton_class.constants # => [.., :CONSTANT, ..]
However, by including the constant into the class instead of singleton class, it works:
a.class.send :include, B
a.print # prints: Don't Panic!
I thought it is strange that I can't refer a constant that is defined in a singleton class. If this is reasonable, I want to know why.
The class returned by singleton_class is an anonymous class which was inherited from class of the object a.
So a.class and a.singleton_class refer to different objects.
puts a.class.object_id # => 79495930
puts a.singleton_class.object_id # => 79495830
And also different classes: a.singleton_class is a child class of A.
a.singleton_class.superclass # => A
a.singleton_class.ancestors # => [B, A, Object, Kernel, BasicObject]
a.class.ancestors # => [A, Object, Kernel, BasicObject]
And because of this, a.singleton_class doesn't share its own constants to parent just like any other child class do.
puts a.class.constants # => []
puts a.singleton_class.constants # => [:CONSTANT]

Resources