How To Delegate Methods in Ruby & Ruby on Rails

This article is about method delegation in Ruby.

You’re going to learn how to use the delegate method, the Forwardable module & the SimpleDelegator class.

Why do we need delegation?

In Object-Oriented Programming, there are two ways for classes to work together.

They are:

  • Inheritance
  • Composition

With inheritance, you create class hierarchies, where a parent class shares methods, constants & instance variable definitions with any class that inherits from it.

For example:

In Ruby, every object inherits from the Object class by default.

That’s why you get access to methods like puts, class & object_id.

With composition a class creates (or is given) objects from another class… then it uses these objects to delegate work to them.

For example:

A computer is made from many parts (objects) & each part knows how to do one thing well.

If you want to render something on screen the computer will tell the graphics card what to show, but not how to do it.

That’s composition!

But what if you’re the computer & you want to give access to the graphics card methods?

You have to build some kind of “bridge” between them.

Let’s see how to do that.

Ruby Method Delegation – Example

We have two classes:

  • Computer
  • Memory

Given the following code:

class Computer
  def initialize
    @memory = Memory.new
  end
end

class Memory
  def initialize
    @data = []
  end

  def write(data)
    @data << data
  end

  def read(index)
    @data[index]
  end
end

computer = Computer.new

We want to be able to access the memory only through the Computer.

This can be for many reasons:

  • You only have 1 memory unit, that means you only want 1 memory object. By centralizing access in one object you can achieve that.
  • You want to control who can access the memory (different applications) & what parts of memory they can use.
  • You want to log every memory access for security or debugging purposes.

Now, if you try to do this:

computer.write("rubyguides")

It will fail with a NoMethodError because there is no write method on Computer.

undefined method `write' for #<Computer:0x000055c2e8f7d310> (NoMethodError)

The Computer class doesn't know what you mean by write.

Unless...

You create a write method!

Here's how:

class Computer
  def initialize
    @memory = Memory.new
  end

  def write(data)
    @memory.write(data)
  end

  def read(index)
    @memory.read(index)
  end
end

We are passing along the requests to the @memory object.

That's method delegation.

Note: This makes these methods part of your public interface (they become available to everyone) & you don't always want that.

Is there some kind of shortcut for method delegation?

Yes!

Let's take a look...

How to Use The Forwardable Module

Now:

You can use the Forwardable module included with Ruby to define delegator methods.

It works like this:

require 'forwardable'

class Computer
  extend Forwardable

  def_delegators :@memory, :read, :write

  def initialize
    @memory = Memory.new
  end
end

This allows you to remove the methods we created earlier & it will do the same thing.

Forwardable will take care of method arguments (including blocks!) for you, so you don't have to worry about that.

Isn't that nice? 🙂

How to Use The Rails Delegate Method

If you're using Rails, or the ActiveSupport gem by itself, then you've access to the delegate method.

This is how it works:

class Computer
  delegate :read, :write, to: :@memory

  def initialize
    @memory = Memory.new
  end
end

The delegate method takes a prefix argument, which allows you to append a prefix to the method names.

Example:

class Computer
  delegate :read, :write, prefix: "memory", to: :@memory

  def initialize
    @memory = Memory.new
  end
end

Resulting in two methods:

  • memory_read
  • memory_write

How to Delegate Everything With SimpleDelegator

These techniques you just learned are used to forward or delegate specific methods.

But if you want to wrap an object & expose all of its methods...

You can use the SimpleDelegator class!

Here's how:

require 'delegate'

class CoolArray < SimpleDelegator
end

cool = CoolArray.new([])

cool << "ruby"
cool << "gems"

p cool

Now CoolArray behaves like the object you pass to new.

Why is this useful?

You can add new methods to this object, without having to change the original class.

Video Tutorial

[responsive_video type='youtube' hide_related='0' hide_logo='0' hide_controls='0' hide_title='1' hide_fullscreen='0' autoplay='0']https://www.youtube.com/watch?v=qbAcpubjLJU[/responsive_video]

Summary

You've learned about object composition in Ruby & how to delegate methods using different techniques!

If you enjoyed this article please share it with your Ruby friends.

Thanks for reading 🙂

4 thoughts on “How To Delegate Methods in Ruby & Ruby on Rails”

  1. Under the heading “How to Use The Forwardable Module” it says def_delegator in the first image. This doesn’t work. In the video he uses def_delegators and that, of course, works perfectly… Just a typo.

Comments are closed.