Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

ActiveRecord serialize column update error with using update_attributes #8575

Closed
lazing opened this Issue Dec 21, 2012 · 3 comments

Comments

Projects
None yet
3 participants

lazing commented Dec 21, 2012

class TermType < ActiveRecord::Base
  serialize :insures, Insure

  validates_presence_of :name
end

class Insure
  extend ActiveModel::Translation
  INSURES = %W{life annuality  }
  MAX =  (1 << INSURES.size)-1

  def self.load mask
    INSURES.select{|insure| (1 << INSURES.index(insure)) & mask > 0}
  end

  def self.dump insures
    numbers = insures.compact.uniq.collect{|insure|INSURES.index(insure)}.compact
    numbers.inject(0){|sum,n| sum + (1<<n)}
  end

end

  create_table :term_types do |t|
      t.string :name, :null=>false
      t.references :parent
      t.integer :insures, :null=>false, :default=>0
    end

when execute

Loading development environment (Rails 3.2.9)
1.8.7 :001 > TermType.first.update_attributes :insures=>['life']

  TermType Load (0.3ms)  SELECT `term_types`.* FROM `term_types` LIMIT 1
   (0.2ms)  BEGIN
   (0.2ms)  ROLLBACK
NoMethodError: undefined method `to_i' for ["life"]:Array
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/connection_adapters/column.rb:178:in `value_to_integer'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/connection_adapters/column.rb:78:in `type_cast'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/attribute_methods/dirty.rb:86:in `_field_changed?'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/attribute_methods/dirty.rb:63:in `write_attribute'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/attribute_methods/write.rb:14:in `insures='
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/attribute_assignment.rb:85:in `send'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/attribute_assignment.rb:85:in `assign_attributes'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/attribute_assignment.rb:78:in `each'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/attribute_assignment.rb:78:in `assign_attributes'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/persistence.rb:216:in `update_attributes'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/transactions.rb:313:in `with_transaction_returning_status'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/connection_adapters/abstract/database_statements.rb:192:in `transaction'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/transactions.rb:208:in `transaction'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/transactions.rb:311:in `with_transaction_returning_status'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/persistence.rb:215:in `update_attributes'
    from (irb):1

doesn't serialize support integer column? but following function works fine

1.8.7 :003 > t = TermType.first
1.8.7 :007 > t.insures.clear
 => [] 
1.8.7 :008 > t.save
   (0.3ms)  BEGIN
   (0.7ms)  UPDATE `term_types` SET `insures` = 0 WHERE `term_types`.`id` = 1
   (27.2ms)  COMMIT
 => true 

also exception

Loading development environment (Rails 3.2.9)
1.8.7 :001 > t = TermType.first
  TermType Load (0.7ms)  SELECT `term_types`.* FROM `term_types` LIMIT 1
 => #<TermType id: 1, insures: []> 
1.8.7 :002 > t.insures = ['life']
NoMethodError: undefined method `to_i' for ["life"]:Array
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/connection_adapters/column.rb:178:in `value_to_integer'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/connection_adapters/column.rb:78:in `type_cast'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/attribute_methods/dirty.rb:86:in `_field_changed?'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/attribute_methods/dirty.rb:63:in `write_attribute'
    from /home/ryan/.rvm/gems/ruby-1.8.7-p371@ins/gems/activerecord-3.2.9/lib/active_record/attribute_methods/write.rb:14:in `insures='
    from (irb):2

Contributor

ehtb commented Dec 21, 2012

Rails doesn't serialize before save, so you'll have to take care of that yourself.

Changing insures to a string will work:
t.string :insures, :null=>false, default: ''

This will always mark it as dirty though, because of read_attribute eventually calling the load method (before save) on Insure:
https://github.com/rails/rails/blob/3-2-stable/activerecord/lib/active_record/attribute_methods/dirty.rb#L60

This should probably be fixed, but for now convert before save to prevent this.

Owner

rafaelfranca commented Dec 21, 2012

Of course it serializes before save. This is occurring before even Rails think about save. I'll take a look

@rafaelfranca rafaelfranca added a commit to rafaelfranca/omg-rails that referenced this issue Dec 21, 2012

@rafaelfranca rafaelfranca Serialized attribute can be serialized in an integer column
Fix #8575
28a25ac

@rafaelfranca rafaelfranca added a commit that referenced this issue Dec 21, 2012

@rafaelfranca rafaelfranca Serialized attribute can be serialized in an integer column
Fix #8575

Conflicts:
	activerecord/CHANGELOG.md
	activerecord/lib/active_record/attribute_methods/serialization.rb
	activerecord/test/cases/serialized_attribute_test.rb
	activerecord/test/models/person.rb
885f59f

lazing commented Dec 24, 2012

really quick fix! thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment