天天看點

Rails開發細節《三》Participating in the Monitoring Process

參與監控過程

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 &lt; 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 &lt; 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 &lt; ActiveRecord::Observer 

  def after_save(an_order) 

    an_order.logger.info("Order #{an_order.id} created") 

上面的這個observer會自動的注冊到Order這個model,因為rails有這方面的約定。

有時候也會打破這個約定,可以在observer類中指定需要觀察的model。

class AuditObserver &lt; 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,如需轉載請自行聯系原作者