Delegating methods in Ruby (and Rails)
Yesterday I was writing a Ruby API wrapper (as a GEM) for the 3DSecure.IO service for reducing risks associated with online CNP (card-not-preseransactions. While writing code to handle individual end-points, I thought that "Delegating Methods" would be a good topic for a blog post.
Why delegate?
Software engineering in general and OOP, in particular, follows a design guideline known as the Law of Demeter (LoD). This is also known as the principle of least knowledge. Simply put an object should only talk to its closest friend. The following example is not a good example, but that is the theory.
Usecase
Let us consider a case where there is a:
module MyApi
class Client
def post(url, params)
# ...
end
end
end
module MyApi
module Endpoints
class Auth
def initialize(client)
@client = client
end
def call(card_number:)
options = { acctNumber: card_number }
post('/auth', options)
end
end
end
end
The obvious solution would be to use @client.post(...)
, but let's say we don't want to do so. Instead, I prefer delegating the #post
method to the @client
. What are my options?
- Explicit delegation (by defining a new method and calling
@client.post(...)
- Ruby's
Forwardable
module (from the standard library - stdlib) - Rails
delegate
method
Explicit delegation
We can define a method in the Auth
class as follows. We can use the Ruby splat operator to flexibly capture arguments to the method and forward them to the destination method.
class Auth
# ...
def post(*args)
@client.post(*args)
end
end
Using Forwardable (stdlib)
Ruby's standard library has a module called Forwardable
. It allows us to easily delegate methods to an object as follows using the def_delegators
method.
require 'forwardable'
class Auth
extend Forwardable
def_delegators :@client, :get, :post
def initialize(client)
@client = client
end
# ...
end
delegate
when on Ruby on Rails
The delegate
method is available on Rails projects without requiring any imports or extends. I recommend using it instead of the other two approaches if you are on a Ruby on Rails project.
class Auth
delegate :get, :post, to: :@client
def initialize(client)
@client = client
end
# ...
end
Conclusion
I think the delegate
method is my favourite. It looks like natural english. While it makes sense to use with Ruby on Rails projects, when we work on Ruby gems or other projects it would make sense to use the Forwardable
module.
About the Author
Ziyan Junaideen -
Ziyan is an expert Ruby on Rails web developer with 8 years of experience specializing in SaaS applications. He spends his free time he writes blogs, drawing on his iPad, shoots photos.