¿Dónde definir los tipos de error personalizados en Ruby y / o Rails?


149

¿Existe alguna práctica recomendada para definir tipos de error personalizados en una biblioteca Ruby (gema) o una aplicación Ruby on Rails? Específicamente:

  1. ¿Dónde pertenecen estructuralmente en el proyecto? ¿Un archivo separado, alineado con la definición de módulo / clase relevante, en otro lugar?
  2. ¿Hay convenciones que establecen cuando a y cuando no a crear un nuevo tipo de error?

Las diferentes bibliotecas tienen diferentes formas de hacer las cosas, y no he notado ningún patrón real. Algunas bibliotecas siempre usan tipos de error personalizados, mientras que otras no las usan en absoluto; algunos tienen todos los errores extendiendo StandardError mientras que otros tienen jerarquías anidadas; algunos son solo definiciones de clase vacías, otros tienen todo tipo de trucos ingeniosos.

Ah, y solo porque siento que llamar a estos "tipos de error" es algo ambiguo, lo que quiero decir es esto:

class AuthenticationError < StandardError; end
class InvalidUsername < AuthenticationError; end

Respuestas:


219

Para las gemas

He visto muchas veces que define excepciones de esta manera:

gem_dir / lib / gem_name / exceptions.rb

y definido como:

module GemName

  class AuthenticationError < StandardError; end
  class InvalidUsername < AuthenticationError; end

end

un ejemplo de esto sería algo así en httparty

Para Ruby on Rails

Póngalos en su carpeta lib / debajo de un archivo llamado excepciones.rb, que se vería así:

module Exceptions
  class AuthenticationError < StandardError; end
  class InvalidUsername < AuthenticationError; end
end

y lo usarías así:

raise Exceptions::InvalidUsername

En el caso de las gemas, parece que también debe incluir el archivo de excepción. Vea este ejemplo, nuevamente en httparty: github.com/jnunemaker/httparty/blob/…
Jason Swett

37
¿Por qué los espacios de nombres en el Exceptionsmódulo?
ABMagil

13
Creo que ese /libno es el lugar para errores. Son muy específicos de la aplicación y tengo la impresión de que el código que estoy poniendo /libestá destinado a ser código que podría reutilizarse en otras aplicaciones.
wuliwong

1
Las instrucciones de Ruby on Rails no me funcionaron. ¿Se necesita algún paso adicional para cargar este nuevo archivo en el caso típico?
Meekohi el

1
@ABMagil parece que, de lo contrario, Unable to autoload constant Exceptions, expected /app/lib/exceptions.rb to define itla otra opción sería una clase por excepción, creo
ryan2johnson9

25

Creo que para tener archivos fuente cohesivos en su proyecto, debe definir errores en la clase en la que pueda arrojarlos y en ningún otro lugar.

Alguna jerarquía puede ser útil: los espacios de nombres son buenos para mantener las cadenas redundantes fuera de los nombres de tipo, pero eso es más una cuestión de gustos: no hay necesidad de exagerar siempre que tenga al menos un tipo de excepción personalizado en su aplicación que use para diferenciar entre casos de excepción 'intencionales' y 'accidentales'.


8
Si bien en teoría tiene razón, ¿qué sucede cuando varias clases pueden generar el mismo error en situaciones totalmente diferentes?
Alain

1
@Alain ¿Por qué no definir los errores utilizados por más de una clase en un módulo de Excepciones / Errores, pero dejar todos los demás definidos en la clase única que los usa?
Scott W

@ScottW, en ese caso, confiamos en que el desarrollador recuerde verificar.
Josh Saint Jacque

22

en rieles puedes hacer app/errorsdirectorio

# app/errors/foo_error.rb
class FooError < StandardError; end

reinicie spring / server y debería recogerlo


¿Cómo debo plantear estas excepciones?
Nikhil Wagh

@NikhilWagh raise FooError, "Example message..."oraise FooError.new("Example message...")
schpet

13

Esta es una pregunta antigua, pero quería compartir cómo manejo los errores personalizados en Rails, incluyendo adjuntar mensajes de error, pruebas y cómo manejar esto con los ActiveRecordmodelos.

Creando error personalizado

class MyClass
  # create a custome error
  class MissingRequirement < StandardError; end

  def my_instance_method
    raise MyClass::MissingRequirement, "My error msg" unless true   
  end
end

Prueba (minitest)

test "should raise MissingRequirement if ____ is missing"
  # should raise an error
  error = assert_raises(MyClass::MissingRequirement) {
    MyClass.new.my_instance_method
  }

  assert error.message = "My error msg"
end

Con ActiveRecord

Creo que vale la pena señalar que si se trabaja con un ActiveRecordmodelo, un patrón popular es agregar un error al modelo como se describe a continuación, para que sus validaciones fallen:

def MyModel < ActiveRecord::Base
  validate :code_does_not_contain_hyphens

  def code_does_not_contain_hyphens
    errors.add(:code, "cannot contain hyphens") if code.include?("-")
  end
end

Cuando se ejecutan las validaciones, este método se acoplará a la ActiveRecord::RecordInvalidclase de error de ActiveRecord y hará que las validaciones fallen.

¡Espero que esto ayude!


9

Para garantizar que la carga automática funcione como se esperaba en Rails 4.1.10 para múltiples clases de error personalizadas, querrá especificar archivos separados para cada una. Esto debería funcionar en desarrollo con su recarga dinámica.

Así es como configuro errores en un proyecto reciente:

En lib/app_name/error/base.rb

module AppName
    module Error
        class Base < StandardError; end
    end
end

y en errores personalizados posteriores, como en lib/app_name/error/bad_stuff.rb

module AppName
    module Error
        class BadStuff < ::AppName::Error::Base; end
    end
end

Entonces debería poder llamar a sus errores a través de:

 raise AppName::Error::BadStuff.new("Bad stuff just happened")

Y si no desea un archivo separado para cada nuevo error, simplemente lib/app_name/error.rb
póngalos

Obteniendo un uninitialized constant MyController::AppName. Estoy llamando a subir en mi controlador
Nikhil Wagh
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.