我正在尝试动态地将范围添加到活动记录对象。我通过元编程我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
我尝试过使用纯红宝石对象的几种变体,但我认为用这个上下文提问可能会得到一些不同的想法/答案。
感谢。
更新
到目前为止,我已设法提出以下内容,但它无效
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
我已经硬编码了几件事。主要的想法就在那里。
答案 0 :(得分:0)
在玩完之后,我发现解决方案比我原先想象的要简单一些。
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
以上将结合所有适当的方法并执行查询。
需要进行一些调整,但它几乎可以满足我的需求。主动搜索类仍然需要被清理并变得通用,但我认为它可以解决这个问题。
答案 1 :(得分:0)
我想你可能只是在重新发明范围。
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