【问题标题】:Is there a way to dynamically add a scope to an active record class?有没有办法将范围动态添加到活动记录类?
【发布时间】:2015-06-13 20:48:26
【问题描述】:

我正在尝试向活动记录对象动态添加范围。我通过元编程trying 所做的一些事情不起作用。以下是我想要实现的目标。

class UtilityClass
   def add_scope_to_class klass, name, lambda
     # should add something like
     # scope :published, -> { where(published: true) }
     code = lambda { filter_clause(value, &lambda) }
     klass.class_exec(&code)
   end
end

class Article < ActiveRecord::Base
end

UtilityClass.add_scope_to_class

Article.published # returns published articles

我已经用普通的 ruby​​ 对象尝试了一些变体,但我认为在这种情况下提出问题可能会得到一些不同的想法/答案。

谢谢。

更新

到目前为止,我已经设法想出了以下方法,但它不起作用

class ActiveSearch
  attr_accessor :collection
  attr_reader :params

  def initialize params={}
    @params = params
  end

  def results
    @collection = Person.where(nil)
    params.compact.each do |key, value|
      @collection = @collection.send("#{key}_filter", value)
    end
    @collection
  end

  def self.filter name, filter_proc
    code = lambda { |filter_name| scope("#{filter_name.to_s}_filter".to_s, filter_proc) }
    Person.class_exec(name, &code)
  end
end

class PersonSearch < ActiveSearch
  filter :first_name, ->(name){ where(first_name: name) }
end

我已经硬编码了一些东西。主要思想就在那里。

【问题讨论】:

    标签: ruby scope metaprogramming


    【解决方案1】:

    玩了一圈,发现解决方案比我想象的要简单一些。

    class ActiveSearch
      attr_accessor :collection
      attr_reader :params
    
      def initialize params={}
        @params = params
      end
    
      def results
        @collection = Person.where(nil)
        params.compact.each do |key, value|
          @collection = @collection.send("#{key}_filter", value)
        end
        @collection
      end
    
      def self.filter name, filter_proc
        Person.define_singleton_method "#{name.to_s}_filter".to_sym, &filter_proc # does most of the heavy lifting
      end
    end
    
    class PersonSearch < ActiveSearch
      filter :first_name, lambda { |name| where(first_name: name) }
      filter :last_name, lambda { |name| where(last_name: name) }
    end
    
    
    PersonSearch.new({first_name: 'ryan', last_name: 'test'}).results
    

    上面将结合所有适当的方法并执行查询。

    需要进行一些调整,但它几乎可以满足我的需求。主动搜索类仍然需要清理并使其通用,但我认为它可以理解这个想法。

    【讨论】:

    • 这与Person.where(first_name: 'ryan', last_name: 'test').all有什么本质不同?
    • 是一样的。我希望能够将哈希 {first_name: 'ryan', last_name: 'test' } 传递给对象并能够获取人员列表。现在我可以更进一步地使用 PersonSearch 并使 attr_accessors 在初始化时设置。然后扩展对象以充当活动记录(但没有与之相关的实际表)。然后可以将其用作搜索的表单对象。
    【解决方案2】:

    我认为您可能只是在重新发明范围。

    class Person < ActiveRecord::Base
    end
    
    Person.send :scope, :first_name, -> {|name| where(first_name: name) }
    Person.send :scope, :last_name,  -> {|name| where(last_name: name)  }
    
    Person.first_name("chris").last_name("heald").first
    
    Person.send :scope, :name, ->{|f, l| first_name(f).last_name(l) }
    Person.name("chris", "heald").first
    

    【讨论】:

    • 我只是在尝试一些东西。我试图通过创建一个类似于活动记录对象并接受转换为模型范围的参数散列的对象来简化表单搜索。搜索对象包含与搜索有关的所有内容。这样我就可以在处理搜索时使用 form_for 。我知道您可以直接在模型上添加范围,但我只是想看看将它们从模型中分离到自己的类中的影响。模型上有很多范围可能会变得臃肿。这也是掌握元编程的学习练习。
    • 您只是以一种或另一种方式向模型添加范围。将它们隐藏在元编程后面并不会让它们变得不那么臃肿。范围实际上只是为了开发人员的理智而封装行为的一种方式。如果您实际上只是从字段和值构造查询,那么 where 已经这样做了。
    • 是的,我想这更像是一种学习练习。谢谢。
    猜你喜欢
    • 2021-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-21
    • 2021-09-11
    • 1970-01-01
    相关资源
    最近更新 更多