3

这个问题的背景非常复杂和令人费解,当我正在寻找一个简单的答案时,我将在解释我的问题时将其搁置一边,而是提供这种假设情况。

如果我有一个名为汽车的简单 ActiveRecord 模型,named_scopes 如下所示:

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 中的 named_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

忽略我在这里所做的事情的“正确性”,有没有更好的方法可以检索给 named_scope 的实际 Hash 或 Proc?我不喜欢在这个函数中使用“eval”,如果我真的可以检索 Hash 或 Proc,那么我将能够引入一些更强大的合并逻辑。对此的任何想法将不胜感激。谢谢。

4

1 回答 1

2

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

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

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

Model.scopes[:scope_name]给你proc。 Model.send(:scope_name).proxy_options为您提供给范围的选项哈希,即: { :conditions => ["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

这对于需要参数的范围来说效果不佳,因为它们可能引发异常。不幸的是,我想不出一个简单的方法来解决它。

我能想到的最好的办法是测试 proc 的数量,然后提供唯一的虚拟参数并分析返回的代理选项以找出发生了什么变化。但这需要做很多工作,因为任何命名范围的元数都是 -2。检索 arity 的最佳方法是调用 proc,挽救参数错误并将其解析为预期的数量或参数。然后使用该数量的虚拟参数。

整个过程需要一个救援块和一些 eval 魔法才能工作。那是在您可以处理 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 也不做任何类型检查。

于 2009-10-21T04:52:55.443 回答