【问题标题】:Inheriting and routing a custom action from ApplicationController in Rails从 Rails 中的 ApplicationController 继承和路由自定义操作
【发布时间】:2010-10-22 15:19:36
【问题描述】:

我使用的是 Rails 2.3.2,但我确信这也适用于较新的版本。我想在 ApplicationController 中定义一个自定义操作。但是,我不想为每个使用此操作的控制器子类添加自定义路由。是否有捷径可寻?

我的第一个倾向是直接路由到 ApplicationController,因为该方法不需要被任何子类覆盖。但我认为 Rails 不再允许您路由到 ApplicationController。

其他人提出了这样的建议:

map.connect ":controller/:action", :controller => my_regex, :action => my_regex

但我想知道这是否有可能与其他路线发生冲突或覆盖?或者如果通常有更好的方法?谢谢!

【问题讨论】:

    标签: ruby-on-rails


    【解决方案1】:

    我认为这不是修改ApplicationController 的情况,而是猴子修补ActionDispatch::Routing 中的代码以包含您想要的新操作。在事情的计划中,这似乎是一件非常疯狂的事情,因为没有标准的方法来增加或扩展通常的 REST 操作。我希望你有充分的理由这样做。

    在查看代码时,您可以看到默认操作的定义位置,并且您可以引入一个新操作。 Rails 3 的结构略有不同,但思路是一样的:

        class ActionDispatch::Routing::Mapper::Resources::Resource
          ENHANCED_DEFAULT_ACTIONS = DEFAULT_ACTIONS + [ :myaction ]
    
          def self.default_actions
            ENHANCED_DEFAULT_ACTIONS
          end
        end
    

    您还必须修改 ActionDispatch::Routing::Mapper::Resources#resources 以使其行为不同,但您没有指定您是在谈论集合、新还是成员类型的操作,因此您只需复制和修改例行公事,随心所欲。

    【讨论】:

    • 我这样做的原因是因为我希望我的 application.js 能够吐出 Rails 表单标签。我的应用程序在 AJAX 和 js 中很重,我想要集中的方法来给我 Rails 表单、输入等。基本上,我正在尝试创建一个 JavaScript FormBuilder。因此,在我的 application.js 中,我将对我的自定义操作进行 AJAX 调用,该操作将发送一个 js.erb 文件。该文件将有一个 FormBuilder“类”,其中包含为我提供由 rails 表单构建器标签生成的 html 的方法。所以当我的 application.js 调用 deleteWithAjax() 时,我可以生成一个带有 Rails 标签的 Rails 表单。
    • 这听起来是一个非常有趣的想法,并且可以很好地工作,但问题是为什么它需要与标准 REST 操作有如此大的差异。您真正需要的是为将正确处理表单的方法定义 JavaScript 响应程序。如果没有找到模板,你可以通过拦截和自动处理 .js 响应的生成来更容易地做到这一点,而不是引入新的操作。
    • 这是一个有趣的想法。你能不能给我举个例子?到目前为止,在所有情况下,我对 ajax 调用的响应都是 js.erb 模板或 json 对象。我希望返回的 js.erb 模板和 json 响应的处理程序都可以访问我的 js 表单构建器。因此,找不到模板的情况可能不够用。也许另一种选择是使 js_form_builder 成为仅响应 get 的资源,并且我的 application.js 文件可以调用该控制器以加载表单构建器。
    • 为其创建资源效果很好。现在我必须解决路由到资源的正确方法的问题,如果我想传入资源名称和 id 供 javascript 表单构建器使用:)
    【解决方案2】:

    如果您将此控制器声明为诸如map.resource 之类的资源,那么您将不得不使用默认操作或通过向该资源添加membercollection 来创建自己的操作。

     map.resources :post, :member => {:update_live_boolean => :post }, :collection => {:get_all_live_posts => :get}
    

    否则,如果您使用旧的路由格式并且不使用 REST

      map.connect ':controller/:action/:id'
      map.connect ':controller/:action/:id.:format'
    

    那么链接到自定义控制器所需要做的就是在需要时提供:controller' and:action/:id` 变量

    <%= link_to "New Custom Controller", {:controller => "new_custom_controller", :action => "index"%>
    

    【讨论】:

      【解决方案3】:

      这是您帖子中与我有关的部分:

      我不想添加自定义路线 每一个控制器子类 使用此操作。

      rails 社区越来越谨慎地避免过度定义不必要的路线。看到这样的事情变得越来越普遍:

      map.resources :comments, :only => [:new, :create]
      

      在上面的例子中,只生成了newcreate 路由。这是更好的安全性和更清洁的路由。虽然我没有直接回答您关于如何为每个资源提供新路线的问题,但我是说 Rails 最佳实践会阻止它。仅将自定义路由添加到实际使用它的资源。

      【讨论】:

      • 我理解您的担忧,但如果我希望每个控制器都使用此自定义操作,那么为每个控制器显式路由它似乎是多余的,不是吗?
      • 老实说,我不这么认为。因为我想不出每个资源都需要它的单个现实世界应用程序。此外,应用程序中的路由数量有限,通常只有几十个。相信我,我真的理解你希望保持代码干燥的愿望——我也是这样。除非我对此有强烈的感觉,否则我不会回答“不回答”。我确实理解提出问题并让人们说“不要这样做”的烦恼:)
      【解决方案4】:

      最后我决定将我的 js_form_builder 视为它自己的资源,所以我有一个控制器来交付它。控制器采用可选的 resource_name 和 id 参数。如果提供,我可以像这样实例化一个对象:

      @object = params[:resource_name].classify.constantize.find(params[:id]) 
      

      然后我只需发送一个 js.erb 模板,其中包含我所有的 js_form_builder 优点。如果 @object 已被实例化,那么我可以创建一个 form_for @object,然后遍历其属性并在 javascript 表单构建器对象上创建方法,该方法将为每个属性返回输入,使用 Rails FormBuilder 标签创建它们。

      例如:

      window.FormBuilder = function() {
        var builder = {};
      
        builder.form = function() {
          var js = "";
          js += '<%= form_for @object do |f| %>';
          <% @object.attributes.each do |name, val| %>  
            var methodName = '<%= name.camelize(:lower) %>';
            <% if val.class == String %>
              builder[methodName] = $('<%= f.text_field name.to_sym  %>');
            <% end %>
            <% if val.class == TrueClass || val.class == FalseClass %>  
              builder[methodName] = $('<%= f.check_box name.to_sym %> <%= f.label name.to_sym %>');
            <% end %>    
          <% end %>
          js += '<% end %>';
          return $(js);
        };  
      
        builder.newForm = function() {
          var js = "";
          js += '<%= form_for @object.class.new do |f| %>';
          js += '<% end %>';  
        }
      
        return builder;
      }
      

      我目前不确定这些输入会有多大用处,因为我想不出我不会只为这些输入使用 html.erb 的场景。但让它发挥作用肯定很有趣! :)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-12-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-22
        • 2012-06-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多