ActiveRecord 命名范围、.scopes

发布于 2024-08-08 04:52:41 字数 1247 浏览 7 评论 0原文

这个问题的背景相当复杂和令人费解,由于我正在寻找一个简单的答案,因此我将在解释我的问题时将其放在一边,而是提供这个假设的情况。

如果我有一个名为 Automobile 的简单 ActiveRecord 模型,其命名范围如下所示:

named_scope :classic, :conditions => { :build_date <= 1969 }
named_scope :fast, lambda { |speed| :top_speed >= speed }

忽略范围本身,如果我要调用:

Automobile.scopes

这到底会返回什么?我在控制台中看到的是:

[ :classic => #<Proc:0x01a543d4@/Users/user_name/.gem/ruby/1.8/gems/activerecord-2.3.4/lib/active_record/named_scope.rb:87>,
  :fast => #<Proc:0x01a543d4@/Users/user_name/.gem/ruby/1.8/gems/activerecord-2.3.4/lib/active_record/named_scope.rb:87> ]

在我看来,这是一个键/值数组,键是命名范围的符号,值是指向 ActiveRecord 中的 name_scope.rb 文件的 Proc。

如果我想要将哈希或 Proc 作为实际命名范围给出(对于 :classic,我会收到“:conditions => { :build_date <= 1969 }”,我怎样才能找到这个?

我正在写一个有条件合并一些named_scopes的插件,我在这方面遇到了一些阻力。 我目前正在使用以下内容来合并这些作用域:

scopes_to_use = Automobile.scopes
scoped_options = {}
Automobile.scopes.each do |scope|
    scoped_options.safe_merge!(eval "Automobile.#{scope}(self).proxy_options")
end

忽略我在这里所做的“正确性”,是否有更好的方法可以检索给定命名范围的实际哈希或过程?我不喜欢在这个函数中使用“eval”,如果我实际上可以检索哈希或过程,那么我将能够引入一些更强大的合并逻辑。任何对此的想法将不胜感激。谢谢。

The background to this problem is quite complex and convoluted, and as I am looking for a simple answer, I will leave it by the wayside in explaining my problem, and instead provide this hypothetical situation.

If I have a simple ActiveRecord model called Automobile, with named_scopes like below:

named_scope :classic, :conditions => { :build_date <= 1969 }
named_scope :fast, lambda { |speed| :top_speed >= speed }

Ignoring the scopes themselves, if I were to call:

Automobile.scopes

What exactly would this be returning? What I see in the console is:

[ :classic => #<Proc:0x01a543d4@/Users/user_name/.gem/ruby/1.8/gems/activerecord-2.3.4/lib/active_record/named_scope.rb:87>,
  :fast => #<Proc:0x01a543d4@/Users/user_name/.gem/ruby/1.8/gems/activerecord-2.3.4/lib/active_record/named_scope.rb:87> ]

This seems to me to be an array of key/values, the key being the symbol of the named scope, and the value being a Proc pointing to the named_scope.rb file in ActiveRecord.

If I want the hash or Proc given as the actual named scope (meaning for :classic, I would receive ":conditions => { :build_date <= 1969 }", how could I go about finding this?

I am writing a plugin that conditional merges some named_scopes, and I am running up against some resistance in regards to this.
I am currently using the following to merge these scopes:

scopes_to_use = Automobile.scopes
scoped_options = {}
Automobile.scopes.each do |scope|
    scoped_options.safe_merge!(eval "Automobile.#{scope}(self).proxy_options")
end

Ignoring the 'correctness' of what I am doing here, is there a better way that I can retrieve the actual Hash or Proc given to the named_scope? I don't like using 'eval' in this function, and if I could actually retrieve the Hash or Proc, then I would be able to introduce some much more powerful merging logic. Any thoughts on this would be much appreciated. Thanks.

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

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

发布评论

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

评论(1

旧情勿念 2024-08-15 04:52:41

您在示例中定义的命名范围都不会执行任何操作。它们在语法上是错误的。这可能会导致您遇到问题。

假设这些示例是仓促创建的并且您已经有了可用的示例。继续回答。

我怎样才能找到作为实际命名范围给出的哈希或 Proc。

Model.scopes[:scope_name] 为您提供过程。
Model.send(:scope_name).proxy_options 为您提供给范围的选项哈希,即:
{ :条件=>; ["build_date <= ?", 1969] }

要以编程方式检索模型中每个命名范围的选项哈希,您可以执行以下操作:

scopes_to_use = Automobile.scopes
scoped_options = {}
Automobile.scopes.keys.each do |scope|
    scoped_options.safe_merge!(Automobile.send(scope).proxy_options)
end

这对于需要参数的范围来说效果不佳,因为它们可以提出例外情况。不幸的是我想不出一个简单的方法来解决它。

我能想到的最好办法是测试过程的数量,然后提供唯一的虚拟参数并分析返回的代理选项以找出发生了什么变化。但这是一项繁重的工作,因为任何命名作用域的数量都是 -2。在检索参数时,您可以做的最好的事情就是调用过程,挽救参数错误并解析它以获取所需的数字或参数。然后使用该数量的虚拟参数。

整个过程需要救援块和一些评估魔法才能工作。这是在您可以处理 proxy_options 哈希以进行安全合并之前。

简而言之,您会想要做一些与此类似的事情,它并不漂亮,但它有效:

scopes_to_use = Automobile.scopes
    scoped_options = {}
Automobile.scopes.each do |scope,proc|
  next if scope == :scoped
  number_of_args = 1
  begin
    scoped_options.safe_merge! Automobile.send(scope, "#{scope}_argument_1").proxy_options
  rescue 
    $!.to_s.match /(\d+)\)$/
    number_of_args = $1.to_i
    puts number_of_args
  end
     scoped_options.safe_merge!(Automobile.send(scope, *(1..number_of_args).map{|i| "#{scope}_argument_#{i}"}.proxy_options)
end

这应该是安全的,因为 proxy_options 不执行 SQL 也不执行任何类型检查。

Neither named scope you define in your example would do anything. They are wrong syntactically. That could cause problems you might be having.

Assuming that the examples were hastily created and you've got working ones. On to the answer.

How could I go about finding the hash or Proc given as the actual named scope.

Model.scopes[:scope_name] gives you the proc.
Model.send(:scope_name).proxy_options gives you the options hash given to the scope ie:
{ :conditions => ["build_date <= ?", 1969] }

To programatically retrieve the options hash of each named scope in a model you can do this:

scopes_to_use = Automobile.scopes
scoped_options = {}
Automobile.scopes.keys.each do |scope|
    scoped_options.safe_merge!(Automobile.send(scope).proxy_options)
end

This doesn't work as nicely for scopes that require arguments, because they could raise exceptions. Unfortunately I can't think of a simple way around it.

The best I can come up with is to test for arity of the proc and then provide unique dummy arguments and analyze the returned proxy options to figure out what changed. But that's a lot of work, because the arity of any named scope is -2. The best you could do in retrieving the arity is call the proc, rescue an argument error and parse it for the number or arguments expected. Then use that number of dummy arguments.

The whole process requires a rescue block and some eval magic to work. And that's before you can process the proxy_options hash for your safe merge.

In short you'll want to do something close to this, it's not pretty but it works:

scopes_to_use = Automobile.scopes
    scoped_options = {}
Automobile.scopes.each do |scope,proc|
  next if scope == :scoped
  number_of_args = 1
  begin
    scoped_options.safe_merge! Automobile.send(scope, "#{scope}_argument_1").proxy_options
  rescue 
    $!.to_s.match /(\d+)\)$/
    number_of_args = $1.to_i
    puts number_of_args
  end
     scoped_options.safe_merge!(Automobile.send(scope, *(1..number_of_args).map{|i| "#{scope}_argument_#{i}"}.proxy_options)
end

Which should be safe because proxy_options doesn't execute the SQL nor does it do any type checking.

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