參與監控過程
ActiveRecord控制着model對象的生命周期,它建立它們,在修改,儲存和更新的時候監控它們,并且在删除的時候也進行監控。使用回調函數,ActiveRecord允許我們的代碼參與這個監控過程。
ActiveRecord總共定義了20個回調函數。18個成對的before和after,還有兩個例外:after_find和after_initialize。
<a target="_blank" href="http://blog.51cto.com/attachment/201210/135658888.jpg"></a>
實作callback有兩種方式。
第一種,直接在對象的回調方法中寫代碼。
class Order < ActiveRecord::Base
def after_save
self.payment_due ||= Time.now + 30.days
end
end
第二種,為回調聲明一個處理器,處理器可以是一個方法,或者是一個block。
before_validation :normalize_credit_card_number
after_create do |order|
logger.info "Order #{order.id} created"
protected
def normalize_credit_card_number
self.cc_number.gsub!(/[-\s]/, '')
你可以為一個回調函數指定多個處理程式,多個處理程式會按照指定的順序執行,除非其中一個處理程式傳回false,這時候才會終止後面的處理程式。
因為需要優化性能,定義after_find和after_initialize隻能用方法的方式,如果使用其他方式,定義的處理程式會被忽略。
Grouping Related Callbacks Together
callback分組
可以将相關的callback處理方法定義在單獨的類中,這樣這些處理方法就可以在多個model中共享。一個處理類就是在一個類中定義回調方法,把這些類放在app/models檔案夾中。
class CreditCardCallbacks
def before_validation(model)
model.cc_number.gsub!(/[-\s]/, '')
before_validation CreditCardCallbacks.new
class Subscription < ActiveRecord::Base
上面的CreditCardCallbacks的before_validation就是共享的,這需要Order和Subscription都包含cc_number屬性。共享的處理程式,需要處理相同的屬性,肯定需要共享處理程式的model有相同名稱的屬性。
我們可以定義一個加密和解密的處理程式。可以在存入資料庫之前對資料加密,從資料庫取出來之後再進行解密。
class Encrypter
def initialize(attrs_to_manage)
@attrs_to_manage = attrs_to_manage
def before_save(model)
@attrs_to_manage.each do |field|
model[field].tr!("a-Z", "b-za")
end
def after_save(model)
model[field].tr!("b-za", "a-Z")
end
alias_method :after_find, :after_save
require "encrypter"
encrypter = Encrypter.new([:name, :email])
before_save encrypter
after_save encrypter
after_find encrypter
def after_find
我們看到在上面的類中定義了空的after_find方法。前面我們說過after_find和after_initialize的特殊性,這種特殊處理的後果就是ActiveRecord不知道需要調用after_find和after_initialize,除非在model類中存在一個after_find和after_initialize的定義。是以說需要在model中定義一個空的after_find方法。
但是需要用到這個加解密處理的model都需要添加上面的8行代碼,我們可以做得更好。擴充一下ActiveRecord::Base類。
class ActiveRecord::Base
def self.encrypt(*attr_names)
encrypter = Encrypter.new(attr_names)
before_save encrypter
after_save encrypter
after_find encrypter
defind_method(:after_find) { }
encrypt(:name, :email)
o = Order.new
o.name = "swb"
o.address = "sdfsf"
o.email = "asdasdf"
o.save
puts o.name
o = Order.find(o.id)
callback是一個很好的技術,但是有時候濫用的話,在model中會産生一些和model不太相關功能。例如在after_save中寫日志這樣的功能。
ActiveRecord的觀察者observer可以克服這些限制。
Observers觀察者
ActiveRecord的observer是一個對象,可以透明的和model類進行連接配接,在model中注冊自己,但是不需要修改model的任何代碼。
class OrderObserver < ActiveRecord::Observer
def after_save(an_order)
an_order.logger.info("Order #{an_order.id} created")
上面的這個observer會自動的注冊到Order這個model,因為rails有這方面的約定。
有時候也會打破這個約定,可以在observer類中指定需要觀察的model。
class AuditObserver < ActiveRecord::Observer
observe Order, Payment, Refund
model.logger.info("[Audit] #{model.class.name} #{model.id} created")
按照約定,observer類應該放在app/models檔案夾中。
Instantiating Observers
執行個體化觀察者
觀察者需要執行個體化,如果不執行個體化它們,就不會激活它們。
如果在rails應用中使用觀察者,你就需要在config/environment.rb檔案中設定。
config.active_record.observers = :order_observer, :audit_observer
如果在單獨的應用中使用ActiveRecord對象,你需要手動建立執行個體。
OrderObserver.instance
AuditObserver.instance
在某種程度上,observers給rails的面向方面程式設計(Aspect-oriented Programming AOP)帶來了很多的好處。允許我們在不改變model代碼的同時,給model注入一些行為。
本文轉自 virusswb 51CTO部落格,原文連結:http://blog.51cto.com/virusswb/1016391,如需轉載請自行聯系原作者