Class: ActiveRecord::Associations::JoinDependency

Inherits:
Object
  • Object
show all
Defined in:
lib/active_record/associations/join_dependency.rb,
lib/active_record/associations/join_dependency/join_base.rb,
lib/active_record/associations/join_dependency/join_part.rb,
lib/active_record/associations/join_dependency/join_association.rb

Overview

:nodoc:

Defined Under Namespace

Classes: Aliases, JoinAssociation, JoinBase, JoinPart

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(base, associations, joins, eager_loading: true) ⇒ JoinDependency

base is the base class on which operation is taking place. associations is the list of associations which are joined using hash, symbol or array. joins is the list of all string join commands and arel nodes.

Example :

class Physician < ActiveRecord::Base
  has_many :appointments
  has_many :patients, through: :appointments
end

If I execute `@physician.patients.to_a` then
  base # => Physician
  associations # => []
  joins # =>  [#<Arel::Nodes::InnerJoin: ...]

However if I execute `Physician.joins(:appointments).to_a` then
  base # => Physician
  associations # => [:appointments]
  joins # =>  []

95
96
97
98
99
100
101
# File 'lib/active_record/associations/join_dependency.rb', line 95

def initialize(base, associations, joins, eager_loading: true)
  @alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins, base.type_caster)
  @eager_loading = eager_loading
  tree = self.class.make_tree associations
  @join_root = JoinBase.new base, build(tree, base)
  @join_root.children.each { |child| construct_tables! @join_root, child }
end

Instance Attribute Details

#alias_trackerObject (readonly)

Returns the value of attribute alias_tracker


48
49
50
# File 'lib/active_record/associations/join_dependency.rb', line 48

def alias_tracker
  @alias_tracker
end

#base_klassObject (readonly)

Returns the value of attribute base_klass


48
49
50
# File 'lib/active_record/associations/join_dependency.rb', line 48

def base_klass
  @base_klass
end

#join_rootObject (readonly)

Returns the value of attribute join_root


48
49
50
# File 'lib/active_record/associations/join_dependency.rb', line 48

def join_root
  @join_root
end

Class Method Details

.make_tree(associations) ⇒ Object


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

def self.make_tree(associations)
  hash = {}
  walk_tree associations, hash
  hash
end

.walk_tree(associations, hash) ⇒ Object


56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/active_record/associations/join_dependency.rb', line 56

def self.walk_tree(associations, hash)
  case associations
  when Symbol, String
    hash[associations.to_sym] ||= {}
  when Array
    associations.each do |assoc|
      walk_tree assoc, hash
    end
  when Hash
    associations.each do |k, v|
      cache = hash[k] ||= {}
      walk_tree v, cache
    end
  else
    raise ConfigurationError, associations.inspect
  end
end

Instance Method Details

#aliasesObject


128
129
130
131
132
133
134
135
# File 'lib/active_record/associations/join_dependency.rb', line 128

def aliases
  Aliases.new join_root.each_with_index.map { |join_part, i|
    columns = join_part.column_names.each_with_index.map { |column_name, j|
      Aliases::Column.new column_name, "t#{i}_r#{j}"
    }
    Aliases::Table.new(join_part, columns)
  }
end

#instantiate(result_set, aliases) ⇒ Object


137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/active_record/associations/join_dependency.rb', line 137

def instantiate(result_set, aliases)
  primary_key = aliases.column_alias(join_root, join_root.primary_key)

  seen = Hash.new { |i, object_id|
    i[object_id] = Hash.new { |j, child_class|
      j[child_class] = {}
    }
  }

  model_cache = Hash.new { |h, klass| h[klass] = {} }
  parents = model_cache[join_root]
  column_aliases = aliases.column_aliases join_root

  message_bus = ActiveSupport::Notifications.instrumenter

  payload = {
    record_count: result_set.length,
    class_name: join_root.base_klass.name
  }

  message_bus.instrument("instantiation.active_record", payload) do
    result_set.each { |row_hash|
      parent_key = primary_key ? row_hash[primary_key] : row_hash
      parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases)
      construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
    }
  end

  parents.values
end

#join_constraints(outer_joins, join_type) ⇒ Object


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.rb', line 107

def join_constraints(outer_joins, join_type)
  joins = join_root.children.flat_map { |child|

    if join_type == Arel::Nodes::OuterJoin
      make_left_outer_joins join_root, child
    else
      make_inner_joins join_root, child
    end
  }

  joins.concat outer_joins.flat_map { |oj|
    if join_root.match? oj.join_root
      walk join_root, oj.join_root
    else
      oj.join_root.children.flat_map { |child|
        make_outer_joins oj.join_root, child
      }
    end
  }
end

#reflectionsObject


103
104
105
# File 'lib/active_record/associations/join_dependency.rb', line 103

def reflections
  join_root.drop(1).map!(&:reflection)
end