Class: ActiveRecord::Associations::JoinDependency

Inherits:
Object
  • Object
show all
Defined in:
lib/brick.rb

Instance Method Summary collapse

Instance Method Details

#apply_column_aliases(relation) ⇒ Object

An intelligent .eager_load() and .includes() that creates t0_r0 style aliases only for the columns used in .select(). To enable this behaviour, include the flag :_brick_eager_load as the first entry in your .select(). More information: discuss.rubyonrails.org/t/includes-and-select-for-joined-data/81640



1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
# File 'lib/brick.rb', line 1277

def apply_column_aliases(relation)
  if (sel_vals = relation.select_values.map(&:to_s)).first == '_brick_eager_load'
    used_cols = {}
    # Find and expand out all column names being used in select(...)
    new_select_values = sel_vals.each_with_object([]) do |col, s|
      next if col == '_brick_eager_load'

      if col.include?(' ') # Some expression? (No chance for a simple column reference)
        s << col # Just pass it through
      else
        col = if (col_parts = col.split('.')).length == 1
                [col]
              else
                [col_parts[0..-2].join('.'), col_parts.last]
              end
        used_cols[col] = nil
      end
    end
    if new_select_values.present?
      relation.select_values = new_select_values
    else
      relation.select_values.clear
    end

    @aliases ||= Aliases.new(join_root.each_with_index.map do |join_part, i|
      join_alias = join_part.table&.table_alias || join_part.table_name
      keys = [join_part.base_klass.primary_key] # Always include the primary key

      # # %%% Optional to include all foreign keys:
      # keys.concat(join_part.base_klass.reflect_on_all_associations.select { |a| a.belongs_to? }.map(&:foreign_key))

      # Add foreign keys out to referenced tables that we belongs_to
      join_part.children.each { |child| keys << child.reflection.foreign_key if child.reflection.belongs_to? }

      # Add the foreign key that got us here -- "the train we rode in on" -- if we arrived from
      # a has_many or has_one:
      if join_part.is_a?(ActiveRecord::Associations::JoinDependency::JoinAssociation) &&
         !join_part.reflection.belongs_to?
        keys << join_part.reflection.foreign_key
      end
      keys = keys.compact # In case we're using composite_primary_keys
      j = 0
      columns = join_part.column_names.each_with_object([]) do |column_name, s|
        # Include columns chosen in select(...) as well as the PK and any relevant FKs
        if used_cols.keys.find { |c| (c.length == 1 || c.first == join_alias) && c.last == column_name } ||
           keys.find { |c| c == column_name }
          s << Aliases::Column.new(column_name, "t#{i}_r#{j}")
        end
        j += 1
      end
      Aliases::Table.new(join_part, columns)
    end)
  end

  relation._select!(-> { aliases.columns })
end