Ruby on rails 轨道;验证服务对象中的参数

Ruby on rails 轨道;验证服务对象中的参数,ruby-on-rails,Ruby On Rails,我在Rails中创建了一个服务对象来封装为Stripe()创建计划的业务逻辑。 对于服务对象,是否有处理参数验证的好模式 对于验证: 我必须检查所有输入是否为零或类型错误。有什么方法可以方便地验证吗?也许是铁路的延伸 这是一个例子 # app/services/service.rb module Service extend ActiveSupport::Concern included do def self.call(*args) new(*args).ca

我在Rails中创建了一个服务对象来封装为Stripe()创建计划的业务逻辑。 对于服务对象,是否有处理参数验证的好模式

对于验证:

  • 我必须检查所有输入是否为零或类型错误。有什么方法可以方便地验证吗?也许是铁路的延伸
这是一个例子

# app/services/service.rb
module Service
  extend ActiveSupport::Concern

  included do
    def self.call(*args)
      new(*args).call
    end
  end
end

# app/services/plan/create.rb
class Plan::Create
  include Service

  attr_reader :params

  def initialize(params = {})
    @params = params.dup
  end

  def call
    plan = Plan.new(attrs)
    return plan unless plan.valid?

    begin
      external_card_plan_service.create(api_attrs)
    rescue Stripe::StripeError => e
      plan.errors[:base] << e.message
      return plan
    end

    plan.save
    plan.update(is_active: true, activated_at: Time.now.utc)
    plan
  end

  private

  def external_card_plan_service
    Stripe::Plan
  end

  def build_data_hash
    {
      id: params.fetch(:stripe_plan_id)
      stripe_plan_id: params.fetch(:stripe_plan_id)
      amount: params.fetch(:amount)
      currency: params.fetch(:currency)
      interval: params.fetch(:interval)
      name: params.fetch(:name)
      description: params.fetch(:description)
    }
  end

  def attrs
    build_data_hash.slice(:stripe_plan_id, :amount, :currency, :interval, :name, :description)
  end

  def api_attrs
    build_data_hash.slice(:id, :amount, :currency, :interval, :name)
  end
end
我试着使用Virtus()。 但是,我不知道如何在这种类型的服务对象中使用它

更新

# api/v1/controller/plans_controller.rb
    module Api
      module V1
        class PlansController < Api::V1::ApiController
          before_action :check_type, only: [:create]

          def create
            @result = Plan::Create.call(plan_info_params)

            if @result.errors.blank?
              resource = Api::V1::PlanResource.new(@result, nil)

              # NOTE: Include all domains created within this routine.
              serializer = JSONAPI::ResourceSerializer.new(Api::V1::PlanResource)
              json_body = serializer.serialize_to_hash(resource)
              render json: json_body, status: 201 # :ok
            else
              errors = jsonapi_errors(@result)
              response = { errors: errors }
              render json: response, status: 422 # :unprocessable_entity
            end
          end

          private

          def check_type
            data_type = 'plans'
            unless params.fetch('data', {}).fetch('type', {}) == data_type
              render json: { errors: [{ title: 'Unprocessable Entity', detail: "Type must be #{data_type}" }] }, status: 422
            end
          end

          def plan_info_params
            params.require(:data).require(:attributes).permit(:stripe_plan_id, :amount, :currency, :interval, :name, :description)
          end
        end
      end
    end
#api/v1/controller/plans_controller.rb
模块Api
模块V1
类PlansController
是一个不错的解决方案

class PlanForm
    include DryValidationForm

    Schema = Dry::Validation.Form do
      required(:stripe_plan_id).filled(:int?)
      required(:stripe_plan_id).filled(:int?)
      required(:amount).filled(:int?, gt?: 0)
      required(:currency).filled(:str?)
      required(:name).filled(:str?)
      optional(:description).maybe(:str?)
    end
  end
end

可能调用表单的最佳位置是从控制器获取参数。然后将有效参数传递给服务对象

谢谢您的回答。这里使用的是表单对象,对吗?另外,我不确定Form对象是否可以,因为我们一直在构建API。@TSH表单用于验证和准备参数,以便进一步处理。所以,是的,表单对象在这里是一个很好的选择,不管它是否用于API。谢谢!你觉得virtus+表单对象怎么样?我很想知道利弊。@TSH在我们的项目中,我们从基于virtus的表单(有gem
reform
)切换到了
dry validation
,因为这更方便。然而,我不记得virtus有什么特别的问题。当表单变得复杂(嵌套对象等)时,会有一些麻烦。没什么不可能解决的,听起来不错。我一直在尝试添加它,但找不到任何应用内实现示例。如果您根据上面的示例向我展示如何在控制器中实现它,那将非常棒。我更新了我的问题!
class PlanForm
    include DryValidationForm

    Schema = Dry::Validation.Form do
      required(:stripe_plan_id).filled(:int?)
      required(:stripe_plan_id).filled(:int?)
      required(:amount).filled(:int?, gt?: 0)
      required(:currency).filled(:str?)
      required(:name).filled(:str?)
      optional(:description).maybe(:str?)
    end
  end
end