codedecoder

breaking into the unknown…

mixin in ruby : include prepend extend

Leave a comment

Ruby do not support multiple inheritance directly, but implement it through Mixin . Mixin in ruby is achieved with include, prepend and extend. prepend is introduced with Ruby 2.0. Lets see, how they work and differ from each other.

Lets define few module and classes. We will experiment with them on irb or Rails console and try to understand their behaviour

module Introduction
  def introduce
    puts "Iam Arun"
  end
end

module Welcome
  def greet
    puts "Hi..How are you"
  end
end

module Host
  def serve_food
    puts "chiken biryani"
  end
end

module SeeOff
  def bye
    puts "Byee..."
  end
end

class Person
  prepend Introduction
  include Welcome
  include Host
  extend  SeeOff
end

class Celebrity
  
end

Copy paste above code in irb and keep experimenting as you proceed below :

1 => include and prepend add the module method as instance method, while extend add it as class method.

Calling as class method :

2.2.1 :058 > Person.introduce
NoMethodError: undefined method `introduce’ for Person:Class

2.2.1 :059 > Person.greet
NoMethodError: undefined method `greet’ for Person:Class

2.2.1 :060 > Person.bye
Byee…

You can see that, only bye method of module SeeOff worked as it is extended , so added as class method

Calling as instance method

2.2.1 :061 > Person.new.introduce
Iam Arun

2.2.1 :062 > Person.new.greet
Hi..How are you

2.2.1 :063 > Person.new.bye
NoMethodError: undefined method `bye’ for #<Person:0x8edec04>

Here include and prepend added the module method as instance method to the class, so it worked, but bye method of SeeOff module failed as it is added as a class method so can’t be call on the instance of Person.

Now considering the above behaviour, you may think when to use mixin as class method(using extend) and when to use as instance method(using include or prepened). It depended on the feature a module is providing and how you want to reuse it in your class. Infact in a ordinary class also, you may use the below thumb rule to define a method as a instance method or a class method.

Define a method as class method if you say want to use it within different methods of a class. say prity_print can be a class method
as it can be used in any method to well format the content before retuning it.

Second scenario is that the method behaviour not depend on the instance variable, for example it is not good idea to make full_name as class method as it depend on user’s first_name and last_name which differ from user to user, But say active_users can be a class method as it do not depend on any individual user

2. Adding module method at runtime : use extend to add method only to a specific instantiated object and use include or prepend to add instance methods to all the instance .

Above, the Celebrity class do not have any method, nor it has included or extended any of the module. Now say we want to add bye method at run time to Celebrity.

2.2.1 :065 > Celebrity.bye
NoMethodError: undefined method `bye’ for Celebrity:Class

> Celebrity.extend SeeOff
=> Celebrity
2.2.1 :067 > Celebrity.bye
Byee…

Now say. we have Katrina and Deepika as two celebrity.

2.2.1 :074 > katrina = Celebrity.new
=> #<Celebrity:0xe317eb0>
2.2.1 :075 > deepika = Celebrity.new
=> #<Celebrity:0xe3161f0>

But we want to greet katrina only

2.2.1 :076 > katrina.extend Welcome
=> #<Celebrity:0xe317eb0>

2.2.1 :077 > katrina.greet
Hi..How are you

2.2.1 :078 > deepika.greet
NoMethodError: undefined method `greet’ for #<Celebrity:0xe3161f0>

So you can see that, when extend act on katrina i,e an object already instantiated it add greet method of Welcome as instance method to it(remember it add module method as class method to person). Basically, extend can add a module method as class Method or instance method depending on who called it at the runtime. You can check that , we have extended SeeOff as the class method above, so it will not available to katrina or deepika who are basically a instance of Celebrity class.

katrina.bye
NoMethodError: undefined method `bye’ for #<Celebrity:0xe29b964>

Now say, you want to introduce yourself to any celebrity, thus the introduce method must be available to all its instance. In this case we have to use include.

2.2.1 :116 >Celebrity.include Introduction

2.2.1 :116 > salman = Celebrity.new
=> #<Celebrity:0xe26d848>
2.2.1 :117 > salman.introduce
Iam Arun

2.2.1 :118 >katrina.introduce
Iam Arun

Notice that, Introduction module is mixed to Celebrity class after creation of katrina object, but its introduce method also get available to it, this is because in ruby methods are accessed by reference, any object just contain its own variable and refer for any method in its class, so if some method is added later on then also it will be accessible to a object.

3 => Just like all classes are instances of Ruby’s Class, all modules in Ruby are instances of Module.Module is the superclass of Class, so this means that all classes are also modules, and can be used as such.

You can easily check it by calling ancestors method on Person class

2.2.1 :068 > Person.ancestors
=> [Introduction, Person, Host, Welcome, Object, PP::ObjectMixin, Delayed::MessageSending, ActiveSupport::Dependencies::Loadable, V8::Conversion::Object, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]
2.2.1 :069 >

You can note down below from the above result

  1. extend do not add the module to the ancestors chain
  2. prepend add the module as child of the class in which it is called , see that Person become ancestor of Introduction
  3. include add the ancestors in the reverse order of their call. Host include in last, so it become first ancestor of Person

As you know we can override any method in ancestors class in its child class, and also use super to keep behaviour of ancestor intact and at the same time adding our own behaviour. Let rewrite our initial code to explain this.

module Introduction
  def introduce
    puts "Iam Arun"
    super
  end
end

module Welcome
  def greet
    puts "Hi..How are you"
  end
end

module Host
  def serve_food
    puts "chiken biryani"
  end
end

module SeeOff
  def bye
    puts "Byee..."
  end
end

class Person
  prepend Introduction
  include Welcome
  include Host
  extend  SeeOff
  
  def serve_food
    super
    puts "try lassi"
  end
  
  def introduce
    puts "Happy to meet you"
  end
end

 

Now you can get below result when call different methods of the Person class.

2.2.1 :110 > person = Person.new
=> #<Person:0xb4cd9d58>

2.2.1 :111 > person.serve_food
chiken biryani
try lassi

2.2.1 :112 > person.introduce
Iam Arun
Happy to meet you

And finally, let’s see an alternative way of extending a class. I do not use it myself as I believe in keeping the things simpler. But if you go through, gems and different library, you can see the use of self.included(base) .

Basically included is a callback , which get triggered whenever a module is included into another module or class. The argument base passed to it is basically the target module or class i,e that in which the module is included. Don’t get confused as initially I was. See the example below:

module Welcome
  def self.included(base)
    base.extend SeeOff
  end
  
  def greet
    puts "Hi..How are you"
  end
end

module SeeOff
  def bye
    puts "Byee..."
  end
end

class Person
  include Welcome
end

Note that, In Person class, we have only included the Welcome module, but in the Welcome module we have defined included method within which we have extended the SeeOff class with the line base.extend SeeOff , since Welcome module is included in Person class, when included callback get fired, Person will be become base class for it.So base.extend SeeOff will evaluated as Person.extend SeeOff .

You can check the result in console now:

2.2.1 :037 > Person.bye
Byee…

So you can see that bye method of SeeOff class get added as class method to Person class.

That’s all about mixin in ruby.. 🙂

Advertisements

Author: arunyadav4u

over 7 years experience in web development with Ruby on Rails.Involved in all stage of development lifecycle : requirement gathering, planing, coding, deployment & Knowledge transfer. I can adept to any situation, mixup very easily with people & can be a great friend.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s