Fix tracking previous changes for ActiveRecord::Store accessors with underlying JSON data column

This commit is contained in:
Robert DiMartino 2023-08-07 22:14:49 -04:00
parent 2942958827
commit eceaa38e06
No known key found for this signature in database
GPG Key ID: 9F8BF8FFCD68883E
5 changed files with 28 additions and 3 deletions

View File

@ -1,3 +1,9 @@
* Fix previous change tracking for `ActiveRecord::Store` when using a column with JSON structured database type
Before, the methods to access the changes made during the last save `#saved_change_to_key?`, `#saved_change_to_key`, and `#key_before_last_save` did not work if the store was defined as a `store_accessor` on a column with a JSON structured database type
*Robert DiMartino*
* Fully support `NULLS [NOT] DISTINCT` for PostgreSQL 15+ indexes. * Fully support `NULLS [NOT] DISTINCT` for PostgreSQL 15+ indexes.
Previous work was done to allow the index to be created in a migration, but it was not Previous work was done to allow the index to be created in a migration, but it was not

View File

@ -163,19 +163,19 @@ module ActiveRecord
define_method("saved_change_to_#{accessor_key}?") do define_method("saved_change_to_#{accessor_key}?") do
return false unless saved_change_to_attribute?(store_attribute) return false unless saved_change_to_attribute?(store_attribute)
prev_store, new_store = saved_change_to_attribute(store_attribute) prev_store, new_store = saved_changes[store_attribute]
prev_store&.dig(key) != new_store&.dig(key) prev_store&.dig(key) != new_store&.dig(key)
end end
define_method("saved_change_to_#{accessor_key}") do define_method("saved_change_to_#{accessor_key}") do
return unless saved_change_to_attribute?(store_attribute) return unless saved_change_to_attribute?(store_attribute)
prev_store, new_store = saved_change_to_attribute(store_attribute) prev_store, new_store = saved_changes[store_attribute]
[prev_store&.dig(key), new_store&.dig(key)] [prev_store&.dig(key), new_store&.dig(key)]
end end
define_method("#{accessor_key}_before_last_save") do define_method("#{accessor_key}_before_last_save") do
return unless saved_change_to_attribute?(store_attribute) return unless saved_change_to_attribute?(store_attribute)
prev_store, _new_store = saved_change_to_attribute(store_attribute) prev_store, _new_store = saved_changes[store_attribute]
prev_store&.dig(key) prev_store&.dig(key)
end end
end end

View File

@ -163,6 +163,23 @@ class StoreTest < ActiveRecord::TestCase
assert_equal "Dallas", @john.partner_name_before_last_save assert_equal "Dallas", @john.partner_name_before_last_save
end end
test "saved changes tracking for accessors with json column" do
if current_adapter?(:Mysql2Adapter, :TrilogyAdapter) && ActiveRecord::Base.connection.mariadb?
skip "MariaDB doesn't support JSON store_accessor"
end
@john.enable_friend_requests = true
assert @john.enable_friend_requests_changed?
@john.save!
assert_not @john.enable_friend_requests_change
assert @john.saved_change_to_enable_friend_requests?
assert_equal [nil, true], @john.saved_change_to_enable_friend_requests
# Make a second change to test key_before_last_save
@john.enable_friend_requests = false
@john.save
assert_equal true, @john.enable_friend_requests_before_last_save
end
test "object initialization with not nullable column" do test "object initialization with not nullable column" do
assert_equal true, @john.remember_login assert_equal true, @john.remember_login
end end

View File

@ -28,6 +28,7 @@ class Admin::User < ActiveRecord::Base
store :preferences, accessors: [ :remember_login ] store :preferences, accessors: [ :remember_login ]
store :json_data, accessors: [ :height, :weight ], coder: Coder.new store :json_data, accessors: [ :height, :weight ], coder: Coder.new
store :json_data_empty, accessors: [ :is_a_good_guy ], coder: Coder.new store :json_data_empty, accessors: [ :is_a_good_guy ], coder: Coder.new
store_accessor :json_options, :enable_friend_requests
def phone_number def phone_number
read_store_attribute(:settings, :phone_number).gsub(/(\d{3})(\d{3})(\d{4})/, '(\1) \2-\3') read_store_attribute(:settings, :phone_number).gsub(/(\d{3})(\d{3})(\d{4})/, '(\1) \2-\3')

View File

@ -38,6 +38,7 @@ ActiveRecord::Schema.define do
t.string :json_data_empty, null: true, default: "", limit: 1024 t.string :json_data_empty, null: true, default: "", limit: 1024
t.text :params t.text :params
t.references :account t.references :account
t.json :json_options
end end
create_table :admin_user_jsons, force: true do |t| create_table :admin_user_jsons, force: true do |t|