westonganger/paper_trail-association_tracking

has_many associations defined on intermediate STI classes fail to reify

JaciBrunning opened this issue · 0 comments

In an example such as the following, PT-AT fails to reify the students association, always returning the latest result as if .reify() was called without has_many: true.

class Individual < ActiveRecord::Base
  has_paper_trail
end

class Teacher < Individual
  has_many :students, class_name: "Studentship"
end

class Professor < Teacher
end

class Student < Individual
end

class Studentship < ActiveRecord::Base
  belongs_to :teacher
  belongs_to :student
  has_paper_trail
end

This is demonstrated in the following bug report script:

Bug Report Script
### Bug Report Template

require "bundler/inline"

# STEP ONE: What versions are you using?
gemfile(true) do
  ruby "2.6"
  source "https://rubygems.org"
  gem "activerecord", "6.0.2"
  gem "minitest"
  gem "paper_trail", "~>10.3.0"
  gem "paper_trail-association_tracking"
  gem "sqlite3"
end

require "active_record"
require "minitest/autorun"
require "logger"

# Please use sqlite for your bug reports, if possible.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")

ActiveRecord::Base.logger = nil

ActiveRecord::Schema.define do
  # STEP TWO: Define your tables here.
  create_table :individuals, force: true do |t|
    t.text :name, null: false
    t.text :flag, null: false
    t.string :type, null: false
    t.timestamps null: false
  end

  create_table :studentships, force: true do |t|
    t.integer :teacher_id, null: false
    t.integer :student_id, null: false 
    t.timestamps null: false
  end

  create_table :versions do |t|
    t.string :item_type, null: false
    t.integer :item_id, null: false
    t.string :event, null: false
    t.string :whodunnit
    t.text :object, limit: 1_073_741_823
    t.text :object_changes, limit: 1_073_741_823
    t.integer :transaction_id
    t.datetime :created_at
  end
  add_index :versions, %i[item_type item_id]
  add_index :versions, [:transaction_id]

  create_table :version_associations do |t|
    t.integer  :version_id
    t.string   :foreign_key_name, null: false
    t.integer  :foreign_key_id
    t.string   :foreign_type            # NOTE: bug report template is out of date, this wasn't in there :) 
  end
  add_index :version_associations, [:version_id]
  add_index :version_associations, %i[foreign_key_name foreign_key_id foreign_type],
    name: "index_version_associations_on_foreign_key"
end

ActiveRecord::Base.logger = Logger.new(STDOUT)

require "paper_trail"
require "paper_trail-association_tracking"

PaperTrail.config.track_associations = true

# STEP FOUR: Define your AR models here.
class Individual < ActiveRecord::Base
  has_paper_trail
end

class Teacher < Individual
  has_many :students, class_name: "Studentship"
end

class Professor < Teacher
end

class Student < Individual
end

class Studentship < ActiveRecord::Base
  belongs_to :teacher
  belongs_to :student
  has_paper_trail
end

# STEP FIVE: Please write a test that demonstrates your issue.
class BugTest < ActiveSupport::TestCase
  def test_sti
    teach1 = Professor.create!(name: "Prof. Alice", flag: "v1")   # Flag is used to trigger a new version
    student1 = Student.create!(name: "Mr Student", flag: "v1")
    student2 = Student.create!(name: "Miss Student", flag: "v1")

    teach1.students.create!(student: student1)

    teach1.update flag: "v2"
    teach1.students.destroy_all

    teach1.update flag: "v3"
    teach1.students.create!(student: student2)

    restore_version = ->(idx) { teach1.versions[idx].reify(has_many: true) }

    # v1 - Prof. Alice should have one student - Mr Student
    v1 = restore_version.call(1)
    assert_equal 1, v1.students.size
    assert_equal student1, v1.students.first.student

    # v2 - Prof. Alice should have NO students
    v2 = restore_version.call(2)
    assert_equal 0, v2.students.size

    # v3 - Prof. Alice should have one student - Miss Student
    v3 = teach1
    assert_equal 1, v3.students.size
    assert_equal student2, v3.students.first.student
  end
end

# STEP SIX: Run this script using `ruby my_bug_report.rb`
# Error log:
#   1) Failure:
# BugTest#test_sti [my_bug_report copy.rb:114]:
# --- expected
# +++ actual
# @@ -1 +1 @@
# -#<Student id: 2, name: "Mr Student", flag: "v1", type: "Student", created_at: "2021-01-01 11:10:53", updated_at: "2021-01-01 11:10:53">
# +#<Student id: 3, name: "Miss Student", flag: "v1", type: "Student", created_at: "2021-01-01 11:10:53", updated_at: "2021-01-01 11:10:53">

I have a functional PR that addresses this issue that I will be submitting momentarily, just working on the tests.