This project is archived and is in readonly mode.

#360 ✓invalid
Andre Foeken

Dirty tracking on serialized columns is broken

Reported by Andre Foeken | June 7th, 2008 @ 01:48 AM | in 3.x

class Monkey < ActiveRecord::Base
  serialize legs, Array
end

Now in the console:

>> m = Monkey.create
>> m.changed? => false
>> m.legs = [1,2,3]
>> m.changed? => false

Comments and changes to this ticket

  • Jeremy Kemper

    Jeremy Kemper June 11th, 2008 @ 07:59 PM

    • Milestone set to 2.1.1
    • State changed from “new” to “open”
    • Assigned user set to “Jeremy Kemper”
  • Ernie Miller

    Ernie Miller July 3rd, 2008 @ 02:51 PM

    • 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

    Ernie Miller July 3rd, 2008 @ 02:58 PM

    Hm. Trying once more since Lighthouse formatting eluded me.

    class Monkey < ActiveRecord::Base
    

    serialize :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

    Ernie Miller July 3rd, 2008 @ 02:59 PM

    Eh, I give up. Trying Textile but it's not working. You get the idea.

  • Ripta Pasay

    Ripta Pasay July 12th, 2008 @ 09:19 PM

    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?
    => false
    

    IMHO, 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

    quake wang July 13th, 2008 @ 04:04 AM

    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

    Jeremy Kemper July 16th, 2008 @ 12:20 AM

    • State changed from “open” to “invalid”
  • barunio

    barunio March 28th, 2009 @ 12:01 AM

    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

    Lluís May 14th, 2010 @ 01:32 PM

    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

    Neeraj Singh May 16th, 2010 @ 04:05 AM

    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

    Karl Varga July 21st, 2010 @ 01:26 AM

    • 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

    Neeraj Singh July 21st, 2010 @ 01:59 AM

    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
    end
    

    All assertions are passing.

  • Karl Varga

    Karl Varga July 22nd, 2010 @ 09:16 PM

    @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

    Neeraj Singh July 22nd, 2010 @ 11:30 PM

    • 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
    end
    

    Opening 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

    José Valim July 22nd, 2010 @ 11:38 PM

    • 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

    Neeraj Singh July 22nd, 2010 @ 11:46 PM

    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

    Glenn Vanderburg August 11th, 2010 @ 09:55 PM

    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
    

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Referenced by

Pages