発端
validates :is_active, presence: true
boolean型のカラムに対して、presence: trueのバリデーションをかけていました。
is_activeをfalseにしてsaveすると...
example = ExampleModel.new(is_active: false)
example.save # => false
example.errors.full_messages
# => ["Is active can't be blank"]
Is active can't be blank エラーが起きてしまいました。
原因
presence: trueは空かどうかの判定にblank?メソッドを使っているからです。
presence.rbにはこう記述されています。
presence.rb
class PresenceValidator < EachValidator # :nodoc:
def validate_each(record, attr_name, value)
record.errors.add(attr_name, :blank, **options) if value.blank?
end
end
つまりvalue.blank?
の結果がtrue
ならバリデーションエラーということですね。
今回はvalueにfalseが入っているので、false.blank?
が実行されることになりますが,その結果が以下です。
value = false
value.blank?
# => true
疑問
falseっていう値入ってるのに空の判定なの?
今度はblank.rbを見に行きます。
blank.rb
def blank?
respond_to?(:empty?) ? !!empty? : !self
end
オブジェクトがempty?
メソッドを持っていればempty?
を実行し、真偽値に変換しています。
しかし、falseのクラスであるFalseClassはempty?
が使えません。↓
false.class #=> FalseClass
FalseClass.respond_to?(:empty?) # => false
そのためself(自分自身)であるfalseにnotをつけた!false
が実行され、false.blank?
はtrueとなるのです。
false.blank?
がtrueということは、空だったと判定され、can't be blank のバリデーションエラーが起きるようです。
解決方法
validates :is_active, inclusion: { in: [true, false] }
参考記事