【问题标题】:Is it a bad idea to reload routes dynamically in Rails?在 Rails 中动态重新加载路由是不是一个坏主意?
【发布时间】:2008-09-24 20:00:52
【问题描述】:

我正在编写一个应用程序,允许管理员为页面、类别等添加别名,并且我想根据别名使用不同的控制器/操作(无需重定向,并且我发现render实际上并没有调用方法,我只是渲染模板)。我尝试了一条捕获所有路由的方法,但我并不热衷于引发和捕获每次都会抛出的 DoubleRender 异常。

我想出的解决方案是在服务器启动时动态生成路由,并在创建/更新/销毁别名时使用别名模型的回调来重新加载路由。 这是我的 routes.rb 中的代码:

Alias.find(:all).each do |alias_to_add|
    map.connect alias_to_add.name, 
            :controller => alias_to_add.page_type.controller, 
            :action => alias_to_add.page_type.action,
            :navigation_node_id => alias_to_add.navigation_node.id
end

我在 Alias 模型中使用如下回调:

after_save :rebuild_routes
after_destroy :rebuild_routes

def rebuild_routes
    ActionController::Routing::Routes.reload!
end

这是否违反 Rails 最佳实践?有没有更好的解决方案?

【问题讨论】:

  • 你能再澄清一点,也许发布一个你的别名应该做什么的例子(也许有点超出你的 routes.rb 文件)?不太明白

标签: ruby-on-rails ruby


【解决方案1】:

本,

我发现您已经使用的方法是最好的。使用 Rails 3,您必须稍微更改代码:

MyNewApplication::Application.reload_routes!

就是这样。

【讨论】:

  • Rails.application.reload_routes! 同样有效,并且不需要您使用应用程序的名称(对我有用,构建模板应用程序)。
  • 请注意:我相信如果您正在运行多个进程(例如在 Heroku 上),则必须在每个进程上运行它,因此如果您在 after_create 或 after_destroy 挂钩中调用它,其他进程不会重新加载其路由。
【解决方案2】:

快速解决方案

在 routes.rb 的底部有一条包罗万象的路线。在将您路由到的操作中实现您想要的任何别名查找逻辑。

在我的实现中,我有一个将定义的 URL 映射到控制器、操作和参数哈希的表。我只是将它们从数据库中取出,然后调用适当的操作,然后尝试为该操作呈现默认模板。如果该操作已经渲染了某些东西,则会引发一个 DoubleRenderError,我会捕获并忽略它。

您可以将此技术扩展为您想要的复杂程度,尽管随着它变得越来越复杂,通过调整您的路由或 Rails 默认路由逻辑而不是自己重新实现所有路由逻辑来实现它更有意义.

如果您没有找到别名,您可以根据需要抛出 404 或 500 错误。

注意事项:

缓存:事先不知道您的 URL 会使页面缓存成为绝对的负担。请记住,它基于提供的 URI 进行缓存,而不是基于 url_for (:action_you_actually_executed)。这意味着如果您使用别名

/foo_action/bar_method

/some-wonderful-alias

您将在缓存目录中获得 some-wonderful-alias.html。并且当您尝试扫描 foo 的栏时,除非您明确指定它,否则您不会扫描该文件。

容错性:检查以确保有人不会意外地对现有路由产生别名。您可以通过将所有别名强制到一个“目录”中来轻松做到这一点,该“目录”已知是不可路由的(在这种情况下,别名在文本上是唯一的,足以确保它们永远不会发生冲突),但这不是最理想的我能想到的一些应用程序的解决方案。

【讨论】:

    【解决方案3】:

    首先,正如其他人建议的那样,在 routes.rb 的底部创建一条包罗万象的路线:

    map.connect ':name', :controller => 'aliases', :action => 'show'
    

    然后,在 AliasesController 中,您可以使用 render_component 来渲染别名操作:

    class AliasesController < ApplicationController
      def show
        if alias = Alias.find_by_name(params[:name])
          render_component(:controller => alias.page_type.controller, 
                            :action => alias.page_type.action,
                            :navigation_node_id => alias.navigation_node.id)
        else
          render :file => "#{RAILS_ROOT}/public/404.html", :status => :not_found
        end
      end
    end
    

    【讨论】:

    【解决方案4】:

    我不确定我是否完全理解这个问题,但您可以在控制器中使用method_missing,然后查找别名,可能是这样:

    class MyController
      def method_missing(sym, *args)
        aliased = Alias.find_by_action_name(sym)
        # sanity check here in case no alias
    
        self.send( aliased.real_action_name )
        # sanity check here in case the real action calls a different render explicitly
        render :action => aliased.real_action_name
      end
    
      def normal_action
        @thing = Things.find(params[:id])
      end
    end
    

    如果你想优化它,你可以在method_missing 中添加一个define_method,这样它只会在第一次调用时“丢失”,并且从那时起它就是一个正常的方法。

    【讨论】:

      猜你喜欢
      • 2011-01-07
      • 2014-02-27
      • 1970-01-01
      • 2013-02-20
      • 2012-01-21
      • 1970-01-01
      • 2011-02-03
      • 2018-07-08
      • 2019-02-01
      相关资源
      最近更新 更多