Sha256: 0b4b30e9e0eb77b939d12af5907d3e697948c3853ca97cf78851d6baf4d2fdaa

Contents?: true

Size: 1.54 KB

Versions: 2

Compression:

Stored size: 1.54 KB

Contents

# frozen_string_literal: true

module Lipstick
  module Filterable
    # Provides case-insensitive matching on a case-sensitive MySQL database
    # (which is mandated by Gumboot)
    class CollatedArelAttribute < Arel::Nodes::Node
      include Arel::Predications

      attr_reader :attribute, :collation

      def initialize(attribute, collation)
        super()
        @attribute = attribute
        @collation = collation
      end
    end

    module VisitCollatedArelAttribute
      # rubocop:disable Naming/MethodName
      def visit_Lipstick_Filterable_CollatedArelAttribute(object, collector)
        visit(object.attribute, collector)
        collector << ' COLLATE ' << object.collation
      end
      # rubocop:enable Naming/MethodName
    end

    Arel::Visitors::ToSql.include(VisitCollatedArelAttribute)

    module ClassMethods
      attr_reader :filterable_fields

      def filterable_by(*fields)
        @filterable_fields = fields
      end

      def filterable_filter(query, collation = 'utf8_unicode_ci')
        filter_terms(query).reduce(all) do |scope, term|
          conds = filterable_fields.map do |f|
            CollatedArelAttribute.new(arel_table[f], collation)
                                 .matches(term)
          end
          scope.where(conds.reduce { |acc, elem| acc.or(elem) })
        end
      end

      private

      def filter_terms(query)
        query.to_s.downcase.split(/\s+/).map { |s| "*#{s}*".gsub(/[%*]+/, '%') }
      end
    end

    def self.included(base)
      base.extend(ClassMethods)
    end
  end
end

Version data entries

2 entries across 2 versions & 1 rubygems

Version Path
aaf-lipstick-4.9.2 lib/lipstick/filterable.rb
aaf-lipstick-4.9.1 lib/lipstick/filterable.rb