palkan/store_attribute

weird issue with dirty tracking on rails 7

Closed this issue · 5 comments

Tell us about your environment

Ruby Version: 2.7.4

Rails Version: 7.0.0.alpha2

Store Attribute Version: 0.9.1

What did you do?

if i have a stored attribute in the database, change another stored attribute and then call #changes, the already present attribute value gets removed. see example below.

this does not happen without the store_attribute gem.

# frozen_string_literal: true

require "bundler/inline"

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

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

  gem "activesupport", "~> 7.0.0.alpha2", require: false
  gem "activerecord", "~> 7.0.0.alpha2", require: false
  gem "store_attribute", "0.9.1", require: false
  gem "sqlite3"
  gem "debug"
end

require "active_support"
require "active_record"
require "store_attribute"
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 :humen, force: true do |t|
    t.text :meta
  end
end

class Human < ActiveRecord::Base
  store :meta, accessors: [ :social_credit, slave: :boolean ], coder: JSON
  after_initialize { self.social_credit ||= 0.0 }
end

class BugTest < Minitest::Test
  def setup
    meta = { slave: true }
    ActiveRecord::Base.connection.execute "DELETE FROM humen;"
    ActiveRecord::Base.connection.execute "INSERT INTO humen (meta) VALUES ('#{meta.to_json}');"
  end

  def test_without_call_to_changes
    human = Human.first
    assert human.slave

    human.social_credit = 1.0
    human.save!

    assert human.slave
  end

  def test_call_to_changes
    human = Human.first
    assert human.slave
    human.changes

    human.save!
    assert human.reload.slave
  end
end

result:

Run options: -v --seed 28294

# Running:

BugTest#test_call_to_changes = D, [2021-10-07T22:00:17.405408 #64486] DEBUG -- :    (0.1ms)  DELETE FROM humen;
D, [2021-10-07T22:00:17.405693 #64486] DEBUG -- :    (0.1ms)  INSERT INTO humen (meta) VALUES ('{"slave":true}');
D, [2021-10-07T22:00:17.406992 #64486] DEBUG -- :   Human Load (0.1ms)  SELECT "humen".* FROM "humen" ORDER BY "humen"."id" ASC LIMIT ?  [["LIMIT", 1]]
D, [2021-10-07T22:00:17.410243 #64486] DEBUG -- :   TRANSACTION (0.0ms)  begin transaction
D, [2021-10-07T22:00:17.410463 #64486] DEBUG -- :   Human Update (0.1ms)  UPDATE "humen" SET "meta" = ? WHERE "humen"."id" = ?  [["meta", "{\"social_credit\":0.0}"], ["id", 1]]
D, [2021-10-07T22:00:17.410621 #64486] DEBUG -- :   TRANSACTION (0.0ms)  commit transaction
D, [2021-10-07T22:00:17.411141 #64486] DEBUG -- :   Human Load (0.1ms)  SELECT "humen".* FROM "humen" WHERE "humen"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
0.01 s = F
BugTest#test_without_call_to_changes = D, [2021-10-07T22:00:17.414410 #64486] DEBUG -- :    (0.1ms)  DELETE FROM humen;
D, [2021-10-07T22:00:17.414565 #64486] DEBUG -- :    (0.1ms)  INSERT INTO humen (meta) VALUES ('{"slave":true}');
D, [2021-10-07T22:00:17.414959 #64486] DEBUG -- :   Human Load (0.0ms)  SELECT "humen".* FROM "humen" ORDER BY "humen"."id" ASC LIMIT ?  [["LIMIT", 1]]
D, [2021-10-07T22:00:17.415384 #64486] DEBUG -- :   TRANSACTION (0.0ms)  begin transaction
D, [2021-10-07T22:00:17.415595 #64486] DEBUG -- :   Human Update (0.1ms)  UPDATE "humen" SET "meta" = ? WHERE "humen"."id" = ?  [["meta", "{\"slave\":true,\"social_credit\":1.0}"], ["id", 2]]
D, [2021-10-07T22:00:17.415784 #64486] DEBUG -- :   TRANSACTION (0.0ms)  commit transaction
0.00 s = .

Finished in 0.011849s, 168.7906 runs/s, 337.5812 assertions/s.

  1) Failure:
BugTest#test_call_to_changes [store_attribute_changes.rb:59]:
Expected nil to be truthy.

2 runs, 4 assertions, 1 failures, 0 errors, 0 skips

What did you expect to happen?

a call to #changes should not change model state.

What actually happened?

it removes the slave column value.

oh. the test case above works with 0.9.0. so 39a0859 must be the culprit.

nevermind. i'm a confused person.

looks like this only happens with json-coder attributes. was trying to come up with a patch all weekend -.-

looks like this only happens with json-coder attributes

Interesting; I wasn't able to reproduce the bug so far. If you're able to provide a PR with a failing test example, that would be really helpful.

failing test example

see above? 👀

see above?

Oops :) Thanks!

Fixed and released in 0.9.2.