Module: ActiveRecord::HasSomeOfMany::Associations

Extended by:
ActiveSupport::Concern
Defined in:
lib/activerecord/has_some_of_many/associations.rb

Class Method Summary collapse

Class Method Details

.build(klass, association_type, name, scope = nil, **options) ⇒ Object

Parameters:

  • klass (ActiveRecord::Base)

    The primary class

  • association_type (Symbol)

    For example, ‘:has_one` or `:has_many`

  • name (Symbol)

    The name of the association

  • scope (Proc, nil) (defaults to: nil)

    A scope to apply to the association

  • options (Hash)

    Additional options to pass to the association



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/activerecord/has_some_of_many/associations.rb', line 13

def self.build(klass, association_type, name, scope = nil, **options)
  primary_key = options[:primary_key] || klass.primary_key
  foreign_key = options[:foreign_key] || ActiveSupport::Inflector.foreign_key(klass, true)
  foreign_key_alias = options[:foreign_key_alias] || "#{foreign_key}_alias"

  lateral_subselection_scope = lambda do
    relation = scope ? instance_exec(&scope) : self
    limit = association_type == :has_one ? 1 : nil
    ActiveRecord::HasSomeOfMany::Associations.build_scope(klass, relation, primary_key: primary_key, foreign_key: foreign_key, foreign_key_alias: foreign_key_alias, limit: limit)
  end

  options[:primary_key] = primary_key
  options[:foreign_key] = foreign_key_alias

  klass.send(association_type, name, lateral_subselection_scope, **options)
end

.build_scope(klass, relation, primary_key:, foreign_key:, foreign_key_alias:, limit: nil) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/activerecord/has_some_of_many/associations.rb', line 30

def self.build_scope(klass, relation, primary_key:, foreign_key:, foreign_key_alias:, limit: nil)
  lateral_table = Arel::Table.new('lateral_table')
  subselect = klass.unscope(:select, :joins, :where, :order, :limit)
                .select(klass.arel_table[primary_key].as(foreign_key_alias), lateral_table[Arel.star])
                .arel.join(
                  relation
                    .then { |query| relation.table.name == klass.arel_table.name ? ActiveRecord::HasSomeOfMany::RelationRewriter.new(query).alias_table("#{klass.table_name}__alias") : query }
                    .then { |query| query.where(query.table[foreign_key].eq(klass.arel_table[primary_key])) }
                    .then { |query| limit ? query.limit(limit) : query }
                    .arel.lateral(lateral_table.name)
                ).on("TRUE")

  select_values = if relation.klass.ignored_columns.any?
                    [relation.klass.arel_table[foreign_key_alias]] + relation.klass.columns.map { |column| relation.klass.arel_table[column.name] }
                  else
                    [relation.klass.arel_table[Arel.star]]
                  end

  relation.klass.select(select_values).from(subselect.as(relation.arel_table.name))
end