この記事では、 ActiveModel::EachValidator
を使った、個別の項目を検証するバリデータの作り方を基礎から応用まで解説します
基礎
まずRailsガイドを読んでみましょう。
個別の属性を検証するためのカスタムバリデータを追加するには、ActiveModel::EachValidatorを使用するのが最も簡単で便利です。この場合、このカスタムバリデータクラスはvalidate_eachメソッドを実装する必要があります。このメソッドは、そのインスタンスに対応する「レコードと属性と値」、バリデーションを行なう属性、そして渡されたインスタンスの属性の値の3つの引数を取ります。
class EmailValidator < ActiveModel::EachValidator def validate_each(record, attribute, value) unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i record.errors[attribute] << (options[:message] || "はメールアドレスではありません") end end end class Person < ActiveRecord::Base validates :email, presence: true, email: true end
上の例に示したように、標準のバリデーションとカスタムバリデーションを組み合わせることもできます。
とあります。
補足です。たしかにこれで動くのは動くとは思いますが、バリデータは app/validators/email_validator.rb
とかに入れ、モデルには
validates :field_name, email: true
など、2つを分けて書くのが普通だと思います。補足終わりです
なるほどなるほど、 email: true
のようなバリデータはこの知識だけで作れますが、 true
だけではすまないバリデータ、すなわち、 inclusion: ['01', '02']
や、オプションも指定した numericality: { only_integer: true }
のようなバリデータは実装できません。
では、応用編です。
応用
オプションありパターン
では理解しやすい、オプションありパターンからいきましょう。 numericality: { only_integer: true }
のように Hash
つきで呼び出すと、 { only_integer: true }
は(引数にありませんが) options
で取れます。つまり、 only_integer
の値が取りたければ、 options[:only_integer]
を呼ぶことになります。
Railsガイドの例でも options[:message]
と書いてある部分があり、
validates :field_name, email: { message: 'を正しく入力してください' }
のように書く機能も備えていたことになります。
オプションなしパターン
では、 Hash
なし、すなわち、 inclusion: ['01', '02']
のような呼び出しパターンでは、与えられた ['01', '02']
の部分はどう受け取ればよいのでしょうか。これ、さらっと探したけどあまり説明がなく、Railsのソースを見てたどり着きました。結論は、
['01', '02']
の部分が、:
-
Range
orArray
の場合、options[:in]
で、 - 上記以外の場合、
options[:with]
で
取ります
えーー! ハッシュの値に指定した型によって、取り方が変わるの?? あっ、でも確かに、オプションの有無によって、
validates :field_name, format: /\A[a-zA-Z]+\z/
validates :field_name, format: { with: /\A[a-zA-Z]+\z/, message: "英文字のみが使用できます" }
validates :field_name, inclusion: %w(small medium large)
validates :field_name, inclusion: { in: %w(small medium large), message: "%{value} のサイズは無効です" }
のように、 in
と with
が(自然と)使い分けられているのを見たことがある方は多いのではないでしょうか。それはこういうことだったのか……。
まとめ
Railsのバリデータを自作するときは in
と with
の使い分けを知らないとプチハマりする。