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
coreyward
fonte

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
Mike Lewis
fonte
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'.

Dean Radcliffe
fonte
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

schpet
fonte
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!

Matt
fonte
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")
spyle
fonte
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