🐘
【Rails】delegate メソッドの解説
概要
Rails の delegate メソッドについて解説します。
初見では「ん?」となるメソッドの1つですが、整理してみると簡単です。
delegate とは
delegate メソッドは Rails の ActiveSupport の機能の1つです。
delegate
には、「委任する」「委譲する」といった、委ねる的な意味があります。
例えば以下のコードがあるとします。
class User < ApplicationRecord
has_one :profile, dependent: :destroy
end
class Profile < ApplicationRecord
def aisatsu
"私の名前は#{name}です。年齢は#{age}歳です。"
end
end
このとき、 普通に User に紐づく Profile#aisatsu を実行しようとすると以下になります。
User.last.profile.aisatsu
=> "私の名前はTanakaです。年齢は20歳です。"
delegate
使うと、「aisatu メソッドの呼び出し元を、 profile に委譲」できます。
class User < ApplicationRecord
has_one :profile, dependent: :destroy
delegate :aisatsu, to: :profile
end
これで、あたかも User インスタンスから aisatu メソッドを呼び出しているかのように使うことができます。
User.last.aisatsu
=> "私の名前はTanakaです。年齢は20歳です。"
複数指定して委譲できます
class User < ApplicationRecord
has_one :profile, dependent: :destroy
delegate :aisatsu, :name, :age, to: :profile
end
User.last.aisatsu
=> "私の名前はTanakaです。年齢は20歳です。"
User.last.name
=> "Tanaka"
User.last.age
=> 20
prefix オプションを true にすると、委譲先の接頭辞をメソッド呼び出し時につける必要があります
class User < ApplicationRecord
has_one :profile, dependent: :destroy
delegate :aisatsu, to: :profile, prefix: true
end
今回の場合だと、profile
が委譲先なので、profile_aisatsu
メソッドになります。
User.last.profile_aisatsu
=> "私の名前はTanakaです。年齢は20歳です。"
接頭辞なしだと、undefined method
になります。
> User.last.aisatsu
/usr/local/bundle/gems/activemodel-7.0.8/lib/active_model/attribute_methods.rb:450:in `method_missing': undefined method `aisatsu' for #<User id: 1, created_at: "2024-04-06 01:29:44.679016000 +0000", updated_at: "2024-04-06 01:29:44.679016000 +0000"> (NoMethodError)
prefix の名前はカスタマイズすることもできます。
class User < ApplicationRecord
has_one :profile, dependent: :destroy
delegate :aisatsu, to: :profile, prefix: :hogehoge
end
User.first.hogehoge_aisatsu
=> "私の名前はTanakaです。年齢は20歳です。"
allow_nil オプションを true にすると、委譲先のインスタンスが存在しない場合、nil を返します
Uesr インスタンスに紐づく profile が存在しないとき、NoMethodError になります。
User.last.profile
=> nil
User.last.aisatsu
/myapp/app/models/user.rb:14:in `rescue in aisatsu': User#aisatsu delegated to profile.aisatsu, but profile is nil: #<User id: 2, created_at: "2024-04-06 02:24:49.116208000 +0000", updated_at: "2024-04-06 02:24:49.116208000 +0000"> (Module::DelegationError)
/myapp/app/models/user.rb:14:in `aisatsu': undefined method `aisatsu' for nil:NilClass (NoMethodError)
# ボッチ演算子を使っても同じ
User.last&.aisatsu
/myapp/app/models/user.rb:14:in `rescue in aisatsu': User#aisatsu delegated to profile.aisatsu, but profile is nil: #<User id: 2, created_at: "2024-04-06 02:24:49.116208000 +0000", updated_at: "2024-04-06 02:24:49.116208000 +0000"> (Module::DelegationError)
/myapp/app/models/user.rb:14:in `aisatsu': undefined method `aisatsu' for nil:NilClass (NoMethodError)
allow_nil: true
とすると、委譲先のインスタンスが存在しないときに nil を返します。
class User < ApplicationRecord
has_one :profile, dependent: :destroy
delegate :aisatsu, to: :profile, allow_nil: true
end
User.last.profile
=> nil
# 委譲先が存在しないと、NomethodError とならずに nil が返る。
User.last.aisatsu
=> nil
おまけ
今回は親→子への委譲について書きましたが、子→親への委譲もできます。
class User < ApplicationRecord
def aisatsu
'こんにちは'
end
end
class Profile < ApplicationRecord
belongs_to :user
delegate :aisatsu, to: :user
end
Profile.last.aisatsu
=> "こんにちは"
Discussion