practice example error custom best ruby exception inheritance exception-handling custom-exceptions

example - Clases de error personalizadas de Ruby: herencia del atributo del mensaje



java custom exception best practice (4)

Parece que no puedo encontrar mucha información sobre clases de excepciones personalizadas.

Lo que sí sé

Puede declarar su clase de error personalizada y dejarla heredar de StandardError , para que pueda ser rescue d:

class MyCustomError < StandardError end

Esto te permite subirlo usando:

raise MyCustomError, "A message"

y luego, recibe ese mensaje cuando rescatas

rescue MyCustomError => e puts e.message # => "A message"

Lo que no sé

Quiero dar a mi excepción algunos campos personalizados, pero quiero heredar el atributo del message de la clase principal. Descubrí leyendo sobre este tema que @message no es una variable de instancia de la clase de excepción, así que me preocupa que mi herencia no funcione.

¿Alguien puede darme más detalles sobre esto? ¿Cómo implementaría una clase de error personalizada con un atributo de object ? Es el siguiente correcto:

class MyCustomError < StandardError attr_reader :object def initialize(message, object) super(message) @object = object end end

Y entonces:

raise MyCustomError.new(anObject), "A message"

Llegar:

rescue MyCustomError => e puts e.message # => "A message" puts e.object # => anObject

¿Funcionará, y si lo hace, es esta la forma correcta de hacer las cosas?


Dado lo que es la documentación de Ruby core de Exception , de la cual todos los otros errores heredan, estados sobre #message

Devuelve el resultado de invocar exception.to_s. Normalmente esto devuelve el mensaje o nombre de la excepción. Al suministrar un método to_str, las excepciones acuerdan ser utilizadas donde se esperan cadenas.

http://ruby-doc.org/core-1.9.3/Exception.html#method-i-message

to_s por redefinir to_s / to_str o el inicializador. Aquí hay un ejemplo en el que queremos saber, en una forma legible en su mayoría humanos, cuando un servicio externo no ha podido hacer algo.

NOTA: La segunda estrategia a continuación utiliza los métodos de cadenas bonitas de rieles, como demodualize , que pueden ser un poco complicados y, por lo tanto, potencialmente imprudentes en una excepción. También podría agregar más argumentos a la firma del método, si lo necesita.

Anulando la estrategia #to_s no #to_str, funciona de manera diferente

module ExternalService class FailedCRUDError < ::StandardError def to_s ''failed to crud with external service'' end end class FailedToCreateError < FailedCRUDError; end class FailedToReadError < FailedCRUDError; end class FailedToUpdateError < FailedCRUDError; end class FailedToDeleteError < FailedCRUDError; end end

Salida de consola

begin; raise ExternalService::FailedToCreateError; rescue => e; e.message; end # => "failed to crud with external service" begin; raise ExternalService::FailedToCreateError, ''custom message''; rescue => e; e.message; end # => "failed to crud with external service" begin; raise ExternalService::FailedToCreateError.new(''custom message''); rescue => e; e.message; end # => "failed to crud with external service" raise ExternalService::FailedToCreateError # ExternalService::FailedToCreateError: failed to crud with external service

Anulación de la estrategia #initialize

Esta es la estrategia más cercana a las implementaciones que he usado en los rieles. Como se indicó anteriormente, utiliza los demodualize , underscore y humanize ActiveSupport . Pero esto podría eliminarse fácilmente, como en la estrategia anterior.

module ExternalService class FailedCRUDError < ::StandardError def initialize(service_model=nil) super("#{self.class.name.demodulize.underscore.humanize} using #{service_model.class}") end end class FailedToCreateError < FailedCRUDError; end class FailedToReadError < FailedCRUDError; end class FailedToUpdateError < FailedCRUDError; end class FailedToDeleteError < FailedCRUDError; end end

Salida de consola

