Toshi Toshi - 2 months ago 13
Ruby Question

Error handling to return error object in services

This is CreateSubscription service class. I put this logic in service because there are api calls and some data insertion occurs.
(This is simplified version.)

This class is called basically from controller, and I need to handle errors there.
However the question is how can I triger errors with user.erros object instead of full_messages, so that I can iterate the errors and create error response with JSON.

{
"error": "Uuid can't be blank, Name can't be blank, and Email is invalid"
}


to

{
"errors": [
{
"uuid": [
"can't be blank"
],"name" : ["can't be blank"]..
}
]
}


services/create_subscription.rb

class CreateSubscription
class UserCreateError < StandardError; end

def self.call(plan, user_info:)
subscription = nil
ActiveRecord::Base.transaction do
user = CreateUser.call(user_info)
raise UserCreateError.new(user.errors.full_messages.to_sentence) unless user.valid?


service/create_user.rb

class CreateUser
def self.call(payment_info)
uuid = payment_info[:uuid]
name = payment_info[:name]
email = payment_info[:email]

user = User.find_by(uuid: uuid)
return user if user.present?

user = User.create(
uuid: uuid,
name: name,
email: email
)
user
end
end


UPDATE



controller/subscriptions_controller.rb

def create
# Some logic ....
rescue CreateSubscription::UserCreateError => e
# TODO: This should be an array, not only each one
render json: { error: e.message }, status: :bad_request

Answer

Try using messages (see the docs):

user = User.create
user.save # => false
user.errors.messages # => {uuid: ["cannot be blank"], name: ["cannot be blank"]}

In addition you can add your own key to make a "root":

{errors: user.errors.messages}

UPDATE:

You really should consider refactoring your code. You're using Exception as a control flow.. (See this why should not)

To replace this you can use custom validators.

Then you can call it like this in your controller:

def create
  user = User.new(<params>)
  if user.save
    # do whatever you like, most likely a redirect
  else
    render json: { errors: user.errors.messages }, status: bad_request
  end
end

This should render a json like this:

{
  "errors": {
    "uuid": [
      "can't be blank"
    ]
    ...
  }
}
Comments