Wo können benutzerdefinierte Fehlertypen in Ruby und / oder Rails definiert werden?

149

Gibt es eine bewährte Methode zum Definieren benutzerdefinierter Fehlertypen in einer Ruby-Bibliothek (Gem) oder einer Ruby on Rails-Anwendung? Speziell:

  1. Wo gehören sie strukturell in das Projekt? Eine separate Datei, die mit der entsprechenden Modul- / Klassendefinition versehen ist, woanders?
  2. Gibt es Konventionen , die festlegen , wann zu und wenn nicht zu einem neuen Fehlertyp erstellen?

Verschiedene Bibliotheken haben unterschiedliche Methoden, und ich habe keine wirklichen Muster bemerkt. Einige Bibliotheken verwenden immer benutzerdefinierte Fehlertypen, während andere diese überhaupt nicht verwenden. Einige haben alle Fehler, die StandardError erweitern, während andere verschachtelte Hierarchien haben. Einige sind nur leere Klassendefinitionen, andere haben alle möglichen cleveren Tricks.

Oh, und nur weil ich das Gefühl habe, diese "Fehlertypen" zu nennen, ist das mehrdeutig. Ich meine Folgendes:

class AuthenticationError < StandardError; end
class InvalidUsername < AuthenticationError; end
Coreyward
quelle

Antworten:

219

Für Edelsteine

Ich habe oft gesehen, dass Sie Ausnahmen folgendermaßen definieren:

gem_dir / lib / gem_name / exception.rb

und definiert als:

module GemName

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

end

Ein Beispiel hierfür wäre so etwas in httparty

Für Ruby on Rails

Legen Sie sie in Ihrem lib / -Ordner unter einer Datei mit dem Namen Ausnahmen.rb ab, die ungefähr so ​​aussehen würde:

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

und du würdest es so benutzen:

raise Exceptions::InvalidUsername
Mike Lewis
quelle
Bei Edelsteinen müssen Sie möglicherweise auch die Ausnahmedatei einschließen. Siehe dieses Beispiel noch einmal von httparty: github.com/jnunemaker/httparty/blob/…
Jason Swett
37
Warum sie in das ExceptionsModul einordnen?
ABMagil
13
Ich würde denken, dass dies /libmöglicherweise nicht der Ort für Fehler ist. Sie sind sehr anwendungsspezifisch und ich habe den Eindruck, dass der von mir eingegebene /libCode Code ist, der in anderen Anwendungen wiederverwendet werden kann.
Wuliwong
1
Ruby on Rails-Anweisungen haben bei mir nicht funktioniert. Ist im typischen Fall ein zusätzlicher Schritt erforderlich, um diese neue Datei tatsächlich zu laden?
Meekohi
1
@ABMagil scheint ich muss, sonst wäre Unable to autoload constant Exceptions, expected /app/lib/exceptions.rb to define itdie andere Option eine Klasse pro Ausnahme, denke ich
ryan2johnson9
25

Ich denke, um zusammenhängende Quelldateien in Ihrem Projekt zu haben, sollten Sie Fehler in der Klasse definieren, in die sie möglicherweise geworfen werden, und nirgendwo anders.

Einige Erben können hilfreich sein - Namespaces sind gut darin, redundante Zeichenfolgen von Typnamen fernzuhalten - aber das ist eher Geschmackssache - Sie müssen nicht über Bord gehen, vorausgesetzt, Sie haben mindestens einen benutzerdefinierten Ausnahmetyp in Ihrer App, den Sie zur Unterscheidung verwenden zwischen "vorsätzlichen" und "zufälligen" Ausnahmefällen.

Dean Radcliffe
quelle
8
Während Sie theoretisch Recht haben, was passiert, wenn derselbe Fehler von verschiedenen Klassen in völlig unterschiedlichen Situationen ausgelöst werden kann?
Alain
1
@Alain Warum nicht die Fehler definieren, die von mehr als einer Klasse in einem Modul für Ausnahmen / Fehler verwendet werden, aber alle anderen in der einzelnen Klasse definiert lassen, die sie verwendet?
Scott W
@ScottW, In diesem Fall verlassen wir uns darauf, dass der Entwickler daran denkt, dies zu überprüfen.
Josh Saint Jacque
22

In Schienen können Sie app/errorsVerzeichnis erstellen

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

Starten Sie Spring / Server neu und es sollte es aufnehmen

schpet
quelle
Wie soll ich diese Ausnahmen auslösen?
Nikhil Wagh
@NikhilWagh entweder raise FooError, "Example message..."oderraise FooError.new("Example message...")
schpet
13

Dies ist eine alte Frage, aber ich wollte mitteilen, wie ich mit benutzerdefinierten Fehlern in Rails umgehe, einschließlich Anhängen von Fehlermeldungen, Testen und wie dies mit ActiveRecordModellen behandelt wird.

Benutzerdefinierten Fehler erstellen

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

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

Testen (am wenigsten)

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

Mit ActiveRecord

Ich denke, es ist erwähnenswert, dass bei der Arbeit mit einem ActiveRecordModell ein beliebtes Muster darin besteht, dem Modell einen Fehler hinzuzufügen, wie unten beschrieben, damit Ihre Validierungen fehlschlagen:

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

Wenn Validierungen ausgeführt werden, wird diese Methode auf die ActiveRecord::RecordInvalidFehlerklasse von ActiveRecord zurückgesetzt und führt dazu, dass Validierungen fehlschlagen.

Hoffe das hilft!

Matt
quelle
9

Um sicherzustellen, dass das automatische Laden in Rails 4.1.10 für mehrere benutzerdefinierte Fehlerklassen wie erwartet funktioniert, müssen Sie für jede Klasse separate Dateien angeben. Dies sollte in der Entwicklung mit seinem dynamischen Nachladen funktionieren.

So richte ich Fehler in einem aktuellen Projekt ein:

Im lib/app_name/error/base.rb

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

und in nachfolgenden benutzerdefinierten Fehlern, wie in lib/app_name/error/bad_stuff.rb

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

Sie sollten dann in der Lage sein, Ihre Fehler aufzurufen über:

 raise AppName::Error::BadStuff.new("Bad stuff just happened")
spyle
quelle
Und wenn Sie nicht für jeden neuen Fehler eine separate Datei wünschen, fügen Sie sie einfach alle einlib/app_name/error.rb
jlhonora
Ein bekommen uninitialized constant MyController::AppName. Ich rufe Raise in meinem Controller an
Nikhil Wagh