从Agile Web Development With Rails一书
class Order < ActiveRecord::Base
named_scope :last_n_days, lambda { |days| {:conditions =>
['updated < ?' , days] } }
named_scope :checks, :conditions => {:pay_type => :check}
end
声明
orders = Orders.checks.last_n_days(7)
只会导致对数据库的一次查询。
rails如何实现这一目标?我是Ruby的新手,我想知道是否有一个允许这种情况发生的特殊结构。
为了能够链接这样的方法,named_scope生成的函数必须返回自己或者可以进一步限定范围的对象。但Ruby如何知道它是最后一个函数调用,它现在应该查询数据库?
我问这个是因为上面的语句实际上是在查询数据库,而不仅仅是返回一个由链接产生的SQL语句。
答案 0 :(得分:7)
在named_scope magic中使用了两种技巧(或模式,如果你愿意的话)。
代理模式 - 在类或关联上调用命名范围方法始终返回ActiveRecord :: NamedScope :: Scope类的实例,而不是集合过滤的AR对象。这种模式非常有用,有时会使事情变得模糊,因为代理对象本质上是矛盾的。
延迟加载 - 由于延迟加载(在此上下文中意味着 - 仅在必要时才访问数据库)命名范围可以链接到您需要使用由范围。每当您请求底层的colleciton时,都会评估所有链接的范围并执行数据库查询。
最后一点说明:在IRB中使用命名范围(或任何使用某种委托的东西)时,要记住一件事。每次按Enter键时,都会评估您事先编写的内容,并在返回的值上调用inspect
方法。对于链式命名范围,虽然整个表达式被计算为Scope实例,但是当IRB在其上调用inspect
方法时,将评估范围并触发数据库查询。这是因为inspect
方法是通过委托传播到所有范围对象直到底层集合的事实。
答案 1 :(得分:3)
class Order < ActiveRecord::Base
class << self
def last_n_days(n)
scoped(:conditions => ['updated < ?', days])
end
def checks
scoped(:conditions => {:pay_type => :check})
end
end
end
@orders = Order.last_n_days(5)
@orders = Order.checks
@orders = Order.checks.last_n_days(5)
这仍然是你喜爱的所有懒人装载。也就是说,在您尝试访问记录之前,它不会进行查询。 奖金: Rails 3兼容!
答案 2 :(得分:0)
非常酷。我想在Javascript中做这样的事情但Javascript表现得相当奇怪。
声明:
var x = SomeObject;
不会调用SomeObject的toString()函数。但声明:
var x;
x = SomeObject;
按预期正确调用toString()函数。
这可以防止Javascript使用链接做很酷的事情。 =(