Class: ActiveRecord::Associations::JoinDependency::JoinAssociation

Inherits:
JoinPart
  • Object
show all
Includes:
ActiveRecord::Associations::JoinHelper
Defined in:
activerecord/lib/active_record/associations/join_dependency/join_association.rb

Overview

:nodoc:

Instance Attribute Summary collapse

Attributes inherited from JoinPart

#base_klass

Instance Method Summary collapse

Methods inherited from JoinPart

#aliased_primary_key, #aliased_table, #column_names_with_alias, #extract_record, #instantiate, #record_id

Constructor Details

#initialize(reflection, join_dependency, parent = nil) ⇒ JoinAssociation

Returns a new instance of JoinAssociation


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 30

def initialize(reflection, join_dependency, parent = nil)
  reflection.check_validity!

  if reflection.options[:polymorphic]
    raise EagerLoadPolymorphicError.new(reflection)
  end

  super(reflection.klass)

  @reflection      = reflection
  @join_dependency = join_dependency
  @parent          = parent
  @join_type       = Arel::InnerJoin
  @aliased_prefix  = "t#{ join_dependency.join_parts.size }"
  @tables          = construct_tables.reverse
end

Instance Attribute Details

#aliased_prefixObject (readonly)

These implement abstract methods from the superclass


23
24
25
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 23

def aliased_prefix
  @aliased_prefix
end

#join_dependencyObject (readonly)

The JoinDependency object which this JoinAssociation exists within. This is mainly relevant for generating aliases which do not conflict with other joins which are part of the query.


13
14
15
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 13

def join_dependency
  @join_dependency
end

#join_typeObject

What type of join will be generated, either Arel::InnerJoin (default) or Arel::OuterJoin


20
21
22
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 20

def join_type
  @join_type
end

#parentObject (readonly)

A JoinBase instance representing the active record we are joining onto. (So in Author.has_many :posts, the Author would be that base record.)


17
18
19
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 17

def parent
  @parent
end

#reflectionObject (readonly)

The reflection of the association represented


8
9
10
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 8

def reflection
  @reflection
end

#tablesObject (readonly)

Returns the value of attribute tables


25
26
27
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 25

def tables
  @tables
end

Instance Method Details

#==(other) ⇒ Object


50
51
52
53
54
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 50

def ==(other)
  other.class == self.class &&
    other.reflection == reflection &&
    other.parent == parent
end

#aliased_table_nameObject


174
175
176
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 174

def aliased_table_name
  table.table_alias || table.name
end

#build_constraint(klass, table, key, foreign_table, foreign_key) ⇒ Object

Builds equality condition.

Example:

class Physician < ActiveRecord::Base
  has_many :appointments
end

If I execute `Physician.joins(:appointments).to_a` then
  reflection    #=> #<ActiveRecord::Reflection::AssociationReflection @macro=:has_many ...>
  table         #=> #<Arel::Table @name="appointments" ...>
  key           #=>  physician_id
  foreign_table #=> #<Arel::Table @name="physicians" ...>
  foreign_key   #=> id

152
153
154
155
156
157
158
159
160
161
162
163
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 152

def build_constraint(klass, table, key, foreign_table, foreign_key)
  constraint = table[key].eq(foreign_table[foreign_key])

  if klass.finder_needs_type_condition?
    constraint = table.create_and([
      constraint,
      klass.send(:type_condition, table)
    ])
  end

  constraint
end

#find_parent_in(other_join_dependency) ⇒ Object


56
57
58
59
60
61
62
63
64
65
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 56

def find_parent_in(other_join_dependency)
  other_join_dependency.join_parts.detect do |join_part|
    case parent
    when JoinBase
      parent.base_klass == join_part.base_klass
    else
      parent == join_part
    end
  end
end

#join_constraintsObject


67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 67

def join_constraints
  joins         = []
  tables        = @tables.dup

  foreign_table = parent.table
  foreign_klass = parent.base_klass

  scope_chain_iter = reflection.scope_chain.reverse_each

  # The chain starts with the target table, but we want to end with it here (makes
  # more sense in this context), so we reverse
  chain.reverse_each do |reflection|
    table = tables.shift
    klass = reflection.klass

    case reflection.source_macro
    when :belongs_to
      key         = reflection.association_primary_key
      foreign_key = reflection.foreign_key
    when :has_and_belongs_to_many
      # Join the join table first...
      joins << join(
        table,
        table[reflection.foreign_key].
          eq(foreign_table[reflection.active_record_primary_key]))

      foreign_table, table = table, tables.shift

      key         = reflection.association_primary_key
      foreign_key = reflection.association_foreign_key
    else
      key         = reflection.foreign_key
      foreign_key = reflection.active_record_primary_key
    end

    constraint = build_constraint(klass, table, key, foreign_table, foreign_key)

    scope_chain_items = scope_chain_iter.next.map do |item|
      if item.is_a?(Relation)
        item
      else
        ActiveRecord::Relation.create(klass, table).instance_exec(self, &item)
      end
    end

    if reflection.type
      scope_chain_items <<
        ActiveRecord::Relation.create(klass, table)
          .where(reflection.type => foreign_klass.base_class.name)
    end

    scope_chain_items.concat [klass.send(:build_default_scope)].compact

    rel = scope_chain_items.inject(scope_chain_items.shift) do |left, right|
      left.merge right
    end

    if rel && !rel.arel.constraints.empty?
      constraint = constraint.and rel.arel.constraints
    end

    joins << join(table, constraint)

    # The current table in this iteration becomes the foreign table in the next
    foreign_table, foreign_klass = table, klass
  end

  joins
end

#join_relation(joining_relation) ⇒ Object


165
166
167
168
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 165

def join_relation(joining_relation)
  self.join_type = Arel::OuterJoin
  joining_relation.joins(self)
end

#parent_table_nameObject Also known as: alias_suffix


47
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 47

def parent_table_name; parent.table_name; end

#tableObject


170
171
172
# File 'activerecord/lib/active_record/associations/join_dependency/join_association.rb', line 170

def table
  tables.last
end