新手问题 ActiveRecord 中回调方法的困惑

shangrenzhidao · 2014年11月20日 · 最后由 pynix 回复于 2014年11月22日 · 2164 次阅读

这个是 Agile Rails 的一个例子 Code A

class Order < ActiveRecord::Base 
  before_validation :normalize_credit_card_number 
  after_create do |order|
    logger.info "Order #{order.id} created" 
  end
protected
  def normalize_credit_card_number
    self.cc_number.gsub!(/[-\s]/, '')
  end
end

Code B

class CreditCardCallbacks
  # Normalize the credit card number
  def before_validation(model) 
    model.cc_number.gsub!(/[-\s]/, '')             #b1
  end 
end

class Order < ActiveRecord::Base
  before_validation CreditCardCallbacks.new       #b2
  # ...
end

class Subscription < ActiveRecord::Base
  before_validation CreditCardCallbacks.new         #b3
  # ...
end

Code A 比较简单,但是 Code B 中我不是很能理解,Code B 是将方法抽取到 CreditCardCallbacks 类中,在 b1 处,为什么复写 before_validation, 并且带有一个 model 参数,而在,b2 和 b3 调用方法的时候参数却变成了 CreditCardCallbacks 的 instance ? 不是很明确其中的道理。请指教。提前感谢。

自己顶一下吧。

代码缩进不忍直视。

从 before_validation 开始搜索

# File activemodel/lib/active_model/validations/callbacks.rb, line 56
def before_validation(*args, &block)
  options = args.last
  if options.is_a?(Hash) && options[:on]
    options[:if] = Array(options[:if])
    options[:on] = Array(options[:on])
    options[:if].unshift lambda { |o|
      options[:on].include? o.validation_context
    }
  end
  set_callback(:validation, :before, *args, &block)
end

# File activesupport/lib/active_support/callbacks.rb, line 600
def set_callback(name, *filter_list, &block)
  type, filters, options = normalize_callback_params(filter_list, block)
  self_chain = get_callbacks name
  mapped = filters.map do |filter|
    Callback.build(self_chain, filter, type, options)
  end

  __update_callbacks(name) do |target, chain|
    options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
    target.set_callbacks name, chain
  end
end

# File 'activesupport/lib/active_support/callbacks.rb', line 549

def normalize_callback_params(filters, block) # :nodoc:
  type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
  options = filters.extract_options!
  filters.unshift(block) if block
  [type, filters, options.dup]
end

# This is used internally to append, prepend and skip callbacks to the
# CallbackChain.
def __update_callbacks(name) #:nodoc:
  ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse_each do |target|
    chain = target.get_callbacks name
    yield target, chain.dup
  end
end

# File activesupport/lib/active_support/callbacks.rb, line 740
def set_callbacks(name, callbacks)
  send "_#{name}_callbacks=", callbacks
end

#2 楼 @Rei 哈哈,看看这回吧

#3 楼 @loveltyoic 看完后,我的疑问更多了。

源代码看的云里雾里,果然功力尚浅,同求解答,顶一顶

两个模型中都使用了,复用。。。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号