ruby on rails命名范围实现

来自Agile Web Development With Rails一书

class Order  ['updated  {:pay_type => :check} end 

该声明

 orders = Orders.checks.last_n_days(7) 

将导致只对数据库进行一次查询。

rails如何实现这一目标? 我是Ruby的新手,我想知道是否有一个允许这种情况发生的特殊结构。

为了能够链接这样的方法,named_scope生成的函数必须返回自己或者可以进一步限定范围的对象。 但Ruby如何知道它是最后一个函数调用,它现在应该查询数据库?

我问这个是因为上面的语句实际上是查询数据库而不只是返回一个由链接产生的SQL语句。

在named_scope魔法中使用了两个技巧(或模式,如果你愿意)。

代理模式 – 在类或关联上调用命名范围方法始终返回ActiveRecord :: NamedScope :: Scope类的实例,而不是已过滤的AR对象的集合。 这种模式非常有用,有时会使事情变得模糊,因为代理对象本质上是矛盾的。

延迟加载 – 由于延迟加载(在此上下文中意味着 – 只在必要时才访问数据库)命名范围可以链接到需要使用范围定义的集合时。 每当您请求基础集合时,都会评估所有链接的作用域并执行数据库查询。

最后一点注意:在IRB中使用命名范围(或任何使用某种委托的东西)时,要记住一件事。 每次按Enter键时,都会评估您事先编写的内容,并在返回的值上调用inspect方法。 对于链式命名范围,虽然整个表达式被计算到Scope实例,但是当IRB对其调用inspect方法时,将评估范围并触发数据库查询。 这是因为inspect方法是通过委托传播通过所有范围对象直到底层集合。

你可能想试试这个

 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兼容!

命名范围已经死亡

很酷。 我想在Javascript中做这样的事情,但Javascript表现得相当奇怪。

该声明:

 var x = SomeObject; 

不会调用SomeObject的toString()函数。 但声明:

 var x; x = SomeObject; 

按预期正确调用toString()函数。

这可以防止Javascript用链接做很酷的事情。 =(