This article is about method delegation in Ruby.
You’re going to learn how to use the
delegate method, the
Forwardable module & the
Why do we need delegation?
In Object-Oriented Programming, there are two ways for classes to work together.
With inheritance, you create class hierarchies, where a parent class shares methods, constants & instance variable definitions with any class that inherits from it.
In Ruby, every object inherits from the
Object class by default.
That’s why you get access to methods like
With composition a class creates (or is given) objects from another class… then it uses these objects to delegate work to them.
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.
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:
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
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:
It will fail with a
NoMethodError because there is no
write method on
undefined method `write' for #<Computer:0x000055c2e8f7d310> (NoMethodError)
Computer class doesn't know what you mean by
You create a
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
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?
Let's take a look...
How to Use The Forwardable Module
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
This is how it works:
class Computer delegate :read, :write, to: :@memory def initialize @memory = Memory.new end end
delegate method takes a
prefix argument, which allows you to append a prefix to the method names.
class Computer delegate :read, :write, prefix: "memory", to: :@memory def initialize @memory = Memory.new end end
Resulting in two methods:
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
require 'delegate' class CoolArray < SimpleDelegator end cool = CoolArray.new() cool << "ruby" cool << "gems" p cool
CoolArray behaves like the object you pass to
Why is this useful?
You can add new methods to this object, without having to change the original class.
[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]
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 🙂