Rails宝典之第六十二式: Hacking ActiveRecord

看下面一个场景:
[code]
# 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
[/code]
运行ProductTest,结果出错,因为Product的price有validates_presence_of声明

我们能否暂时将Product的validation关闭呢,比如:
[code]
# 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
[/code]

好,就让我们一起来打开ActiveRecord的盒子,Hack一把:
[code]
# 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
[/code]
我们定义了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默认的行为的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值