ruby on Rails 命名范围实现

发布于 2024-08-28 10:26:39 字数 590 浏览 10 评论 0原文

摘自《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 语句。

From the book 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

The statement

orders = Orders.checks.last_n_days(7)

will result to only one query to the database.

How does rails implement this? I'm new to Ruby and I'm wondering if there's a special construct that allows this to happen.

To be able to chain methods like that, the functions generated by named_scope must be returning themselves or an object than can be scoped further. But how does Ruby know that it is the last function call and that it should query the database now?

I ask this because the statement above actually queries the database and not just returns an SQL statement that results from chaining.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

玩套路吗 2024-09-04 10:26:39

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

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

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

最后一点:在 IRB 中使用命名范围(或任何使用某种委托的事物)时需要记住一件事。每次按 Enter 键时,都会对您之前编写的内容进行评估,并根据返回值调用 inspect 方法。在链接命名作用域的情况下,尽管整个表达式被计算为 Scope 实例,但当 IRB 对其调用 inspect 方法时,将对作用域进行计算并触发数据库查询。这是由于 inspect 方法是通过委托方式传播到所有作用域对象直至底层集合的事实造成的。

There are two tricks (or patterns if you will) employed in the named_scope magic.

Proxy pattern - calling a named scope method on a class or an association always returns an instance of the ActiveRecord::NamedScope::Scope class, not a colleciton of filtered AR objects. This pattern, altough very useful, makes things kind of blurry sometimes, since the proxy objects are ambivalent in their nature.

Lazy loading - thanks to lazy loading (which in this context means - hitting the database only if neccessary) named scopes can be chained up to the point when you need to work with the collection defined by the scopes. Whenever you request the underlying colleciton, all the chained scopes are evaluated and a database query is executed.

One final note: There's one thing to have in mind when playing with named scopes (or with any thing that uses delegation of some kind) in IRB. Everytime you hit Enter, the thing you wrote beforehand is evaluated and the inspect method is called on the returned value. In the case of chained named scopes, although the whole expression is evaluated to a Scope instance, when the IRB calls the inspect method on it, the scopes are evaluated and the database query is fired. This is caused by the fact that the inspect method is by means of delegation propagated through all the scope objects up to the underlying collection.

伪心 2024-09-04 10:26:39

您可能想尝试这种

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!

命名范围已死

You might want to try this

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

usage is the same

@orders = Order.last_n_days(5)
@orders = Order.checks
@orders = Order.checks.last_n_days(5)

This still does all the lazy loading you love. That is, it won't make a query until you attempt to access the records. Bonus: Rails 3 compatible!

Named Scopes Are Dead

哭了丶谁疼 2024-09-04 10:26:39

非常酷。我正在考虑在 Javascript 中做类似的事情,但 Javascript 的行为相当奇怪。

语句:

var x = SomeObject;

不调用SomeObject的toString()函数。但语句:

var x;
x = SomeObject;

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

这会阻止 Javascript 通过链接做一些很酷的事情。 =(

Very cool. I was thinking of doing something like this in Javascript but Javascript behaves rather weird.

The statement:

var x = SomeObject;

does not call SomeObject's toString() function. But the statement:

var x;
x = SomeObject;

correctly calls the toString() function as expected.

This prevents Javascript from doing cool stuff with chaining. =(

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文