我正在为我的网页开发扩展搜索功能。
我看了ransack,但是它缺少我需要的一些功能,使得url-query字符串很长并且有一些错误(报告)。
因此我开始实施自己的黑客攻击。
首先,我想提出我的想法,之后我想问一下如何解决我的问题,最后是否还有其他改善方法。
这个想法:
模型定义了这样的东西(另外,模型在引擎内):
module EngineName
class Post < ActiveRecord::Base
search_for :name, :as => :string do |b, q|
b.where{name =~ "%#{q}%"}
end
end
end
:name用于定义要使用的查询参数,例如这将是?q [name] =某事 我知道这不像ransack那样完全通用,但是...... [/ p>
:就像建立正确的表单标签一样。 :string将用于text_field,:integer用于number_field,依此类推。我想进一步扩展它以实现为关联等自动生成集合。
现在该块是一个简单的使用范围。 在构建复杂查询时(比如count()等),我遇到了一些hansack的缺点。现在,我可以在squeel中指定自己的优化查询。
我扩展了ActiveRecord :: Base以设置逻辑(全局的,不在引擎内部。我想在任何地方使用它)。 我定义了一个范围:搜索所以我可以像在ransack中一样使用Model.search(param [q])。 另外,我试图保留一个由search_for调用定义的“可搜索”键列表。
class ActiveRecord::Base
@@searchable_attributes = Hash.new({})
def self.search_for(name, *opts, &search_scope)
return unless search_scope
@@searchable_attributes[name] = {
:type => opts[:as],
:condition => search_scope
}
unless @@searchable_attributes.has_key? :nil
@@searchable_attributes[:nil] = Proc.new { scoped }
end
end
scope :search, lambda {|q|
next unless q.kind_of?(Hash)
base = @@searchable_attributes[:nil].call
q.each do |key, search|
next unless base.class.searchable_attributes.has_key?(key)
base = @@searchable_attributes[key][:condition].call(base, search)
end
base
}
end
现在问题:
请查看范围中的第二行:搜索。
在那里,我打电话给上面定义的简单Proc,其中只包括“scoped” 这是为了解决自我返回“ActiveRecord :: Base”而不是像“Post”或“Comment”这样的模型本身的问题。
这是因为在继承的基类上调用了作用域,但是我没有找到任何可以修复它的东西。
当在模型本身(例如Post)上调用search_for时,返回的范围模型是“正确的”。
有谁知道如何规避这个?
接下来的问题是,如何存储“可搜索”范围列表。我用@@变量。但是因为它们在每个子类中共享,所以这将是一个禁忌。 但是,它需要是静态的,因为在没有初始化实例的情况下调用search_for(不是吗?)
最后但并非最不重要的是,总是指定要在每个范围内使用的基础模型,以便我可以将它们链接在一起,这有点可怕。
还有其他可能性来改善这个吗?
答案 0 :(得分:2)
好吧,似乎我最终得到了它,我将其他问题的其他几个答案放在一起。
型号:
module EngineName
class Post < ActiveRecord::Base
searchable
search_for :name, :as => :string do |b, q|
b.where{name =~ "%#{q}%"}
end
end
end
我的“插件”目前作为初始化程序:
class ActiveRecord::Base
def self.searchable
include Searchable
end
end
module Searchable
def self.included(base)
base.class_eval {
@@searchable_attributes = Hash.new({})
def self.search_for(name, opts)
return unless block_given?
@@searchable_attributes[name] = {
:type => opts[:as],
:condition => Proc.new
}
end
# Named scopes
scope :search, lambda {|q|
next unless q.kind_of?(Hash)
base = self.scoped
q.each do |key, search|
key = key.to_sym
next unless @@searchable_attributes.has_key?(key)
base = @@searchable_attributes[key][:condition].call(base, search)
end
base
}
}
end
end
希望它能帮助其他人解决同样的问题。
答案 1 :(得分:1)
Rails为class_attribute提供帮助。这提供了可继承的类属性,但允许子类“改变它们自己的值,它不会影响父类”。但是,使用[] =变异的哈希会影响父项,因此您可以确保在使用rubys inherited方法
进行子类化时创建新副本因此,您可以像这样声明和初始化基类:
module Searchable
extend ActiveSupport::Concern
included do
class_attribute :searchable_attributes
end
module ClassMethods
def inherited(subclass)
subclass.searchable_attributes = Hash.new({})
end
def search_for(name,opts)
return unless block_given?
searchable_attributes[name] = {
:type => opts[:as],
:condition => Proc.new
}
end
end
end
请注意,我使用ActiveSupport :: Concern来获得更直接的语法来直接在类上定义东西,并混合使用类方法。然后,您只需将其添加到活动记录库:
ActiveRecord::Base.send(:include, Searchable)
现在任何类都有自己的属性哈希:
class Post < ActiveRecord::Base
search_for :name, :as => :string do |b, q|
b.where{name =~ "%#{q}%"}
end
end