Rails源码研究之ActiveRecord:五,Callbacks

Callbacks相关的源码在callbacks.rb文件里:
[code]
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
[/code]

从源码中我们可以学习如下几点:
1,有四种类型的callback macros:
1)Method references(symbol)
[code]
class Topic < ActiveRecord::Base
before_destroy :destroy_author
end
[/code]
2)Callback objects
[code]
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
[/code]
3)Inline methods(proc)
[code]
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
[/code]
4)Inline eval methods(string)
[code]
class Topic < ActiveRecord::Base
before_destroy 'self.class.delete_all "parent_id = #{id}"'
end
[/code]

2,执行Model的如下方法时会调用callbacks:
instantiate(find)、initialize、create_or_update、valid?、create、update、destroy

3,activesupport\lib\activesupport\core_text\module\aliasing.rb:
[code]
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
[/code]
比如[b]alias_method_chain :destroy, :callbacks[/b]的效果是调用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拦截的功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值