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

ActiveModel::Dirty is broken in after_commit #51046

Open
kshnurov opened this issue Feb 11, 2024 · 2 comments
Open

ActiveModel::Dirty is broken in after_commit #51046

kshnurov opened this issue Feb 11, 2024 · 2 comments

Comments

@kshnurov
Copy link

kshnurov commented Feb 11, 2024

#45280 broke the expected and documented behaviour by introducing a breaking change in Rails 7.1.
It leads to hard-to-find silent bugs and should be reverted, there's no workaround except disabling it completely with config.active_record.run_commit_callbacks_on_first_saved_instances_in_transaction = true.
The problem it was trying to solve is well-documented and easily dealt with reload.

Steps to reproduce

Oversimplified example:

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem "rails"
  # If you want to test against edge Rails replace the previous line with this:
  # gem "rails", github: "rails/rails", branch: "main"

  gem "sqlite3"
end

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

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :posts, force: true do |t|
    t.integer :comments_count, null: false, default: 0
  end

  create_table :comments, force: true do |t|
    t.boolean :hidden, null: false, default: false
    t.integer :post_id
  end
end

class Post < ActiveRecord::Base
  has_many :comments

  def update_comments_count!
    self.update!(comments_count: comments.size)
  end
end

class Comment < ActiveRecord::Base
  belongs_to :post
  after_commit :update_posts

  def update_posts
    if saved_change_to_post_id?
      post.update_comments_count!
    end
  end
end

class BugTest < Minitest::Test
  def test_association_stuff
    post_1 = Post.create!
    post_1.comments.create!

    assert_equal 1, post_1.comments_count

    post_2 = Post.create!
    comment = post_2.comments.build
    Post.transaction do
      comment.save!
      comment.update!(hidden: true)
    end
    assert_equal 1, post_1.comments_count
    assert_equal 1, post_2.comments_count
  end
end

Expected behavior

saved_change_to_post_id? == true

Actual behavior

saved_change_to_post_id? == false

System configuration

Rails version: 7.1+
Ruby version: 3.2.3

@jreed-mt
Copy link

jreed-mt commented Apr 9, 2024

Is this related to #49898 ?

@palexvs
Copy link

palexvs commented Apr 10, 2024

@jreed-mt no, it's not. The current issue is reproduced on a much simpler configuration with only one model. You only need Rails 7.1 with default configuration + after_commit => changes is empty even there were changes

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

No branches or pull requests

4 participants