This project is archived and is in readonly mode.
Dirty tracking on serialized columns is broken
-
Jeremy Kemper
- Milestone set to 2.1.1
- State changed from new to open
- Assigned user set to Jeremy Kemper
-
Ernie Miller
- Tag set to activerecord, bug, dirty
Is this bug valid? I can't reproduce in Rails 2.1, anyway.
class Monkey < ActiveRecord::Base
serialize :legs, Array
end
And in script/console:
Loading development environment (Rails 2.1.0)
>> m = Monkey.create
=> #
>> m.changed?
=> false
>> m.legs = [1,2,3,4]
=> [1, 2, 3, 4]
>> m.changed?
=> true
-
Ernie Miller
Hm. Trying once more since Lighthouse formatting eluded me.
class Monkey < ActiveRecord::Baseserialize :legs, Array
end
and
Loading development environment (Rails 2.1.0)>> m = Monkey.create
=> #
>> m.changed?
=> false
>> m.legs = [1,2,3,4]
=> [1, 2, 3, 4]
>> m.changed?
=> true
-
Ernie Miller
Eh, I give up. Trying Textile but it's not working. You get the idea.
-
Ripta Pasay
I couldn't reproduce this either. I did, however, find that dirty tracking doesn't work with in-place manipulation of said serialized column:
>> m.legs => [1, 2, 3] >> m.changed? => false >> m.legs << 4 => [1, 2, 3, 4] >> m.changed? => falseIMHO, AR should let the object itself figure out whether it's been changed or not. If anything, AR could provide a way for either: (a) the serialized object to notify AR that the attribute has been changed, or (b) AR to optionally query the serialized object when its own #changed? is called.
-
quake wang
I can't reproduce this error in rails 2.1, however, dirty checking is broken is this case:
>> m.reload
>> m.legs = m.legs << 4
>> m.changed?
=> false
-
Jeremy Kemper
- State changed from open to invalid
-
barunio
Why was this ticket changed to 'invalid'? This is still broken as far as I can tell (yes, AR knows if the field went from nil to an array, but it still can't tell if an existing serialized array was modified).
If you have a serialized field named "array_field", then "array_field_was" method doesn't return what array_field was, and "array_field_changed?" doesn't tell you if array_field was changed. To me that looks like clearly broken behavior (and I don't think the docs anywhere specify that one ought to expect different behavior for serialized fields)..
-
Lluís
This still happens on Rails 2.3.5
if serialized changes of type (nil to Array, Array to Hash..) changed? method returns true, but not if serialized changes conserving its type (adding an element to array..) -
Neeraj Singh
This is no longe an issue with rail3.
This is what I found on rails edge.
ActiveRecord::Schema.define(:version => 20100516025941) do create_table "users", :force => true do |t| t.string "name" t.text "bio" end end class User < ActiveRecord::Base serialize :bio end #rails -v : Rails 3.0.0.beta3 #ruby -v : ruby 1.8.7 (2010-01-10 patchlevel 249) [i686-darwin10.3.0] > u = User.create > u.changed? => false > u.bio = 'w' => "w" > u.changed? => true > u.changes => {"bio"=>[nil, "w"]} -
Karl Varga
- Importance changed from to High
I've just run into this now on Rails 2.3.8.
Neeraj, that's not a good test. You want to use a Hash and add a new key, then you'll see what happens.
In this output I have a User model with a prefs serialized Hash:
(rdb:158) current_user.changed? false
(rdb:158) current_user.prefs[:some_new_key] = 42 42
(rdb:158) current_user.changed? false -
Neeraj Singh
In rails 3 this is what I am getting
class User < ActiveRecord::Base serialize :bio def self.lab u = User.create assert !u.changed? u.bio = {:social => :github} assert u.changed? u.reload u.bio = {} u.bio[:social] = :github assert u.changed? end endAll assertions are passing.
-
Karl Varga
@neeraj, if u.bio is nil then assigning a hash to it will show a change because it is no longer nil.
Try it with a user that has bio already set and saved, then assign a key:
u.bio = {}
u.save
u.bio[:social] = :github
assert u.changed? -
Neeraj Singh
- Milestone changed from 2.1.1 to 3.x
- State changed from invalid to open
- Tag changed from activerecord, bug, dirty to rails 3, activerecord, bug, dirty
- Assigned user changed from Jeremy Kemper to Neeraj Singh
- Importance changed from High to Low
@Karl thanks
class User < ActiveRecord::Base serialize :bio def self.lab u = User.create(:bio => {:eye_color => "black"}) assert !u.changed? u.bio = {:social => :github} assert u.changed? # passes u.reload u.bio[:eye_color] = :green assert u.changed? # Fails end endOpening the ticket.
PS: I think the case where it is failing is okay. That is because changed? works by trakcing assigning changed valeu and in this case in-memory value is being changed. Nothing is being assigned.
-
José Valim
- State changed from open to invalid
Yes, this ticket is invalid because we cannot possibly track changes in place. This is mentioned in the documentation. You can either: 1) do not change serialized attributes in place or 2) call attribute_will_change!
-
Neeraj Singh
In case someone wants to see the complete solution. Here it is with the usage of attribute_will_change!
class User < ActiveRecord::Base serialize :bio def self.lab u = User.create(:bio => {:eye_color => "black"}) assert !u.changed? u.bio = {:social => :github} assert u.changed? u.reload u.send(:attribute_will_change!, :bio) u.bio[:eye_color] = :green assert u.changed? end end -
Glenn Vanderburg
José, you said this is mentioned in the documentation. Where?
And here's a more idiomatic example of how to do the right thing with attribute_will_change!:
class User < ActiveRecord::Base serialize :bio def self.lab u = User.create(:bio => {:eye_color => "black"}) assert !u.changed? u.bio = {:social => :github} assert u.changed? u.reload u.bio_will_change! u.bio[:eye_color] = :green assert u.changed? end end