ActiveModel で 関連モデルのバリデーションを実行する(validates_associated を実装)
Ruby on Railsで開発をしているときに、DBに紐付かないデータを扱う際にActiveModelを使用することが多いと思います。
関連するモデルのバリデーションをしたい時には、ActiveRecordの場合はvalidates_associatedを使えば済みますが、ActiveModelにはこのメソッドはありません。
今回はActiveModelでvalidates_assosicated
と同等の処理(関連モデルのバリデーション)を実装でカバーする方法を紹介します。
validates_associatedとは
そもそもvalidates_associated
とは、ActiveRecordで関連するモデルのvalidationを実行したいときに使用するメソッドです。
ActiveModelで使用したいときは関連するモデルをattributeとして持ち、操作するような場合が挙げられます。
今回は以下のような例で考えてみます。
class User < ApplicationRecord
end
class UserInfo
include AcitveModel::Model
attr_accessor :user
delegate :name, :name=, to: :user, allow_nil: true
def save
return false if invalid?
user.save!
true
end
end
ここで、UserInfo#save
を実行した際に、user
のvalidationを実行するべきです。
valid?をオーバーライドする
上記の例でuser
のvalidationを実行するなら、簡単に処理を追加するならUserInfo#save
のif invalid?
に|| user.invalid?
を追加すれば良さそうに思えます。
しかし、UserInfo#valid?
では同等の結果が得られず、クラスの実装としては不十分と言えます。
そこで、UserInfo#valid?
をオーバーライドすることでControllerからActiveRecordと同様に使えるようにします。
class User::Info
include ActiveModel::Validations
~
def valid?(context = nil)
if super && user.valid?
true
else
error.merge!(user.errors)
false
end
end
こうすることで ControllerからはActiveModelかどうかを意識せずに、valid?
やsave
を使うことができ、コードをシンプルに保つことができます。
まとめ
ActiveModelはとても便利なモジュールですが、時々ActiveRecordとの違いで困ることがあります。今回やってみて、validates_associated
の代わりは実装でカバーすることが出来ると思いました。個人的にはRails標準のActiveModelをなるべく使いたいと考えているので、この方法でよりActiveModelを使えるケースが増えればいいなと思います。
Discussion