Recide, zweitpersonen Singular Gegenwart im Rahmen recidere : - zurückfallen, kommen Sie nicht; zu kürzen
assoc / update in die Ex-Data 'Ex-Data von Fehlerndeferrordeferror-groupgetCurrentSanitizationLevel()createSuppressionMap(...)sanitize(Throwable) , sanitize(Throwable, IPersistentMap)ErrorForm ex-info von Clojure ist ein sehr nützliches Konstrukt: Sie können eine Karte willkürlicher Daten an eine geworfene Ausnahme anhängen, die Code ermöglicht, mit der die Ausnahme diese ex-data untersucht, protokolliert oder auf andere Weise verarbeiten kann.
Beispielsweise liefern assert -Aussagen wertvolle Gesundheitsprüfungen in der Geschäftslogik, aber wenn sie versagen, ist es normalerweise sehr wünschenswert zu wissen, wie sie fehlgeschlagen sind. Man könnte versuchen, diese Informationen in die Ausnahmebestufe einzufügen, aber manchmal sind die relevanten Daten für eine Meldung von Ausnahmemahlen viel zu groß. Wenn Sie stattdessen die gewünschte Eigenschaft überprüfen und einen ex-info mit allen relevanten Daten werfen, erhalten Sie die Prägnanz von Fehlermeldungen und speichern gleichzeitig den Entwickler enormen Zeitmengen, insbesondere beim Debuggen der Repl.
Eine der wichtigsten Schwächen von ex-info ist, dass seine Verwendung Ad-hoc-Ausnahmen ohne Standards fördern kann Nur können Sie nicht auf einen bestimmten Schlüssel zählen, der in der ex-data auftaucht. Es ist durchaus möglich, dass die Karte völlig leer ist.
Wenn Sie die Vorteile der Verwendung von ex-info in einem großen Projekt genießen möchten, aber Sie möchten auch behalten ein Maß an Verstand Die Vorteile gut definierter Fehler, es ist wahrscheinlich, dass Sie in jeder logischen Komponente Ihrer Anwendung irgendwann zurückgreifen, entweder auf das Brechen mit universellen Clojure-Idiomen oder zur Definition eines eigenen Standards von "Wurffunktionen", die ex-info , aber garantieren Sie eine bestimmte Starrheit: möglicherweise ein gemeinsames Präfix für die Ausnahmebestimmungen, möglicherweise bestimmte garantierte Schlüssel in den ex-data -Karten.
Der Hauptzweck dieser Bibliothek ist die Bereitstellung von Tools, um diesen Prozess zu erleichtern. Es bietet Versorgungsunternehmen für die Definition von Standard-Ex-Info-Formularen sowie die Kapazität für die Überprüfung der Kompilierung , die sie als beabsichtigt verwendet werden.
Alle ex-info -Karten, die von Tools in recide erzeugt werden, enthalten mindestens zwei Schlüssel:
:recide/error , dessen Wert eine Instanz der ErrorForm ist.ErrorForm definierter "Typtaste" (Standard in Recide ist :recide/type ). Clojure -API -Dokumentation finden Sie hier. Java API -Dokumentation finden Sie hier.
recide.core/insist ist analog zu assert . Seine Signatur ist die gleiche und assert nur dann, wenn es nur dann ausgeführt wird, wenn clojure.core/*assert* wahr ist.
Anstatt einen AssertionError zu werfen, wirft es ein ex-info mit einer erklärenden Zeichenfolge des Formulars aus: "Behauptung fehlgeschlagen: <Asserted Expression oder Lieferung von Nachricht>". Der Typ in der Ex-Daten lautet :recide/assertion . Es gibt zwei weitere Schlüssel, die von insist verwendet werden:
:expression , dessen Wert der tatsächliche Ausdruck ist, der im insist enthalten ist:values , deren Wert eine Karte aus jeder Variablen ist, die zum Zeitpunkt des Versagens im Ausdruck zum Wert verwendet wird. :values sind nur dann vorhanden, wenn recide.impl/*capture-insists* ist wahr, aber standardmäßig falsch. In der Ladezeit der Bibliothek wird es auf True gesetzt, wenn mindestens einer der folgenden Aussagen wahr ist:
Die Signatur von insist ist [[expr] [expr message]] , und die Ex-Daten im resultierenden Ex-Info hat das folgende Formular:
{ :recide/type :recide/assertion ,
:expression <expr>
:values { ... }}Beispiel bei der Verwendung:
( let [y not
x true ]
( insist ( y x)))
; ; Unhandled clojure.lang.ExceptionInfo
; ; Assertion failed: (y x)
; ; {:expression (y x),
; ; :values {y #function[clojure.core/not],
; ; x true},
; ; :recide/type :recide/assertion} recide.core/error hat zwei arities: ([type msg data] [type msg data cause]) . error konstruiert ein ex-info mit den angehängten data , deren Typ (erneut von :recide/type standardmäßig typisch) type ist. Die Lieferung einer cause liefert lediglich die Ausnahme eine Ursache nach Java Idiom.
( let [x " not b! haha " ]
( raise :my-type
" my explanation! "
{ :a " a "
:b x}))
; ; #error {
; ; :cause "my explanation!"
; ; :data {:a "a",
; ; :b "not b! haha",
; ; :recide/type :my-type, :recide/error #object[...]}
; ; :via
; ; [{:type clojure.lang.ExceptionInfo
; ; :message "my explanation!"
; ; :data {:a "a",
; ; :b "not b! haha",
; ; :recide/type :my-type, :recide/error #object[...]}
; ; :at [clojure.core$ex_info invokeStatic "core.clj" 4725]}]
; ; :trace ... } recide.core/raise hat die gleichen zwei arities. raise wirft die nach error konstruierte Ausnahme aus.
assoc / update in die Ex-Data 'Ex-Data von Fehlern Convenience-Funktionen: recide.core/assoc-error und recide.core/update-error jeder gibt neue Ausnahmen des ursprünglichen Typs zurück, wobei die Ex-Data wie bei assoc und update geändert wird.
Manchmal kann es bequem sein, eine Kartendarstellung eines Fehlers zu übergeben und zu manipulieren, bevor er ihn wieder in eine Ausnahme umwandelt und wirft. Zu diesem Zweck bieten wir recide.core/error->map und recide.core/error-map->throwable was so tun, was Sie erwarten würden.
Darüber hinaus bieten wir eine alternative error-map "Konstruktorin" für Fehler, die das MAP-Formular zurückgeben, und die entsprechende Prädikat error-map? .
Für einen beliebigen Fehler vom Typ type mit Message msg , Ex-Data- data und Ursache cause sieht die persistente Karte so aus:
{ :recide/error <ErrorForm>,
:recide/type type,
:recide/msg msg,
:recide/cause cause,
:recide/data data} Alle diese Schlüssel außer :recide/error können durch Angabe eines angepassten ErrorForm geändert werden (siehe unten für Einzelheiten).
raise und error tun nur sehr wenig, um Standard -Ausnahmetypen bereitzustellen. Um dies weiter zu beheben, bietet Recide deferror und deferror-group .
deferror deferror ist ein Makro, das einen Fehlernamen, einen Typ und eine "generische" Zeichenfolge aufweist, die die Nachrichten aller Fehler dieses Typs vorfixiert. Optional ist auch optional eine Sammlung von erforderlichen Schlüssel erforderlich. Wenn die erforderlichen Schlüssel angegeben werden, werden Kompilierungszeitfehler jederzeit geworfen, wenn die durch deferror definierten Tools verwendet werden, ohne diese Schlüssel ausdrücklich im Quellcode anzugeben.
Beispiel Verwendung:
( deferror storage-timeout
:storage/timeout
" A storage operation timed out "
[ :method-at-fault :timeout-ms ]) In diesem Beispiel definiert dieser Aufruf bei deferror zwei neue Makros: storage-timeout und raise-storage-timeout . Für Ihre Bequemlichkeit kann jede kompetente IDE auf detaillierte Docstrings auf diesen neuen VARs zugreifen:
> ( clojure.repl/doc storage-timeout)
; ; -------------------------
; ; my-ns/storage-timeout
; ; [[detail-str data] [detail-str data cause]]
; ; Macro
; ; Records this raise-site under :storage/timeout in recide, and expands into the equivalent of:
; ;
; ; (ex-info (str "A storage operation timed out: " detail-str)
; ; (assoc data :recide/type :storage/timeout)
; ; cause)
; ;
; ; The following keys are required in the data-map:
; ; #{:method-at-fault,
; ; :timeout-ms}
> ( clojure.repl/doc raise-storage-timeout)
; ; -------------------------
; ; [[detail-str data] [detail-str data cause]]
; ; Macro
; ; Records this raise-site under :storage/timeout in recide, and expands into:
; ;
; ; (raise :storage/timeout
; ; (str "A storage operation timed out: " detail-str)
; ; data
; ; cause)
; ;
; ; The following keys are required in the data-map:
; ; #{:method-at-fault,
; ; :timeout-ms}Wenn Sie versuchen, eine davon ohne in Ihrer Datenkarte zu verwenden, wird der Clojure -Compiler eine Ausnahme ausgelöst:
> ( raise-storage-timeout " blah " { :method-at-fault 'not-really-a-method})
; ; Unhandled clojure.lang.ExceptionInfo
; ; Assertion failed: storage-timeout requires the following missing
; ; keys in its data: :timeout-msdeferror-group deferror-group ist ein Makro, das eine ganze Familie von Fehlern definiert. Es nimmt einen Fehlernamen, eine Erklärung des Basistyps und eine gewisse Anzahl von Subtyp-Deklarationen an.
Jede Basistypdeklaration muss entweder ein Schlüsselwort sein, das den gebräuchlichen Namespace für diese Gruppe von Fehlern darstellt, oder ein Tupel, dessen erstes Element ein solches Schlüsselwort ist und dessen zweites Element eine Folge der erforderlichen Schlüssel ist. Hier angegebene Schlüssel sind in jedem Subtyp erforderlich.
Jede Subtyp -Deklaration besteht aus einer Sequenz, deren erster Term ein Symbol ist, der zweite Term eine generische Zeichenfolge für den Fehler und der dritte (optionale) Term eine Abfolge von erforderlichen Schlüssel für diesen Subtyp.
Beispiel:
( deferror-group parse-err
( :query.invalid [ :expression ])
( find-spec " Invalid find spec " )
( inputs " Invalid inputs " [ :invalid ])) In diesem Beispiel sind zwei Fehlertypen definiert :: :query.invalid/find-spec und :query.invalid/inputs . Der erste erfordert :expression in seiner Datenkarte, aber die zweite erfordert sowohl :expression als auch :invalid .
Wie bei deferror haben die von deferror-group produzierten Versorgungsunternehmen detaillierte Docstrings:
> ( clojure.repl/doc parse-err)
; ; -------------------------
; ; recide/parse-err
; ; [[subtype detail-str data] [subtype detail-str data cause]]
; ; Macro
; ; Records this raise-site under :query.invalid/<subtype> in recide, and expands into the
; ; equivalent of:
; ;
; ; (ex-info (str "<subtype-generic-str>: " detail-str)
; ; (assoc data
; ; :recide/type
; ; :query.invalid/<subtype>)
; ; cause)
; ;
; ; The following map shows, for each subtype, what keywords are required in
; ; the data map, and what the generic portion of the string will be:
; ;
; ; {:find-spec {:required #{:expression},
; ; :generic-str "Invalid find spec"},
; ; :inputs {:required #{:expression :invalid},
; ; :generic-str "Invalid inputs"}}
> ( clojure.repl/doc raise-parse-err)
; ; -------------------------
; ; recide/raise-parse-err
; ; [[subtype detail-str data] [subtype detail-str data cause]]
; ; Macro
; ; Records this raise-site under :query.invalid/<subtype> in recide, and expands into:
; ;
; ; (raise :query.invalid/<subtype>
; ; (str "<subtype-generic-str>: " detail-str)
; ; data
; ; cause)
; ;
; ; The following map shows, for each subtype, what keywords are required in
; ; the data map, and what the generic portion of the string will be:
; ;
; ; {:find-spec {:required #{:expression},
; ; :generic-str "Invalid find spec"},
; ; :inputs {:required #{:expression :invalid},
; ; :generic-str "Invalid inputs"}}Wie bereits erwähnt, generieren die erforderlichen Schlüssel bei der Auslassung Kompilierungszeitfehler .
> ( raise-parse-err :inputs " detailed this, detailed that " { :expression nil })
; ; Unhandled clojure.lang.ExceptionInfo
; ; Assertion failed: parse-err called with subtype :inputs requires
; ; the following missing keys in its data: :invalid Wenn wir Schlüsselwörter verwenden, um Fehlertypen zu benennen, wäre es nützlich, mit diesen Schlüsselwörtern Fehler catch . Recide bietet try* für diesen Zweck. try* ist ein Makro, das sich auf Clojure's try ausdehnt, einer der wenigen besonderen Formen von Clojure. try* und sollten sich genau wie try verhalten. Es unterscheidet sich darin, dass es verbesserte catch -Funktionalität enthüllt. Sie können fangen:
instance? Überprüfen.ErrorForm mit der sie konstruieren werden)recide.core/try*
[( try* expr* catch-clause* finally-clause?)]
Macro
Expands to Clojure's try Special Form, allowing for enhanced `catch` clauses:
You can catch:
* Classes/Interfaces ( represents an instance? check)
`( catch RuntimeException e ...)`
* keywords ( recide error types ; fully-qualified: :namspace/name, wildcard: :namespace/*)
`( catch :library/error e ...)`
* arbitrary predicates
`( catch bad-error? e ...)`
You can also catch conjunctions/disjunctions of these:
* conjunction
`( catch :and [RuntimeException :library/error bad-error?] e ...)`
* disjunction
`( catch :or [IllegalArgumentException :library/error bad-error?] e ...)`
You can also negate each of these:
`( catch ( :not RuntimeException) e ...)`
`( catch :and [( :not RuntimeException) :library/* ] e ...)`
Otherwise, behavior should match 'normal' catch clauses in `clojure.core/try`. Beachten Sie, dass Sie Schlüsselwörter des Formulars verwenden können :namespace/* als Wildcards, um Familien mit Rückgang von Fehlern zu fangen, z deferror-group
> ( try* ( raise :genus/species-1
" went extinct "
{ :year -1839421 })
( catch :genus/* e
( println ( :year ( ex-data e)))))
; ; -1839421 Recide bietet eine Sammlung von Tools, um eine sanitäre Version einer Ausnahme zu erwerben, die als sicher zu protokolliert werden sollte (ist jedoch möglicherweise nicht nützlich).
Diese Klasse enthält eine Handvoll statischer Nutzenmethoden:
getCurrentSanitizationLevel() Äquivalent zu Deref'ing recide.sanex/*sanitization-level* .
createSuppressionMap(...)Erstellt eine ipersistentmap mit den entsprechenden Schlüsselwörtern, die den Booleschen Argumenten entsprechen.
sanitize(Throwable) , sanitize(Throwable, IPersistentMap) Verknüpfung zu Clojure ifn recide.sanex/sanitize .
ErrorForm recide.utils/deserialize-throwable :recide/type ex-info recide.utils/serialize-throwable die in dieser Bibliothek aufgeworfen :recide/msg , :recide/data und :recide/cause sie als Standard -Schlüsselwörter.
Durch das Definieren eines neuen ErrorForm können Sie dieses Verhalten für Ihre eigene Bibliothek ändern. Durch Ändern der Schlüsselwörter können Sie "Marken" -Fehler aus Ihrer Bibliothek "" "Marken". Sie können ex-info gegen einen anderen Konstruktor derselben Arität austauschen, von dem erwartet wird, dass er ein IExceptionInfo zurückgibt. Zum Beispiel könnten Bedenken im Zusammenhang mit Java Interop motivieren, eine neue Ausnahmegebieter zu schaffen, während Clojure-Idiome möglicherweise die Bleibkompatibilität bei der Ex-Info motivieren.
Sie können Ihre Anpassung einfach mit recide.core/def-error-form definieren und nur die Methoden überschreiben, die Sie überschreiben möchten. Methoden nicht angegeben, um die Bibliotheksausstellungen zu reduzieren.
Die Fehlerbehandlungsmethoden in recide.core sind für die verwendete spezifische ErrorForm agnostisch. Um Fehler mithilfe eines benutzerdefinierten ErrorForm zu erstellen , können Sie problemlos eine vollständige Suite von Recide-Methoden erstellen, die speziell auf Ihre Anpassungen mit recide.core/generate-library! .
( ns my-library.error
( :require [recide.core :as rc]))
( rc/def-error-form custom-error-form
( type-kw [_] :my-library/type )
( constructor [_] my-library/error-constructor)
; ; all other methods are filled out with recide defaults.
( def ^:dynamic *capture-insists?* true )
( rc/generate-library! custom-error-form *capture-insists?*)
; ; recide.core/generate-library!
; ; [custom-error capture-flag]
; ; Macro
; ; Generates and defs custom versions of the following recide.core methods, tailored specifically
; ; to custom-error, with variable capture in the generated insist subject to capture-flag.
; ; * error
; ; * error?
; ; * error-map
; ; * error-map?
; ; * throwable->error-map
; ; * raise
; ; * insist
; ; * deferror
; ; * deferror-group
; ;
; ; custom-error should be an instance of recide.error/ErrorForm (see def-error-form).
; ; capture-flag must be resolvable to a dynamic var.