Controller that can only be accessed by an administrator
1 2 3 4 5 6 7 8 9
before_filter :login_required # ... the controller private def authorized? logged_in? && current_user.administrator? end
Refactorings
No refactoring yet !
jamesgolick
September 27, 2007, September 27, 2007 10:41, permalink
So, I added this
application.rb
1 2 3 4 5 6 7
def self.requires_role(role) before_filter :login_required send(:define_method, :authorized?) do logged_in? && current_user.send("#{role}?") end end
controller
1 2
requires_role :administrator
macournoyer
September 27, 2007, September 27, 2007 10:45, permalink
Really good idea James, but what if some actions require admin, other to be the author and stuff like that ?
macournoyer
September 27, 2007, September 27, 2007 11:43, permalink
What about this:
1 2 3
def self.requires_role(role, options={}) before_filter(options) { logged_in? && current_user.send("#{role}?") ? true : access_denied } end
In your controller
1 2
require_role :admin, :only => [:destroy] require_role :author, :except => [:index, :show]
jamesgolick
September 27, 2007, September 27, 2007 11:56, permalink
Problem is, that block seems to get called at the class's scope. The controller is passed to it, but all those methods are protected. Send hack to the rescue!
NoMethodError: undefined method `logged_in?' for ProductsController:Class
1 2 3
def self.requires_role(role, options={}) before_filter(options) { |controller| controller.send(:logged_in?) && controller.send(:current_user).send("#{role}?") ? true : controller.send(:access_denied) } end
jamesgolick
September 27, 2007, September 27, 2007 11:59, permalink
Marc's suggestion. Which one does everybody like better?
1 2 3
def self.requires_role(role, options={}) before_filter(options) { |controller| controller.instance_eval { logged_in? && current_user.send("#{role}?") ? true : controller.access_denied } } end
jamesgolick
September 27, 2007, September 27, 2007 12:06, permalink
As an aside, it's helpful to have this in your user model, if you want current_user.send("#{role}?") to work
user.rb
1 2 3 4 5 6 7
ROLES = %w( administrator staff ) ROLES.each do |current_role| send(:define_method, "#{current_role}?") do self.role == current_role end end
macournoyer
September 27, 2007, September 27, 2007 12:30, permalink
I don't think you need the send on define_method if you're doing this in the class. It's a class method.
This is a couple of lines that I was writing a lot