Closed drkelly58 closed 8 months ago
Hi @drkelly58 . Please provide a proper code example that I can run myself. Like this it's hard to understand what you mean.
class TestJob < ApplicationRecord
jsonb_accessor :metadata,
attribute1: :integer,
attribute2: :string
def test
self.attribute1 = 100
self.attribute2 = 'just testing'
puts metadata
puts "value of attribute1 via accessor: #{ attribute1 }"
puts "value of attribute1 via square bracket notation: #{ metadata['attribute1'] }"
metadata['attribute1'] = 105
puts 'updated attribute1 value using square bracket notation'
puts "value of attribute1 via accessor: #{ attribute1 }"
puts "value of attribute1 via square bracket notation: #{ metadata['attribute1'] }"
end
end
Sample results from console
job = TestJob.new
3.0.3 :048 > job.test {"attribute1"=>100, "attribute2"=>"just testing"} value of attribute1 via accessor: 100 value of attribute1 via square bracket notation: 100 updated attribute1 value using square bracket notation value of attribute1 via accessor: 100 value of attribute1 via square bracket notation: 105
You are right, there is something fishy. I am on it.
OK so I have looked into this. Unfortunately your use case is not supported.
There are some ways to update a value. Either you replace the whole metadata
(in your case) hash using instance.metadata = { ... }
or you use the generated setters, e.g. instance.attribute1 = 100
or even instance.update(attribute1: 100)
.
The explanation is that this gem relies on https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html. For each key in your config (attribute1
and attribute2
) it creates a Rails attribute. After an object initializes it goes through the raw JSON hash and uses write_attribute
to populate the attributes. The gem also overwrites the method metadata=
. Here again it can go through that hash and use write_attribute
to make sure the Rails attributes are in sync.
But when you do instance.metadata['attribute1'] = 105
you are updating the hash itself and the gem has no way of knowing about this update. So in this case instance.attribute1
(the Rails attribute) gets out of sync.
@haffla Thanks for the explanation, that makes sense
I have a simple class as follows:
class Foo < ApplicationRecord jsonb_accessor :data, field1: :string, field2: [:string, default: 'default for field2'], field3: :string end
if I create a new instance of foo, and set foo.field1, & foo.field2. Values get populated into jsonb and are also present via accessor attributes.
But if i set foo['field1'] or data.update({field1:'new', field2:'value'}). Jsonb gets correct values but accessor attributes still have old values.
Once record is persisted & reloaded values are correct, however prior to a reload values remain out of sync. This presents a real problem with callbacks after_save & after_commit as they get out of sync values.