带参数的before_filter

85

我有一个方法,类似于这样:

before_filter :authenticate_rights, :only => [:show]

def authenticate_rights
  project = Project.find(params[:id])
  redirect_to signin_path unless project.hidden
end

我还想在一些其他控制器中使用这种方法,所以我将该方法复制到一个辅助程序中,并将其包含在application_controller中。

问题是,在某些控制器中,项目的id不是:id符号,而是例如:project_id(同时也存在另一个模型的:id

你如何解决这个问题?是否有选项可以向before_filter操作添加参数(以传递正确的参数)?

5个回答

85

我会这样做:

before_filter { |c| c.authenticate_rights correct_id_here }

def authenticate_rights(project_id)
  project = Project.find(project_id)
  redirect_to signin_path unless project.hidden
end

其中correct_id_here是访问一个Project的相关id。


2
有没有办法添加 ,:only => [:show] 符号?我尝试使用 before_filter { |c| c.authenticate_rights correct_id_here }, :only => [:show] 时出现错误。 - choise
29
尝试另一种方法:before_filter(:only => [:show]) { <block_code_here> }。这里有更多的例子:http://apidock.com/rails/ActionController/Filters/ClassMethods/before_filter - fguillen
1
如果您通过将before_filter设置为私有方法来保护它,那么可能需要进行重构(例如将其移动到父控制器ApplicationController等),您需要使用c.send(:filter_name, ...),因为过滤器不会在控制器上下文中运行。http://guides.rubyonrails.org/action_controller_overview.html#other-ways-to-use-filters - Richard Michael
2
这样做可以防止 before_filter 被覆盖或跳过,因为它没有名称(proc)。我想传递的值是类级别的。这可能吗? - oreoshake
1
通过使用 send 传递块的事实意味着最好像 @vadym-tyemirov 的答案建议的那样使用 lambda。 - David Pelaez
显示剩余2条评论

67

用一些语法糖:

before_filter -> { find_campaign params[:id] }, only: [:show, :edit, :update, :destroy]

或者,如果您决定变得更加花哨:

before_filter ->(param=params[:id]) { find_campaign param }, only: %i|show edit update destroy|

自从Rails 4引入了before_action,它是before_filter的同义词,因此可以这样编写:

而自 Rails 4 开始,before_action被引入作为before_filter的同义词,所以可以按照以下方式书写:

before_action ->(param=params[:id]) { find_campaign param }, only: %i|show edit update destroy|

NB

-> 代表 lambda,被称为 lambda 字面量,自 Ruby 1.9 开始引入

%i 将创建一个符号数组


5
这个答案更加优雅,因为lambda函数默认使用类的执行环境,因此可以在不使用.send的情况下调用私有方法。 - David Pelaez
@Vadym Tyemirov,“find_campaign”是私有方法名称吗?在“param=params[:id]”中,“param”是将作为参数传递给“find_campaign”的新局部变量的名称吗?这意味着,在“find_campaign”私有方法内部,我们使用“param”,而不是“params{:id]”吗? - ahnbizcad
1
find_campaign 可以是公共的,但为了确保不暴露未被使用的内容,我会将其设置为私有。params 是我们方法中可用的哈希变量,param 是您需要传递给 find_campaign 方法的任何变量,例如 before_action ->(campaign_id=params[:id]) { find_campaign(campaign_id) }, only: %i| show edit update destroy | - Vadym Tyemirov

14

继续@alex的回答,如果你想:except:only一些方法,以下是语法:

before_filter :only => [:edit, :update, :destroy] do |c| c.authenticate_rights params[:id] end 

这里找到了相关内容


6
我认为使用花括号而不是do...end块的方法是最清晰的选项。
before_action(only: [:show]) { authenticate_rights(id) }

before_actionbefore_filter 的新语法,更被推荐使用。


-1

这应该可以工作:

project = Project.find(params[:project_id] || params[:id])

如果在params哈希中存在,应该返回params[:project_id],否则返回params[:id]


问题是,有时两者都存在(嵌套),并且它会找到错误的项目。 - choise
@选择:这不应该发生:如果project_id存在,则or子句将确保使用此值-仅当未提供project_id时才会选择id参数。换句话说:当两个参数都提供时,or子句确保使用正确的值,因为它总是优先选择project_id。自然地,您不希望在两个参数都不存在或没有project_id但存在不引用项目的id时调用此方法。 - Ola Tuvesson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接