首页 > 解决方案 > 如何在 Rails 控制器中处理和干燥验证

问题描述

我试图找到一种优雅的方式来处理两个控制器之间的一些共享验证。

例子:

我有两个帐户控制器。一个用于同步处理与用户的帐户关联(例如,使用包含此案例逻辑的 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

...

我如何以及在何处将验证逻辑仅放置在一个位置并在两个控制器中使用它?我认为首先要解决一个问题,但我不确定它们是否仅适用于这种验证逻辑的情况。

标签: ruby-on-railsrubyvalidationmodel-view-controllerdry

解决方案


为此,您应该使用关注点。这就是它们的设计目的。

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

推荐阅读