Delegate in Ruby on Rails

What is Delegate

Delegate by the document, which refers to a class method used to easily expose contained objects' public methods as your own. Simply, the concept of delegate is to take some methods and send them off to another object to be processed.

delegate(*methods, to: nil, prefix: nil, allow_nil: nil)

Options:

:to - Specifies the target object

:prefix - Prefixes the new method with the target name or a custom prefix

:allow_nil -if set to true, prevents a NoMethodError to be raised.

Why to use delegate

Law of Demeter or principle of least knowledge is a software design guideline that, according to Wikipedia, is summarized as -

  • Each unit should have only limited knowledge about other units: only units “closely” related to the current unit.
  • Each unit should only talk to its friends; don’t talk to strangers.
  • Only talk to your immediate friends.

delegate method in rails help to enforce this law by exposing only necessary methods of containing objects, thereby, making them more easily accessible.

How to use delegate methods

This may sound a little hard to understand, but let's dive into an example to get under it. Let assume we have two active record models Project and user with the following associations:

class User < ActiveRecord::Base
  has_many :projects
end

class Project < ActiveRecord::Base
  belongs_to :user
end

Now, if we needed to access the user's objects from projects, it would be like

project = Project.find(3)
project.user.cellphone
project.user.address

Now let's use the delegate method and try to access the same columns from the project model.

class Project < ActiveRecord::Base
   belongs_to :user
   delegate  :address, :cellphone, to:  :user
end

We can get the address and cellphone from the project as:

project.cellphone
project.address

Now what if the project model and user model have the same column name? That's where the Prefix argument comes into the play. Lets modify our project model:

class Project < ActiveRecord::Base
   belongs_to :user
   delegate  :name, :address, :cellphone, to: :user, prefix: true

   def name
   "project"
  end
end

If we have the prefix: true, then we can access the project's name and user's name as:

project.name

project.user_name

When we try to access an object that is nil, it will raise an exception. To suppress the error and instead receive the nil response, we can use the allow_nil argument as below:

class Project < ActiveRecord::Base
   belongs_to :user
   delegate  :name, :address, :cellphone, to: :user, 
    prefix: true, allow_nil: true
end
Project.new.name

will return a nil response.

Let's hope you have got some basic ideas on the delegate method and how to use it.