Blog

Thoughts from my daily grind

Delegating methods in Ruby (and Rails)

Posted by Ziyan Junaideen |Published: 22 July 2022 |Category: Ruby Language
Default Upload |

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 Forwardablemodule.

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.

Comments