【问题标题】:Determine if action is on member or on collection确定操作是针对成员还是针对集合
【发布时间】:2013-05-27 13:22:54
【问题描述】:

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

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

resources :projects

您会在“集合”上定义以下方法:

  • 索引
  • 创建

以下是关于“成员”的定义:

  • 更新
  • 显示
  • 删除

我正在尝试找到一种方法来在视图中利用这种出色的模式,例如:

<% 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]

这很烦人,我的想法是所有这些操作都有一个共同的行为:它们是在 collections 中定义的。

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

注意:我问这个问题的原因是因为我正在寻找在开发方面可扩展的东西,例如 before_filters 或某些部分的行为会自动被新的自定义操作继承。

谁从来不用写下类似的东西

<% 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 %>

【问题讨论】:

    标签: ruby-on-rails ruby controller


    【解决方案1】:

    在发布此问题将近一年后,我知道已经为这个问题找到了合理的解决方案。

    如果我们看看on =&gt; :memberon =&gt; :collection 的真正作用,区别在于on =&gt; :member 生成的路径包含:id 参数,而on =&gt; :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 %>
    

    【讨论】:

      【解决方案2】:

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

      然而,在实践中,在 Rails 中,您通常使用您已经确定的那些:

      • 集合
        • 创建条目(POST
        • 查看条目 (GET)
      • 条目
        • 更新条目(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 %>
      

      我自己不会将其提取为常量,而是直接编写数组,因为我认为稍后回来阅读时会更清楚。此外,大多数人每次遇到这些远非最佳的方法时都必须三思而后行。

      【讨论】:

      • [问题更新] 感谢您的回答。我实际上是在寻找一个可扩展的解决方案,这样当我添加一个新动作时,我的视图和我的控制器不必在 befor_filters 和向用户显示的动作方面进行太多更改。
      • 常量解决方案不是很好吗?尽管我仍然会说,如果您的控制器具有这么多想要执行的操作,那是一种代码味道。在这种情况下,将不同控制器中的相同数组传递给before_filter 不会违反 DRY,而是会创建可读代码。
      • 它会解决问题的(简单的)部分。另一部分是我为每个控制器创建的自定义操作
      • 我真的不确定你想要什么。您可以尝试更新您的问题吗?看起来您与 Rails 的战斗不止一个角度,但我不确定您到底在战斗什么。
      • 问题已更新。我只想能够以与您在 routes.rb 中添加自定义路由的方式一致的方式说“这适用于集合上定义的所有操作”:get "archive_all", :on => :collection
      【解决方案3】:

      您遇到了一个非常常见的问题,该问题已经在 Rails 中为我们解决了,而且几乎完全颠倒了。您应该tell您的视图和控制器来做它需要做的事情,但它知道如何做,而不是询问控制器它在整个应用程序中的状态。

      既然您已经确定 indexcreatenew 操作适用于“集合”,而 updateshowdelete 适用于“成员”,您应该定义每个相应的控制器操作和视图都可以使用这些形式的资源。

      回到您给出的示例,链接应该在部分中定义,并让适当的视图模板呈现部分。

      你的before_filter应该写成before_filter :find_project, :only =&gt; [:update, :show, :delete]

      一想到在您的应用程序中到处都看到对@controller.action.applies_on_members? 的调用,我就想到了一个标志,即您需要重新考虑您的设计。同样,您正在尝试解决 Rails 已经为您解决的问题。

      【讨论】:

      • 谢谢。我知道 before_filters 的常见用法,就像你描述的那样。我正在为我的问题寻找可扩展的解决方案。我不确定 Rails 是如何解决这个问题的
      • 预见到需要在控制器中添加如此多的操作,您已经打败了“可扩展性”。特别是因为它显然已经到了需要“扩展”这个在控制器上添加越来越多动作的问题的地步。你只是在深入挖掘自己。
      猜你喜欢
      • 2010-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-07
      • 1970-01-01
      • 1970-01-01
      • 2013-08-21
      • 2016-08-26
      相关资源
      最近更新 更多