F6eddf2f983d23c2d031e407852625e9

This is a couple of lines that I was writing a lot

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 !

F6eddf2f983d23c2d031e407852625e9

jamesgolick

September 27, 2007, September 27, 2007 10:41, permalink

1 rating. Login to rate!

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
Bfec5f7d1a4aaafc5a2451be8c42d26a

macournoyer

September 27, 2007, September 27, 2007 10:45, permalink

No rating. Login to rate!

Really good idea James, but what if some actions require admin, other to be the author and stuff like that ?

Bfec5f7d1a4aaafc5a2451be8c42d26a

macournoyer

September 27, 2007, September 27, 2007 11:43, permalink

2 ratings. Login to rate!

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]
F6eddf2f983d23c2d031e407852625e9

jamesgolick

September 27, 2007, September 27, 2007 11:56, permalink

1 rating. Login to rate!

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
F6eddf2f983d23c2d031e407852625e9

jamesgolick

September 27, 2007, September 27, 2007 11:59, permalink

No rating. Login to rate!

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
F6eddf2f983d23c2d031e407852625e9

jamesgolick

September 27, 2007, September 27, 2007 12:06, permalink

No rating. Login to rate!

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
Bfec5f7d1a4aaafc5a2451be8c42d26a

macournoyer

September 27, 2007, September 27, 2007 12:30, permalink

No rating. Login to rate!

I don't think you need the send on define_method if you're doing this in the class. It's a class method.

Your refactoring





Format Copy from initial code

or Cancel