Railsでは、on:
を使うことで、バリデーション実行タイミングを指定できたり、カスタムコンテキストとして実行するかしないかを調整できます。
on:
で定義したカスタムコンテキストをvalid?
等の引数に渡すことでバリデーションのチェックができます。
Railsガイドではvalid?(:xxx)
の使い方が載っていますが、一度にカスタムコンテキストを複数指定する方法を書いておきたいと思います。
結論
結論としては、配列で指定してあげれば良いです。
def create
book.valid?([:xxx, :yyy])
...
end
with_options on: :xxx do
validates :title, presence: true
...略
end
with_options on: :yyy do
validates :content, presence: true
...略
end
こうすることで、:xxx
と:yyy
どちらのバリデーションチェックもかけることができます。
疑問に思ったこと
配列で指定できるのは良かったのですが、
「なんで配列でも機能するし、シンボルを1つ渡しただけでも機能するんだろう?」と思いました。
なので、ソースコードを除いてみることにしました。
Rails内の仕組み
初級者のため読むのにかなり苦労しました。ざっくりしかわかっていない点はご容赦ください、、、
1.まず、valid?
メソッドの中でself.validation_context
に、引数をそのまま渡しています。(上記の例だと[:xxx, :yyy]
)
def valid?(context = nil)
current_context, self.validation_context = validation_context, context
errors.clear
run_validations!
ensure
...略
2.そして、以下ところで定義されているオプション(options[:on]
)と先ほど代入したvalidation_context
で一致するものがあるかどうかを調べていました。
def validate(*args, &block)
...略
options[:if].unshift ->(o) {
!(options[:on] & Array(o.validation_context)).empty? # ①
}
end
...略
end
つまり、、、
Array(o.validation_context)
として配列の形にしてから処理をしているので、[:xxx, :yyy]
のような配列であっても、:xxx
のように単にシンボルであっても処理ができるということがわかって、腑に落ちました。
方法としてはシンプルですが、この方法は自分のコーディングにも活かせそうだなと思いました。
ちなみに
ちなみに上記のコードを読んでいる中ですごくRubyの勉強になった点がありました。
def make_lambda
lambda do |target, value, &block|
target, block, method, *arguments = expand(target, value, block)
target.send(method, *arguments, &block)
# target => #<Book id: nil, title: "", content: "", created_at: nil, updated_at: nil>
# method => :instance_exec
# arguments => [#<Book id: nil, title: "", content: "", created_at: nil, updated_at: nil>]
# block => #<Proc:.../lib/active_model/validations.rb:166 (lambda)> ⇦上記コード①のオブジェクト
end
end
ここのtarget.send(method, *arguments, &block)
でおこなっている事についてです。
各変数に入っている値がコメントアウトで示した場合、ここで行われているのは、
send
メソッドでinstance_exec
メソッドが、引数をarguments
を展開したBookオブジェクトとして呼ばれ、①のブロックのオブジェクトが実行される、というものでした。
つまり、ここでは①のブロックが、Bookオブジェクトを引数oとして実行されている、ということになりそうです。
最後に
今までは配列にすれば複数指定できる、OK!として終わってしまっていたところを、完全に理解できなかったとはいえ、ソースコードを追うところまでやってみたのは良かったかなと思います。
「ちなみに」で書いた部分については、send
メソッドの理解、instance_exec
というメソッドの存在と理解、lambda
の理解が足りていなかった私にとっては読み解くのが難しく、頭が痛くなりそうでしたが、これを機に身をもって理解できて良かったと思いました。