Railsでアプリを作ると、必ず困ることの一つにActiveRecordのvalidatorがあります。
一般のユーザ登録の時はnameが必須だけど、管理画面からはなくても登録できるようにしたい、というケースが良くあります。
その場合、validates :name, presence: true, if: :require_name?
という風にif
オプションで対応することになります。
しかし、すべてのvalidates
にこれを追加するのはちょっと違うなぁとも思います。
validates
にscope
オプションを追加して、save_with(:name)
みたいメソッドを定義し、そこで使うvalidatorを選択すればいいかなとか考えましたが、もっとシンプルにobjectにvalidatorをmix-inで追加できるようにしてみました。
Ruby 1.9.2からあるObject#singleton_classを使うと下記のようにしてオブジェクトにvalidatorを追加できます。
user = User.new
user.singleton_class.class_eval do
validates :name, presence: true
end
これをcontrollerに書くのはダメなので、moduleにして読み込めるようにする、object_extender gemを作ってみました。
require 'object_extender'
class User < ActiveRecord::Base; end
module RequireName
extend ObjectExtender
extended_class do
validates :name, presence: true
end
end
me = User.new(params)
me.save! # OK
# nameを必須にする
me.extend(RequireName)
me.save! # Failed
このように使うシーンに合わせてvalidates
を追加したり、データの正規化を行うためにbefore_validation
を追加することができます。
Railsで使うときは、app/models/user/require_name.rb
にmodule User::RequireName
という形で置けばいいかなーと思っています。
週末に思いつきでお風呂で作ったので、コメントをお待ちしています。
ソースは短くて、30行程度です。
https://github.com/masuidrive/object_extender_rb/blob/develop/lib/object_extender.rb
こういう問題をスマートに解決する方法を一緒に考えてくれる人いないかなぁということで、考えてくれる人は、3,000万人の予約を支えるRailsエンジニア募集!でお待ちしています。
よければ、トレタメンバーのQiitaも見ていって下さい。