# frozen_string_literal: true module Care::AutoFinder class Searcher attr_reader :relation attr_reader :query attr_reader :columns attr_reader :attributes attr_reader :arel_table attr_reader :table attr_reader :pattern def initialize(params) @relation = params[:items] @columns = params[:columns] @attributes = params[:columns] @query = params[:search] @arel_table = relation.arel_table @table = relation.arel_table @pattern = "%#{sanitaze(query)}%" end def self.call(params) new(params).call end def call predicate = complile_node(relation, attributes).reduce { |memo, expression| memo ? memo.or(expression) : memo } relation.where(predicate) end protected def complile_node(relation, attributes) expression = [*attributes].map { |attribute| if attribute.is_a?(Symbol) compile(relation, attribute) elsif attribute.is_a?(Hash) attribute.map do |child_relation, child_attributes| complile_node(inner_relation(relation, child_relation), child_attributes) end end } expression.flatten end # # rubocop:disable Metrics/AbcSize # def compile(relation, attribute) if relation.columns_hash[attribute.to_s] && relation.columns_hash[attribute.to_s].type == :date Arel.sql("to_char(#{attribute}, 'DD.MM.YYYY')").matches(pattern) elsif relation.columns_hash[attribute.to_s] && relation.columns_hash[attribute.to_s].type == :datetime Arel.sql("to_char(#{attribute}, 'DD.MM.YYYY')").matches(pattern) elsif relation.columns_hash[attribute.to_s] && relation.columns_hash[attribute.to_s].type == :integer Arel.sql("to_char(#{attribute}, '999')").matches(pattern) elsif relation.columns_hash[attribute.to_s]&.array? Arel.sql("array_to_string(#{attribute}, ', ')").matches(pattern) elsif relation.respond_to?(:custom_columns) && relation.custom_columns[attribute] Arel.sql(relation.custom_columns[attribute]).matches(pattern) else relation.arel_table[attribute].matches(pattern) end end # # rubocop:enable Metrics/AbcSize # private def sanitaze(string, escape_character = '\\') pattern = Regexp.union(escape_character, "%", "_") string.gsub(pattern) { |x| [escape_character, x].join } end def inner_relation(relation, target_relation) target = relation.reflections[target_relation.to_s] target ? target.klass : ArtificalRelation.new(target_relation) end # # Исскуственая модель, имеющая функцию arel_table # class ArtificalRelation attr_accessor :table_name def initialize(table_name) @table_name = table_name end def arel_table Arel::Table.new(table_name) end def columns_hash {} end end end end