Elena Tanasoiu Elena Tanasoiu - 3 months ago 22
Ruby Question

Sending parameter to Ransack from Active Admin filter

I'm trying to make a filter in Active Admin on a customer's first name and last name in one single input field. The first_name and last_name are separate fields.

My solution fails if you have a middle name. If the customer's name is "John H. Smith", then "John" and "Smith" give the correct result, but "John Smith" will not find anything because of the extra 'H.'.

ActiveAdmin.register Customer do
...
filter :full_name_cont, label: 'Name', as: :string
end

# In the Customer model:

ransacker :full_name, formatter: proc { |full_name| full_name.downcase.to_s } do |parent|
Arel::Nodes::InfixOperation.new('||',
Arel::Nodes::InfixOperation.new('||',
parent.table[:first_name], ' '
),
parent.table[:last_name]
)
end


So now I'm trying to send the search term "John H. Smith" as a parameter to ransacker, so I can split the name up into multiple parts and search if any of them match first name or last name:

ransacker :full_name, args: [:parent, :ransacker_args] do |parent, args|
full_name = args.first
search_items = full_name.split(" ")
condition = "concat_ws(first_name, ' ', last_name) LIKE '%#{search_items.first}%'"
search_items.each do |item|
condition += " OR concat_ws(first_name, ' ', last_name) LIKE '%#{item}%'" if item != search_items.first
end
Arel.sql(condition)
end


Unfortunately, args is always empty.

Does anyone know how to send parameters to ransacker?

Answer

The solution for this was to create a new Ransack predicate that considers spaces as wildcards.

The idea was also presented in this issue: Make an OR with ransack

# In config/initializers/ransack.rb

Ransack.configure do |config|
  config.add_predicate 'contains',
         :arel_predicate => 'matches', # so we can use the SQL wildcard "%"
         # Format the incoming value: add the SQL wildcard character "%" to the beginning and the end of
         # the string, replace spaces by "%", and replace multiple occurrences of "%" by a single "%".
         # For using * and ? instead as wild cards use this #{Ransack::Constants.escape_wildcards(v).gsub("*", "%").gsub('?','_')}
         :formatter => proc {|v| ("%#{v.gsub(" ", "%")}%").squeeze("%")}
end

And then in the model I used this ransack code from the examples in https://github.com/activerecord-hackery/ransack/wiki/using-ransackers#5-search-on-a-concatenated-full-name-from-first_name-and-last_name-several-examples:

# ActiveAdmin - customers.rb
# I re-open the model 
class Customer
  ransacker :full_name do |parent|
       Arel::Nodes::InfixOperation.new('||',
          Arel::Nodes::InfixOperation.new('||',
              parent.table[:first_name], ' '),
                  parent.table[:last_name])
  end
end

ActiveAdmin.register Customer do
...
end