【问题标题】:Rails call same action code from two controllers (with a small difference)Rails 从两个控制器调用相同的操作代码(略有不同)
【发布时间】:2015-04-17 11:20:41
【问题描述】:

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

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 中的惯用方法是什么?

谢谢, 洛朗

【问题讨论】:

  • 请贴一些代码
  • 一般我们在瘦控制器胖模型中使用rails,所以如果所有与模型相关的代码都在模型中编写通用逻辑并在两个控制器中从那里调用
  • @ratnakar 问题更新了一些代码。我理解瘦控制器/胖模型的事情,但这里我们实际上是在加载“参考数据”(填充搜索过滤器等)

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


【解决方案1】:

我会在引用路由中添加一个中间件,然后调用 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 结构。

【讨论】:

  • 谢谢,我喜欢这种方法。它以非常明确但简洁的方式封装了主页所需的工作。
【解决方案2】:

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

所以在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

【讨论】:

    【解决方案3】:

    您可以在控制器操作中指定要呈现的模板/布局。例如在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 中抽象公共代码的方法有很多。

    【讨论】:

    • 将代码移动到 application_controller 的答案更多 rails way
    • 我不会将这种代码放在应用程序控制器中,特定于两个操作的代码将可用于从应用程序控制器派生的所有控制器是没有意义的。使用模块可以让你更好地对事物进行分类。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多