concord-consortium/rigse

View on GitHub
rails/lib/searchable_model.rb

Summary

Maintainability
A
3 hrs
Test Coverage
# === Module SearchableModel
#
# An extension of code described here:
#
#   http://github.com/mislav/will_paginate/wikis/simple-search
#
# Extend a model class with SearchableModel to support paginated searching
#
#    self.extend SearchableModel
#
# Specify attributes to be searched in a class variable and accessor called:
#
#    @@searchable_attributes = %w{name description model_type.name}
#    class <<self
#      def searchable_attributes
#        @@searchable_attributes
#      end
#    end
#
# Create @searchable_attributes instance variable in controller for model
# for later use in view.
#
#   @searchable_attributes = Investigation.searchable_attributes
#
# Create controller paginated model instance variable like this:
#
#    @investigations = Investigation.search(params[:search], params[:page], current_visitor)
#
# Optionally add an include parameter to eagerly load asociations:
#
#   @investigations = Investigation.search(params[:search], params[:page], current_visitor, [{:learners => :learner_sessions}])
#
module SearchableModel
  # see: http://github.com/mislav/will_paginate/wikis/simple-search
  def search(search, page, user, includes={}, policy_scope=nil)
    sql_parameters = []
    sql_conditions = ""
    # pass in a username to limit the search to the users items
    if (!user.nil?) && (!user.id.nil?)
      if column_names.include? 'user_id'
        if self != User
          # sql_conditions = "(#{table_name}.user_id = ? or #{table_name}.public = '1') and "
          sql_conditions = "(#{table_name}.user_id = ?) and "
          sql_parameters << user.id
        end
      end
    end

    if !search.nil? && !search.empty?
      search_terms = get_search_terms(search)

      new_sql_conditions =  search_terms.map do
        ' (' + searchable_attributes.collect {|a| "#{table_name}.#{a} like ?"}.join(' or ') + ')'
      end
      sql_conditions = sql_conditions + new_sql_conditions.join(' and')

      search_terms.each do |st|
        searchable_attributes.length.times {sql_parameters << "%#{st}%"}
      end
    end

    conditions = []
    # in Rails 5 passing [""] to where causes invalid SQL to be generated
    if sql_conditions.length > 0
      conditions = [sql_conditions] + sql_parameters
    end

    per_page = self.per_page || 20
    if policy_scope
      policy_scope.where(conditions).includes(includes).page(page).per_page(per_page)
    else
      where(conditions).includes(includes).page(page).per_page(per_page)
    end
  end

  def get_search_terms(search)
    # split search string on white space not contained within a set of double quotes into an array of search terms
    search_terms = search.split(/\s(?=(?:[^"]|"[^"]*")*$)/)

    # remove any double quotation marks from search terms
    search_terms.each do |st|
      st.gsub!(/\"/, '')
    end
  end
end