Rails从两个控制器调用相同的动作代码(差别很小)

时间:2015-04-17 11:20:41

标签: ruby-on-rails ruby ruby-on-rails-3 url-routing

我有两个不同的路线需要输出相同的页面,只有很小的差异(标题,开放的图形标签等)

的routes.rb

match 'referral/:ref' => 'referral#home'
root :to => "home#index"

home_controller.rb

class HomeController < ApplicationController

  def index
    @passion    = Passion.new
    @workshop   = Workshop.new
    @regions    = Region.where("workshops_count > 0").order("name ASC")
    @categories = Category.where("passions_count > 0 AND parent_id IS NULL").order("name ASC")
  end
end

我不愿意将完全相同的代码从HomeController复制到ReferralController,我不想重定向,因为标题和OG标记必须不同(为了在页面时有一个特殊的引用标题是共享的)

使用express.js应用程序(我更熟悉),我会在引用路由中添加一个中间件,然后调用HomeController #index动作。所以一切都将在路由级别完成。

Rails中的惯用方法是什么?

谢谢, 劳伦

3 个答案:

答案 0 :(得分:2)

  

我会在引荐路由中添加一个中间件,然后调用HomeController #index动作。所以一切都将在路由级别完成。

不要陷入为了聪明而牺牲清晰度的陷阱。对于稍后进入您项目的人(包括您自己)而言,这可能是非常不直观和令人沮丧的。这种方法也会受到一些任意决定的影响,比如哪个控制器动作被路由到哪个?

最好的代码是最简单的解决方案,仍然清楚地将您的意图传达给读者。出于这个原因,我更喜欢尽可能使用普通的旧Ruby对象。您可以将逻辑提取到一个简单的查询对象中,例如:

# app/queries/home_query.rb
class HomeQuery
  attr_reader :passion, :workshop, :regions, :categories

  def initialize
    @passion    = Passion.new
    @workshop   = Workshop.new
    @regions    = Region.where("workshops_count > 0").order("name ASC")
    @categories = Category.where("passions_count > 0 AND parent_id IS NULL").order("name ASC")
  end
end

然后使用它来传递控制器操作中的值:

# app/controllers/home_controller.rb
def index
  @stuff = HomeQuery.new
end

在您看来,您可以使用@stuff.passion@stuff.workshop等来访问您的内容。

它是DRY,它的意图很明确,它使用大多数人都熟悉的常见Ruby结构。

答案 1 :(得分:1)

您可以指定要在控制器操作中呈现的模板/布局。例如,在referral#home控制器操作中:

render :template => 'home/index'

这假设控制器操作中没有很多逻辑(这是Rails中的最佳实践)。

如果每个操作都有通用设置代码,那么您可以将其提取到模块中并将其包含在两个控制器中:

 module CommonFunctionality
   def set_ivars
     @passion    = Passion.new
     @workshop   = Workshop.new
     @regions    = Region.where("workshops_count > 0").order("name ASC")
     @categories = Category.where("passions_count > 0 AND parent_id IS NULL").order("name ASC")      
   end
 end

 class HomeController
   include CommonFunctionality
   def index
     set_ivars
   end
 end

 class ReferralController
   include CommonFunctionality
   def index
     set_ivars
     render :template => 'home/index'
   end
 end

这不是唯一可行的解​​决方案,有许多方法可以在Ruby中抽象出公共代码。

答案 2 :(得分:1)

您可以创建一个帮助器方法,该方法执行所有常见操作步骤,然后从每个控制器操作调用该方法以将其干掉。

所以在application_controller.rb

def setfoo
  @foo = "setting foo"
end

然后在你的控制器动作中:

def oneaction
  setfoo
end

def anotheraction
  setfoo
end

相信这篇优秀的帖子Best Practices for reusing code between controllers in Ruby on Rails