Recupere, o presente singular de segunda pessoa Imperativo de recidere : - Para voltar, venha em nada; Para reduzir
assoc / update no Ex-Data de Recuperação de Errosdeferrordeferror-groupgetCurrentSanitizationLevel()createSuppressionMap(...)sanitize(Throwable) , sanitize(Throwable, IPersistentMap)ErrorForm personalizado ex-info do Clojure é um construto muito útil: você pode anexar um mapa de dados arbitrários a uma exceção lançada, permitindo o código que pegou a exceção para examinar, registrar ou processar esse ex-data .
Por exemplo, as declarações assert fornecem verificações valiosas de sanidade na lógica de negócios, mas quando falham, geralmente é altamente desejável saber como falharam. Pode -se tentar inserir essas informações na string de exceção, mas às vezes os dados relevantes são muito grandes para uma mensagem de exceção. Em vez disso, checar a propriedade desejada e lançar um ex-info com todos os dados relevantes anexados pode preservar a sucessão das mensagens de erro ao salvar o enorme tempo do desenvolvedor, especialmente quando a depuração no REPL.
Uma das principais fraquezas do ex-info é que seu uso pode incentivar exceções ad-hoc, sem padrões: se você pegar um ex-info, seu tipo é o mesmo que todos os outros ex-infainos, sua string é arbitrária e não Somente você não pode contar com nenhuma chave específica que apareça nos ex-data , é bem possível que o mapa esteja totalmente vazio.
Se você deseja aproveitar os benefícios de usar ex-info amplamente em um grande projeto, mas também deseja reter uma medida de sanidade Os benefícios de erros bem definidos, é provável que você finalmente recorrente, em cada componente lógico do seu aplicativo, seja para quebrar com idiomas universais de clojure ou definir sua própria definição de um conjunto padrão de "funções de arremesso" que usam ex-info , mas garantem uma certa rigidez: talvez um prefixo comum às seqüências de exceção, talvez certas chaves garantidas nos mapas ex-data .
O objetivo principal desta biblioteca, Recida , é fornecer ferramentas para facilitar esse processo. Ele fornece serviços públicos para definir formulários ex-Info padrão, bem como a capacidade de verificar em tempo de compilação que eles estão sendo usados como pretendido.
Todos os mapas ex-info gerados por ferramentas em recide contêm pelo menos duas teclas:
:recide/error , cujo valor é uma instância do ErrorForm .ErrorForm (o padrão no RECIDE IS :recide/type ). A documentação da API da Clojure pode ser encontrada aqui. A documentação da API Java pode ser encontrada aqui.
recide.core/insist é análogo a assert . Sua assinatura é a mesma, e assim como assert ela é executada apenas quando clojure.core/*assert* é verdadeiro.
Mas, em vez de jogar um AssertionError , ele lança um ex-info com uma sequência explicativa do formulário: "Afirção falhou: <expressão afirmada ou mensagem fornecida>". O tipo nos ex-dados é :recide/assertion . Existem duas outras chaves usadas por insist :
:expression , cujo valor é a expressão real contida no insist:values , cujo valor é um mapa de cada variável usada na expressão para seu valor no momento da falha. :values só estão presentes sempre que recide.impl/*capture-insists* é verdadeiro, mas é falso por padrão. No tempo de carregamento da biblioteca, ele está definido como TRUE se pelo menos um dos seguintes itens for verdadeiro:
A assinatura de insist é [[expr] [expr message]] , e os ex-dados no ex-info resultante têm a seguinte forma:
{ :recide/type :recide/assertion ,
:expression <expr>
:values { ... }}Exemplo em uso:
( 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 possui duas AUTIDADES: ([type msg data] [type msg data cause]) . error constrói um ex-info com os data do mapa anexado, cujo tipo (novamente, significado por :recide/type por padrão) é type . O fornecimento de uma cause apenas dá a exceção uma causa de acordo com o 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 TEM os mesmos duas áreas. raise lança a exceção construída por error .
assoc / update no Ex-Data de Recuperação de Erros Funções de conveniência: recide.core/assoc-error e recide.core/update-error Cada retorno novas exceções do tipo original, com os ex-dados modificados como assoc e update .
Às vezes, pode ser conveniente passar e manipular uma representação do mapa de um erro antes de transformá -lo em uma exceção e jogá -lo. Para esse fim, fornecemos recide.core/error->map recide.core/error-map->throwable .
Além disso, fornecemos um "construtor" error-map alternativo para erros que retornam o formulário do mapa e o error-map? .
Para qualquer erro de type de tipo com msg de mensagem, data ex-dados e cause , o mapa persistente se parece com o seguinte:
{ :recide/error <ErrorForm>,
:recide/type type,
:recide/msg msg,
:recide/cause cause,
:recide/data data} Todas essas chaves, exceto :recide/error podem ser modificadas fornecendo uma ErrorForm personalizada (veja abaixo para obter detalhes).
raise e error fazem muito pouco para fornecer tipos de exceção padrão. Para resolver isso ainda mais, o Recide fornece deferror e deferror-group .
deferror deferror é uma macro que leva um nome de erro, um tipo e uma string "genérica" que prefixará as mensagens de todos os erros desse tipo. Opcionalmente, opcionalmente, leva uma coleção de chaves necessárias. Se as chaves necessárias forem especificadas, os erros de compilação no tempo serão lançados sempre que as ferramentas definidas pelo deferror serão usadas sem especificar essas chaves explicitamente no código-fonte.
Exemplo de uso:
( deferror storage-timeout
:storage/timeout
" A storage operation timed out "
[ :method-at-fault :timeout-ms ]) Neste exemplo, essa chamada para deferror definirá duas novas macros, storage-timeout e raise-storage-timeout . Para sua conveniência, qualquer IDE competente poderá acessar documentos detalhados nesses novos VARs:
> ( 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}Se você tentar usar qualquer um deles sem, no seu mapa de dados, especificando cada uma das teclas necessárias, o compilador de clojure lançará uma exceção:
> ( 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 é uma macro que define uma família inteira de erros. É preciso um nome de erro, uma declaração do tipo base e algum número de declarações de subtipo.
Cada declaração do tipo base deve ser uma palavra -chave que representa o espaço de nome comum para esse grupo de erros, ou uma tupla cujo primeiro elemento é uma palavra -chave e cujo segundo elemento é uma sequência de teclas necessárias. As chaves especificadas aqui serão necessárias em todos os subtipo.
Cada declaração de subtipo consiste em uma sequência cujo primeiro termo é um símbolo, o segundo termo é uma sequência genérica para o erro e o terceiro termo (opcional) é uma sequência de teclas necessárias para esse subtipo.
Exemplo:
( deferror-group parse-err
( :query.invalid [ :expression ])
( find-spec " Invalid find spec " )
( inputs " Invalid inputs " [ :invalid ])) Neste exemplo, existem dois tipos de erro definidos :: :query.invalid/find-spec e :query.invalid/inputs . O primeiro requer :expression em seu mapa de dados, mas o segundo requer ambos :expression e :invalid .
Assim como deferror , os utilitários produzidos pelo deferror-group têm Docstrings detalhados:
> ( 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"}}Como visto antes, as teclas necessárias geram erros de tempo de compilação quando omitidos.
> ( 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 Se estivermos usando palavras -chave para designar tipos de erro, seria útil poder catch erros por meio dessas palavras -chave. O RECIDE fornece try* para esse fim. try* é uma macro que se expande para a try de Clojure, que é uma das poucas formas especiais de Clojure. Na maioria dos casos, try* então deve se comportar exatamente como try . Difere na medida em que expõe a funcionalidade catch aprimorada. Você pode pegar:
instance? Verifique.ErrorForm usada para construí -las)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`. Observe que você pode usar palavras-chave do formulário :namespace/* como curingas para capturar famílias de erros de recuperação, como os definidos pelo deferror-group .
> ( try* ( raise :genus/species-1
" went extinct "
{ :year -1839421 })
( catch :genus/* e
( println ( :year ( ex-data e)))))
; ; -1839421 A Recide fornece uma coleção de ferramentas para adquirir uma versão higienizada de uma exceção que deve ser considerada segura para registrar (mas pode não ser útil como resultado).
Esta classe contém um punhado de métodos de utilidade estática:
getCurrentSanitizationLevel() Equivalente a deref'ing recide.sanex/*sanitization-level* .
createSuppressionMap(...)Cria um IPERSISTENTMAP com as palavras -chave apropriadas correspondentes aos args booleanos.
sanitize(Throwable) , sanitize(Throwable, IPersistentMap) Atalho para clojure ifn recide.sanex/sanitize .
ErrorForm personalizado Por padrão, os erros levantados por esta biblioteca usam ex-info como construtor, recide.utils/serialize-throwable com e recide.utils/deserialize-throwable para (de) serialização e, na forma de mapa, eles usam :recide/type , :recide/msg , :recide/data e :recide/cause como suas palavras -chave padrão.
Ao definir uma nova ErrorForm , você pode alterar todo esse comportamento para sua própria biblioteca. Ao modificar as palavras -chave, você pode "erros de marca" saindo da sua biblioteca. Você pode trocar ex-info por outro construtor da mesma arity, que deverá devolver um IExceptionInfo . Por exemplo, preocupações relacionadas à interop Java podem motivar a criação de uma nova classe de exceção, enquanto os idiomas de clojure podem motivar a retenção de compatibilidade ex-Info.
Você pode definir facilmente sua personalização com recide.core/def-error-form , substituindo apenas os métodos que você deseja substituir; Métodos não especificados padrão para recuperar os padrões da biblioteca.
Os métodos de manuseio de erros em recide.core são agnósticos à ErrorForm específica usada. Para criar erros usando uma ErrorForm personalizada, você pode gerar facilmente um conjunto completo de métodos de recuperação adaptados especificamente às suas personalizações com 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.