How Does Inheritance Work in Ruby

How does inheritance work when it comes to class methods in Ruby?

There is only one copy of the method.

Explanation:

1. Singleton classes are instances

SubClass and SuperClass are both instances of Class. If you inspect the singleton class in IRB, you'll see something like:

> SuperClass.singleton_class
=> #<Class:SuperClass>
> SubClass.singleton_class
=> #<Class:SubClass>

The SuperClass.singleton_class is an ancestor of SubClass.singleton_class just the same way that SuperClass is an ancestor of SubClass. Said another way: classes are also instances (of Class), and work by the same rules.

2. instance_methods takes an optional argument

These rules say that SubClass.singleton_class.instance_methods will return every instance method available, no matter which ancestor defines it. If you would like to only see instance methods defined on SubClass.singleton_class, you need to invoke SubClass.singleton_class.instance_methods(false).

http://ruby-doc.org/core-2.4.1/Module.html#method-i-instance_methods

How does Inheritance work in Ruby?

Eigenclass is a sneaky hidden one. You have successfully revealed it by opening class. But it does not exist in the ancestors of normal class. And since it is hidden, you cannot see it by sending ancestors method to a eigenclass itself. The inheritance tree is like the following:

B ---S-->  Dave   ---S---> Object  ---S---> BasicObject
| | | |
E E E E
| | | |
#B --S--> #Dave ---S---> #Object ---S---> #BasicObject --S---> Class,,Object,BasicObject

S stands for superclass, while E for eigenclass.

How does class method look-up work during inheritance in ruby?

OK, since this question was upvoted, I’ll leave the correct answer here.

Child.hello is defined because Child’s eigenclass is derived from Parent’s eigenclass:

▶ Child.singleton_class.superclass
#⇒ #<Class:Parent>

The whole ancestor chain of this eigenclass would be:

▶ Child.singleton_class.ancestors
#⇒ [#<Class:Child>, #<Class:Parent>,
# #<Class:Object>, #<Class:BasicObject>,
# Class, Module, Object, Kernel, BasicObject]

First four ancestors are eigenclasses of the respective classes in Child.ancestors chain. That is how eigenclasses are built/derived in ruby, making calls like Child.hello available.

And, while :hello method is not shown directly by Child.singleton_class.instance_methods(false), since it’s defined not on Child’s eigenclass, it’s still there, thanks to Parent’s eigenclass:

▶ Child.singleton_class.instance_methods.detect &:hello.method(:==)
#⇒ :hello

Understanding Ruby Inheritance

Class methods may be inherited and overridden just as instance methods can be. If your parent class defines a class method, the subclass inherits that method. That is, if your subclass does not define it's own class method, then it inherits from it's superclass.

As a recommendation: when invoking a class method with an explicit receiver, you should avoid relying on inheritance. Always invoke the class method through the class that defines it. Otherwise, it would be very difficult for someone who relies on your code to find the parent class which defines the class method.

Referring back to your original assumption: the invocation of a class method from a subclass it's possible because the class methods are instance methods of the eigenclass.

class C
# instance methods goes here
class << self # open the eigenclass
# class methods go here as instance methods of the eigenclass
end
end

In general, it's clearer to define class methods as individual singleton methods without explicitly opening the eigenclass.

For a clear explanation read The Ruby Programming Language by David Flanagan and Yukihiro Matsumoto

Ruby Inheritance and overwriting class method

I think this should clarify:

class Parent
def self.inherited(child)
puts "Inherited"
end
end

class Child < Parent
puts "Starting to define methods"
def self.stuff; end
end

Output makes it clear that .inherited gets called the moment you open the new class, not when you close it. Thus, as you guessed, Child.custom_class_method does not exist at the point when you are trying to call it - all .inherited sees is a blank slate.

(As to how to get around it... I can't say without more insight into what you are trying to do, unfortunately.)

Inheriting from class Module

Module is indeed a Ruby class. An instance of the class Module is a Ruby module. To illustrate, these two ways of defining a module are equivalent:

module MyModule
# ...
end

# is equivalent to

MyModule = Module.new do
# ...
end

If an instance of Module is a Ruby module, that means that an instance of any subclass of Module is also a Ruby module, including Shrine::Attachment. This makes sense, because we know that we can include only modules, so an instance of Shrine::Attachment has to be a module.

Because of Shrine's plugin system design, this:

class Attachment < Module
@shrine_class = ::Shrine
end

isn't the whole implementation of Shrine::Attachment; the actual implementation is defined in the Shrine::Plugins::Base::AttachmentMethods module, which gets included into Shrine::Attachment.

If we look at the implementation of Shrine::Attachment.new, we can see that it dynamically defines methods on itself based on the given attribute name. For example, Shrine::Attachment.new(:image) will generate a module with the following methods defined: #image_attacher, #image=, #image, and #image_url. These methods will then get added to the model that includes that Shrine::Attachment instance.


Why didn't I just have a method that creates a new module via Module.new (like Refile does), instead of creating a whole subclass of Module? Well, two main reasons:

First, this gives better introspection, because instead of seeing #<Module:0x007f8183d27ab0> in your model's ancestors list, you now see an actual Shrine::Attachment instance that points to its definition. You could still manually override #to_s and #inspect, but this is better.

Second, since Shrine::Attachment is now a class, other Shrine plugins can extend it with more behaviour. So remote_url plugin adds the #<attachment>_remote_url accessor, data_uri plugin adds the #<attachment>_data_uri accessor etc.

Inheritance and instance variable in Ruby

B inherits initialize from A.

At object creation, initialize is invoked. So you get @x set to 2 even for objects of class B.

I think, the sentences you are quoting refer to this scenario:

class A
def initialize
@x = 42
end
end

class B < A
def initialize
@x = 23
end
end

h = B.new

Now, h has just one instance variable @x with value 23. It is not like there is one @x from B and one from A. You can see this here:

class A
def initialize
@x = 42
end

def set_x_from_a
@x = 12
end

def print_x_from_a
puts @x
end
end

class B < A
def initialize
@x = 23
end

def set_x_from_b
@x = 9
end

def print_x_from_b
puts @x
end
end

h = B.new
h.print_x_from_a # => 23
h.print_x_from_b # => 23
h.set_x_from_a
h.print_x_from_b # => 12
h.set_x_from_b
h.print_x_from_a # => 9

Why do we use super in ruby when I can inherit without its use?

Using super lets a class override a method that it inherits from its parent and customize it.

For instance, in your example, Dog and Cat inherit #initialize from Animal - but what if we wanted some special logic for Dog?

class Dog < Animal
def initialize(type, breed, age)
raise "Sorry, dogs don't live that long!" if age > 100

# Everything looks good - let Animal#initialize run now
super
end
end

This lets Dog customize what its initialize method does, but still call through to the original inherited method.



Related Topics



Leave a reply



Submit