結論
delegateはメソッドを委譲することができる
delegateを使うことで記述が短く済み、意味も明快となる
Userモデルにログイン情報があり、それに関連する名前などの情報はProfileモデルにあるとする。
class User < ApplicationRecord
has_one :profile
end
class Profile < ApplicationRecord
belongs_to :profile
end
user.profile.name
# => "山田太郎"
delegateを用いて記述すると、以下のようになる。
class User < ApplicationRecord
has_one :profile
delegate :name, to: :profile
end
class Profile < ApplicationRecord
belongs_to :profile
end
user.name
# => "山田太郎"
基本機能
結論でも述べたように、メソッドを他のモデルから委譲することができます。
もしdelegateを使わずに他モデルのメソッドの使用するとなると、以下のようにそれぞれメソッドを定義しなければなりません。
class User < ApplicationRecord
has_one :profile
def name
profile.name
end
def gender
profile.gender
end
end
class Profile < ApplicationRecord
belongs_to :profile
end
user.name
# => "山田太郎"
user.gender
# => "male"
このまま必要になったメソッドを追加していけば、徐々に見通しの悪いmodelとなっていきます。
それを防ぐ技術がdelegateというわけです。
# Profileモデルは省略
class User < ApplicationRecord
has_one :profile
# Profileモデルからname, genderを委譲している。
delegate :name, :gender, to: :profile
end
user.name
# => "山田太郎"
user.gender
# => "male"
Userモデルがとてもスッキリとしました。
オプション
prefix
prefixオプションは名前の通り、委譲するメソッドにprefixをつけることができます。
メソッド名は、委譲元のオブジェクト_メソッド名となります。
class User < ApplicationRecord
has_one :profile
delegate :name, :gender, to: :profile, prefix: true
end
# profileオブジェクトからnameを委譲しているため、profile_nameとなる。
user.profile_name
# => "山田太郎"
user.profile_gender
# => "male"
文字列 or シンボルでprefixを指定することでカスタマイズすることもできます。
class User < ApplicationRecord
has_one :profile
delegate :name, :gender, to: :profile, prefix: :test # or 'test'
end
# prefixをtestと指定しているため、test_nameとなる。
user.test_name
# => "山田太郎"
user.test_gender
# => "male"
allow_nil
委譲時にNoMethodError
が発生して対象がnil
の場合、NoMethodError
が伝搬します。
allow_nil
オプションを使うと、例外の代わりにnil
を返すようにできます。
下記は、Profileモデルのnameがnilの場合の動作です。
# modelの設定(allow_nilなし)
# delegate :name, to: :profile
user.name
# undefined method `name' for nil:NilClass (NoMethodError)
# modelの設定 (allow_nilあり)
# delegate :name, to: :profile, allow_nil: true
user.name
# => nil
private
private
オプションはメソッドのスコープを変更します。
委譲されたメソッドはデフォルトでpublicになりますが、private: true
を渡すことで変更できます。
delegate :name, to: :profile, private: true
注意点
委譲されるのはpublicなメソッドのみ
モデル内にprivateメソッドを定義していて、それを委譲しようとしてもできません。
委譲しようとするメソッドがpublicであるか確認するようにしてください。
引用・参考資料
Active Support コア拡張機能 - Railsガイド
Active Supportで定義されているRubyのコア拡張機能に関するドキュメントです。