begin; raise ExternalService::FailedToCreateError; rescue => e; e.message; end # => "Failed to create error using NilClass" begin; raise ExternalService::FailedToCreateError, Object.new; rescue => e; e.message; end # => "Failed to create error using Object" begin; raise ExternalService::FailedToCreateError.new(Object.new); rescue => e; e.message; end # => "Failed to create error using Object" raise ExternalService::FailedCRUDError # ExternalService::FailedCRUDError: Failed crud error using NilClass raise ExternalService::FailedCRUDError.new(Object.new) # RuntimeError: ExternalService::FailedCRUDError using Object

Herramienta de demostración

Esta es una demostración para mostrar el rescate y los mensajes de la implementación anterior. La clase que plantea las excepciones es una API falsa para Cloudinary. Simplemente descargue una de las estrategias anteriores en la consola de sus rieles, seguido de esto.

require ''rails'' # only needed for second strategy module ExternalService class FailedCRUDError < ::StandardError def initialize(service_model=nil) @service_model = service_model super("#{self.class.name.demodulize.underscore.humanize} using #{@service_model.class}") end end class FailedToCreateError < FailedCRUDError; end class FailedToReadError < FailedCRUDError; end class FailedToUpdateError < FailedCRUDError; end class FailedToDeleteError < FailedCRUDError; end end # Stub service representing 3rd party cloud storage class Cloudinary def initialize(*error_args) @error_args = error_args.flatten end def create_read_update_or_delete begin try_and_fail rescue ExternalService::FailedCRUDError => e e.message end end private def try_and_fail raise *@error_args end end errors_map = [ # Without an arg ExternalService::FailedCRUDError, ExternalService::FailedToCreateError, ExternalService::FailedToReadError, ExternalService::FailedToUpdateError, ExternalService::FailedToDeleteError, # Instantiated without an arg ExternalService::FailedCRUDError.new, ExternalService::FailedToCreateError.new, ExternalService::FailedToReadError.new, ExternalService::FailedToUpdateError.new, ExternalService::FailedToDeleteError.new, # With an arg [ExternalService::FailedCRUDError, Object.new], [ExternalService::FailedToCreateError, Object.new], [ExternalService::FailedToReadError, Object.new], [ExternalService::FailedToUpdateError, Object.new], [ExternalService::FailedToDeleteError, Object.new], # Instantiated with an arg ExternalService::FailedCRUDError.new(Object.new), ExternalService::FailedToCreateError.new(Object.new), ExternalService::FailedToReadError.new(Object.new), ExternalService::FailedToUpdateError.new(Object.new), ExternalService::FailedToDeleteError.new(Object.new), ].inject({}) do |errors, args| begin errors.merge!( args => Cloudinary.new(args).create_read_update_or_delete) rescue => e binding.pry end end if defined?(pp) || require(''pp'') pp errors_map else errors_map.each{ |set| puts set.inspect } end


Tu idea es correcta, pero la forma en que la llamas es incorrecta. Debería ser

raise MyCustomError.new(an_object, "A message")


Yo quería hacer algo similar. Quería pasar un objeto a #new y configurar el mensaje en función de algún procesamiento del objeto pasado. Los siguientes trabajos.

class FooError < StandardError attr_accessor :message # this is critical! def initialize(stuff) @message = stuff.reverse end end begin raise FooError.new("!dlroW olleH") rescue FooError => e puts e.message #=> Hello World! end

Tenga en cuenta que si no declara attr_accessor :message entonces no funcionará. Al abordar el problema del OP, también puede pasar el mensaje como un argumento adicional y almacenar lo que quiera. La parte crucial parece ser anular el #mensaje.


raise ya establece el mensaje para que no tengas que pasarlo al constructor:

class MyCustomError < StandardError attr_reader :object def initialize(object) @object = object end end begin raise MyCustomError.new("an object"), "a message" rescue MyCustomError => e puts e.message # => "a message" puts e.object # => "an object" end

He reemplazado la rescue Exception de rescue MyCustomError con el rescue MyCustomError , mira ¿Por qué es un estilo malo ''rescatar Exception => e` en Ruby? .