Неудача и обработка ошибок
Описание методов и исключений
Работа сервиса может быть завершена преждевременно при помощи вызова одного из этих методов:
fail_input!
;fail_internal!
;fail_output!
;fail!
;fail_result!
.
Эти методы в свою очередь вызовут исключение.
Из списка выше только следующие методы можно будет обработать после вызова через call
:
fail!
;fail_result!
.
Остальные методы будут всегда вызывать исключение.
Помимо этого присутствуют автоматические проверки input, internal и output атрибутов. В случае, например, валидационных проблем с этими атрибутами будет также вызвано соответствующее исключение. Это поведение будет идентично тому что происходит при вызове этих методов:
fail_input!
;fail_internal!
;fail_output!
.
Внутри сервиса может присутствовать логика, которая будет вызывать свои исключения. Например, это может быть ActiveRecord::RecordInvalid
. Для таких случаев был разработан метод fail_on!
на уровне класса.
Методы
Метод fail_input!
Предназначен для вызова исключения от лица input атрибута.
Метод fail_input!
позволяет передать текст ошибки, дополнительную информацию через атрибут meta
, а также требует указать имя input атрибута.
При любом вызове сервиса будет вызвано исключение с классом ApplicationService::Exceptions::Input
.
make :check!
def check!
return if inputs.invoice_number.start_with?("AA")
fail_input!(
:invoice_number,
message: "Invalid invoice number",
meta: {
received_invoice_number: inputs.invoice_number
}
)
end
Пример информации, которую может предоставить исключение ApplicationService::Exceptions::Input
:
exception.service # => <Actor: @class_name="InvoiceService::Check", @i18n_root_key="servactory">
exception.detailed_message # => Invalid invoice number (ApplicationService::Exceptions::Input)
exception.message # => Invalid invoice number
exception.input_name # => :invoice_number
exception.meta # => {:received_invoice_number=>"BB-7650AE"}
Метод fail_internal!
Предназначен для вызова исключения от лица internal атрибута.
Метод fail_internal!
позволяет передать текст ошибки, дополнительную информацию через атрибут meta
, а также требует указать имя internal атрибута.
При любом вызове сервиса будет вызвано исключение с классом ApplicationService::Exceptions::Internal
.
make :check!
def check!
return if internals.invoice_number.start_with?("AA")
fail_internal!(
:invoice_number,
message: "Invalid invoice number",
meta: {
received_invoice_number: internals.invoice_number
}
)
end
Пример информации, которую может предоставить исключение ApplicationService::Exceptions::Internal
:
exception.service # => <Actor: @class_name="InvoiceService::Check", @i18n_root_key="servactory">
exception.detailed_message # => Invalid invoice number (ApplicationService::Exceptions::Internal)
exception.message # => Invalid invoice number
exception.internal_name # => :invoice_number
exception.meta # => {:received_invoice_number=>"BB-7650AE"}
Метод fail_output!
Предназначен для вызова исключения от лица output атрибута.
Метод fail_output!
позволяет передать текст ошибки, дополнительную информацию через атрибут meta
, а также требует указать имя output атрибута.
При любом вызове сервиса будет вызвано исключение с классом ApplicationService::Exceptions::Output
.
make :check!
def check!
return if outputs.invoice_number.start_with?("AA")
fail_output!(
:invoice_number,
message: "Invalid invoice number",
meta: {
received_invoice_number: outputs.invoice_number
}
)
end
Пример информации, которую может предоставить исключение ApplicationService::Exceptions::Output
:
exception.service # => <Actor: @class_name="InvoiceService::Check", @i18n_root_key="servactory">
exception.detailed_message # => Invalid invoice number (ApplicationService::Exceptions::Output)
exception.message # => Invalid invoice number
exception.output_name # => :invoice_number
exception.meta # => {:received_invoice_number=>"BB-7650AE"}
Метод fail!
Предназначен для описания пользовательских ошибок.
Метод fail!
позволяет передать текст ошибки, дополнительную информацию через атрибут meta
, а также позволяет указывать type
.
По умолчанию type
имеет значение base
, но вы можете передавать любое значение для дальнейшей обработки.
При вызове сервиса через метод call!
будет вызвано исключение с классом Servactory::Exceptions::Failure
. При вызове метода через метод call
ошибка будет зафиксирована и доступна в Result
.
make :check!
def check!
return if inputs.invoice_number.start_with?("AA")
fail!(message: "Invalid invoice number")
end
fail!(
:base,
message: "Invalid invoice number",
meta: {
invoice_number: inputs.invoice_number
}
)
Пример информации, которая будет предоставлена:
exception.detailed_message # => Invalid invoice number (ApplicationService::Exceptions::Failure)
exception.message # => Invalid invoice number
exception.type # => :base
exception.meta # => {:invoice_number=>"BB-7650AE"}
Метод fail_result!
Начиная с 2.1.0
Требует Result
и внутри себя вызывает метод fail!
.
Предназначен для сокращенного написания кода для передачи ошибки из одного сервиса в текущий. Например, из API сервиса в сервис приложения.
fail_result!(service_result)
Код выше эквивалентен этому:
fail!(
service_result.error.type,
message: service_result.error.message,
meta: service_result.error.meta
)
Метод fail_on!
Начиная с 2.5.0
Предназначен для перехвата указанных исключений.
Метод fail_on!
позволяет передать класс исключения или исключений, а также позволяет кастомизировать текст сообщения.
Вместо указанных исключений будет использован вызов метода fail!
. Информация об оригинальном исключении будет передана в метод fail!
через meta
.
Использование
module ApplicationService
class Base < Servactory::Base
fail_on! ActiveRecord::RecordNotFound,
ActiveRecord::RecordInvalid
# ...
end
end
Если вам нужно кастомизировать текст сообщения, то это можно сделать следующим образом:
fail_on! ActiveRecord::RecordNotFound,
with: ->(exception:) { exception.message }
Альтернативный вариант:
fail_on!(ActiveRecord::RecordNotFound) { |exception:| exception.message }