Delegate vs Forwardable vs SimpleDelegator — Ruby vs Rails
In this article, we will be getting through the delegate method in Rails. On the way to learning the delegate, we will also come across the forwardable module and SimpleDecorator Class in Ruby itself. But before all, we should be familiar with the word delegate which means to assign a task to someone.
Delegate — When we take DRY Principle Seriously
Delegate is a class method of the ActiveSupport(means a method of Rails, not Ruby). The delegate will expose the public method of the object to another object. In simple terms, it will give its public behaviors(methods) to the delegated object. Let’s see with an example.
#Catalogue Model
class Catalogue < ApplicationRecord
has_many :orders
def price_name
"#{price} #{name}"
end
end
#Order Model
class Order < ApplicationRecord
belongs_to :catalogue
delegate :name, :price, :price_name, to: :catalogue
end
#console
order = Order.first
# Order Load (0.2ms) SELECT "orders".* FROM "orders" ORDER BY "orders"."id" ASC LIMIT ? [["LIMIT", 1]]
# =>
# <Order:0x00007f8d4cd80bb8
# id: 1,
# total_price: 100,
# total_tax: 10,
# category: "Test1",
# name: "C1",
# catalogue_id: 1,
# created_at: Mon, 04 Dec 2023 07:02:41.995460000 UTC +00:00,
# updated_at: Mon, 04 Dec 2023 07:02:41.995460000 UTC +00:00>
order.price_name
# "100 C1"
order.price
# "100"
In the above example, we have two models Catalogue and Order, where Catalogue had been associated with the Order. We have delegated the method such as name, price, and price_name of the Catalogue to the Order Model. Thus, when we call any of these methods on Order directly, we will get the values without the NoMethodError.
Now this has been done with the help of ActiveSupport. But did Ruby not have any module or anything for the delegation in its core library? The answer is Yes it had. Not just one but two ways for the delegation — Module ‘Forwardable’ and class ‘SimpleDelegator’. Let's have a look at Both of them.
Forwardable — Module to Forward Object Behaviours(Methods)
The Forwardable module of Ruby provides two methods to delegate the object methods to another object — def_delegator and def_delegators. They are the same as the delegate method of ActiveSupport, which exposes the public method of another object to an object. Let’s understand them through an example.
require 'forwardable'
class Person
extend Forwardable
def_delegators :@holding, :person_net_holdings, :person_total_asset_price
#def_delegator :@holding, :person_net_holdings #For Single Method
def initialize(name, total_asset_price, net_holdings)
@holding = Holding.new(total_asset_price, net_holdings)
end
end
class Holding
def initialize(total_asset_price, net_holdings)
@total_asset_price = total_asset_price
@net_holdings = net_holdings
end
def person_net_holdings
"Person Net Holding #{@net_holdings}"
end
def person_total_asset_price
"Person Total Asset Price #{@total_asset_price}"
end
end
person = Person.new("Williams", "$100000", "$10000")
puts person.person_total_asset_price
#Person Total Asset Price $100000
puts person.person_net_holdings
#Person Net Holding $10000
In the above example, there are two classes Person and Holding. Holding had delegated its method(person_total_asset_price and person_net_holdings) to the Person. Now, the person object has access to the methods of the holding object.
Simple Delegator — Make anything out of Me
SimpleDelegator is a class that inherits the Delegator class. Simple Delegator has two methods in it — __getobj__ and __setobj__. This makes the class that inherits Simple Delegator which will clone any object it had been given. Let us see with an example:
require 'delegate'
class Anything < SimpleDelegator
end
class Example
def testing
"We are testing SimpleDelegator"
end
end
array = Anything.new([])
array << "Hey"
array << "I am Array"
puts array
# Hey
# I am Array
string = Anything.new("")
string << "Hey "
string << "I am String"
puts string
# Hey I am String
example = Anything.new(Example.new)
puts example.testing
# We are testing SimpleDelegator
In the above example, we had to form an Anything class which inherits the SimpleDelegator class. The Anything class is now able to take the form of any object it has been passed. As in the above, we had first passed the array, then the string, and again an object of the Example class It simply takes the form of the object provided and then we can perform any method on the Anything object of the object provided.
To be continued
We can have different uses of this delegation technique in Ruby and Rails. Delegation helps in the DRY(Don’t Repeat Yourself) principle to be followed so that the public method of any other object can be accessed and no need to repeat again and again the same code. We will continue again with new topics.