【发布时间】:2017-05-11 08:49:44
【问题描述】:
我在 Rails 中创建了一个服务对象来封装为 Stripe(https://stripe.com/)创建计划的业务逻辑。 有没有什么好的模式来处理Service对象的参数验证?
对于验证:
- 我必须检查所有输入是否为 nil 或类型错误。有什么方法可以轻松验证吗?也许是 Rails 扩展?
这是一个例子;
# 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
参考:http://brewhouse.io/blog/2014/04/30/gourmet-service-objects.
更新
上面的例子并不复杂。
但是,如果在服务对象中调用服务对象,最好验证参数。 CreateUser.call(email_address)
class CreateSubscription
def self.call(plan, email_address, token)
user, raw_token = CreateUser.call(email_address)
subscription = Subscription.new(
plan: plan,
user: user
)
begin
stripe_sub = nil
if user.stripe_customer_id.blank?
customer = Stripe::Customer.create(
source: token,
email: user.email,
plan: plan.stripe_id,
)
user.stripe_customer_id = customer.id
user.save!
stripe_sub = customer.subscriptions.first
else
customer = Stripe::Customer.retrieve(user.stripe_customer_id)
stripe_sub = customer.subscriptions.create(
plan: plan.stripe_id
)
end
subscription.stripe_id = stripe_sub.id
subscription.save!
rescue Stripe::StripeError => e
subscription.errors[:base] << e.message
end
subscription
end
end
我尝试使用 Virtus(https://github.com/solnic/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
【问题讨论】:
标签: ruby-on-rails