lib/filterrific/active_record_extension.rb
#
# Adds Filterrific methods to ActiveRecord::Base model_class.
#
require "filterrific/param_set"
module Filterrific
module ActiveRecordExtension
# Adds Filterrific behavior to class when called like so:
#
# filterrific(
# :available_filters => [:sorted_by, :search_query, :with_state]
# :default_filter_params => { :sorted_by => "created_at_asc" },
# )
#
# @params opts [Hash] with either string or symbol keys, will be stringified.
# @option opts [Array<String, Symbol>] available_filters: a list of filters to be exposed by Filterrific.
# @option opts [Hash, optional] default_filter_params: default filter parameters
# @return [void]
def filterrific(opts)
class << self
attr_accessor :filterrific_available_filters
attr_accessor :filterrific_default_filter_params
end
self.filterrific_available_filters = []
opts.stringify_keys!
# define_sorted_by_scope(opts['sorted_by']) if opts['sorted_by']
# define_search_query_scope(opts['search_query']) if opts['search_query']
assign_filterrific_available_filters(opts)
validate_filterrific_available_filters
assign_filterrific_default_filter_params(opts)
validate_filterrific_default_filter_params
end
# Returns ActiveRecord relation based on filterrific_param_set.
# Use like so: `ModelClass.filterrific_find(@filterrific)`
#
# @param filterrific_param_set [Filterrific::ParamSet]
# @return [ActiveRecord::Relation] with filters applied
def filterrific_find(filterrific_param_set)
unless filterrific_param_set.is_a?(Filterrific::ParamSet)
raise(
ArgumentError,
"Invalid Filterrific::ParamSet: #{filterrific_param_set.inspect}"
)
end
# Initialize ActiveRecord::Relation
ar_rel = if ActiveRecord::Relation === self
# self is already an ActiveRecord::Relation, use as is
self
else
# Send `:all` to class to get an ActiveRecord::Relation
all
end
# Apply filterrific params
filterrific_available_filters.each do |filter_name|
filter_param = filterrific_param_set.send(filter_name)
next if filter_param.blank? # skip blank filter_params
ar_rel = ar_rel.send(filter_name, filter_param)
end
ar_rel
end
protected
# Defines a :sorted_by scope based on attrs
# @param attrs [Hash] with keys as
# def define_sorted_by_scope(attrs)
# scope :sorted_by, lambda {}
# end
# Assigns available filters.
# @param opts [Hash] the complete options hash passed to `filterrific`.
# This method uses the 'available_filters', 'sorted_by', and 'search_query' keys.
# @return [void]
def assign_filterrific_available_filters(opts)
self.filterrific_available_filters = (
filterrific_available_filters + (opts["available_filters"] || [])
).map(&:to_s).uniq.sort
end
# Validates presence of at least one available filter.
# @return [void]
def validate_filterrific_available_filters
if filterrific_available_filters.blank?
raise(ArgumentError, ":available_filters can't be empty")
end
end
def assign_filterrific_default_filter_params(opts)
self.filterrific_default_filter_params = (
opts["default_filter_params"] || {}
).stringify_keys
end
def validate_filterrific_default_filter_params
# Raise exception if defaults contain keys that are not present in available_filters
if (
inv_fdfps = filterrific_default_filter_params.keys - filterrific_available_filters
).any?
raise(ArgumentError, "Invalid default filter params: #{inv_fdfps.inspect}")
end
end
end
end