Rails - How to declare attr_accessible for multiple roles without duplication
Asked Answered
R

3

14

Is there a way to declare attr_accessible for multiple roles without a ton of duplication?

If I have several user roles, and each role is allowed to edit a different subset of attributes, here's what my attr_accessible declaration looks like:

attr_accessible :first_name, :last_name, :active, :as => :admin
attr_accessible :first_name, :last_name, :as => :manager
attr_accessible :first_name, :last_name, :as => :guest

I'd like to either

  • A) define an array of accessible attributes that can be shared among different roles or
  • B) define an array of roles than can access the same attributes

Is this possible?

Reduction answered 29/7, 2011 at 19:10 Comment(2)
What authorization framework are you using that allows you to restrict attr_accessible on roles? That isn't standard Rails AFAIK.Classis
Oh - I'm using Rails 3.1, so it might be a newer feature. The :as argument shows up in the MassAssignmentSecurity module (github.com/rails/rails/blob/master/activemodel/lib/active_model/…)Reduction
S
4

All ruby code is still just ruby code... and is thus infinitely hackable. eg

ROLES = [:admin, :manager, :support, :user, :guest]
ACTIVE_ROLES = [:admin, :support]
ROLES.each do |role|
   fields = [:first_name, :last_name]
   fields += [:active] if ACTIVE_ROLES.include?(role)
   attr_accessible *fields, :as => role
end
Sheave answered 29/7, 2011 at 20:38 Comment(1)
For Rails 3 apps, see the answer by @AdrianMacneilSulfapyridine
G
44

I just spent a long time trying to figure out the best way to do this. It seemed strange that the rails folk would expect you to duplicate a whole bunch of code!

After some digging around in the rails source, it turns out you can simply pass an array to assign attributes to multiple roles at once (:default being the default Active Record role)

attr_accessible :name, :email, :as => [ :default, :admin ]    
attr_accessible :featured, :as => :admin

No messy ruby arrays in your model!

Glace answered 26/11, 2011 at 11:10 Comment(2)
Just like to say that this is prettier and readable code than the accepted answer.Fealty
Yes it is... but it's also Rails3-specific... and this question was asked some time ago.Sheave
S
4

All ruby code is still just ruby code... and is thus infinitely hackable. eg

ROLES = [:admin, :manager, :support, :user, :guest]
ACTIVE_ROLES = [:admin, :support]
ROLES.each do |role|
   fields = [:first_name, :last_name]
   fields += [:active] if ACTIVE_ROLES.include?(role)
   attr_accessible *fields, :as => role
end
Sheave answered 29/7, 2011 at 20:38 Comment(1)
For Rails 3 apps, see the answer by @AdrianMacneilSulfapyridine
G
3

Did you try something like:

COMMON_FIELDS = [:first_name, :last_name]

attr_accessible COMMON_FIELDS | [:active, :as => :admin]
attr_accessible COMMON_FIELDS | [:as => :manager]
attr_accessible COMMON_FIELDS | [:as => :guest]

Another possible way (untested):

attr_accessible :first_name, :last_name
ADMIN_ACCESSIBLE   = [:active]
MANAGER_ACCESSIBLE = []
GUEST_ACCESSIBLE   = []

protected

def mass_assignment_authorizer
  if role == :all
    self.class.protected_attributes
  else
    super + (eval("#{role}_accessible".upcase) || [])
  end
end
Grating answered 29/7, 2011 at 19:49 Comment(1)
Note, that it works only with Ruby 1.9 – [:as => :admin] will cause syntax errorDottiedottle

© 2022 - 2024 — McMap. All rights reserved.