Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

after_add callback not called when updating using the foreign_key #81

Open
rluvaton opened this issue Mar 16, 2023 · 1 comment
Open
Assignees
Labels

Comments

@rluvaton
Copy link

Hey, for some reason, when I update the foreign_key of the collection it does not run the after_add callback

# frozen_string_literal: true
require 'torque-postgresql'

require 'byebug'
require "active_record"
require "minitest/autorun"
require "logger"

ActiveRecord::Base.establish_connection(
  adapter:  "postgresql",
  database: "test",
  encoding: "unicode",
  host:     "localhost",
  port:     "5432",
  password: "12345",
  username: "test")

ActiveRecord::Schema.define do
  drop_table "employees", if_exists: true
  drop_table "projects", if_exists: true

  create_table "employees" do |t|
    t.string "name"
    t.timestamps
  end

  create_table "projects" do |t|
    t.string "title"
    t.bigint "employees_ids", array: true
    t.timestamps
  end
end

class Employee < ActiveRecord::Base
  has_many :projects, array: true, foreign_key: :employees_ids

  after_commit :on_update, on: :update

  def on_update
    puts "Employee got updated"
  end
end

class Project < ActiveRecord::Base
  belongs_to_many :employees, foreign_key: "employees_ids" , after_add: :on_employee_added

  def on_employee_added(employee)
    puts "project: #{self.title} | employee added: #{employee.name}"
  end
end

# Not working
def run_using_ids
  employee_id = (Employee.create!(name: 'employee_by_id')).id
  project_id = (Project.create!(title: 'project_by_id')).id

  puts "[ids] Adding employee to project"
  project = Project.find_by(id: project_id)
  project.employees_ids = [employee_id]
  project.save!
end

# Working
def run_using_records
  employee = (Employee.create!(name: 'employee_by_record'))
  project_id = (Project.create!(title: 'project_by_record')).id

  puts "[records] Adding employee to project"
  project = Project.find_by(id: project_id)
  project.employees = [employee]
  project.save!
end



run_using_ids

run_using_records

the run_by_ids function does not call the on_employee_added after_add method while the run_using_records does

Currently, this will be logged would be:

[ids] Adding employee to project
[records] Adding employee to project
project: project_by_record | employee added: employee_by_record

(we missing project: project_by_id | employee added: employee_by_id line)

@crashtech crashtech self-assigned this Mar 24, 2023
@crashtech crashtech added the bug label Mar 24, 2023
@crashtech
Copy link
Owner

I have attempted to fix this, but the problem is that it can generate double event triggering, since the hook for project.employees_ids = is both problematic (due to possible model overrides) and doesn't actually invoke a save or anything.

From further investigation, this is kind of caused by the inheritance of the CollectionAssociation. The belongs_to_many association is an intersection of belongs_to and CollectionAssociations. That said, it feels to me that it should not have such hooks because it should be outside of the intersection.

You can achieve the same and will work with both presented approaches if you use an after_save hook, where you check if the employees_ids was changed. That is also my suggestion. Don't use the after_add hook from the association, just use after_save :on_employee_changed, if: :employees_ids_previously_changed?. Inside there, you can: added = employees_ids - employees_ids_previously_was and removed = employees_ids_previously_was - employees_ids.

Let me know what do you think and if this helps you with the problem. (Sorry for the long-standing ticket)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants