在没有HTTP重定向的情况下,从另一个控制器操作运行一个控制器操作的正确方法是什么?

我希望能够基于查询参数和数据库中的数据的组合,从一个控制器动作有条件地发送到另一个控制器动作。

我现在拥有的是:

class OldController < ApplicationController def old_controller_action if should_use_new_controller new_params = params.dup new_params[:controller] = "new_controller_action" redirect_to new_params return end # rest of old and busted end end class NewController < ApplicationController def new_controller_action # new hotness end end 

这很好用,但它会发出HTTP重定向,这很慢。 我希望能够做同样的事情,但在同一个HTTP请求中。

有干净的方法吗?

编辑:赏金将转到可以向我展示干净方式的人,使控制器及其操作相对不受影响(除了重定向代码本身)。

而不是跨操作调用代码,将代码提取到lib /或其他东西,并从两个控制器调用该代码。

 # lib/foo.rb module Foo def self.bar # ... end end # posts_controller def index Foo.bar end # things_controller def index Foo.bar end 

创建控制器类的实例:

 @my_other_controller = MyOtherController.new 

然后调用它上面的方法:

 @my_other_controller.some_method(params[:id]) 

我更喜欢模块的想法,但这应该可以解决问题。

我怀疑你想要选项3,但我们首先考虑一些替代方案

选项1 – 将控制器选择逻辑推入帮助程序,将正确的链接插入到视图中。 Benifits – 控制器保持清洁,缺点 – 如果决策逻辑取决于提交的值,这种方法将无效。 如果外部网站正在调用URL,则此操作无效。

选项2 – 将逻辑推回到您的模型中。 Pro’s – 保持控制器清洁。 缺点 – 如果你有很多sesson,params或render / redirect_to交互,那么效果不佳。

选项3 – 保持在同一个控制器内。 我怀疑您正试图用一些新function替换某些现有function,但仅在某些情况下。 Pro’s – 简单,可以访问您需要的一切。 缺点 – 只有在使用相同的控制器时才有效,即您正在使用同一个实体,例如用户,地点或公司。

让我们看一下选项3的一个例子。我的链接控制器对管理员的行为完全不同于其他用户……

 class LinksController < ApplicationController #... def new #Check params and db values to make a choice here admin? ? new_admin : new_user end #... private def new_admin #All of the good stuff - can use params, flash, etc render :action => 'new_admin' end def new_user #All of the good stuff - can use params, flash, etc render :action => 'new_user' end end 

如果两个控制器试图做同样的事情,那么这应该是一个非常好的机会。 仔细看看你的设计 – 对不起,我不知道你对MVC的经验水平 – 阅读薄控制器技术:

http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model http://www.robbyonrails.com/articles/2007/06/19/put-your-controllers-on-a -diet-already http://andrzejonsoftware.blogspot.com/2008/07/mvc-how-to-write-controllers.html

如果问题是你需要另一个控制器进行渲染,那么路径应该指向那里开始,并且仍然瘦的控制器技术应该保存一天。

如果将控制器之间的公共代码提取到模块中对您不起作用,我会使用Rack中间件。 我没有看到在中间件中使用ActiveRecord代码,但我不知道为什么它不可能,因为人们已经使用过Redis之类的东西。

否则我认为你唯一的选择是重新开始处理请求,例如(未经测试,伪示例):

 env['REQUEST_URI'] = new_controller_uri_with_your_params call(env) 

这与集成测试的实现方式类似。 但我不知道从你call到控制器之前的所有事情都是幂等的,可以安全地重新运行。 您可以浏览源并查看。 但即使现在没问题,它也可能会在未来版本的rails或rack中出现问题。

使用中间件可以通过让您在运行之前拦截请求来避免这种情况。 您仍然可以通过将代码提取到两个位置包含的常用模块来与rails应用程序共享代码。

老实说,我认为只是简单地将公共控制器代码分解出来可能更简洁,但是如果没有你的情况细节就很难知道,所以我想我会继续建议。

做这个:

 class OldController < ApplicationController def old_controller_action if should_use_new_controller new_controller_action end # rest of old and busted end end 

和新的控制器

 class NewController < OldController def new_controller_action # new hotness end end