[quads id=1]
今回はメソッドを委譲することができるdelegateメソッドについて簡単に解説したいと思います。「メソッドの委譲」と聞くと難しいイメージを抱きますが、使い方は比較的簡単ですので順番に見ていきましょう。
delegateメソッドとは
delegateとは、メソッドを委譲することができるメソッドになります。
例を見ていきます。
以下のようにUserモデルとPostモデルがあり、それらが1対多の関係で紐づいていると仮定します。またUserモデルはnicknameカラムを持っているとします。
# Userモデルはnicknameカラムを持つ
class User < ApplicationRecord
has_many :posts
end
class Post < ApplicationRecord
belongs_to :user
end
ここで、ある投稿(post)に紐づいたユーザ(user)の名前(nickname)を取得したいとなった場合、以下のようなコードを書くことで実現できます。
# ある投稿に紐づいたユーザ名を取得する
post.user.nickname
Postモデルにnicknameというメソッドを定義することで、よりシンプルにユーザー名を取得することができるようにもなります。
class Post < ApplicationRecord
belongs_to :user
def nickname
user.nickname
end
end
# ある投稿に紐づいたユーザ名を取得する
post.nickname
しかし上記のコードは、delegateメソッドを用いることでさらにシンプルに記述することができるようになります。
先ほどのnicknameというメソッドをdelegateを用いて移譲します。delegate
の後ろには委譲したいメソッド名(カラム名)、to:
の後ろには関連先(アソシエーション先)を記載します。
class Post < ApplicationRecord
belongs_to :user
delegate :nickname, to: :user
end
# ある投稿に紐づいたユーザ名を取得する
post.nickname
複数のメソッドを委譲したい場合は、以下のように記述することも可能です。
class Post < ApplicationRecord
belongs_to :user
delegate :nickname, :age, :gender, :status, to: :user
end
このようにdelegateメソッドを使用することで、メソッドチェーンを多用することなく実装を行うことができるようになります。
定義方法
delegateメソッドは以下のように定義することができます。
delegate :カラム名, to: :アソシエーション名
delegateメソッドのオプション
allow_nil
アソシエーション先がない状態でdelegateメソッドを使用すると、NoMethodError
が発生してしまいます。allow_nil
オプションを付与することで、例外の代わりにnil
を返すことができるようになります。
例を見てみましょう。
以下のようにdelegateメソッドを定義していますが、アソシエーション先がnil
だと仮定します。この場合、ある投稿に紐づいたユーザ名を取得するためpost.nickname
をするとNoMethodError
が発生してしまいます。
class Post < ApplicationRecord
belongs_to :user
delegate :nickname, to: :user
end
ある投稿に紐づいたユーザ名を取得する
post.nickname
=> NoMethodErrorが発生する
しかし以下のようにallow_nil
オプションを付与することで、NoMethodError
を回避することができます。
class Post < ApplicationRecord
belongs_to :user
delegate :nickname, to: :user, allow_nil: true
end
ある投稿に紐づいたユーザ名を取得する
post.nickname
=> nil
ぼっち演算子を付与した場合と同様の挙動ですね。
class Post < ApplicationRecord
belongs_to :user
def nickname
user&.nickname
end
end
ある投稿に紐づいたユーザ名を取得する
post.nickname
=> NoMethodErrorが発生する

prefix
prefix
オプションをtrue
にすることで、生成されたメソッド名に対してプレフィックスを追加することができます。既に同名のメソッド名を持っている場合に使えるオプションになります。
例を見てみましょう。
以下のようにprefix: true
を付与した場合、nickname
ではなく、user_nickname
というメソッドが生成されます。そのため、ある投稿に紐づいたユーザ名を取得する記述はpost.user_nickname
となります。
class Post < ApplicationRecord
belongs_to :user
delegate :nickname, to: :user, prefix: true
end
# ある投稿に紐づいたユーザ名を取得する
post.user_nickname
またprefix
オプションは以下のようにカスタマイズすることも可能です。
class Post < ApplicationRecord
belongs_to :user
delegate :nickname, to: :user, prefix: :avator
end
# ある投稿に紐づいたユーザ名を取得する
post.avator_nickname
private
delegateメソッドで定義されたメソッドは、デフォルトでpublic
なメソッドになるためどこからでもアクセスが可能になります。
アクセスを制限した場合は、private
オプションを付与することでメソッドのスコープを変更することができます。
class Post < ApplicationRecord
belongs_to :user
delegate :nickname, to: :user, private: true
end
以下のようにprivateメソッドを付与した場合と同じになりますね。
class Post < ApplicationRecord
belongs_to :user
private
def nickname
user.nickname
end
end
インスタンス変数や定数へもアクセスも可能
delegateメソッドはインスタンス変数や定数へのアクセスも可能です。
# インスタンス変数へのアクセス
delegate :nickname, to: :@user
# 定数へのアクセス
delegate :nickname, to: :USER_CONSTANT
全て委譲したいならdelegate_missing_toメソッド
delegate_missing_to
メソッドを使用することで、全てのメソッドを委譲することができます。以下のような、委譲するメソッドが大量にある場合に便利なメソッドになります。
delegate :nickname, :age, :gender, :status, :postal_code, :address, relationship, to: :user
class Post < ApplicationRecord
belongs_to :user
delegate_missing_to :user
end
まとめ
- delegateとは、メソッドを委譲することができるメソッド
- delegateは複数メソッドを委譲することも可能
- allow_nilオプションを付与することで、例外の代わりにnilを返すことができる
- prefixオプションをtrueにすることで、生成されたメソッド名にプレフィックスを追加することができる
- prefixはカスタマイズすることも可能
- privateオプションを付与することでメソッドのスコープを変更することができる
- delegateメソッドはインスタンス変数や定数へのアクセスも可能
- delegate_missing_toメソッドを使用することで、全てのメソッドを委譲することができる
参考
今回はメソッドを委譲することができるdelegateメソッドについて解説しました。delegateメソッドを使用することで、クラスメソッドを省略したり、アソシエーションの記述を減らしたりとコードをスッキリさせることができます。ぜひこの機会に使ってみてください。