include vs extend vs prepend

include vs extend vs prepend

Object Hierarchy in Ruby in brief

Ruby, being an object oriented language supports classes & modules. These classes and modules can be used along with each other in really interesting ways other than just inheriting.

If you do Class.ancestors in console, you will get the ancestry chain of the objects.

irb(main):034> Class.ancestors
=> [Class, Module, Object, JSON::Ext::Generator::GeneratorMethods::Object, PP::ObjectMixin, Kernel, BasicObject]        

It returns a list of the parents of the Class itself. Every new class you make is just an instance of Class. Creating a new class and then checking the ancestry shows that the new class will be placed in front of 'Class'.

all of the above holds true for modules as well.

irb(main):037> class NewClass; end
=> nil
irb(main):038> NewClass.ancestors
=> [NewClass, Object, JSON::Ext::Generator::GeneratorMethods::Object, PP::ObjectMixin, Kernel, BasicObject]

# similary if you create a subclass of NewClass

irb(main):041> class DerivedClass < NewClass; end
irb(main):043> DerivedClass.ancestors
=> [DerivedClass, NewClass, Object, JSON::Ext::Generator::GeneratorMethods::Object, PP::ObjectMixin, Kernel, BasicObject]        

Include and Prepend

Include and prepend lets you play with this ancestry chain of objects. Both include and prepend add the included/prepended module respectively in the ancestry of the current module/class.

module TestModule; end
module TestModule2; end
module TestModule3; end

TestModule.class_eval do
  include TestModule2
  prepend TestModule3
end

TestModule.ancestors

=> [TestModule3, TestModule, TestModule2] #output
        

As you can notice included module comes after the current module but a prepended module comes before the current module in the ancestry chain. Ruby interpreter will look for the method in prepended module before the current class. It can have different use cases.

The included and prepended modules adds the method as instance methods, they will not be accessible directly by a class but by an instance of those classes.

These two keywords are the basis of Mixins in rails.

Then what is extend

extend is really different from include or prepend. 'extend' makes all the methods of the module act like class methods of the current class. But it won't add the extended class in the ancestry chain.

 module ExtendTestModule
   def return_true
     true
   end
   def return_false
     false
   end
 end
 
 class TestClass
   extend ExtendTestModule
 end
 
irb(main):058>  TestClass.ancestors
=> [TestClass, Object, JSON::Ext::Generator::GeneratorMethods::Object, PP::ObjectMixin, Kernel, BasicObject]
irb(main):061> TestClass.return_true
=> true
irb(main):062> TestClass.return_false
=> false        

Mind you, on extending, the methods become class methods for TestClass so if you want to do TestClass.new.return_true, It will throw an error.


Whats the use of it

Although these could be used in creating reusable modules and mixins, the true magic happens when these are mixed with ruby's metaprogramming. In this example I have sneakingly used class_eval in a code example to use include and prepend an already created module.

These keywords can also be used to modify existing libraries, like ActiveRecord::Base to add any custom behavior for your models or controllers.


To view or add a comment, sign in

Others also viewed

Explore content categories