ActiveModel::Dirty and JSON fields
Asked Answered
G

1

7

I am using ActiveModel::Dirty to track changes made in a form. Now everything is working as I expect. With stuff like phone numbers that get dashes in them when in the form I simply format them and then the phone number will not appear in the list of .changed which is expected behavior.

However I run into an issue where I am using a jsonb field in my Profile model. So the issue is ActiveModel will list the JSONB field as being changed even though I format it specifically to match how it looked before. This is not expected behavior. What's stranger still is that another JSONB column I have does not experience this madness.

The JSONB field I'm having trouble with looks like this store_accessor :user_details, :names, :other_field store_accessor :bank_details, :bank_city, :bank_name user_details and bank_details are jsonb columns. Some things to note: names is an array, other_field is a string. bank_city and bank_name are strings.

Can anyone shed some insight into why :user_details specifically is struggling with this issue and not the :bank_details JSON column?

I am suspecting it might be due to me using an array within the :user_details and I suspect that the comparison is being thrown off somewhere in the ActiveModel source code, but maybe I'm wrong?

Edit: I have discovered it is definitely because I am using an array for :names. I changed it to be a string and it stopped thinking that the JSON column had been changed. Going to dig into the ActiveModel source code to see if I can find a reason why.

Edit #2: For some reason I thought I solved the issue by doing nothing but I'm a dummy and realized that I had removed something in the form. So this issue still hasn't been solved for me. Any insight would be amazing. I can't figure out from looking into the ActiveModel::Dirty source code why this is happening. I'm not entirely sure where to look. Going to slap in byebugs to see if that helps.

Edit #3: Steps to repeat this issue

Create a rails Model with a JSONB column. Set store accessors you just need one to do this. Have it default using either a validator or formatter to be an empty array. Give your model the ActiveModel::Dirty include. Run the rails console. run the following commands. Imagine that user_details is the JSONB column and its store accessor is names.

  a = Profile.user_details
  a.user_details = { "names" => [{"first_name" => "", "last_name" => "" }] } # This is to replicate what it would look like in a form when a user is submitting a blank entry.
  a.changed # This will show that user_details has changed which is correct
  a.names = []
  a.changed # This will still show that user_details has changed even though it has been set back to its initial state of an empty array. This would work if it was a string field instead of an array.
Gorgonzola answered 26/11, 2018 at 22:34 Comment(0)
G
3

After opening an issue on the Rails Github I got a response https://github.com/rails/rails/issues/34537#issuecomment-442265161

The change is forced through the attributes for json, jsonb, hstore, and serialized attribute types is what I was told. Source code here: https://github.com/rails/rails/blob/06ab7b27ea1c1ab357085439abacdb464f6742bf/activerecord/lib/active_record/store.rb#L181

The reason I came across this is something I'm no longer trying to attempt as the project I'm working on isn't going to be using ActiveModel::Dirty and isn't going to try tracking the changes the way I was attempting to do it.

So any future generations that come across this issue good luck and feel free to go into that github issue and complain about this not working.

Gorgonzola answered 28/11, 2018 at 16:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.