Rails开发细节《三》Participating in the Monitoring Process

简介:

参与监控过程

ActiveRecord控制着model对象的生命周期,它创建它们,在修改,保存和更新的时候监控它们,并且在删除的时候也进行监控。使用回调函数,ActiveRecord允许我们的代码参与这个监控过程。

ActiveRecord总共定义了20个回调函数。18个成对的before和after,还有两个例外:after_find和after_initialize。

 

实现callback有两种方式。

第一种,直接在对象的回调方法中写代码。

 

 
  1. class Order < ActiveRecord::Base 
  2.   def after_save 
  3.     self.payment_due ||= Time.now + 30.days 
  4.   end 
  5. end 

第二种,为回调声明一个处理器,处理器可以是一个方法,或者是一个block。

 
  1. class Order < ActiveRecord::Base 
  2.   before_validation :normalize_credit_card_number 
  3.  
  4.   after_create do |order| 
  5.     logger.info "Order #{order.id} created" 
  6.   end 
  7.  
  8.   protected 
  9.   def normalize_credit_card_number 
  10.     self.cc_number.gsub!(/[-\s]/, ''
  11.   end 
  12. end 

你可以为一个回调函数指定多个处理程序,多个处理程序会按照指定的顺序执行,除非其中一个处理程序返回false,这时候才会终止后面的处理程序。

因为需要优化性能,定义after_find和after_initialize只能用方法的方式,如果使用其他方式,定义的处理程序会被忽略。

Grouping Related Callbacks Together

callback分组

可以将相关的callback处理方法定义在单独的类中,这样这些处理方法就可以在多个model中共享。一个处理类就是在一个类中定义回调方法,把这些类放在app/models文件夹中。

 

 
  1. class CreditCardCallbacks 
  2.   def before_validation(model) 
  3.     model.cc_number.gsub!(/[-\s]/, ''
  4.   end 
  5. end 
  6.  
  7. class Order < ActiveRecord::Base 
  8.   before_validation CreditCardCallbacks.new 
  9. end 
  10.  
  11. class Subscription < ActiveRecord::Base 
  12.   before_validation CreditCardCallbacks.new 
  13. end 

上面的CreditCardCallbacks的before_validation就是共享的,这需要Order和Subscription都包含cc_number属性。共享的处理程序,需要处理相同的属性,肯定需要共享处理程序的model有相同名称的属性。

我们可以定义一个加密和解密的处理程序。可以在存入数据库之前对数据加密,从数据库取出来之后再进行解密。

 

 
  1. class Encrypter 
  2.   def initialize(attrs_to_manage) 
  3.     @attrs_to_manage = attrs_to_manage 
  4.   end 
  5.  
  6.   def before_save(model) 
  7.     @attrs_to_manage.each do |field| 
  8.       model[field].tr!("a-Z""b-za"
  9.     end 
  10.   end 
  11.  
  12.   def after_save(model) 
  13.     @attrs_to_manage.each do |field| 
  14.       model[field].tr!("b-za""a-Z"
  15.     end 
  16.   end  
  17.  
  18.   alias_method :after_find:after_save 
  19. end 

 

 

 

 
  1. require "encrypter" 
  2.  
  3. class Order < ActiveRecord::Base 
  4.   encrypter = Encrypter.new([:name:email]) 
  5.  
  6.   before_save encrypter 
  7.   after_save encrypter 
  8.   after_find encrypter 
  9.  
  10.   protected 
  11.     def after_find 
  12.     end 
  13. end 

我们看到在上面的类中定义了空的after_find方法。前面我们说过after_find和after_initialize的特殊性,这种特殊处理的后果就是ActiveRecord不知道需要调用after_find和after_initialize,除非在model类中存在一个after_find和after_initialize的定义。所以说需要在model中定义一个空的after_find方法。

但是需要用到这个加解密处理的model都需要添加上面的8行代码,我们可以做得更好。扩展一下ActiveRecord::Base类。

 

 
  1. class ActiveRecord::Base 
  2.   def self.encrypt(*attr_names) 
  3.     encrypter = Encrypter.new(attr_names) 
  4.  
  5.     before_save encrypter 
  6.     after_save encrypter 
  7.     after_find encrypter 
  8.  
  9.     defind_method(:after_find) { } 
  10.   end 
  11. end 
  12.  
  13.  
  14. class Order < ActiveRecord::Base 
  15.   encrypt(:name:email
  16. end 
  17.  
  18.  
  19. o = Order.new 
  20. o.name = "swb" 
  21. o.address = "sdfsf" 
  22. o.email = "asdasdf" 
  23. o.save 
  24. puts o.name 
  25.  
  26. o = Order.find(o.id) 
  27. puts o.name 

callback是一个很好的技术,但是有时候滥用的话,在model中会产生一些和model不太相关功能。例如在after_save中写日志这样的功能。

ActiveRecord的观察者observer可以克服这些限制。

Observers观察者

ActiveRecord的observer是一个对象,可以透明的和model类进行连接,在model中注册自己,但是不需要修改model的任何代码。

 

 
  1. class OrderObserver < ActiveRecord::Observer 
  2.   def after_save(an_order) 
  3.     an_order.logger.info("Order #{an_order.id} created") 
  4.   end 
  5. end 

上面的这个observer会自动的注册到Order这个model,因为rails有这方面的约定。

有时候也会打破这个约定,可以在observer类中指定需要观察的model。

 

 
  1. class AuditObserver < ActiveRecord::Observer 
  2.   observe Order, Payment, Refund 
  3.  
  4.   def after_save(model) 
  5.     model.logger.info("[Audit] #{model.class.name} #{model.id} created"
  6.   end 
  7. end 

按照约定,observer类应该放在app/models文件夹中。

Instantiating Observers

实例化观察者

观察者需要实例化,如果不实例化它们,就不会激活它们。

如果在rails应用中使用观察者,你就需要在config/environment.rb文件中设置。

 

 
  1. config.active_record.observers = :order_observer:audit_observer 

如果在单独的应用中使用ActiveRecord对象,你需要手动创建实例。

 

 
  1. OrderObserver.instance 
  2. AuditObserver.instance 

在某种程度上,observers给rails的面向方面编程(Aspect-oriented Programming AOP)带来了很多的好处。允许我们在不改变model代码的同时,给model注入一些行为。




本文转自 virusswb 51CTO博客,原文链接:http://blog.51cto.com/virusswb/1016391,如需转载请自行联系原作者

目录
相关文章
|
6月前
|
存储 缓存 前端开发
SAP Spartacus 中的 Commands and queries
SAP Spartacus 中的 Commands and queries
38 1
|
9月前
|
设计模式 分布式计算 Kubernetes
译|Design patterns for container-based distributed systems(上)
译|Design patterns for container-based distributed systems
45 0
|
9月前
|
设计模式 缓存 监控
译|Design patterns for container-based distributed systems(下)
译|Design patterns for container-based distributed systems(下)
41 0
|
Kubernetes Docker 容器
Kubernetes分享-ImagePullBackOff Error的trouble shooting
Kubernetes分享-ImagePullBackOff Error的trouble shooting
131 0
|
自然语言处理
Re26:读论文 Don’t Stop Pretraining: Adapt Language Models to Domains and Tasks
Re26:读论文 Don’t Stop Pretraining: Adapt Language Models to Domains and Tasks
Re26:读论文 Don’t Stop Pretraining: Adapt Language Models to Domains and Tasks
|
JavaScript 前端开发
Guidelines for Function Compute Development - Troubleshoot Timeout Issues
Endless codes and endless bugs When you write code, you may inadvertently introduce some hidden bugs, even if you test a large proportion of the codes to the maximum extent possible.
1598 0
|
中间件 Serverless Go
Backend-as-a-Service (BaaS) for Efficient Software Development
The adoption of the Internet and mobile technologies has revolutionized the business ecosystem, with entrepreneurs able to implement ideas quickly by .
1715 0
Backend-as-a-Service (BaaS) for Efficient Software Development
笔记:The Seven Steps to Building a Successful Software Development Company
笔记:The Seven Steps to Building a Successful Software Development Company 建立成功软件公司的七个步骤,感觉说的大都是常识,不过毕竟他整理出来了,看看也挺有意思的。
1462 0