Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I send a parameter to a before filter?

I'd like to create a before_filter method in my application controller like this...

def check_role(role_name)
  unless logged_in_user.has_role? role_name
    flash[:notice] = 'Access to that area requires additional privileges.'
    redirect_to :back
  end
end

However, it doesn't look as though before filters can take parameters.

Is there a way to parameterize this call, or am I trying to drive a screw with a hammer?

like image 248
Ethan Avatar asked Mar 23 '09 23:03

Ethan


3 Answers

You should be able to do this with a block:

before_filter {|controller| controller.check_role('admin') }
like image 92
Greg Campbell Avatar answered Nov 02 '22 07:11

Greg Campbell


You can use a bit of meta-programming. Something like this (completely untested, just something to give you an idea of how it might go):

Module RoleWithIt
  Role.all.each do |role|
    define_method("check_#{role.name}_role".to_sym) do
      check_role(role.name)
    end
  end

  def check_role(role_name)
    return if logged_in_user.has_role?(role_name)
    flash[:notice] = 'Access to that area requires additional privileges.'
    redirect_to :back
  end
end

ApplicationController.send :include, RoleWithIt

To have it load when your app initialises, just put it in a file called role_with_it.rb and put it in your lib directory.

like image 43
Aupajo Avatar answered Nov 02 '22 05:11

Aupajo


am I trying to drive a screw with a hammer?

Er, possibly ;-)

If I'm reading this correctly, you have a situation where actions within a controller have different access levels, so you want to remove the duplication by creating a single check function?

So you're looking to do something like this?

before_filter :check_role('admin'), :only => [:admin, :debug]
before_filter :check_role('power'), :only => [:edit, :delete]

But the parameter in parens thing is not legal. And anyway, I still see a fair bit of duplication here!

In general, with an area of functionality as well-visited as controller filters, if you can't do something, it's probably because you're looking at something the wrong way. (Remember that Rails is proud to describe itself as "opinionated software"!)

How would it be if you were able to know the action name in your filter method?

Then we'd have

before_filter :check_role

Which is pretty DRY.

We could define permissions in a Hash, perhaps:

Perms = { :admin => ['admin'], :edit => ['admin', 'power'], etc

... which seem to encapsulate the distinct elements of the duplication. If it got complex then the whole thing could move off into a table, although then you're probably duplicating functionality already available in a plugin.

And we'd have

protected
def check_role
  for required_role in Perms[params[:action]]
    return if logged_in_user.has_role? required_role
  end
  flash[:notice] = 'Access to that area requires additional privileges.'
  redirect_to :back
end

Or something similar. params[:action] works on my current Rails version (2.1.2), although the Rails book (v2) mentions an action_name method that seems to return blank for me.

like image 4
Mike Woodhouse Avatar answered Nov 02 '22 05:11

Mike Woodhouse