确定操作是在成员上还是在集合上

时间:2013-05-27 13:22:55

标签: ruby-on-rails ruby controller

我一直试图找到一种干净的方法来确定一个动作(在一个rails控制器中)适用于成员或集合。

例如,当你在routes.rb中声明

resources :projects

您可以在“集合”中定义以下方法:

  • 索引
  • 创建

以及“on members”上定义了以下内容:

  • 更新
  • 显示
  • 删除

我试图找到一种方法来在视图中利用这个伟大的模式,例如:

<% if @controller.action.applies_on_members? %>
  <%= link_to :destroy, :method => "delete", :confirm => "RU Sure" %>
  <%= link_to :show, "show" %>
<% end %>

同样,这在before_filters

中也很有用
before_filter :find_project, :only_on_members => true

目前我必须执行以下操作:

before_filter :find_project, :except => [:new, :create, :index, :export_all, :destroy_all, :archive_all]

这非常烦人,我的想法是所有这些操作都有共同的行为:它们是在集合上定义的。

有没有人知道如何以干净的方式实现这一目标?

注意:我问这个问题的原因是因为我正在寻找可扩展的开发方面的内容,以便新的自定义操作会自动继承before_filters或某些partials的行为。

谁从来没有写下像

这样的东西
<% unless ["new", "index", "some_other_action1", "some_other_action2", "some_other_action3", "some_other_action4"].include? @controller.action_name %>
  <%= link_to "Destroy", :project, :method => :delete %>  
<% end %>

3 个答案:

答案 0 :(得分:2)

发布这个问题差不多一年后,我知道找到了一个合理的解决方案。

如果我们查看on => :memberon => :collection真正做了什么,差异基本上是on => :member生成包含:id参数的路径,而on => :collection }版本没有。

然后,一个简单的解决方案将在自定义方法中测试params[:id]是否为空。它看起来像这样(比如application_controller.rb):

class ApplicationController < ActionController::Base

  def on_member?
    !params[:id].blank?
  end

  helper_method :on_member? #method will be accessible in views
end

this post中所述,我们可以在before_filter中执行类似的操作:

before_filter :find_project, :if => :on_member?

在视图中,这允许我检查类似的方法类型:

<% if on_member? %>
  <%= link_to :destroy, :method => "delete", :confirm => "RU Sure" %>
  <%= link_to :show, "show" %>
<% end %>

答案 1 :(得分:1)

首先,对集合及其条目进行操作的模式是REST的一部分,它是Rails的支柱之一。您确定的内容基本上是REST称为集合条目。理论上,您可以对集合或该集合中的条目应用the most common HTTB verbs,其中: GET ;检索, PUT ;替换, POST ;创建和删除;破坏。虽然有点不清楚POST会对一个条目做什么。

然而,在实践中,在Rails中,您通常会使用您已识别的那些:

  • 集合
    • 创建条目( POST
    • 查看条目(获取
  • 条目
    • 更新条目( PUT
    • 显示条目( GET
    • 删除条目( DELETE

这些是Rails添加的RESTful路由。但是,如您所知,Rails还会选择其他两个操作来实现可以为这些端点提供必要数据的用户页面。 (修改),

我很少看到人们接受这些定义。如我真的想解决这个问题,至少有两种方法可以解决这个问题(见帖子的底部):

你可以通过猴子补丁"_normalize_callback_options"来获得你提出的确切语法。这是相对简单的,但如果上游改变了结构,你必须改变你的补丁,因此它不是很可持续。因此,无论合成糖的味道多么有品味,我都不会推荐这种方法。当然你也可以尝试在上游提交,但由于下面提到的原因,它不太可能被接受。 : - )

相反,我会将您的定义放在初始值设定项中:config/initializer/rest.rb

ENTRY_ACTIONS      = [:edit, :new, :show, :delete, :update]
COLLECTION_ACTIONS = [:create, :index]

然后在过滤器中使用它们,如:

before_filter :find_project, only: ENTRY_ACTIONS

要在您的视图中进行访问,请向您的应用程序助手添加entry_action?collection_action?方法:

module ApplicationHelper
  def entry_action?
    ENTRY_ACTIONS.include?(controller.action_name.to_sym)
  end

  def collection_action?
    COLLECTION_ACTIONS.include?(controller.action_name.to_sym)
  end
end

然后在你的观点中:

<% if entry_action? %>
   # code
<% end %>

我不会自己将它提取到常量,但只是直接编写数组,因为在我看来,当我稍后回来时,它会更加清晰。此外,大多数人每次遇到这些远非最佳的方法时都要三思而后行。

答案 2 :(得分:0)

你正在解决一个已经在Rails中为我们解决的非常普遍的问题,而且几乎把它颠倒了。您应该tell您的视图和控制器执行它需要做的事情,而不是询问控制器在您的应用程序中的状态。但是它知道如何执行它。

由于您已经确定indexcreatenew操作适用于“集合”以及updateshow和{{ 1}}适用于“成员”,您应该定义每个控制器的操作和视图以使用这些形式的资源。

回到你给出的例子,链接应该在局部定义,并使适当的视图模板呈现部分。

您的delete应写为before_filter

只是想到在你的应用程序中看到对before_filter :find_project, :only => [:update, :show, :delete]的调用,我想起了一个标志,你需要重新考虑你的设计。再一次,你正试图解决Rails已经为你解决的问题。