浏览 3441 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2007-06-22
module ActiveRecord module Callbacks CALLBACKS = %w( after_find after_initialize before_save after_save before_create after_create before_update after_update before_validation after_validation before_validation_on_create after_validation_on_create before_validation_on_update after_validation_on_update before_destroy after_destroy ) def self.included(base) #:nodoc: base.extend(ClassMethods) base.class_eval do class << self include Observable alias_method_chain :instantiate, :callbacks end [:initialize, :create_or_update, :valid?, :create, :update, :destroy].each do |method| alias_method_chain method, :callbacks end end CALLBACKS.each do |method| base.class_eval <<-"end_eval" def self.#{method}(*callbacks, &block) callbacks << block if block_given? write_inheritable_array(#{method.to_sym.inspect}, callbacks) end end_eval end end module ClassMethods #:nodoc: def instantiate_with_callbacks(record) object = instantiate_without_callbacks(record) if object.respond_to_without_attributes?(:after_find) object.send(:callback, :after_find) end if object.respond_to_without_attributes?(:after_initialize) object.send(:callback, :after_initialize) end object end end def create_or_update_with_callbacks #:nodoc: return false if callback(:before_save) == false result = create_or_update_without_callbacks callback(:after_save) result end def create_with_callbacks #:nodoc: return false if callback(:before_create) == false result = create_without_callbacks callback(:after_create) result end def update_with_callbacks #:nodoc: return false if callback(:before_update) == false result = update_without_callbacks callback(:after_update) result end def valid_with_callbacks? #:nodoc: return false if callback(:before_validation) == false if new_record? then result = callback(:before_validation_on_create) else result = callback(:before_validation_on_update) end return false if result == false result = valid_without_callbacks? callback(:after_validation) if new_record? then callback(:after_validation_on_create) else callback(:after_validation_on_update) end return result end def destroy_with_callbacks #:nodoc: return false if callback(:before_destroy) == false result = destroy_without_callbacks callback(:after_destroy) result end private def callback(method) notify(method) callbacks_for(method).each do |callback| result = case callback when Symbol self.send(callback) when String eval(callback, binding) when Proc, Method callback.call(self) else if callback.respond_to?(method) callback.send(method, self) else raise ActiveRecordError, "Callbacks must be a symbol denoting the method to call, a string to be evaluated, a block to be invoked, or an object responding to the callback method." end end return false if result == false end result = send(method) if respond_to_without_attributes?(method) return result end def callbacks_for(method) self.class.read_inheritable_attribute(method.to_sym) or [] end end end 从源码中我们可以学习如下几点: 1,有四种类型的callback macros: 1)Method references(symbol) class Topic < ActiveRecord::Base before_destroy :destroy_author end 2)Callback objects class BankAccount < ActiveRecord::Base before_save EncryptionWrapper.new("credit_card_number") after_save EncryptionWrapper.new("credit_card_number") after_initialize EncryptionWrapper.new("credit_card_number") end class EncryptionWrapper def initialize(attribute) @attribute = attribute end def before_save(record) record.credit_card_number = encrypt(record.credit_card_number) end def after_save(record) record.credit_card_number = decrypt(record.credit_card_number) end alias_method :after_find, :after_save private def encrypt(value) # Secrecy is committed end def decrypt(value) # Secrecy is unveiled end end 3)Inline methods(proc) class Firm < ActiveRecord::Base # Destroys the associated clients and people when the firm is destroyed before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" } before_destroy { |record| Client.destroy_all "client_of = #{record.id}" } end 4)Inline eval methods(string) class Topic < ActiveRecord::Base before_destroy 'self.class.delete_all "parent_id = #{id}"' end 2,执行Model的如下方法时会调用callbacks: instantiate(find)、initialize、create_or_update、valid?、create、update、destroy 3,activesupport\lib\activesupport\core_text\module\aliasing.rb: class Module def alias_method_chain(target, feature) aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1 yield(aliased_target, punctuation) if block_given? alias_method "#{aliased_target}_without_#{feature}#{punctuation}", target alias_method target, "#{aliased_target}_with_#{feature}#{punctuation}" end end 比如alias_method_chain :destroy, :callbacks的效果是调用destroy方法时会调用destroy_with_callbacks 而destroy_with_callbacks方法先调用callback(:before_destroy),然后destroy_without_callbacks(又被alias为destroy),然后callback(:after_destroy) 然后callback(:before_destroy)和callback(:after_destroy)分别调用before_destroy和after_destroy后面的macros ActiveRecord的Callback就像一个Around_filter,实现了aop拦截的功能 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |