ActiveRecord named_scope,.scopes

时间:2009-10-20 22:03:13

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

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

如果我有一个名为Automobile的简单ActiveRecord模型,请使用如下的named_scope:

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。

如果我想将hash或Proc作为实际命名范围(意思是:classic,我会收到“:conditions =&gt; {:build_date&lt; = 1969}”,我怎么能找到这个?< / p>

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

1 个答案:

答案 0 :(得分:2)

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

假设示例是匆忙创建的,并且您已经有了工作。回答。

  

我怎样才能找到作为实际命名范围的hash或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的arity然后提供唯一的伪参数并分析返回的代理选项以找出更改的内容。但这是很多工作,因为任何命名范围的arity都是-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也不执行任何类型检查。