Dove definire i tipi di errore personalizzati in Ruby e / o Rails?


149

Esiste una procedura ottimale per la definizione di tipi di errore personalizzati in una libreria Ruby (gemma) o Ruby on Rails? In particolare:

  1. Dove appartengono strutturalmente al progetto? Un file separato, integrato con la relativa definizione modulo / classe, da qualche altra parte?
  2. Ci sono convenzioni che stabiliscono quando a quando non creare un nuovo tipo di errore?

Librerie diverse hanno modi diversi di fare le cose e non ho notato alcun modello reale. Alcune librerie usano sempre tipi di errore personalizzati mentre altre non le usano affatto; alcuni hanno tutti gli errori che estendono StandardError mentre altri hanno gerarchie nidificate; alcuni sono solo definizioni di classe vuote, altri hanno tutti i tipi di trucchi intelligenti.

Oh, e solo perché ho voglia di chiamare questi "tipi di errore" è un po 'ambiguo, ciò che intendo è questo:

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

Risposte:


219

Per le gemme

Ho visto molte volte che definisci le eccezioni in questo modo:

gem_dir / lib / gem_name / exceptions.rb

e definito come:

module GemName

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

end

un esempio di questo sarebbe qualcosa di simile in httparty

Per Ruby on Rails

Inseriscili nella cartella lib / in un file chiamato exceptions.rb, che assomiglierebbe a questo:

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

e lo useresti in questo modo:

raise Exceptions::InvalidUsername

Per le gemme, sembra che potresti dover includere anche il file delle eccezioni. Vedi questo esempio, sempre da httparty: github.com/jnunemaker/httparty/blob/…
Jason Swett,

37
Perché namespace nel Exceptionsmodulo?
ABMagil,

13
Penserei che /libpotrebbe non essere il luogo per errori. Sono molto specifici dell'applicazione e ho l'impressione che il codice che sto inserendo /libsia destinato a essere codice che potrebbe essere riutilizzato in altre applicazioni.
wuliwong,

1
Le istruzioni di Ruby on Rails non hanno funzionato per me - è necessario qualche passaggio aggiuntivo per caricare effettivamente questo nuovo file nel caso tipico?
Meekohi,

1
@ABMagil sembra che dovrei altrimenti Unable to autoload constant Exceptions, expected /app/lib/exceptions.rb to define itl'altra opzione sarebbe una classe per eccezione penso
ryan2johnson9

25

Penso che per avere file sorgente coerenti nel tuo progetto, dovresti definire errori nella classe in cui potrebbero generarli e in nessun altro luogo.

Una certa erirarchia può essere utile - gli spazi dei nomi sono bravi a tenere le stringhe ridondanti fuori dai nomi dei tipi - ma è più una questione di gusti - non è necessario esagerare a condizione che tu abbia almeno un tipo di eccezione personalizzato nella tua app che usi in tutto per differenziare tra casi di eccezione "intenzionale" e "accidentale".


8
Mentre in teoria hai ragione, cosa succede quando lo stesso errore può essere sollevato da varie classi in situazioni totalmente diverse?
Alain,

1
@Alain Perché non definire quegli errori utilizzati da più di una classe in un modulo Eccezioni / Errori, ma lasciare tutti gli altri definiti nella singola classe che li utilizza?
Scott W,

@ScottW, in tal caso, facciamo affidamento sullo sviluppatore per ricordarsi di controllare.
Josh Saint Jacque,

22

nelle rotaie puoi creare una app/errorsdirectory

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

riavvia spring / server e dovrebbe recuperarlo


Come dovrei sollevare queste eccezioni?
Nikhil Wagh,

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

13

Questa è una vecchia domanda, ma volevo condividere il modo in cui gestisco gli errori personalizzati in Rails, incluso il collegamento di messaggi di errore, i test e come gestirli con i ActiveRecordmodelli.

Creazione di errore personalizzato

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

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

Test (minimo)

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

Penso che valga la pena notare che se si lavora con un ActiveRecordmodello, un modello popolare è quello di aggiungere un errore al modello come descritto di seguito, in modo che le convalide falliscano:

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 vengono eseguite le convalide, questo metodo tornerà alla ActiveRecord::RecordInvalidclasse di errore di ActiveRecord e causerà il fallimento delle convalide.

Spero che questo ti aiuti!


9

Per garantire che il caricamento automatico funzioni come previsto in Rails 4.1.10 per più classi di errore personalizzate, ti consigliamo di specificare file separati per ognuno. Questo dovrebbe funzionare nello sviluppo con la sua ricarica dinamica.

Ecco come ho impostato gli errori in un progetto recente:

Nel lib/app_name/error/base.rb

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

e nei successivi errori personalizzati, come in lib/app_name/error/bad_stuff.rb

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

Dovresti quindi essere in grado di chiamare i tuoi errori tramite:

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

E se non vuoi un file separato per ogni nuovo errore, inseriscili tuttilib/app_name/error.rb
jlhonora

Ottenere un uninitialized constant MyController::AppName. Chiamo rilancio nel mio controller
Nikhil Wagh il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.