如何让两个控制器共享一个控制器动作?

时间:2015-04-11 21:32:15

标签: ruby-on-rails ruby-on-rails-4 activerecord

在我的Rails 4应用程序中,我有两个控制器具有几乎相同的操作:

class InvoicesController < ApplicationController

  ...

  def load
    @invoice = Invoice.find_by(:download_code => params[:id])
    if @invoice
      respond_to do |format|
      format.pdf { |pdf| render_pdf("attachment") }
    end
  else
    flash[:notice] = "Not found."
    redirect_to signin_path
  end

  ...

end

class RemindersController < ApplicationController

  ...

  def load
    @reminder = Reminder.find_by(:download_code => params[:id])
    if @reminder
      respond_to do |format|
      format.pdf { |pdf| render_pdf("attachment") }
    end
  else
    flash[:notice] = "Not found."
    redirect_to signin_path
  end

  ...

end

为了干掉我的代码,将这两者结合起来的最佳实践方法是什么?

感谢您的任何想法。

4 个答案:

答案 0 :(得分:2)

我首先要让ReminderInvoice扩展共享的超类,或者将模块混合到它们的共享功能中。我更喜欢后者,因为我更喜欢委托和mixins继承。

其次,您应该使用以下方法创建一个混合到控制器中的模块:

def download_helper(your_superclass_instance)
  if @your_superclass_instance
    respond_to do |format|
    format.pdf { |pdf| render_pdf("attachment") }
  end
  #all the rest where you replace @reminder or @invoice with @superclass_instance
end 

将包含download_helper的模块混合到您的控制器中,然后您可以像这样简单地调用它:

def download
  @invoice = Invoice.find_by(:download_code => params[:id])
  download_helper(@invoice)
end

显然对RemindersController也一样。

我很确定您可以做一些聪明的元编程,即使find_by调用download_helper的第一个Invoice部分,也可以简单地传入Reminder或{{1}类本身。但我必须研究如何做到这一点,而且我认为过于过于聪明,并且在这个过程中使事情变得不那么容易接近过度工程。

答案 1 :(得分:1)

将共享代码放在application_controller.rb中或创建一个模块并将其包含在需要的控制器类中。

答案 2 :(得分:1)

当你构建一个重用它的方法时,很容易使它变得多态(在不同的上下文中可以使用而不需要特别改编)。我想首先向ApplicationController添加一个实例方法,这样可以使用rails约定从控制器名称中轻松推断出模型类。 #controller_name是一个有用的rails方法,可以为你完成部分工作。

def model_class controller_name.singularize.constantize end

然后,您的方法可以调用@object = model_class.find_by(:download_code => params[:id]),而不是专门引用它需要搜索的类。之后,我同意上述答案,然后您可以将方法提取到超类或共享模块。

您选择的其中一个可以通过您应用的当前复杂性来指导。如果这是您现在需要的唯一共享方法,只需将其粘贴在ApplicationController中,但如果该类已经忙或者您尝试提取其他共享方法,请将代码移动到新模块并将其混合到两个控制器中。

答案 3 :(得分:1)

要扩展@errata,请将before_action:download放在应用程序controller.rb的顶部