Rails

【Rails】メソッドを委譲することができるdelegateメソッドについて初心者向けに解説

Rails

[quads id=1]

 

今回はメソッドを委譲することができるdelegateメソッドについて簡単に解説したいと思います。「メソッドの委譲」と聞くと難しいイメージを抱きますが、使い方は比較的簡単ですので順番に見ていきましょう。

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が発生する
Rails
【Rails】エラーハンドリングに役立つぼっち演算子(.&)とtry(try!)メソッドについてRailsのエラーハンドリングで役立つぼっち演算子(.&)とtry、try!メソッドについて紹介しています。nilで条件分岐する「user.name if user.present?」のような冗長な記述をしている箇所は、ぼっち演算子(.&)に置き換えるよう修正しましょう。...

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_missing_toメソッドはpublicなメソッドだけが委譲される点に注意してください。

まとめ

  • delegateとは、メソッドを委譲することができるメソッド
  • delegateは複数メソッドを委譲することも可能
  • allow_nilオプションを付与することで、例外の代わりにnilを返すことができる
  • prefixオプションをtrueにすることで、生成されたメソッド名にプレフィックスを追加することができる
  • prefixはカスタマイズすることも可能
  • privateオプションを付与することでメソッドのスコープを変更することができる
  • delegateメソッドはインスタンス変数や定数へのアクセスも可能
  • delegate_missing_toメソッドを使用することで、全てのメソッドを委譲することができる

参考

 

今回はメソッドを委譲することができるdelegateメソッドについて解説しました。delegateメソッドを使用することで、クラスメソッドを省略したり、アソシエーションの記述を減らしたりとコードをスッキリさせることができます。ぜひこの機会に使ってみてください。