1. Skip to navigation
  2. Skip to content

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.

Comments

Paul Cook at 2:51 PM on May 18 2007

Thanks. You definitely helped one developer.

russell Roberts at 1:54 PM on June 14 2008

and another. Thank you

Sheryl Mcneil at 1:15 AM on November 13 2008

dzec8p13bh65q91z

Add a comment


home | services | Ruby on Rails Development | code | blog | company