【问题标题】:How to handle and dry validations in Rails controllers如何在 Rails 控制器中处理和干燥验证
【发布时间】:2019-03-09 08:09:07
【问题描述】:

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

例子:

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


    【解决方案1】:

    让它们从某个新控制器继承并添加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 类。

    【讨论】:

      【解决方案2】:

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

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多