# frozen_string_literal: true module Pursuit # :nodoc: # class PredicateSearch # @return [Boolean] `true` when aggregate modifiers can be used in queries, `false` otherwise. # attr_accessor :permit_aggregate_modifiers # @return [Hash<Symbol, Arel::Attributes::Attribute>] The attributes permitted for use in queries. # attr_reader :permitted_attributes # @return [ActiveRecord::Relation] The relation to which the predicate clauses are added. # attr_reader :relation # Creates a new predicate search instance. # # @param relation [ActiveRecord::Relation] The relation to which the predicate clauses are added. # @param permit_aggregate_modifiers [Boolean] Whether aggregate modifiers can be used or not. # @param block [Proc] The proc to invoke in the search instance (optional). # def initialize(relation, permit_aggregate_modifiers: false, &block) @relation = relation @permit_aggregate_modifiers = permit_aggregate_modifiers @permitted_attributes = HashWithIndifferentAccess.new instance_eval(&block) if block end # @return [Pursuit::PredicateParser] The parser which converts queries into trees. # def parser @parser ||= PredicateParser.new end # @return [Pursuit::PredicateTransform] The transform which converts trees into ARel nodes. # def transform @transform ||= PredicateTransform.new end # Permits use of the specified attribute in predicate queries. # # @param name [Symbol] The name used in the query. # @param attribute [Arel::Attributes::Attribute, Symbol] The underlying attribute to query. # @return [Arel::Attributes::Attribute] The underlying attribute to query. # def permit_attribute(name, attribute = nil) attribute = relation.klass.arel_table[attribute] if attribute.is_a?(Symbol) permitted_attributes[name] = attribute || relation.klass.arel_table[name] end # Parse a predicate query into ARel nodes. # # @param query [String] The predicate query. # @return [Hash<Symbol, Arel::Nodes::Node>] The ARel nodes representing the predicate query. # def parse(query) tree = parser.parse(query) transform.apply( tree, permitted_attributes: permitted_attributes, permit_aggregate_modifiers: permit_aggregate_modifiers ) end # Returns #relation filtered by the predicate query. # # @param query [String] The predicate query. # @return [ActiveRecord::Relation] The updated relation with the predicate clauses added. # def apply(query) nodes = parse(query) relation = self.relation relation = relation.where(nodes[:where]) if nodes[:where] relation = relation.having(nodes[:having]) if nodes[:having] relation end end end