【问题标题】:ActiveRecord named_scope, .scopesActiveRecord named_scope, .scopes
【发布时间】:2009-10-20 22:03:13
【问题描述】:

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

如果我有一个名为汽车的简单 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

我正在编写一个有条件地合并一些 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,那么我将能够引入一些更强大的合并逻辑。对此的任何想法将不胜感激。谢谢。

【问题讨论】:

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


    【解决方案1】:

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

    假设这些示例是匆忙创建的,而您已经有了工作示例。继续回答。

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

    Model.scopes[:scope_name] 为您提供 proc。 Model.send(:scope_name).proxy_options 为您提供给定范围的选项哈希,即: { :conditions =&gt; ["build_date &lt;= ?", 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 也不做任何类型检查。

    【讨论】:

    • 这个解决方案看起来可行,但比我现在想要的要密集得多。我一直在寻找可以直接通过 Rails API 获得的东西,但如果什么都不存在,那么我想我宁愿为我的整体问题找到其他解决方案,而不是使用命名范围的有问题的解决方案。感谢您的回答。它肯定回答了我的问题,但也为我提出了更多问题。如果解决方案这么复杂,那么我的目标一定是错误的。
    • 此时并不重要,但我已经了解了 * 运算符。我已经编辑了解决方案以使用它来代替 eval。
    • 我遇到了同样的问题,很遗憾,唯一的解决方案就是这个。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多