代码之家  ›  专栏  ›  技术社区  ›  Ben Crouse

在Rails中动态重新加载路由是个坏主意吗?

  •  7
  • Ben Crouse  · 技术社区  · 16 年前

    我正在编写一个应用程序,允许管理员为页面、类别等添加别名,我希望根据别名使用不同的控制器/操作(不重定向,我发现render实际上并不调用该方法)。我只是呈现模板)。我尝试了一个“全捕获”的方法,但我并不热衷于引起和捕获每次抛出的双重引用异常。

    我想到的解决方案是在服务器启动时动态生成路由,并在创建/更新/销毁别名时使用别名模型中的回调重新加载路由。 这是我的路线代码。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
    

    我在别名模型中使用回调,如下所示:

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

    这是否违背了Rails的最佳实践?有更好的解决方案吗?

    4 回复  |  直到 14 年前
        1
  •  5
  •   Patrick McKenzie    16 年前

    快速解决方案

    在routes.rb的底部有一个catch all route。在路由到的操作中实现所需的任何别名查找逻辑。

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

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

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

    记住的东西:

    缓存: 如果事先不知道URL,页面缓存将成为一个绝对负担。记住,它是基于提供的URI缓存的,而不是基于 url_for (:action_you_actually_executed )这意味着如果你

    /foo_action/bar_method
    

    /some-wonderful-alias
    

    您将在缓存目录中找到一些很棒的alias.html。当您试图扫描foo的条时,除非您明确指定,否则不会扫描该文件。

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

        2
  •  11
  •   mjnissim    14 年前

    本,

    我发现你已经用的方法是最好的。使用Rails3,您需要将代码稍微更改一下,以:

    MyNewApplication::Application.reload_routes!
    

    这就是全部。

        3
  •  2
  •   tomafro    16 年前

    首先,如其他人所建议的,在routes.rb的底部创建一个catch all route:

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

    然后,在AliaseController中,可以使用“渲染”组件渲染别名操作:

    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
  •  0
  •   Orion Edwards    16 年前

    我不确定我完全理解这个问题,但你可以用 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
    

    如果你想优化它,你可以 define_method 方法遗失 ,因此它只在第一次调用时“丢失”,从那时起将是一个正常的方法。