The ELC Community Blog
A knowledge exchange on Ruby on Rails and Agile Development
Don't mix attr_protected and attr_accessible.
by jsiegel on May 12, 2007
The symptoms
While recently working on a plugin-heavy project, I added an attr_accessible assertion to my user model only to find that creating a new user now returned an error page:
You have a nil object when you didn't expect it! You might have expected an instance of Array. The error occurred while evaluating nil.each
The error corresponded to a line equivalent to:
@user.update_attributes params[:user]
This was confounding to me for some time--attr_accessible is used to protect attributes from mass updating and I thought my change was relatively straightforward. I had added to the user model this line:
attr_accessible :first_name, :last_name, :email
This tells ActiveRecord to ONLY ALLOW setting of the first_name, last_name and email parameters through bulk update methods like @user.update_attributes(params) or User.new(params). This is convenient to avoid other fields like @user.is_admin from being set by a malicious user adding an extra form post parameter user[is_admin]=1.
After combing through my usage of attr_accesible in other projects and seeing that the usage looked accurate, I decided to track down the source of the error. I expanded the stack trace and got a pointer to active_record/base.rb line 1671. For future searches of this problem, the line looked like:
/usr/local/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/base.rb:1671:in `attributes='
While digging through the code I realized that ActiveRecord has been built to make attr_protected OR attr_accessible work correctly, but the usage of both will cause an error condition on any future call to ActiveRecord::attributes= (called from update_attributes and new). I returned to my code and started looking for a call to attr_protected that was causing this conflict with my attr_accessible addition.
The Solution
Here is an excerpt of my User model code with all the necessary clues:
class User < ActiveRecord::Base
attr_accessible :first_name, :last_name, :email
acts_as_authorizable
acts_as_authorized_user
belongs_to :account
end
Nowhere in my model code did I see a conflicting attr_protected. To see what else might be causing the problem, I did a grep for attr_protected in my project directory. Sure enough I saw attr_protected appear referenced in acts_as_authorizable and acts_as_authorized_user which are both part of Writertopia's Authorization Plugin. Cracking these open I saw:
module Authorization
module ObjectRolesTable
module UserExtensions
def self.included( recipient )
recipient.extend( ClassMethods )
end
module ClassMethods
def acts_as_authorized_user
has_and_belongs_to_many :roles
attr_protected :role_ids
include Authorization::ObjectRolesTable::UserExtensions::InstanceMethods
#include Authorization::Identity::UserExtensions::InstanceMethods # Provides all kinds of dynamic sugar via method_missing
end
end
...
There it was--the attr_protected call that was causing my issues. I commented it out and the app came back to life.
Now this was clearly a temporary fix, but doing a little web sleuthing uncovered a known Rails ticket #6004 requesting a fix for this issue. It seems that this will likely be resolved within Rails in the coming months, and until then this post will hopefully provide some relief to others who wind up in my position.
Timeline
- Using and testing multiple databases in rails part 2
- RailsConf Europe
- Why associated models don't save
- RailsConf 2007 Highlights
- RailsConf 2007 - Day 1
- Don't mix attr_protected and attr_accessible.
- sortable column headers
- DRY validates_inclusion_of with introspection
- HTTP Auth with Restful Authentication
- TuneCore covered on TUAW!
- TabTerm Release
Comments
Thanks. You definitely helped one developer.
and another. Thank you