Method delegation in Rails can help you to keep your code organized and avoid deep call chains (law of demeter) by forwarding calls from one object to another. Rails provides several ways to accomplish this. Below is a concise overview of the most common approaches:
Single-Method delegation with delegate
Use the built-in delegate
method from ActiveSupport
to forward specific methods:
class User < ApplicationRecord
has_one :profile
delegate :full_name, :age, to: :profile, prefix: true
end
-
delegate: full_name, :age, to: :profile
forwardsfull_name
andage
to the profile association -
prefix: true
changes the delegated methods toprofile_full_name
andprofile_age
in theUser
class
This is ideal for forwarding a small number of methods without cluttering your model
Delegating all method calls via method_missing
and respond_to_missing?
But what do we do if we want to forward all method calls to a different object? Do we have to keep a long list of all the methods and update it whenever changes are made?
Fortunately not, because there is a common pattern for this in Ruby. By overwriting method_missing
and respond_to_missing?
in our class, we can forward all method calls that are not explicitly defined in our own class to the wrapped object.
class UserDecorator
def initialize(user)
@user = user
end
private
def respond_to_missing?(method_name, include_private = false)
@user.respond_to?(method_name, include_private)
end
def method_missing(method_name, *args, &block)
@user.public_send(method_name, *args, &block)
end
end
Rails shortcut: delegate_missing_to
Because this is such a common pattern (e.g. for building something like decorators), Rails provides a shortcut:
delegate_missing_to
Show archive.org snapshot
class UserDecorator
attr_reader :user
delegate_missing_to :user
def initialize(user)
@user = user
end
end
Rails automatically implements the necessary method_missing
and respond_to_missing?
for you, reducing boilerplate code.