Thursday, October 25, 2012

Rails 3.2 and virtual attributes

I was fighting with virtual attributes for a few hours, and I'll share what I did wrong:

Here's a model with one virtual attribute as well as a text field

class User < ActiveRecord::Base
  attr_accessible :sanity_level
  attr_accessor   :sanity_level

  validates_presence_of :sanity_level, :on => :update
end

I would expect the following to work:

user = User.create(:name => "Leif")
user.save(:sanity_level => 5)
user.valid?
# => false # What?
user.errors.inspect
# => Errors show: Sanity Level cannot be blank... but it is 5?
user.update_attributes(:sanity_level => 5)
user.valid?
# => true

I expected both to work, but it doesn't by design. After looking at activerecord and mysql2 source, it is easly spotted that save calls create_or_update without forwarding the hash, so of course it will fail!
To summarize and remember for the future:
  1. Use update_attributes when updating with a hash for the attributes to update
  2. Use save after setting attributes manually
  3. Use create when creating new records
  4. Use build when building new records with a hash and saving later
And a note to myself again: Don't use save when updating attributes.