Skip to content

Instantly share code, notes, and snippets.

@dougc84
Last active July 17, 2017 04:44
Show Gist options
  • Save dougc84/5fc60eb8a2224d358f88 to your computer and use it in GitHub Desktop.
Save dougc84/5fc60eb8a2224d358f88 to your computer and use it in GitHub Desktop.
Ransack - Polymorphic Associations Search Ransacker
class MyModel < ActiveRecord::Base
belongs_to :owner, polymorphic: true
ransacker :owner_name, formatter: proc { |value|
# To use this, you have to know what all of the possible owner types can be.
# For this example, the #owner_type could be either Group or Advertiser.
# The fields we want to search on are:
# Group#name
# Advertiser#contact_name
#
# This method does perform one database hit for each related table.
# This makes sense considering you are searching on each table's data.
#
# First, gather the IDs for MyModel where it matches the Advertiser#contact_name.
# Using a nested query on the #where conditional for #owner_id eliminates the
# need for two queries.
#
# Second, gather the IDs for MyModel where it matches Group#name.
#
# Each query will be finished with #pluck, which returns an array - either the IDs,
# or an empty array. Each array is joined simply by adding them together.
#
my_model_ids = MyModel.where(
owner_id: Advertiser.select('advertisers.id as owner_id').where('contact_name LIKE ?', "%#{value}%"),
owner_type: 'Advertiser'
).pluck(:id) + MyModel.where(
owner_id: Group.select('groups.id as owner_id').where('name LIKE ?', "%#{value}%"),
owner_type: 'Group'
).pluck(:id)
# Now that there is a list of the appropriate IDs that match the specification,
# return the array of IDs as the formatted value for the ransacker.
# #presence is provided by ActiveSupport, which basically is shorthand for:
#
# my_model_ids.any? ? my_model_ids : nil
#
# If there are no values, it will return nil, which tells Ransack that no
# records exist. An empty array may perform other searches unnecessarily.
my_model_ids.presence
} do |parent|
# #parent references the ransacker, and #table references the table for this
# model (MyModel). Pass in :id to #table to tell the ransacker the search is
# based on the ID field.
parent.table[:id]
end
end
= search_form_for @search do |f|
# For this search, the #in predicate needs to be used for Ransack to work properly,
# since, based on the implementation of the ransacker, we are finding IDs IN the
# current search model. Other predicates expect one singular return value.
#
# A value needs to manually be specified for the field. If the search is on
# "Minnesota", Ransack will output this as "[\"Minnesota\"]" when the page reloads,
# since it is an "in" predicate search, and, due to its nature, assumes it is an array
# of values.
#
= f.text_field :owner_name_in, value: params.fetch(:q, {}).fetch(:owner_name_in, nil)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment