检查before_save定义在哪里

[1] pry(main)> Student.method(:before_save).source_location
=> ["/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/activemodel-5.1.2/lib/active_model/callbacks.rb", 125]


源码klass.define_singleton_method("before_#{callback}")如下

module ActiveModel

  module Callbacks

    # 因是增加类方法define_model_callbacks,所以需要extend
    # 而为了让define_model_callbacks能使用activesupport的define_callbacks
    # 以及其后的before_save等callback定义方法能使用set_callback
    # 还要让目标类include ActiveSupport::Callbacks
    # 简单来说ActiveModel::Callbacks背后是使用ActiveSupport::Callbacks的
    def self.extended(base)
      base.class_eval do
        include ActiveSupport::Callbacks
      end
    end

    # 为model类定义事件名和对应的callback chain
    # (对于activerecord的使用者来说,其实不会直接调用这方法)
    def define_model_callbacks(*callbacks)
      options = callbacks.extract_options!
      options = {
        skip_after_callbacks_if_terminated: true,
        scope: [:kind, :name],
        only: [:before, :around, :after]
      }.merge!(options)

      types = Array(options.delete(:only))

      callbacks.each do |callback|
        define_callbacks(callback, options)

        # 针对before、around、after,分别定义便利方法
        # before_create、before_save等等
        types.each do |type|
          send("_define_#{type}_model_callback", self, callback)
        end
      end
    end

    # 便利方法before_create、before_save等实际上是用set_callback来为事件绑定callback
    private

      def _define_before_model_callback(klass, callback)
        klass.define_singleton_method("before_#{callback}") do |*args, &block|
          set_callback(:"#{callback}", :before, *args, &block)
        end
      end

      def _define_around_model_callback(klass, callback)
        klass.define_singleton_method("around_#{callback}") do |*args, &block|
          set_callback(:"#{callback}", :around, *args, &block)
        end
      end

      # ActiveSupport::Callbacks的after的逆序执行的,但ActiveModel::Callbacks
      # 会用prepend: true改变callback入队方式,使它们最终顺序执行
      # 同时增加一个value != false的条件,以使invalid的model不执行after的callback
      def _define_after_model_callback(klass, callback)
        klass.define_singleton_method("after_#{callback}") do |*args, &block|
          options = args.extract_options!
          options[:prepend] = true
          conditional = ActiveSupport::Callbacks::Conditionals::Value.new { |v|
            v != false
          }
          options[:if] = Array(options[:if]) << conditional
          set_callback(:"#{callback}", :after, *(args << options), &block)
        end
      end


ActiveRecord本身就自带的before_create、before_save等方法,不难想象ActiveRecord肯定已调用过define_model_callbacks,搜索源码便可发现,它为以下事件(:initialize, :find, :touch, only: :after,:save, :create, :update, :destroy)定义了callback。而validate相关的callabck,则应该在ActiveModel::Validations::Callbacks中

module ActiveRecord
  # This module exists because `ActiveRecord::AttributeMethods::Dirty` needs to
  # define callbacks, but continue to have its version of `save` be the super
  # method of `ActiveRecord::Callbacks`. This will be removed when the removal
  # of deprecated code removes this need.
  module DefineCallbacks
    extend ActiveSupport::Concern

    module ClassMethods
      include ActiveModel::Callbacks
    end

    included do
      include ActiveModel::Validations::Callbacks

      define_model_callbacks :initialize, :find, :touch, only: :after
      define_model_callbacks :save, :create, :update, :destroy
    end
  end
end


这样看来,DefineCallbacks应该是会被include到ActiveRecord中的,搜索源码可发现

module ActiveRecord
  class Base
    #...
    include DefineCallbacks
    #...
    include Callbacks
    #...
  end
end


而下面的include Callbacks,则是

module ActiveRecord
  module Callbacks
    extend ActiveSupport::Concern

    CALLBACKS = [
      :after_initialize, :after_find, :after_touch, :before_validation, :after_validation,
      :before_save, :around_save, :after_save, :before_create, :around_create,
      :after_create, :before_update, :around_update, :after_update,
      :before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
    ]

    def destroy #:nodoc:
      @_destroy_callback_already_called ||= false
      return if @_destroy_callback_already_called
      @_destroy_callback_already_called = true
      _run_destroy_callbacks { super }
    rescue RecordNotDestroyed => e
      @_association_destroy_exception = e
      false
    ensure
      @_destroy_callback_already_called = false
    end

    def touch(*) #:nodoc:
      _run_touch_callbacks { super }
    end

  private

    def create_or_update(*)
      _run_save_callbacks { super }
    end

    def _create_record
      _run_create_callbacks { super }
    end

    def _update_record(*)
      _run_update_callbacks { super }
    end
  end
end


可见这里直接利用了ActiveSupport::Callbacks的define_callbacks所产生的_run_#{name}_callbacks便利方法来调用callback