module Ardm::Ar::PredicateBuilder::Rails4::ClassMethods

Public Instance Methods

build_from_hash(klass, attributes, default_table) click to toggle source
# File lib/ardm/ar/predicate_builder/rails4.rb, line 39
def build_from_hash(klass, attributes, default_table)
  queries = []

  #
  # attributes {
  #   Ardm::Query::Operator(target: :attr, operator: :not) =>
  attributes.each do |column, value|
    table = default_table

    if value.is_a?(Hash)
      if value.empty?
        queries << '1=0'
      else
        table       = Arel::Table.new(column, default_table.engine)
        association = klass.reflect_on_association(column.to_sym)

        value.each do |k, v|
          queries.concat expand(association && association.klass, table, k, v)
        end
      end
    else
      if Ardm::Query::Operator === column
        operator = column.operator
        target_column = column.target.to_s
      else
        target_column = column.to_s
        operator = nil
      end

      if target_column.include?('.')
        table_name, target_column = target_column.split('.', 2)
        table = Arel::Table.new(table_name, default_table.engine)
      end

      query = expand(klass, table, target_column, value)
      # TODO make nicer
      if [:not_eq, :not_in].include?(operator)
        # Logical not factorization !(a && b) == (!a || !b)
        query.map! &:not
        query = [query.inject { |composite, predicate| composite.or(predicate) }]
      end
      queries.concat query
    end
  end

  queries
end
expand(klass, table, column, value) click to toggle source
# File lib/ardm/ar/predicate_builder/rails4.rb, line 87
def expand(klass, table, column, value)
  if klass && association = klass.reflect_on_association(column.to_sym)
    expand_association(association, table, column, value)
  else
    [build(table[column], value)]
  end
end
expand_association(association, table, column, value) click to toggle source

Find the foreign key when using queries such as: Post.where(author: author)

For polymorphic relationships, find the foreign key and type: PriceEstimate.where(estimate_of: treasure)

Attempt to build a query that makes sense for an association name in the query, but if we can’t generate a propery query, fallback to using the original key we received.

# File lib/ardm/ar/predicate_builder/rails4.rb, line 104
def expand_association(association, table, column, value)
  queries = []
  case association.macro
  when :belongs_to
    if association.polymorphic? && base_class = polymorphic_base_class_from_value(value)
      queries << build(table[association.foreign_type], base_class)
    end
    queries << build(table[association.foreign_key], value)
  when :has_many, :has_one
    table = Arel::Table.new(association.klass.table_name, table.engine)
    queries << build(table[association.klass.primary_key], value)
  else
    queries << build(table[column], value)
  end
  queries
end
polymorphic_base_class_from_value(value) click to toggle source
# File lib/ardm/ar/predicate_builder/rails4.rb, line 121
def polymorphic_base_class_from_value(value)
  case value
  when Relation
    value.klass.base_class
  when Array
    val = value.compact.first
    val.class.base_class if val.is_a?(Base)
  when Base
    value.class.base_class
  end
end
references(attributes) click to toggle source
# File lib/ardm/ar/predicate_builder/rails4.rb, line 133
def references(attributes)
  attributes.map do |key, value|
    if value.is_a?(Hash)
      key
    else
      key = key.to_s
      key.split('.').first if key.include?('.')
    end
  end.compact
end
register_handler(klass, handler) click to toggle source

Define how a class is converted to Arel nodes when passed to where. The handler can be any object that responds to call, and will be used for any value that +===+ the class given. For example:

MyCustomDateRange = Struct.new(:start, :end)
handler = proc do |column, range|
  Arel::Nodes::Between.new(column,
    Arel::Nodes::And.new([range.start, range.end])
  )
end
ActiveRecord::PredicateBuilder.register_handler(MyCustomDateRange, handler)
# File lib/ardm/ar/predicate_builder/rails4.rb, line 155
def register_handler(klass, handler)
  handlers.unshift([klass, handler])
end
resolve_column_aliases(klass, hash) click to toggle source
# File lib/ardm/ar/predicate_builder/rails4.rb, line 29
def resolve_column_aliases(klass, hash)
  hash = hash.dup
  hash.keys.grep(Symbol) do |key|
    if klass.attribute_alias? key
      hash[klass.attribute_alias(key)] = hash.delete key
    end
  end
  hash
end

Private Instance Methods

build(attribute, value) click to toggle source
# File lib/ardm/ar/predicate_builder/rails4.rb, line 161
def build(attribute, value)
  handler_for(value).call(attribute, value)
end
handler_for(object) click to toggle source
# File lib/ardm/ar/predicate_builder/rails4.rb, line 165
def handler_for(object)
  handlers.detect { |klass, _| klass === object }.last
end