ruby on rails命名范围实现

时间:2010-04-09 13:14:58

标签: ruby-on-rails ruby named-scope

从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语句。

3 个答案:

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

Named Scopes Are Dead

答案 2 :(得分:0)

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

声明:

var x = SomeObject;

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

var x;
x = SomeObject;

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

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