Delegating with Forwardable in Ruby

Chris Temple
May 25, 2016 · 2 min read

Delegation in programming is simply a matter of passing responsibility to an object more suitable to handle a request. It’s good to keep things simple and delegate when you can, that’s why Ruby has made doing so easy by providing the Forwardable module.

Including the Forwardable module in our classes gives us the ability to forward a request/method call to an underlying attribute more appropriate to deal with it.

Forwardable in the wild

Let’s assume that we’re building an app for an agency with agents who have different levels of clearance.

# agent.rb
class Agent
def initialize(name, level)
@name = name
@clearance_level = level
end
def has_clearance?(clearance_level_required)
@clearance_level < clearance_level_required
end
end

If we lived in a simple world, this code would suffice in getting the job done; reality however is that now there are multiple aspects to security at this organisation such as: access rights to files, rooms, secret headquarters etc.

Given all of these extra security features, should the agent be responsible for taking care of them all? Nope! If the agent is anything like me, they would delegate it.

# agent.rb
class Agent
attr_reader :name, :clearance_level
def initialize(name, level)
@name = name
@clearance_level = ClearanceLevel.new(level)
end
end
# clearance_level.rb
class ClearanceLevel
def initialize(agent)
@level = level
end
def is_newb?
@level.zero?
end
def is_secret?
@level > 4
end
def is_top_secret?
@level == 10
end
def has_clearance?(level_required)
@level <= level_required
end
end

Okay, so now the ClearanceLevel class is responsible for deciding on what level of clearance an agent has, however now if we want to do anything useful with our agent the code is going to look something like this:

agent = Agent.new("Phil", 10)
if agent.clearance_level.has_clearance?(4)
puts "Welcome to Sector 4 #{agent.name}."
if agent.clearance_level.is_top_secret?
puts "You've got top secret agent clearance!"
end
end

Having to reference agent.clearance each time to find out information on an agents’ clearance level could get repetitive, it would be a lot nicer if we could just ask clearance questions onagent, for example:

agent = Agent.new("Phil", 10)
if agent.has_clearance?(4)
puts "Welcome to Sector 4 #{agent.name}."
if agent.is_top_secret?
puts "You've got top secret agent clearance!"
end
end

Without Forwardable

Without knowing about the Ruby Forwardable module, to accomplish this we would write wrapper functions in agent.rb

# agent.rb
class Agent
attr_reader :name, :clearance_level
def initialize(name, level)
@name = name
@clearance_level = ClearanceLevel.new(level)
end
def has_clearance?(level)
@clearance_level.has_clearance?(level)
end
def is_top_secret?
@clearance_level.is_top_secret?
end
[...]
end

With Forwardable

But, since we now know that the Forwardable module can forward method calls to an object more responsible, we can use it to delegate the wrapper methods to @clearance_level in theagent like so:

# agent.rb
class Agent
extend Forwardable
attr_reader :name, :clearance_level
def initialize(name, level)
@name = name
@clearance_level = ClearanceLevel.new(level)
end
def_delegators :@clearance_level, :is_newb?, :is_secret?, :is_top_secret?, :has_clearance?
end

So by using the Forwardable module we can now call our clearance methods directly on our agent objects were they’ll be delegated to the @clearance_level, meaning we’ve been able to keep agent.rb simple and clean instead of littering it with wrapper methods. Yay!

I hope you find this helpful.

Let me know ways that you’ve used Forwardable!

whynot.io

when people say it can’t be done

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store