F6eddf2f983d23c2d031e407852625e9

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

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

def self.requires_role(role)
  before_filter :login_required
  send(:define_method, :authorized?) do
    logged_in? && current_user.send("#{role}?")
  end
end
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:

def self.requires_role(role, options={})
  before_filter(options) { logged_in? && current_user.send("#{role}?") ? true : access_denied }
end
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

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?

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

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