class OccamsRecord::EagerLoaders::Habtm

Eager loader for has_and_belongs_to_many associations.

Private Instance Methods

fetch_join_rows(rows) click to toggle source

Fetches (and caches) an array of rows from the join table. The rows are [fkey, assoc_fkey].

@param rows [Array<OccamsRecord::Results::Row>] @return [Array<Array<String>>]

# File lib/occams-record/eager_loaders/habtm.rb, line 69
def fetch_join_rows(rows)
  conn = @model.connection
  join_table = conn.quote_table_name @ref.join_table
  assoc_fkey = conn.quote_column_name @ref.association_foreign_key
  fkey = conn.quote_column_name @ref.foreign_key
  quoted_ids = rows.map { |row|
    begin
      id = row.send @ref.active_record_primary_key
    rescue NoMethodError => e
      raise MissingColumnError.new(row, e.name)
    end
    conn.quote id
  }

  quoted_ids.any? ? conn.
    exec_query("SELECT #{fkey}, #{assoc_fkey} FROM #{join_table} WHERE #{fkey} IN (#{quoted_ids.join ','})").
    rows : []
end
merge!(assoc_rows, rows, join_rows) click to toggle source

Merge the association rows into the given rows.

@param assoc_rows [Array<OccamsRecord::Results::Row>] rows loaded from the association @param rows [Array<OccamsRecord::Results::Row>] rows loaded from the main model @param join_rows [Array<Array<String>>] raw join'd ids from the db

# File lib/occams-record/eager_loaders/habtm.rb, line 26
def merge!(assoc_rows, rows, join_rows)
  joins_by_id = join_rows.reduce({}) { |a, join|
    id = join[0].to_s
    a[id] ||= []
    a[id] << join[1].to_s
    a
  }

  assoc_order_cache = {} # maintains the original order of assoc_rows
  assoc_rows_by_id = assoc_rows.each_with_index.reduce({}) { |a, (row, idx)|
    begin
      id = row.send(@ref.association_primary_key).to_s
    rescue NoMethodError => e
      raise MissingColumnError.new(row, e.name)
    end
    assoc_order_cache[id] = idx
    a[id] = row
    a
  }

  assign = "#{name}="
  rows.each do |row|
    begin
      id = row.send(@ref.active_record_primary_key).to_s
    rescue NoMethodError => e
      raise MissingColumnError.new(row, e.name)
    end
    assoc_fkeys = (joins_by_id[id] || []).uniq.
      sort_by { |fkey| assoc_order_cache[fkey] || 0 }

    associations = assoc_rows_by_id.values_at(*assoc_fkeys).compact.uniq
    row.send assign, associations
  end
end
query(rows) { |any? ? where(association_primary_key => assoc_ids) : nil, join_rows| ... } click to toggle source

Yield one or more ActiveRecord::Relation objects to a given block.

@param rows [Array<OccamsRecord::Results::Row>] Array of rows used to calculate the query. @yield

# File lib/occams-record/eager_loaders/habtm.rb, line 13
def query(rows)
  join_rows = fetch_join_rows(rows)
  assoc_ids = join_rows.map { |row| row[1] }.compact.uniq
  yield assoc_ids.any? ? base_scope.where(@ref.association_primary_key => assoc_ids) : nil, join_rows
end