I am updating an old ruby\\rails application that has an ActiveAdmin component (ActiveAdmin 0.6, Ruby 1.9.3 and Rails 3.2). The user has requested a filter that searches all fields in a given model. I don't think this is practical because you can't search a date or numeric value for "a" so I have compromised on just searching text with the filter.
Having looked at the ActiveAdmin documentation this states that you can create a filter for several attributes using " or " between the attributes. So if I wanted to search the "circumstances" or "accident_type" attributes I would use the filter below:
filter :circumstances_or_accident_type, :as => :string, label: "Search All Text Fields"
If I use this syntax the filter works as expected.
I now want to find all the string\\text attributes to create by filter attributes which I did using this code (there are probably neater ways of doing this but it works):
xfilter_text = ""
Notification.columns.each do |xfield|
if xfield.type == :string or xfield.type == :text
if xfilter_text.length == 0
xfilter_text = xfield.name
else
xfilter_text << "_or_"
xfilter_text << xfield.name
end
end
end
I used the result to hard-code the values into the filter which gave me the following (yes there are a few attributes in the model):
filter :circumstances_or_accident_type_or_author_type_or_location_or_immediate_action_or_injury_details_or_outcome_type_or_investigation_findings_or_action_to_prevent_recurrence_or_building_or_classification_or_manager_email_or_manager_name_or_current_stage_or_injured_last_name_or_injured_first_name_or_injured_gender_or_injured_address_or_injured_home_telephone_or_injured_work_status_or_injured_job_title_or_injured_working_pattern_or_injured_email_or_riddor_document_or_body_part_or_kind_of_accident_or_injury_type_or_service_or_team_or_defects_or_witness_details_or_location_details_or_hse_reference_number_or_riddor_category_or_address_or_details_of_treatment_or_processor_actions_or_business_unit_or_other_author_type_or_lost_time_details_or_changed_by_or_details_of_hospital_treatment, :as => :string, label: "Search All Text Fields"
I tested this and it worked. All good so far. I could just leave it here but I wanted to ensure the code is self maintaining so any changes in the model would not require changes to the custom filter. This is the part I am having trouble with. I would like to change the hardcoded attributes to use the results of the code that creates the filter attributes somehow. Something like this:
filter :get_filter, :as => :string, label: "Search All Text Fields"
def get_filter
xfilter_text = ""
Notification.columns.each do |xfield|
if xfield.type == :string or xfield.type == :text
if xfilter_text.length == 0
xfilter_text = xfield.name
else
xfilter_text << "_or_"
xfilter_text << xfield.name
end
end
return xfilter
end
end
I expect that I would need something that checks that attributes are returned otherwise the filter would fail. I can add that once I get the code working.
Appreciate any help or suggestions.
I'd be inclined to take the messy business of generating the query and delegate it to the model, using its own scope/class method. Then you just need to inform MetaSearch/Ransack (depending on your ActiveAdmin version) that it can search that scope, and you can add it as a filter.
For bonus points, you could drop the search method into a concern that you can include into any model.
app/admin/notifications.rb
filter :containing_text, as: :string, label: 'Text Search:'
app/models/notification.rb
# for MetaSearch
# search_methods :containing_text
# for Ransack
def self.ransackable_scopes(_opts)
[:containing_text]
end
# this could be dropped into a concern as-is
def self.containing_text(query)
# select text-type columns
cols = columns.select { |c| [:string, :text].include?(c.type) }
# generate query fragment
fragment = cols.map { |c| "#{ table_name }.#{ c.name } LIKE ?" }
.join(' OR ')
# execute sanitized query
where(fragment, *Array.new(cols.size, "%#{ query }%"))
end
### EDIT by OP ###
I had never used concerns before so eventually worked out how to get it working:
1) Add the concern path to your application.rb
config/application.rb
class Application < Rails::Application
config.autoload_paths += %W(#{config.root}/app/models/concerns)
end
2) Add the include to the Searchable concern and method call into the notifcation model
app/models/notification.rb
include Searchable
search_methods :containing_text
3) Created the concern:
/app/models/concerns/searchable.rb
module Searchable
extend ActiveSupport::Concern
module ClassMethods
def self.containing_text(query)
# select text-type columns (string and text)
cols = columns.select { |c| [:string, :text].include?(c.type) }
# generate query fragment
fragment = cols.map { |c| "#{ table_name }.#{ c.name } LIKE ?" }
.join(' OR ')
# execute sanitized query
where(fragment, *Array.new(cols.size, "%#{ query }%"))
end
end
end
That then seemed to work. I probably should rename the searchable into something better but it works.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.