khiav223577 / deep_pluck

Allow you to pluck attributes from nested associations without loading a bunch of records.
MIT License
460 stars 14 forks source link

Cannot deep_pluck HABTM association with different name not working when doing inverse lookup. #46

Closed TaylorMerritt closed 3 years ago

TaylorMerritt commented 3 years ago

It appears when deep pluck attempts to figure out the association in reverse, it doesn't observer the correct association name. It seems to be using the class name rather than the association name (training_programs rather than borrower_training_programs).

begin
  require 'bundler/inline'
rescue LoadError => e
  warn 'Bundler version 1.10 or later is required. Please update your Bundler'
  raise e
end

gemfile(true) do
  source 'https://rubygems.org'
  gem 'deep_pluck', '~> 1.1.6'
  gem 'sqlite3'
end

require 'active_record'
require 'minitest/autorun'
require 'logger'

ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table 'training_programs', force: :cascade do |t|
    t.string :name
  end

  create_table 'training_providers', force: :cascade do |t|
    t.string :name
  end

  create_table 'training_programs_training_providers', id: false, force: :cascade do |t|
    t.bigint 'training_provider_id', null: false
    t.bigint 'training_program_id', null: false
  end
end

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end

class TrainingProgram < ApplicationRecord
  has_and_belongs_to_many :training_provider,
    inverse_of: :borrower_training_programs,
    join_table: 'training_programs_training_providers'
end

class TrainingProvider < ApplicationRecord
  has_and_belongs_to_many :borrower_training_programs,
    class_name: 'TrainingProgram',
    inverse_of: :training_provider,
    join_table: 'training_programs_training_providers'
end

class BugTest < Minitest::Test
  def setup
    TrainingProgram.create!(
      name: 'tp', training_provider: [TrainingProvider.create!(name: 'as')]
    )
  end

  def teardown
    TrainingProvider.destroy_all
    TrainingProgram.destroy_all
  end

  def test_data
    assert_equal TrainingProgram.first.training_provider.first.name, 'as'
    assert_equal TrainingProvider.first.borrower_training_programs.first.name, 'tp'
  end

  def test_deep_pluck
    assert_equal TrainingProgram.deep_pluck(:name, training_provider: :name), [
      'name' => 'tp',
      training_provider: [
        'name' => 'as'
      ]
    ]
  end
end
khiav223577 commented 3 years ago

Sorry for the late reply. I missed the notification :<

This issue is related to https://github.com/khiav223577/deep_pluck/pull/36 The problem is that we have no way to guarantee we can find the inverse association of training_program.training_provider It tried to use training_programs, but in this example, it should be borrower_training_programs

This is the pseudo code of what happening when you call TrainingProgram.deep_pluck(:name, training_provider: :name)

  1. data1= TrainingProgram.all
  2. data2 = TrainingProvider.joins(:training_program).where(id: data1.ids)
  3. merge(data1, data2)

Maybe we can use the hints from the inverse_of option. I'm going to explore the rails source code and figure out how it works.

khiav223577 commented 3 years ago

Just release v1.1.7. It should be fixed in this version. :)

See the CHANGELOG for more details.

TaylorMerritt commented 3 years ago

Thank you @khiav223577 ! Appears to be working in my project now.