Как отправить параметр перед фильтром?

Я хотел бы создать метод before_filter в моем контроллере приложений, как это...

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

Однако, похоже, что фильтры не могут принимать параметры.

Есть ли способ параметризовать этот вызов, или я пытаюсь надеть винт молотком?

Ответ 1

Вы должны иметь возможность сделать это с помощью блока:

before_filter {|controller| controller.check_role('admin') }

Ответ 2

Вы можете использовать немного метапрограмм. Что-то вроде этого (полностью непроверено, просто что-то, чтобы дать вам представление о том, как это может произойти):

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

Чтобы загрузить его, когда ваше приложение инициализируется, просто поместите его в файл с именем role_with_it.rb и поместите его в свой каталог lib.

Ответ 3

Я пытаюсь ввернуть винт с молоток?

Er, возможно; -)

Если я читаю это правильно, у вас есть ситуация, когда действия в контроллере имеют разные уровни доступа, поэтому вы хотите удалить дублирование, создав одну функцию проверки?

Итак, вы хотите сделать что-то вроде этого?

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

Но параметр в игре parens не является законным. И вообще, я все еще вижу справедливое дублирование здесь!

В целом, с областью функциональности, также посещаемой как фильтры контроллера, если вы не можете что-то сделать, это, вероятно, потому, что вы смотрите на что-то не так. (Помните, что Rails с гордостью описывает себя как "самоуверенное программное обеспечение"!)

Как бы это было, если бы вы знали имя действия в вашем методе фильтрации?

Тогда мы имели бы

before_filter :check_role

Что довольно DRY.

Мы могли бы определять разрешения в Hash, возможно:

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

... которые, кажется, инкапсулируют отдельные элементы дублирования. Если бы он стал сложным, все это могло бы перейти в таблицу, хотя тогда вы, вероятно, дублируете функциональные возможности, уже доступные в плагине.

И у нас будет

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

Или что-то подобное. params [: action] работает в моей текущей версии Rails (2.1.2), хотя в книге Rails (v2) упоминается метод action_name, который, кажется, возвращает пустое значение для меня.

Ответ 4

Я не считаю, что вы можете передавать параметры фильтрам. Так что в прошлом я делал статические методы, которые передают параметр методу, который нуждается в параметрах.

Итак, у меня было бы что-то вроде этого:

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

def check_admin_role
   check_role('admin')
end

def check_blah_role
   check_role('blah')
end

Затем в вашем контроллере вы просто вызываете

before_filter :check_admin_role

Возможно, есть какой-то способ реализовать это с помощью метапрограммирования, но я все еще довольно n00b и пока не понял, что эта часть еще отсутствует;)

Ответ 5

Это старый вопрос, но если кто-то по-прежнему удивляется, можно найти здесь в качестве хорошего араба для рельсов