Ransack:如何使用现有范围?

将Rails 2应用程序转换为Rails 3,我必须替换gem searchlogic 。 现在,使用Rails 3.2.8和gem Ransack我想构建一个使用现有范围的搜索表单。 例:

class Post < ActiveRecord::Base scope :year, lambda { |year| where("posts.date BETWEEN '#{year}-01-01' AND '#{year}-12-31'") } end 

据我所知,这可以通过定义自定义搜索引擎来实现。 可悲的是,我没有找到任何关于此的文档。 我在Post类中试过这个:

 ransacker :year, :formatter => proc {|v| year(v) } 

但这不起作用:

 Post.ransack(:year_eq => 2012).result.to_sql => TypeError: Cannot visit ActiveRecord::Relation 

我尝试了一些ransacker声明的变体,但它们都不起作用。 我需要一些帮助…

更新:上面的范围只是示例。 我正在寻找一种方法来使用Ransack中的每个现有范围。 在Ransack的前身search_methods ,有一个名为search_methods的function用于使用范围。 Ransack 尚未支持此开箱即用。

在合并https://github.com/activerecord-hackery/ransack/pull/390后,ransack支持它开箱即用。 你应该声明ransakable_scopes方法来添加可见的搜索范围。

从手册

继续上一节,按范围搜索需要在模型类上定义ransackable_scopes的白名单。 白名单应该是一个符号数组。 默认情况下,忽略所有类方法(例如范围)。 范围将应用于匹配真值,或者如果范围接受值,则应用于给定值:

 class Employee < ActiveRecord::Base scope :activated, ->(boolean = true) { where(active: boolean) } scope :salary_gt, ->(amount) { where('salary > ?', amount) } # Scopes are just syntactical sugar for class methods, which may also be used: def self.hired_since(date) where('start_date >= ?', date) end private def self.ransackable_scopes(auth_object = nil) if auth_object.try(:admin?) # allow admin users access to all three methods %i(activated hired_since salary_gt) else # allow other users to search on `activated` and `hired_since` only %i(activated hired_since) end end end Employee.ransack({ activated: true, hired_since: '2013-01-01' }) Employee.ransack({ salary_gt: 100_000 }, { auth_object: current_user }) 

Ransack让你为此创建自定义谓词,遗憾的是文档留下了改进空间但是结帐: https : //github.com/ernie/ransack/wiki/Custom-Predicates

此外,我相信你试图解决的问题是他们的问题跟踪器。 那里有一个很好的讨论: https : //github.com/ernie/ransack/issues/34

我写了一个名为siphon的gem,它可以帮助您将参数转换为activerelation范围。 将它与搜索结合起来就可以实现这一目标。

你可以在这里阅读完整的解释 。 与此同时,这是它的要点

风景

 = form_for @product_search, url: "/admin/products", method: 'GET' do |f| = f.label "has_orders" = f.select :has_orders, [true, false], include_blank: true -# -# And the ransack part is right here... -# = f.fields_for @product_search.q, as: :q do |ransack| = ransack.select :category_id_eq, Category.grouped_options ``` 

好吧现在params[:product_search]持有范围和params[:product_search][:q]具有搜索优势。 现在,我们需要找到一种方法将数据分发给表单对象。 首先让ProductSearch在控制器中吞下它:

控制器

 # products_controller.rb def index @product_search = ProductSearch.new(params[:product_search]) @products ||= @product_formobject.result.page(params[:page]) end 

表单对象

 # product_search.rb class ProductSearch include Virtus.model include ActiveModel::Model # These are Product.scopes for the siphon part attribute :has_orders, Boolean attribute :sort_by, String # The q attribute is holding the ransack object attr_accessor :q def initialize(params = {}) @params = params || {} super @q = Product.search( @params.fetch("q") { Hash.new } ) end # siphon takes self since its the formobject def siphoned Siphon::Base.new(Product.scoped).scope( self ) end # and here we merge everything def result Product.scoped.merge(q.result).merge(siphoned) end end