Railsのvalidatesメソッドについて調べてみた
railsでは下記のように書くことでバリデーションを行える(コード参照元)。
class Person < ApplicationRecord
validates :name, presence: true
end
このvalidates
メソッドがどのように実装されているか気になったので調べてみた。
どこで定義されているのか
ApplicationRecord
ActiveRecord::Base
を継承しているクラスであり、rails new
で自動生成される。
ActiveRecord::Base
ActiveRecord::Validationsをincludeしている。
ActiveRecord::Validations
ActiveModel::Validationsをincludeしている。
ActiveModel::Validations
validatesメソッドが実装されているファイルを読み込んでいる。
ActiveModel::Validations::ClassMethods#validates
validates
メソッドが定義されている。
ActiveModel::Validations::ClassMethods#validatesの中身を読む
下記の場合を考える(コード参照元)。
class Order < ApplicationRecord
validates :card_number, presence: true, if: :paid_with_card?
def paid_with_card?
payment_type == "card"
end
end
1. validatesメソッドの引数を確認する
railsはアスタリスクを付けることで引数を配列に指定できるので、attributes = [:name, {:presence=>true, :if=>:paid_with_card?}]
となる。
extract_options!
は下記のようになっている(このメソッドを使用している理由はRailsガイドが参考になる)。
これにより、
defaults = {:presence=>true, :if=>:paid_with_card?}
また、
により、defaults = {:if=>:paid_with_card?}
validations = {:presence=>true}
2. validationのクラスを取得する
key = "PresenceValidator"
となり、const_get
メソッドにより、
validator = ActiveModel::Validations::PresenceValidator
となる(クラスが定義されるときに、PresenceValidator
という名前で、ActiveModel::Validations::PresenceValidator
を値として追加しているため)。
今回はoptions
がtrue
のため、
により、
validates_with(ActiveModel::Validations::PresenceValidator, {:if=>:paid_with_card?, :attributes=>[:name]})
ActiveModel::Validations::ClassMethods#validates_withの中身を読む
1. validates_withメソッドの引数を確認する
args = [ActiveModel::Validations::PresenceValidator, {:if=>:paid_with_card?, :attributes=>[:name]}]
となり、block
にはnil
が入る。
self = Order
なので、
options = {:if=>:paid_with_card?, :attributes=>[:name], :class=>Order}
args = [ActiveModel::Validations::PresenceValidator]
2. validatorのインスタンスを作成する
ActiveModel::Validations::PresenceValidator
のインスタンスを作成する。
3. validatorのインスタンスを使ってvalidateメソッドを呼び出す
ActiveModel::Validations::PresenceValidator
はActiveModel::EachValidator
を継承しているので、validator.attributes = [:name]
となる。
_validators
はクラス属性として定義されており、_validators[:name] = [ActiveModel::Validations::PresenceValidator.new]
となる。
validate
メソッドは次で呼び出される。
validate(ActiveModel::Validations::PresenceValidator.new, {:if=>:paid_with_card?, :attributes=>[:name], :class=>Order})
ActiveModel::Validations::ClassMethods#validateの中身を読む
1. validateメソッドの引数を確認する
args = [ActiveModel::Validations::PresenceValidator.new, {:if=>:paid_with_card?, :attributes=>[:name], :class=>Order}]
となり、block
にはnil
が入る。
extract_options!
により、
options = {:if=>:paid_with_card?, :attributes=>[:name], :class=>Order}
args = [ActiveModel::Validations::PresenceValidator.new]
2. set_callbackメソッドを呼び出す
args.all?(Symbol)
はfalse
となるので、ここは呼び出されない。
options.key?(:on)
はfalse
となるので、ここは呼び出されない。
set_callback
メソッドは次で呼び出される。
set_callback(:validate, ActiveModel::Validations::PresenceValidator.new, {:if=>:paid_with_card?, :attributes=>[:name], :class=>Order})
まとめ
Railsのvalidates
メソッドはコールバックを利用していることがわかった。
別記事でコールバックの仕組みを調べる。
Discussion