Understanding ruby .class and .ancestors methods
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
.
Why does 'class' show 'Class' when 'ancestors.include? Class' is 'false'?
What's the use case for the information supplied by the class method in such cases? What does it really tell me to be of practical use? It doesn't seem to have anything to do with inheritance.
class
works the same way for every object. Calling class
on a class provides the same information as calling class
on an instance. That's because in Ruby, classes are instances, too.
'foo'.class
returns String
because 'foo'
is an instance of String
. Likewise, User.class
returns Class
because User
is an instance of Class
.
In particular, User
is not an instance of ApplicationRecord
.
It might not be obvious that User
is an instance of Class
when creating it via the class
keyword:
class User < ApplicationRecord; end
But it becomes very obvious when you create it explicitly via Class.new
: (both examples produce the same result)
User = Class.new(ApplicationRecord)
User.class #=> Class
Because the above is just like: (using String.new
for demonstration purposes)
foo = String.new('foo')
foo.class #=> String
Nested singleton class method lookup
Much of this explanation is based on How Ruby Method Dispatch Works by James Coglan, a little of the Ruby Hacking Guide, and just a smidge of source.
To begin with a summary, the ancestry looks like this:
+----------------+
| |
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module> |
| ^ ^ |
| | | |
| Class ~~~~~~~~~~~~~~~> #<Class:Class> |
| ^ ^ |
| | | |
| BasicObject ~~~~~> #<Class:BasicObject> ~~> #<Class:#<Class:BasicObject>> |
| ^ ^ ^ |
| | Kernel | | |
| | ^ | | |
| | | | +-----------------------|----------------+
| +-----+----+ | | |
| | | v |
+-------> Object ~~~~~~> #<Class:Object> ~~~~~~~~> #<Class:#<Class:Object>>
^ ^ ^
| | |
Foo ~~~~~~~~> #<Class:Foo> ~~~~~~~~~~> #<Class:#<Class:Foo>>
---> Parent
~~~> Singleton class
Let's start from the beginning and build out. BasicObject
is the root of everything - if you check BasicObject.superclass
, you get nil
. BasicObject
is also an instance of Class
. Yes, that gets circular, and there's a special case in the code to deal with it. When A
is an instance of B
, A.singleton_class
is a child of B
, so we get this:
Class
^
|
BasicObject ~~~~~> #<Class:BasicObject>
Object
inherits from BasicObject
. When A
inherits from B
, A
is a child of B
and A.singleton_class
is a child of B.singleton_class
. Object
also includes Kernel
. When A
includes B
, B
is inserted as the first ancestor of A
(after A
itself, but before A.superclass
).
Class
^
|
BasicObject ~~~~~> #<Class:BasicObject
^ ^
| Kernel |
| ^ |
| | |
+-----+----+ |
| |
Object ~~~~~~> #<Class:Object>
Kernel
is an instance of Module
. It's the only instance of Module
we'll see, and its singleton class doesn't appear in any ancestry chains, so I won't draw beyond it.
Now we get down to Foo
, which inherits from Object
(though you don't need to write < Object
). We can already figure out what Foo
and its singleton class are children of.
Class
^
|
BasicObject ~~~~~> #<Class:BasicObject>
^ ^
| Kernel |
| ^ |
| | |
+-----+----+ |
| |
Object ~~~~~~> #<Class:Object>
^ ^
| |
Foo ~~~~~~~~> #<Class:Foo>
Now Class
inherits from Module
, and Module
inherits from Object
, so add Module
and the appropriate singleton classes. Because Module < Object
and Object < BasicObject
and BasicObject.instance_of?(Class)
, this is where the drawing gets a little funky. Remember you just stop traversing upwards whenever you hit BasicObject
.
+----------------+
| |
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module> |
| ^ ^ |
| | | |
| Class ~~~~~~~~~~~~~~~> #<Class:Class> |
| ^ |
| | |
| BasicObject ~~~~~> #<Class:BasicObject> |
| ^ ^ |
| | Kernel | |
| | ^ | |
| | | | +----------------------------------------+
| +-----+----+ | |
| | | v
+-------> Object ~~~~~~> #<Class:Object>
^ ^
| |
Foo ~~~~~~~~> #<Class:Foo>
Last step. Every instance of Class
has a singleton_class
(though it won't be instantiated until it's needed, or else you'd need more RAM). All of our singleton classes are instances of Class
, so they have singleton classes. Watch out for this sentence: A class's singleton class's parent is the class's parent's singleton class. I don't know if there's a succinct way to state that as far as type systems go, and the Ruby source pretty much says it's just doing it for consistency in any case. So, when you ask for Foo.singleton_class.singleton_class
, the language happily obliges you and propagates the necessary parents upward, leading finally to:
+----------------+
| |
+--------------------------- Module ~~~~~~~~~~~~~~> #<Class:Module> |
| ^ ^ |
| | | |
| Class ~~~~~~~~~~~~~~~> #<Class:Class> |
| ^ ^ |
| | | |
| BasicObject ~~~~~> #<Class:BasicObject> ~~> #<Class:#<Class:BasicObject>> |
| ^ ^ ^ |
| | Kernel | | |
| | ^ | | |
| | | | +-----------------------|----------------+
| +-----+----+ | | |
| | | v |
+-------> Object ~~~~~~> #<Class:Object> ~~~~~~~~> #<Class:#<Class:Object>>
^ ^ ^
| | |
Foo ~~~~~~~~> #<Class:Foo> ~~~~~~~~~~> #<Class:#<Class:Foo>>
If you start from any node in this graph and traverse depth-first, right to left (and stop at BasicObject
, you get the node's ancestor chain, just like we wanted. And, we've built it up from some basic axioms, so we might just be able to trust it. Lacking trust, there are a couple interesting ways to verify the structure further.
Try looking at node.singleton_class.ancestors - node.ancestors
for any node in the graph. This gives us the ancestors of the singleton class that are not the ancestors of the node itself, which eliminates some of the confusing redundancy in the list.
> Foo.singleton_class.singleton_class.ancestors - Foo.singleton_class.ancestors
=> [#<Class:#<Class:Foo>>, #<Class:#<Class:Object>>, #<Class:#<Class:BasicObject>>,
#<Class:Class>, #<Class:Module>]
You can also verify any one parent with node.superclass
.
> Foo.singleton_class.singleton_class.superclass
=> #<Class:#<Class:Object>>
And you can even verify that the object identity is all consistent, so there aren't anonymous classes popping up all over the place with no particular relationship to each other.
> def ancestor_ids(ancestors)
> ancestors.map(&:object_id).zip(ancestors).map{|pair| pair.join("\t")}
> end
> puts ancestor_ids(Foo.ancestors)
70165241815140 Foo
70165216040500 Object
70165216040340 Kernel
70165216040540 BasicObject
> puts ancestor_ids(Foo.singleton_class.ancestors)
70165241815120 #<Class:Foo>
70165216039400 #<Class:Object>
70165216039380 #<Class:BasicObject>
70165216040420 Class
70165216040460 Module
70165216040500 Object # Same as Foo from here down
70165216040340 Kernel
70165216040540 BasicObject
> puts ancestor_ids(Foo.singleton_class.singleton_class.ancestors)
70165241980080 #<Class:#<Class:Foo>>
70165215986060 #<Class:#<Class:Object>>
70165215986040 #<Class:#<Class:BasicObject>>
70165216039440 #<Class:Class>
70165216039420 #<Class:Module>
70165216039400 #<Class:Object> # Same as Foo.singleton_class from here down
70165216039380 #<Class:BasicObject>
70165216040420 Class
70165216040460 Module
70165216040500 Object
70165216040340 Kernel
70165216040540 BasicObject
And that, in a nutshell, is how you snipe a nerd.
IRB and Ruby Object Model
On Ruby classes al also objects, that's why the class of a class is actually Class
.
That explains why:
Class.class #=> Class
MyClass.class #=> Class
Class.class == MyClass.class #=> true
You are not calling class
over an instance of each class but calling it over the classes itself. Is almost the same as:
"hey".class #=> String
"jude".class #=> String
"hey".class == "jude".class #=> true
Here is what you were probably expecting at the beginning:
MyClass.new.class #=> MyClass
Class.new.class #=> Class
MyClass.new.class == Class.new.class #=> false
Confusing behaviour between class and object
Here is a little different way to define a classes/objects:
klass = Class.new {
puts self
def bar
puts self
end
}
# #<Class:0x3fbdbb8>
As you can see, it outputs #<Class:0x3fbdbb8>
. So its class is Class
. You can check it via class
method:
klass.class
# => Class
When you name your class with uppercase letter (for example Example
) for the first time, it use it as output instead of cryptic name like: #<Class:0x3fbdbb8>
.
Foo = Class.new {
puts self
def bar
puts self
end
}
Still outputs cryptic name because Foo =
's part hasn't been evaluated yet.
puts Foo
Outputs correct name - Foo
What if I name it again?
Qux = Foo
# => Foo
Nothing. Foo
will be Foo
forever.
Related Topics
Actionview::Template::Error (Incompatible Character Encodings: Utf-8 and Ascii-8Bit)
Rails - Whenever Gem - Dynamic Values
Ruby on Rails - Drop Down Box on Change Event
Rails - Params with "Dot" (E.G. /Google.Com)
Colon (:) Appears as Forward Slash (/) When Creating File Name
Why Can't a Variable Name End with '' While a Method Name Can
Rails Route to Username Instead of Id
How to Enable Cors in Rails 4 App
Yaml How Many Spaces Per Indent
String#Encode Not Fixing "Invalid Byte Sequence in Utf-8" Error
Rspec/Capybara Loading in Progress, Circular Require Considered Harmful
How to Generate Zip File Without Saving to the Disk with Ruby
Typeerror: Superclass Mismatch for Class Word in Ruby
Looping Through Bits in an Integer, Ruby