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

Ruby on rails 在Rails中动态重新加载路由是个坏主意吗?,ruby-on-rails,ruby,Ruby On Rails,Ruby,我正在编写一个应用程序,允许管理员为页面、类别等添加别名,我希望根据别名使用不同的控制器/操作(无需重定向,我发现render实际上并不调用该方法。我只渲染模板)。我已经尝试了一个全面捕获的方法,但我并不热衷于引发和捕获每次抛出的DoubleRender异常 我提出的解决方案是在服务器启动时动态生成路由,并在创建/更新/销毁别名时使用别名模型的回调来重新加载路由。 以下是my routes.rb中的代码: Alias.find(:all).each do |alias_to_add| m

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

我提出的解决方案是在服务器启动时动态生成路由,并在创建/更新/销毁别名时使用别名模型的回调来重新加载路由。 以下是my 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
map.connect ':name', :controller => 'aliases', :action => 'show'
我在别名模型中使用回调,如下所示:

after_save :rebuild_routes
after_destroy :rebuild_routes

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

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

我不确定我是否完全理解这个问题,但您可以在控制器中使用
方法\u 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
如果您想优化它,可以在
方法\u missing
中添加
define\u方法
,这样它只会在第一次调用时“missing”,从那时起就是一个正常的方法。

快速解决方案 在routes.rb的底部有一个全面覆盖的路由。在将您路由到的操作中实现所需的任何别名查找逻辑

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

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

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

要记住的东西: 缓存:事先不知道您的URL会使页面缓存变得非常困难。记住,它基于提供的URI进行缓存,而不是基于(:action\u you\u实际执行的)的
url\u。这意味着如果您使用别名

/foo_action/bar_method

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


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

首先,正如其他人所建议的,在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
map.connect ':name', :controller => 'aliases', :action => 'show'
然后,在AliaseController中,可以使用render_组件渲染别名动作:

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
类别名控制器alias.page_type.controller,
:action=>alias.page\u type.action,
:navigation\u node\u id=>alias.navigation\u node.id)
其他的
render:file=>“#{RAILS_ROOT}/public/404.html”,:status=>:not_found
结束
结束
结束

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

MyNewApplication::Application.reload_routes!

仅此而已。

您能否进一步澄清一下,或者发布一个示例,说明您的别名应该做些什么(并且可能超出了routes.rb文件)?我不太明白,因为,组件已经被弃用了。我不希望使用可能在未来版本的Rails中消失的东西。
Rails.application.reload\u routes
同样有效,并且不需要您使用应用程序的名称(对我来说很有用,构建一个模板应用程序)。提醒一句:我相信如果您正在运行多个进程(比如在Heroku上),那么您必须在每个进程上运行它,因此如果您在after_create或after_destroy hook中调用它,其他进程将不会重新加载其路由。