Révisez, impératif de la deuxième personne du singulier de recidere : - pour se rabattre, venir à rien; Pour réduire
assoc / update sur Recide Errors 'Ex-datadeferrordeferror-groupgetCurrentSanitizationLevel()createSuppressionMap(...)sanitize(Throwable) , sanitize(Throwable, IPersistentMap)ErrorForm personnalisée ex-info de Clojure est une construction très utile: vous pouvez joindre une carte des données arbitraires à une exception lancée, permettant au code qui a fait l'exception d'examiner, de connecter ou de traiter ces ex-data .
Par exemple, les déclarations assert fournissent des vérifications de santé mentales précieuses dans la logique des affaires, mais lorsqu'ils échouent, il est généralement très souhaitable de savoir comment ils ont échoué. On pourrait tenter d'insérer ces informations dans la chaîne d'exception, mais parfois les données pertinentes sont beaucoup trop importantes pour un message d'exception de tension. Au lieu de cela, la vérification de la propriété souhaitée et le lancement d'un ex-info avec toutes les données pertinentes jointes peuvent préserver la succisse des messages d'erreur tout en économisant le développeur énorme, en particulier lors du débogage au REP.
L'une des principales faiblesses de ex-info est que son utilisation peut encourager des exceptions ad hoc sans normes: si vous attrapez un ex-Info, son type est le même que tous les autres ex-infos, sa chaîne est arbitraire, et non Seulement vous ne pouvez pas compter sur une clé particulière apparaissant dans l' ex-data , il est tout à fait possible que la carte soit entièrement vide.
Si vous souhaitez profiter des avantages de l'utilisation largement ex-info dans un grand projet, mais vous souhaitez également conserver Une mesure de la santé mentale Les avantages des erreurs bien définies, il est probable que vous finirez par recourir, dans chaque composant logique de votre application, soit à la rupture avec les idiomes de Clojure universels, soit pour définir votre propre définition d'un ensemble standard de "fonctions de lancement" qui utilisent ex-info mais garantit une certaine rigidité: peut-être un préfixe commun aux chaînes d'exception, peut-être certaines clés garanties dans les cartes ex-data .
Le principal objectif de cette bibliothèque, Recide , est de fournir des outils pour faciliter ce processus. Il fournit des services publics pour définir des formulaires ex-info standard, ainsi que la capacité de vérifier au moment de la compilation qu'ils sont utilisés comme prévu.
Toutes les ex-info générées par les outils dans recide contiennent au moins deux touches:
:recide/error , dont la valeur est une instance d' ErrorForm .ErrorForm (par défaut dans Recide est :recide/type ). La documentation de l'API Clojure peut être trouvée ici. La documentation de l'API Java peut être trouvée ici.
recide.core/insist est analogue à assert . Sa signature est la même, et tout comme assert elle s'exécute uniquement lorsque clojure.core/*assert* est vrai.
Mais au lieu de lancer une AssertionError , il lance un ex-info avec une chaîne explicative du formulaire: "L'affirmation a échoué: <expression affirmée ou message fourni>". Le type dans l'ex-data est :recide/assertion . Il y a deux autres clés utilisées par insist :
:expression , dont la valeur est l'expression réelle contenue dans l' insist:values , dont la valeur est une carte de chaque variable utilisée dans l'expression à sa valeur au moment de l'échec. :values ne sont présentes que chaque fois que recide.impl/*capture-insists* est vraie, mais elle est fausse par défaut. Au temps de chargement de la bibliothèque, il est défini sur true si au moins l'un des éléments suivants est vrai:
La signature de insist est [[expr] [expr message]] , et l'ex-data dans l'ex-info résultant a le formulaire suivant:
{ :recide/type :recide/assertion ,
:expression <expr>
:values { ... }}Exemple utilisé:
( 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 a deux arités: ([type msg data] [type msg data cause]) . error construit un ex-info avec les data de carte jointes, dont le type (encore une fois, signifié par :recide/type par défaut) est type . Fournir une cause donne simplement à l'exception une cause selon Java Idiome.
( 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 a les deux mêmes arités. raise lance l'exception construite par error .
assoc / update sur Recide Errors 'Ex-data Fonctions de commodité: recide.core/assoc-error et recide.core/update-error chaque retourne les nouvelles exceptions du type d'origine, avec l'ex-data modifié comme avec assoc et update .
Parfois, il peut être pratique de passer et de manipuler une représentation de carte d'une erreur avant de la transformer en une exception et de la lancer. À cette fin, nous fournissons recide.core/error->map and recide.core/error-map->throwable qui font ce à quoi vous vous attendez.
De plus, nous fournissons une autre error-map «constructeur» pour les erreurs qui renvoie le formulaire de carte et la error-map? .
Pour toute erreur de type de type avec le message msg , data ex-data et cause , la carte persistante ressemble à ceci:
{ :recide/error <ErrorForm>,
:recide/type type,
:recide/msg msg,
:recide/cause cause,
:recide/data data} Toutes ces touches sauf :recide/error peut être modifiée en fournissant une ErrorForm personnalisée (voir ci-dessous pour plus de détails).
raise et error ne font pas grand-chose pour fournir des types d'exceptions standard. Pour y remédier davantage, Recide fournit deferror et deferror-group .
deferror deferror est une macro qui prend un nom d'erreur, un type et une chaîne "générique" qui préfixera les messages de toutes les erreurs de ce type. Il faut également, éventuellement, une collection de clés requises. Si les clés requises sont spécifiées, les erreurs de temps de compilation seront lancées à chaque fois que les outils définis par deferror sont utilisés sans spécifier ces clés explicitement dans le code source.
Exemple d'utilisation:
( deferror storage-timeout
:storage/timeout
" A storage operation timed out "
[ :method-at-fault :timeout-ms ]) Dans cet exemple, cet appel à deferror définira deux nouvelles macros, storage-timeout et raise-storage-timeout . Pour votre commodité, tout IDE compétent pourra accéder aux docstrings détaillés sur ces nouveaux VAR:
> ( 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}Si vous essayez d'utiliser l'une ou l'autre sans, dans votre carte de données, en spécifiant chacune des clés requises, le compilateur Clojure lancera une exception:
> ( 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 est une macro qui définit toute une famille d'erreurs. Il prend un nom d'erreur, une déclaration de type de base et un certain nombre de déclarations de sous-type.
Chaque déclaration de type de base doit être soit un mot clé représentant l'espace de noms commun pour ce groupe d'erreurs, soit un tuple dont le premier élément est un tel mot-clé et dont le deuxième élément est une séquence de clés requises. Les clés spécifiées ici seront nécessaires dans chaque sous-type.
Chaque déclaration de sous-type se compose d'une séquence dont le premier terme est un symbole, le deuxième terme est une chaîne générique pour l'erreur, et le troisième terme (facultatif) est une séquence de touches requises pour ce sous-type.
Exemple:
( deferror-group parse-err
( :query.invalid [ :expression ])
( find-spec " Invalid find spec " )
( inputs " Invalid inputs " [ :invalid ])) Dans cet exemple, il existe deux types d'erreur définis :: :query.invalid/find-spec et :query.invalid/inputs . Le premier nécessite :expression dans sa carte de données, mais la seconde nécessite à la fois :expression et :invalid .
Comme pour deferror , les services publics produits par deferror-group ont des docstrings détaillés:
> ( 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"}}Comme le montre précédemment, les clés requises génèrent des erreurs de temps de compilation lorsqu'elles sont omises.
> ( 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 Si nous utilisons des mots clés pour désigner les types d'erreur, il serait utile de pouvoir catch des erreurs au moyen de ces mots clés. Recide offre try* à cet effet. try* est une macro qui se développe à try de Clojure, qui est l'une des rares formes spéciales de Clojure. Dans la plupart des cas, try* alors devrait se comporter exactement comme try . Il diffère en ce qu'il expose une fonctionnalité catch améliorée. Vous pouvez attraper:
instance? Vérifiez.ErrorForm utilisée pour les construire)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`. Notez que vous pouvez utiliser des mots clés du formulaire :namespace/* comme des caractères génériques pour attraper des familles d'erreurs de revide, telles que celles définies par le deferror-group .
> ( try* ( raise :genus/species-1
" went extinct "
{ :year -1839421 })
( catch :genus/* e
( println ( :year ( ex-data e)))))
; ; -1839421 RECIDE fournit une collection d'outils pour acquérir une version désinfectée d'une exception qui devrait être considérée comme sûre à enregistrer (mais peut ne pas être utile en conséquence).
Cette classe contient une poignée de méthodes d'utilité statique:
getCurrentSanitizationLevel() Équivalent à deref'ing recide.sanex/*sanitization-level* .
createSuppressionMap(...)Crée un IPERSistentMap avec les mots clés appropriés correspondant aux args booléens.
sanitize(Throwable) , sanitize(Throwable, IPersistentMap) Raccourci vers Clojure IFN recide.sanex/sanitize .
ErrorForm personnalisée Par défaut, les erreurs soulevées par cette bibliothèque utilisent ex-info comme constructeur, recide.utils/serialize-throwable et recide.utils/deserialize-throwable pour la sérialisation (DE), et sous forme de map qu'ils utilisent :recide/type , :recide/msg , :recide/data , et :recide/cause comme mots clés standard.
En définissant une nouvelle ErrorForm , vous pouvez modifier tout ce comportement pour votre propre bibliothèque. En modifiant les mots clés, vous pouvez "marquer" les erreurs sortant de votre bibliothèque. Vous pouvez échanger ex-info contre un autre constructeur de la même Arity, qui devrait renvoyer un IExceptionInfo . Par exemple, les préoccupations liées à Java Interop pourraient motiver la création d'une nouvelle classe d'exception, tandis que les idiomes de Clojure pourraient motiver la conservation de la compatibilité de l'ex-info.
Vous pouvez facilement définir votre personnalisation avec recide.core/def-error-form , n'étant que les méthodes que vous souhaitez remplacer; Méthodes non spécifiées par défaut pour réciter la bibliothèque par défaut.
Les méthodes d'erreur de traitement dans recide.core sont agnostiques à la ErrorForm spécifique utilisée. Pour créer des erreurs à l'aide d'une ErrorForm personnalisée, vous pouvez facilement générer une suite complète de méthodes de revide adaptées spécifiquement à vos personnalisations avec 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.