Class: ActiveRecord::PredicateBuilder

Inherits:
Object
  • Object
show all
Defined in:
ext/active_record/finder_methods.rb

Overview

:nodoc:

Instance Method Summary collapse

Instance Method Details

#expand_from_hash(attributes, &block) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'ext/active_record/finder_methods.rb', line 7

def expand_from_hash(attributes, &block)
  return ["1=0"] if attributes.empty?

  attributes.flat_map do |key, value|
    if key.is_a?(Array)
      queries = Array(value).map do |ids_set|
        raise ArgumentError, "Expected corresponding value for #{key} to be an Array" unless ids_set.is_a?(Array)
        expand_from_hash(key.zip(ids_set).to_h)
      end
      grouping_queries(queries)
    elsif value.is_a?(Hash) && !table.has_column?(key)
      ka = table.associated_table(key, &block)
        .predicate_builder.expand_from_hash(value.stringify_keys)

      if self.table.instance_variable_get(:@klass).connection.is_a?(ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter)
        ka.each { |k|
          if k.left.is_a?(Arel::Attributes::Attribute) || k.left.is_a?(Arel::Attributes::Relation)
            k.left = Arel::Attributes::Relation.new(k.left, key)
          end
        }
      end
      ka
    elsif table.associated_with?(key)
      # 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)
      associated_table = table.associated_table(key)
      if associated_table.polymorphic_association?
        value = [value] unless value.is_a?(Array)
        klass = PolymorphicArrayValue
      elsif associated_table.through_association?
        next associated_table.predicate_builder.expand_from_hash(
          associated_table.primary_key => value
        )
      end

      klass ||= AssociationQueryValue
      queries = klass.new(associated_table, value).queries.map! do |query|
        # If the query produced is identical to attributes don't go any deeper.
        # Prevents stack level too deep errors when association and foreign_key are identical.
        query == attributes ? self[key, value] : expand_from_hash(query)
      end

      grouping_queries(queries)
    elsif table.aggregated_with?(key)
      mapping = table.reflect_on_aggregation(key).mapping
      values = value.nil? ? [nil] : Array.wrap(value)
      if mapping.length == 1 || values.empty?
        column_name, aggr_attr = mapping.first
        values = values.map do |object|
          object.respond_to?(aggr_attr) ? object.public_send(aggr_attr) : object
        end
        self[column_name, values]
      else
        queries = values.map do |object|
          mapping.map do |field_attr, aggregate_attr|
            self[field_attr, object.try!(aggregate_attr)]
          end
        end

        grouping_queries(queries)
      end
    else
      self[key, value]
    end
  end
end