Onde definir tipos de erro personalizados em Ruby e / ou Rails?


149

Existe uma prática recomendada para definir tipos de erro personalizados em uma biblioteca Ruby (gem) ou aplicativo Ruby on Rails? Especificamente:

  1. Onde eles pertencem estruturalmente ao projeto? Um arquivo separado, alinhado com a definição de módulo / classe relevante, em outro lugar?
  2. Existem quaisquer convenções que estabelecem quando para e quando para não criar um novo tipo de erro?

Bibliotecas diferentes têm maneiras diferentes de fazer as coisas, e eu não notei nenhum padrão real. Algumas bibliotecas sempre usam tipos de erro personalizados, enquanto outras não os usam; alguns têm todos os erros que estendem o StandardError enquanto outros têm hierarquias aninhadas; alguns são apenas definições de classe vazias, outros têm todo tipo de truques inteligentes.

Ah, e só porque sinto que chamar esses "tipos de erro" é meio ambíguo, o que quero dizer é o seguinte:

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

Respostas:


219

Para gemas

Já vi muitas vezes que você define exceções dessa maneira:

gem_dir / lib / gem_name / exceptions.rb

e definido como:

module GemName

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

end

um exemplo disso seria algo parecido com isto em enableparty

Para Ruby on Rails

Coloque-os na sua pasta lib / em um arquivo chamado exceptions.rb, que seria algo como isto:

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

e você usaria assim:

raise Exceptions::InvalidUsername

Para gemas, parece que você também deve incluir o arquivo de exceção. Veja este exemplo, novamente em httparty: github.com/jnunemaker/httparty/blob/…
— Jason Swett

37
Por que namespace-los no Exceptionsmódulo?
— ABMagil

13
Eu acho que /libpode não ser o lugar para erros. Eles são muito específicos de aplicativos e tenho a impressão de que o código que estou inserindo /libdeve ser um código que poderia ser reutilizado em outros aplicativos.
— wuliwong

1
As instruções do Ruby on Rails não funcionaram para mim - são necessárias algumas etapas adicionais para realmente carregar esse novo arquivo no caso típico?
— Meekohi 14/01/19

1
@ABMagil parece que eu tenho que caso contrário Unable to autoload constant Exceptions, expected /app/lib/exceptions.rb to define ita outra opção seria uma classe por exceção, eu acho
— ryan2johnson9

25

Eu acho que, para ter arquivos de origem coesos em seu projeto, você deve definir erros na classe em que os podem ser lançados e em nenhum outro lugar.

Alguma hierarquia pode ser útil - os namespaces são bons para manter seqüências redundantes de nomes de tipo - mas isso é mais uma questão de gosto - não há necessidade de exagerar, desde que você tenha pelo menos um tipo de exceção personalizado no aplicativo que você usa para diferenciar entre casos de exceção 'intencional' e 'acidental'.


8
Enquanto na teoria você está certo, o que acontece quando o mesmo erro pode ser gerado por várias classes em situações totalmente diferentes?
— Alain

1
@Alain Por que não definir os erros usados ​​por mais de uma classe no módulo Exceções / Erros, mas deixar todos os outros definidos na única classe que os utiliza?
— Scott W

@ Scott, nesse caso, contamos com o desenvolvedor para lembrar de verificar.
— Josh Saint Jacque

22

nos trilhos você pode criar app/errorsdiretório

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

reinicie o spring / server e deve buscá-lo


Como devo levantar essas exceções?
— Nikhil Wagh

@NikhilWagh raise FooError, "Example message..."ouraise FooError.new("Example message...")
— schpet 14/01

13

Essa é uma pergunta antiga, mas eu gostaria de compartilhar como estou lidando com erros personalizados no Rails, incluindo anexar mensagens de erro, testes e como lidar com isso com ActiveRecordmodelos.

Criando erro 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

Teste (miniteste)

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

Com o ActiveRecord

Eu acho que vale a pena notar que, se estiver trabalhando com um ActiveRecordmodelo, um padrão popular é adicionar um erro ao modelo, conforme descrito abaixo, para que suas validações falhem:

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

Quando as validações são executadas, esse método retorna à ActiveRecord::RecordInvalidclasse de erro do ActiveRecord e causa falhas nas validações.

Espero que isto ajude!


9

Para garantir que o carregamento automático funcione conforme o esperado no Rails 4.1.10 para várias classes de erro personalizadas, você deve especificar arquivos separados para cada uma. Isso deve funcionar no desenvolvimento com seu recarregamento dinâmico.

É assim que eu configuro erros em um projeto recente:

No lib/app_name/error/base.rb

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

e em erros personalizados subsequentes, como em lib/app_name/error/bad_stuff.rb

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

Você poderá chamar seus erros por meio de:

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

E se você não quiser que um arquivo separado para cada novo erro, apenas colocá-los todos emlib/app_name/error.rb
— jlhonora

Obtendo um uninitialized constant MyController::AppName. Estou chamando raise no meu controller
— Nikhil Wagh 10/01
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.