我正在尝试找到一种优雅的方式来处理两个控制器之间的某些共享验证。
示例:
我有两个Accounts控制器。一个用于同步处理与用户的关联帐户(例如,使用包含这种情况逻辑的PORO),另一个用于与工作人员异步处理关联。 请假设每种情况下的逻辑都不同,并且同步/异步并不是唯一的区别。
然后我有这两个控制器:
module Accounts
class AssociationsController < ApplicationController
def create
return already_associated_account_error if user_has_some_account_associated?
# action = call some account association PORO
render json: action.response, status: action.status_code
end
private
def user_has_some_account_associated?
params[:accounts].any? { |account_number| user_account_associated?(account_number) }
end
def user_account_associated?(account_number)
current_user.accounts.exists?(number: account_number)
end
def already_associated_account_error
render json: 'You already have associated one or more accounts', status: :unprocessable_entity
end
end
end
现在,我要在另一个控制器中应用相同的验证:
module Accounts
class AsyncAssociationsController < ApplicationController
def create
return already_associated_account_error if user_has_some_account_associated?
# Perform asynchronously some account association WORKER
render json: 'Your request is being processed', status: :ok
end
private
def user_has_some_account_associated?
params[:accounts].any? { |account_number| user_account_associated?(account_number) }
end
def user_account_associated?(account_number)
current_user.accounts.exists?(number: account_number)
end
def already_associated_account_error
render json: 'You already have associated one or more accounts', status: :unprocessable_entity
end
end
end
...
如何将验证逻辑放在哪里?并在两个控制器中都使用?我首先考虑的是问题,但是我不确定它们是否仅适用于这种验证逻辑。
答案 0 :(得分:1)
为此,您应该使用关注点。这就是他们的目的。
在controllers
目录下创建一个concerns
目录(如果尚不存在),并在其中创建具有以下内容的文件association_concern.rb
:
module AssociationConcern
extend ActiveSupport::Concern
private
def user_has_some_account_associated?
params[:accounts].any? { |account_number| user_account_associated?(account_number) }
end
def user_account_associated?(account_number)
current_user.accounts.exists?(number: account_number)
end
def already_associated_account_error
render json: 'You already have associated one or more accounts', status: :unprocessable_entity
end
end
控制器常见的任何问题都可以解决
然后在您的控制器中简单地include AssociationConcern
class AssociationsController < ApplicationController
include AssociationConcern
def create
return already_associated_account_error if user_has_some_account_associated?
# action = call some account association PORO
render json: action.response, status: action.status_code
end
end
答案 1 :(得分:0)
使它们从某些新控制器继承并添加before_action
,如下所示:
module Accounts
class AbstractAssociationsController < ApplicationController
before_action :check_for_associated_account, only: [:create]
def check_for_associated_account
if user_has_associated_account?
render json: 'You already have associated one or more accounts', status: :unprocessable_entity
end
end
end
end
module Accounts
class AssociationsController < AbstractAssociationsController
def create
# action = call some account association PORO
render json: action.response, status: action.status_code
end
end
end
然后,根据逻辑是否真的不同,您可以在此抽象控制器中定义user_has_associated_account?
,也可以在单独的控制器中进行定义,也可以将其委托给某些PORO类。