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

Inherits:
JoinPart
  • Object
show all
Includes:
ActiveRecord::Associations::JoinHelper
Defined in:
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.



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/active_record/associations/join_dependency/join_association.rb', line 33

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 '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 '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 '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 '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 '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 '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 '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



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

def aliased_table_name
  table.table_alias || table.name
end

#build_constraint(reflection, 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


143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/active_record/associations/join_dependency/join_association.rb', line 143

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

  if reflection.klass.finder_needs_type_condition?
    constraint = table.create_and([
      constraint,
      reflection.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 '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_relation(joining_relation) ⇒ Object



156
157
158
159
# File 'lib/active_record/associations/join_dependency/join_association.rb', line 156

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

#join_to(manager) ⇒ Object



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
# File 'lib/active_record/associations/join_dependency/join_association.rb', line 67

def join_to(manager)
  tables        = @tables.dup
  foreign_table = parent_table
  foreign_klass = parent.base_klass

  # 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_with_index do |reflection, i|
    table = tables.shift

    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...
      manager.from(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(reflection, table, key, foreign_table, foreign_key)

    scope_chain_items = scope_chain[i]

    if reflection.type
      scope_chain_items += [
        ActiveRecord::Relation.new(reflection.klass, table)
          .where(reflection.type => foreign_klass.base_class.name)
      ]
    end

    scope_chain_items += [reflection.klass.send(:build_default_scope, ActiveRecord::Relation.new(reflection.klass, table))].compact

    scope_chain_items.each do |item|
      unless item.is_a?(Relation)
        item = ActiveRecord::Relation.new(reflection.klass, table).instance_exec(self, &item)
      end

      constraint = constraint.and(item.arel.constraints) unless item.arel.constraints.empty?
    end

    manager.from(join(table, constraint))

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

  manager
end

#scope_chainObject



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

def scope_chain
  @scope_chain ||= reflection.scope_chain.reverse
end

#tableObject



161
162
163
# File 'lib/active_record/associations/join_dependency/join_association.rb', line 161

def table
  tables.last
end