Active Record の Validation
Ruby on Rails の Active Record にある validates って便利ですよね。
class Person < ActiveRecord::Base
validates :name, presence: true
end
というモデルがあったときに、name
に値が入っているかをチェックしてくれて、ない場合にはエラーとかまっで設定してくれる。
便利
値があるのにバリデーションが false
???
ただ、boolean
のフィールドでこれを使った際にちょっとつまづきました。
class Person < ActiveRecord::Base
validates :name, presence: true
validates :has_glasses, presence: true
end
というモデルがあるとして、
irb> person = Person.new(name: "Daisuke", has_glasses: false)
=> #<Person id: nil, name: "Daisuke", has_glasses: false>
irb> person.has_glasses
=> false
irb> person.valid?
=> false
と false
の値が has_glasses
に設定されているはずなのにバリデーションが通りません。
???
で、少し調べてみると、これ、実は presence
の動作が(僕個人の意見として)直感的でないのが原因でした。
presence
の動作を定義している ActiveModel::Validations::PresenceValidator
を見ると
class PresenceValidator < EachValidator
def validate_each(record, attr_name, value)
record.errors.add(attr_name, :blank, options) if value.blank?
end
end
となっています。ここで注目すべきは if value.blank?
です。ここで、value
には false
が入っているわけですが、そうなると…
irb> value = false
=> false
irb> value.blank?
=> true
となり、エラーとして認識されてしまいます!
さらに掘り下げると、どうやら、この blank?
メソッド、Rails のライブラリの1つである ActiveSupport
で Object
クラスに追加されているみたいです。
def blank?
respond_to?(:empty?) ? !!empty? : !self
end
boolean
は empty?
メソッドがないので、!self
の結果、つまり true
が返されます。
正しい Boolean フィールドのチェック方法
Booelan のバリデーションを行う場合には ActiveModel::Validations::InclusionValidator
(validates
に書くときは inclusion
) を使って
validates :has_glasses, inclusion: [true, false]
とするのがいいらしいです。