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.
Recommended by LinkedIn
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.