诗歌rails之Hacking ActiveRecord
关键字: Hacking 看下面一个场景:
运行ProductTest,结果出错,因为Product的price有validates_presence_of声明
我们能否暂时将Product的validation关闭呢,比如:
好,就让我们一起来打开ActiveRecord的盒子,Hack一把:
我们定义了disable_validation方法,先关闭validation,然后将控制权交给block执行,然后恢复validation
而ValidationDisabler模块的代码简直是优美无比!
我们首先定义self.included(base)方法
这个方法就像一个钩子,当下面我们打开ActiveRecord::Base类让它将ValidationDisabler include进来时触发
然后反过来让ActiveRecord::Base执行class_eval,让Base继承ValidationDisabler里的ClassMethods模块
并且给Base的valid?方法加上了AOP -- alias_method_chain
valid_with_disable_check?先判断validation_disabled?,如果返回true(即validation被disable掉了),则valid?方法返回true
如果validation_disabled?返回false(即validation没有disable),则调用valid_without_disable_check?(正常执行valid?方法)
ClassMethods模块里定义一个类变量@@disable_validation来决定是否关闭当前validation
如果大家留意Rails源码和许多Rails插件的源码的话,就会发现很多都是基于上述模式打开ActiveRecord、ActionController
等模块来修改Rails默认的行为的
- # product.rb
- class Product < ActiveRecord::Base
- validates_presence_of :price
- def self.find_ordered
- find(:all, : order => 'name')
- end
- end
- # product_test.rb
- require File.dirname(__FILE__) + '/../test_helper'
- class ProductTest < Test::Unit::TestCase
- def test_find_ordered_should_order_products_by_name
- Product.delete_all
- basket = Product.create!(:name => 'Basket')
- apple = Product.create!(:name => 'Apple')
- assert_equal [apple, basket], Product.find_ordered
- end
- end
我们能否暂时将Product的validation关闭呢,比如:
- # product_test.rb
- require File.dirname(__FILE__) + '/../test_helper'
- class ProductTest < Test::Unit::TestCase
- def test_find_ordered_should_order_products_by_name
- disable_validation do
- Product.delete_all
- basket = Product.create!(:name => 'Basket')
- apple = Product.create!(:name => 'Apple')
- assert_equal [apple, basket], Product.find_ordered
- end
- end
- end
- # test_helper.rb
- class Test::Unit::TestCase
- self.use_transactional_fixtures = true
- self.use_instantiated_fixtures = false
- def disable_validation
- ActiveRecord::Base.disable_validation!
- yield
- ActiveRecord::Base.enable_validation!
- end
- end
- module ValidationDisabler
- def self.included(base)
- base.class_eval do
- extend ClassMethods
- alias_method_chain :valid?, :disable_check
- end
- end
- def valid_with_disable_check?
- if self.class.validation_disabled?
- true
- else
- valid_without_disable_check?
- end
- end
- module ClassMethods
- def disable_validation!
- @@disable_validation = true
- end
- def enable_validation!
- @@disable_validation = false
- end
- def validation_disabled?
- @@disable_validation ||= false
- end
- end
- end
- class ActiveRecord::Base
- include ValidationDisabler
- end
而ValidationDisabler模块的代码简直是优美无比!
我们首先定义self.included(base)方法
这个方法就像一个钩子,当下面我们打开ActiveRecord::Base类让它将ValidationDisabler include进来时触发
然后反过来让ActiveRecord::Base执行class_eval,让Base继承ValidationDisabler里的ClassMethods模块
并且给Base的valid?方法加上了AOP -- alias_method_chain
valid_with_disable_check?先判断validation_disabled?,如果返回true(即validation被disable掉了),则valid?方法返回true
如果validation_disabled?返回false(即validation没有disable),则调用valid_without_disable_check?(正常执行valid?方法)
ClassMethods模块里定义一个类变量@@disable_validation来决定是否关闭当前validation
如果大家留意Rails源码和许多Rails插件的源码的话,就会发现很多都是基于上述模式打开ActiveRecord、ActionController
等模块来修改Rails默认的行为的
莫愁前路无知己,天下无人不识君。