Rails 从两个控制器调用相同的操作代码(略有不同)
Rails call same action code from two controllers (with a small difference)
我有两个不同的路由需要输出相同的页面,只有细微的差别(标题、开放图标签等)
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 中的惯用方法是什么?
谢谢,
洛朗
您可以指定 template/layout 在控制器操作中呈现什么。例如在 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.rb
:
def setfoo
@foo = "setting foo"
end
然后在您的控制器操作中:
def oneaction
setfoo
end
def anotheraction
setfoo
end
归功于此优秀postBest Practices for reusing code between controllers in Ruby on Rails
I would add a middleware in the referral route and then call the HomeController#index action. So everything would be done at the routing level.
不要陷入为了聪明而牺牲清晰度的陷阱。对于后来进入您的项目的人(包括您自己)来说,这可能是非常不直观和令人沮丧的。这种方法也受到一些任意决定的影响,比如哪个控制器动作被路由到哪个?
最好的代码是最简单的解决方案,它仍然可以清楚地将您的意图传达给 reader。出于这个原因,我更喜欢尽可能使用普通的旧 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 结构。
我有两个不同的路由需要输出相同的页面,只有细微的差别(标题、开放图标签等)
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 中的惯用方法是什么?
谢谢, 洛朗
您可以指定 template/layout 在控制器操作中呈现什么。例如在 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.rb
:
def setfoo
@foo = "setting foo"
end
然后在您的控制器操作中:
def oneaction
setfoo
end
def anotheraction
setfoo
end
归功于此优秀postBest Practices for reusing code between controllers in Ruby on Rails
I would add a middleware in the referral route and then call the HomeController#index action. So everything would be done at the routing level.
不要陷入为了聪明而牺牲清晰度的陷阱。对于后来进入您的项目的人(包括您自己)来说,这可能是非常不直观和令人沮丧的。这种方法也受到一些任意决定的影响,比如哪个控制器动作被路由到哪个?
最好的代码是最简单的解决方案,它仍然可以清楚地将您的意图传达给 reader。出于这个原因,我更喜欢尽可能使用普通的旧 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 结构。