Подтвердить что ты не робот

Ransack: Как использовать существующий объем?

Преобразование приложения Rails 2 в Rails 3, мне нужно заменить драгоценный камень 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  

Насколько я знаю, это может быть достигнуто путем определения пользовательского ransacker. К сожалению, я не могу найти документацию об этом. Я пробовал это в классе Post:

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

Но это не работает:

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

Я пробовал некоторые варианты объявления ransacker, но никто из них не работает. Мне нужна помощь...

ОБНОВЛЕНИЕ:. Я ищу способ использовать все существующие области в Ransack. В MetaSearch, предшественнике Ransack, существует функция, называемая search_methods для использования областей. Ransack не поддерживает эту из коробки.

4b9b3361

Ответ 1

ransack поддерживает его из коробки после слияния https://github.com/activerecord-hackery/ransack/pull/390. вы должны объявить метод ransakable_scopes для добавления видимых областей для ransack.

Из руководства

Продолжая работу в предыдущем разделе, поиск по областям требует определения белого списка 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 })

Ответ 2

Ransack позволяет создавать пользовательские предикаты для этого, к сожалению, документация оставляет место для улучшения, но проверка: https://github.com/ernie/ransack/wiki/Custom-Predicates

Также я считаю, что проблема, которую вы пытаетесь решить, зависит от их проблемы. Там идет хорошая дискуссия: https://github.com/ernie/ransack/issues/34

Ответ 3

Я написал драгоценный камень под названием siphon, который поможет вам перевести параметры в области активизации. Сочетание этого с ransack может достичь этого.

Вы можете прочитать полное объяснение здесь. Между тем здесь суть этого

Вид

= 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
```

ok, поэтому теперь 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