实际上,if: :password_required?
的 if
option 特性,并不是来做ActiveModel
而是来自 ActiveSupport::Callbacks。之前好像有一个帖子专门讨论callback
的。
我把整个定义这个 validate 过程写下来,以Rails 5
代码为例,你就能看到在哪里执行 if 了。
首先是 validates
的定义 code
注意这段代码,validates_with(validator, defaults.merge(_parse_validates_options(options)))
validates_with
的定义在这里 code
然后有看到了,validate(validator, options)
validate
的定义在这里 code
首先看到这里,好激动,看到了if
if options.key?(:on)
options = options.dup
options[:if] = Array(options[:if])
options[:if].unshift ->(o) {
!(Array(options[:on]) & Array(o.validation_context)).empty?
}
end
同时下面还有一段 set_callback(:validate, *args, &block)
我们的if
还在 args
里面
到这里大概明白,实际上 validate 就是一个 callback
那么set_callback
是什么鬼?
网上找会看到这段代码
included do
extend ActiveModel::Naming
extend ActiveModel::Callbacks #这个!!这个!!
extend ActiveModel::Translation
然后找到了ActiveModel::Callback
定义的地方。code,但是,实际上这里并没有做什么。
然后又看到了,
def self.extended(base) #:nodoc:
base.class_eval do
include ActiveSupport::Callbacks
end
end
终于到了ActiveSupport::Callbacks
,我们找到的是set_callback
的定义
定义在这里code
其中有这段代码
mapped = filters.map do |filter|
Callback.build(self_chain, filter, type, options)
end
然后要找的就是Callback
的定义了
Callback
code
又一次兴奋的看到了if
def initialize(name, filter, kind, options, chain_config)
@chain_config = chain_config
@name = name
@kind = kind
@filter = filter
@key = compute_identifier filter
@if = Array(options[:if]) # 这个!!这个!!
@unless = Array(options[:unless])
end
接下来的问题,其实就比较轻松了,这和Callback
被执行时的,@if
是如何处理的。
在当前的页面,还能看到这段代码
def conditions_lambdas
@if.map { |c| CallTemplate.build(c, self).make_lambda } +
@unless.map { |c| CallTemplate.build(c, self).inverted_lambda }
end
这里,整个 @if Array
转化成了一个由 CallTemplate.build(c, self).make_lambda
组成的Array
,里面是什么呢?是lambda
。
接着就是要搞这个 conditions_lambdas
返回的 Array
去了哪里。
我们还是在同一个文件里code
def apply(callback_sequence)
user_conditions = conditions_lambdas
user_callback = CallTemplate.build(@filter, self)
case kind
when :before
Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter)
when :after
Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
when :around
callback_sequence.around(user_callback, user_conditions)
end
end
这里其实有涉及到了,ActiveSupport::Callbacks
的执行逻辑,就不再深入,直接跳到最终执行的地方,还是同一个文件code
def skip?(arg)
arg.halted || !@user_conditions.all? { |c| c.call(arg.target, arg.value) }
end
这里有!@user_conditions.all? { |c| c.call(arg.target, arg.value) }
因为这是一个 Array
查看 Enumerable .all?
的定义。code
all? [{ |obj| block } ] → true or false
Passes each element of the collection to the given block. The method returns true if the block never returns false or nil. If the block is not given, Ruby adds an implicit block of { |obj| obj } which will cause all? to return true when none of the collection members are false or nil.
所以,if options
实际上是在这里执行的。