mberlanda/cheidelacoriera

View on GitHub
app/controllers/dry_crud/searchable.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
93%
module DryCrud

  # The search functionality for the index table.
  # Define an array of searchable string columns in your subclassing
  # controllers using the class attribute +search_columns+.
  module Searchable

    extend ActiveSupport::Concern

    included do
      class_attribute :search_columns
      self.search_columns = []

      helper_method :search_support?

      prepend Prepends
    end

    # Prepended methods for searching.
    module Prepends

      private

      # Enhance the list entries with an optional search criteria
      def list_entries
        super.where(search_conditions)
      end

      # Concat the word clauses with AND.
      def search_conditions
        if search_support? && params[:q].present?
          search_word_conditions.reduce do |query, condition|
            query.and(condition)
          end
        end
      end

      # Split the search query in single words and create a list of word
      # clauses.
      def search_word_conditions
        params[:q].split(/\s+/).map { |w| search_word_condition(w) }
      end

      # Concat the column queries of the given word with OR.
      def search_word_condition(word)
        search_column_condition(word).reduce do |query, condition|
          query.or(condition)
        end
      end

      # Create a list of Arel #matches queries for each column and the given
      # word.
      def search_column_condition(word)
        self.class.search_tables_and_fields.map do |table_name, field|
          table = Arel::Table.new(table_name)
          table[field].matches(Arel::Nodes::Quoted.new("%#{word}%"))
        end
      end

      # Returns true if this controller has searchable columns.
      def search_support?
        search_columns.present?
      end

    end

    # Class methods for Searchable.
    module ClassMethods

      # All search columns divided in table and field names.
      def search_tables_and_fields
        @search_tables_and_fields ||= search_columns.map do |f|
          if f.to_s.include?('.')
            f.split('.', 2)
          else
            [model_class.table_name, f]
          end
        end
      end

    end

  end
end