activescaffold/active_scaffold

View on GitHub
lib/active_scaffold/actions/field_search.rb

Summary

Maintainability
A
2 hrs
Test Coverage
F
0%
module ActiveScaffold::Actions
  module FieldSearch
    def self.included(base)
      base.class_eval do
        helper_method :field_search_params, :grouped_search?, :search_group_column, :search_group_function
        include ActiveScaffold::Actions::CommonSearch
        include InstanceMethods
      end
    end

    module InstanceMethods
      # FieldSearch uses params[:search] and not @record because search conditions do not always pass the Model's validations.
      # This facilitates for example, textual searches against associations via .search_sql
      def show_search
        @record = new_model
        super
      end

      protected

      def search_partial
        super || :field_search
      end

      def store_search_params_into_session
        init_field_search_params(active_scaffold_config.field_search.default_params) unless active_scaffold_config.field_search.default_params.nil?
        super
      end

      def init_field_search_params(default_params)
        return unless (params[:search].is_a?(String) || search_params.nil?) && params[:search].blank?
        params[:search] = default_params.is_a?(Proc) ? instance_eval(&default_params) : default_params
      end

      def grouped_search?
        field_search_params.present? && field_search_params['active_scaffold_group'].present?
      end

      def setup_search_group
        @_search_group_name, @_search_group_function = field_search_params['active_scaffold_group'].to_s.split('#')
      end

      def search_group_function
        setup_search_group unless defined? @_search_group_function
        @_search_group_function
      end

      def search_group_name
        setup_search_group unless defined? @_search_group_name
        @_search_group_name
      end

      def search_group_column
        active_scaffold_config.columns[search_group_name] if search_group_name
      end

      def custom_finder_options
        if grouped_search?
          group_sql = calculation_for_group_by(search_group_column&.field || search_group_name)
          select_query = grouped_search_select
          select_query << group_sql.as(search_group_column.name.to_s) if search_group_column && group_sql.respond_to?(:to_sql)
          {group: group_sql, select: select_query}
        else
          super
        end
      end

      def grouped_search_select
        select_query = quoted_select_columns(search_group_column&.select_columns || [search_group_name])
        if active_scaffold_config.model.columns_hash.include?(active_scaffold_config.model.inheritance_column)
          select_query << active_scaffold_config.columns[active_scaffold_config.model.inheritance_column].field
        end
        grouped_columns_calculations.each do |name, part|
          select_query << (part.respond_to?(:as) ? part : Arel::Nodes::SqlLiteral.new(part)).as(name.to_s)
        end
      end

      def grouped_columns_calculations
        @grouped_columns_calculations ||= list_columns[1..-1].each_with_object({}) do |c, h|
          h[c.name] = calculation_for_group_search(c)
        end
      end

      def calculation_for_group_search(column)
        sql_function column.calculate.to_s, column.active_record_class.arel_table[column.name]
      end

      def calculation_for_group_by(group_sql)
        return group_sql unless search_group_function
        group_sql = Arel::Nodes::SqlLiteral.new(group_sql)
        case search_group_function
        when 'year', 'month', 'quarter'
          sql_function(search_group_function, group_sql)
        when 'year_month'
          sql_function('extract', sql_operator(Arel::Nodes::SqlLiteral.new('YEAR_MONTH'), 'FROM', group_sql))
        when 'year_quarter'
          sql_operator(sql_operator(sql_function('year', group_sql), '*', 10), '+', sql_function('quarter', group_sql))
        else
          raise "#{search_group_function} unsupported, override calculation_for_group_by in #{self.class.name}"
        end
      end

      def sql_function(function, *args)
        args.map! { |arg| quoted_arel_value(arg) }
        Arel::Nodes::NamedFunction.new(function, args)
      end

      def sql_operator(arg1, operator, arg2)
        Arel::Nodes::InfixOperation.new(operator, quoted_arel_value(arg1), quoted_arel_value(arg2))
      end

      def quoted_arel_value(value)
        value.is_a?(Arel::Predications) ? value : Arel::Nodes::Quoted.new(value)
      end

      def list_columns
        @list_columns ||=
          if grouped_search?
            columns = grouped_columns || super.select(&:calculation?)
            columns.unshift(search_group_column || search_group_name)
          else
            super
          end
      end

      def grouped_columns
        return if active_scaffold_config.field_search.grouped_columns.blank?
        active_scaffold_config.field_search.grouped_columns.map do |col|
          active_scaffold_config.columns[col]
        end.compact
      end

      def field_search_params
        search_params.is_a?(Hash) ? search_params : {}
      end

      def field_search_respond_to_html
        render(:action => 'field_search')
      end

      def field_search_respond_to_js
        render(:partial => 'field_search')
      end

      def do_search
        if field_search_params.present?
          do_field_search
        else
          super
        end
      end

      def do_field_search
        filtered_columns = []
        text_search = active_scaffold_config.field_search.text_search
        columns = active_scaffold_config.field_search.columns
        search_params.each do |key, value|
          next unless columns.include? key
          column = active_scaffold_config.columns[key]
          search_condition = self.class.condition_for_column(column, value, text_search)
          next if search_condition.blank?

          active_scaffold_conditions << search_condition
          filtered_columns << column
        end
        setup_joins_for_filtered_columns(filtered_columns)
        if filtered_columns.present? || grouped_search?
          @filtered = active_scaffold_config.field_search.human_conditions ? filtered_columns : true
        end

        active_scaffold_config.list.user.page = nil
      end

      def field_search_ignore?
        active_scaffold_config.list.always_show_search && active_scaffold_config.list.search_partial == 'field_search'
      end

      private

      def setup_joins_for_filtered_columns(filtered_columns)
        if grouped_search? || active_scaffold_config.list.user.count_includes.present?
          active_scaffold_outer_joins.concat filtered_columns.map(&:search_joins).flatten.uniq.compact
        else
          set_outer_joins_for_search filtered_columns
        end
      end

      def field_search_formats
        (default_formats + active_scaffold_config.formats + active_scaffold_config.field_search.formats).uniq
      end
    end
  end
end