-
Notifications
You must be signed in to change notification settings - Fork 35
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
Dup'ing models doesn't copy attributes #96
Comments
Hmm. So the dup'd model shares a reference to some same object internally. Would you mind checking to see if this behavior is the same without attr_json, but with a straight :json or :jsonb column? attr_json is intending not to keep state anywhere but that actual original json/jsonb hash in the model. So I wonder if the problem is there with straight ActiveRecord and a json/jsonb column too. If it is, there isn't much attr_json can/will do about it, and I'd try filing it as a 'bug' with AR. If the problem isn't there like that though, there might be something we can do about it. |
I checked the behavior of |
Thanks for checking. That's too bad. I'll try to find time to diagnose and see if I can find a fix. If anyone else wants to do more investigation toward that too, it would be appreciated! |
To investigate and make sure I understand the baseline of how standard ActiveRecord handles standard json or jsonb-typed hashes, I created a model in a blank Rails app with:
Executed my migrations. Then, here's what I see:
You see that the hash stored in However, that does not seem to be the same with
So I guess that is different if attr_json actually behaves differnetly with dup -- but the unexpected behavior is only with I am going to change the title of this issue to "dup'ing models doesn't copy attributes", because I don't think we expect cloning too, based on standard Rails behavior. OK, I still need to investigate more with behavior of attr_json under dup then. |
Oh, I just noticed that you are specifically talking about an AttrJson::Model, not an actual ActiveRecord. I had misunderstood that. I am going to edit the title to "Dup'ing AttrJson::Models doesn't copy attributes", to be clear that we're talking about dup'ing -- I don't think cloning should be expected to -- and that we're talking about AttrJson::Model, not ActiveRecord models using AttrJson::Record. I am not totally sure if this is a bug or feature request, I am not sure what we should expect Can you say more about your use case, what you are doing and why you expect this? Here is an easy workaround to let you make the kind of no-shared-state dup you want: foo = TestModel.new(test: 'foo')
bar = TestModel.new(foo.as_json)
bar.test = 'bar'
foo.test #=> 'foo' I suppose I could implement You could always do it yourself if you want, something like: class TestModel
include AttrJson::Model
attr_json :test, :string
def dup
self.class.new(self.as_json)
end
end |
OK, did more research and thought. Dup and clone are confusing in ruby. Because AttrJson::Model stores everything in an underlying single hash, and that single hash isn't really "deep" dup'd, it ends up sharing state, when it doesn't seem like it "should", because "simple" attributes, while they are shared, will wind up "un-shared" when you set a new thing with an assignment. This is confusing to talk about. But okay, this might be the right thing to do to fix, attr_json should be changed so AttrJson::Model has a method like this def initialize_copy(other)
@attributes = other.attributes.deep_dup
super
end (Or maybe that should be Have to think about it a bit more, but that would probably take care of it. Not sure if it's dangerous in some way, perhaps performance-related, though. Want to look more at what Rails does in various places. Still curious to hear about your use case and how you ran into this. Longer-term, AttrJson::Model should probably converge more with |
My use case is some kind of inheritance where the inherited model apply some modifiers to its parent, so the logic dup (or clone) the parent and then apply the modifiers, in this use case the parent also changes while it shouldn't. I'm currently patching it using your suggestion in #96 (comment). |
It's been a while, but I have #dup in #169 That also makes #deep_dup happen properly, as it's by default just a synonym for #dup. Both will do a deep dup now. #clone right now does NOT copy attributes, both instances end up with references to the same attributes. But that seems to be consistent with what ActiveModel::Model/ActiveModel::Attributes does. So that seems like it should remain as it is. |
Same behavior with
clone
anddeep_dup
.The text was updated successfully, but these errors were encountered: