恢复,第二人称recidere的现场命令: - 倒下,毫无身心;削减
assoc / update重新错误错误的EX-DATAdeferrordeferror-groupgetCurrentSanitizationLevel()createSuppressionMap(...)sanitize(Throwable) , sanitize(Throwable, IPersistentMap)ErrorFormClojure的ex-info是一个非常有用的结构:您可以将任意数据的地图附加到抛出的例外,从而允许捕获例外的代码来检查,日志或以其他方式处理此ex-data 。
例如, assert语句提供了商业逻辑中有价值的理智检查,但是当他们失败时,通常是非常希望知道它们是如何失败的。可以尝试将这些信息插入异常字符串中,但有时相关数据对于一个顽强的异常消息来说太大了。取而代之的是,检查所需的属性并使用所附的所有相关数据投掷ex-info可以保留错误消息的简洁性,同时节省开发人员巨大的时间,尤其是在REPP进行调试时。
ex-info的主要弱点之一是,它的使用可以鼓励无标准的临时例外:如果您抓住了前侵入式,它的类型与所有其他前Infos相同,其字符串是任意的,而不是您只能依靠ex-data中出现的任何特定键,地图很可能完全空了。
如果您想在一个大型项目中享受广泛使用ex-info的好处,但您也想保留理智的措施定义明确的错误的好处,您最终可能会在应用程序的每个逻辑组件中求助于使用通用的clojure成语,或者定义自己的定义一组标准的“投掷功能”,这些功能使用ex-info但可以保证一定的刚性:也许是例外字符串的常见前缀,也许是ex-data图中的某些保证键。
该库的主要目的是,恢复,是提供简化此过程的工具。它提供了定义标准前INFO表格的实用程序,以及在编译时间检查的能力。
recide中工具生成的所有ex-info地图至少包含两个密钥:
:recide/error ,其值是ErrorForm的实例。ErrorForm定义的“类型键”(recide中的默认值为:recide/type )。 可以在此处找到Clojure API文档。 Java API文档可以在此处找到。
recide.core/insist类似于assert 。它的签名是相同的,就像assert它仅在clojure.core/*assert*为true时执行。
但是,它没有抛出AssertionError ,而是以形式的解释性字符串抛出了一个ex-info :“断言失败:<soptert optert opertert ofert oferters conseption>”。 EX-DATA中的类型是:recide/assertion 。 insist使用的另外两个密钥:
:expression ,其值是insist中包含的实际表达式:values ,其值是从表达式中使用的每个变量到其在失败时值的映射。 :只有每当recide.impl/*capture-insists*为true时, :values才存在,但默认情况下是错误的。在库加载时间,如果以下至少一个是正确的,则将其设置为true:
insist的签名是[[expr] [expr message]] ,而由此产生的Ex-Info中的前数据具有以下形式:
{ :recide/type :recide/assertion ,
:expression <expr>
:values { ... }}使用中的示例:
( 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有两个ARITIT: ([type msg data] [type msg data cause]) 。 error构建了带有映射data ex-info ,其类型(再次表示为:recide/type默认为默认情况下)是type 。根据Java Idiom,提供cause仅给出了例外原因。
( 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具有相同的两个Arities。 raise引发由error构建的异常。
assoc / update重新错误错误的EX-DATA便利功能: recide.core/assoc-error和recide.core/update-error每个返回原始类型的新例外,ex-data已修改为与assoc和update一样。
有时,在将误差变回异常并扔掉之前,传递并操纵错误表示错误可能会很方便。为此,我们提供了recide.core/error->map和recide.core/error-map->throwable ,可以按照您的预期进行。
此外,我们为返回地图表单的错误以及相应的谓词错误映射提供了替代error-map “构造函数” error-map? 。
对于带有消息msg ,ex-data data和cause任何类型type错误,持久地图看起来像这样:
{ :recide/error <ErrorForm>,
:recide/type type,
:recide/msg msg,
:recide/cause cause,
:recide/data data}除了:可以通过提供自定义的ErrorForm来修改所有这些密钥:recide/error (有关详细信息,请参见下文)。
raise和error几乎没有提供标准异常类型。为了进一步解决这一问题,恢复提供了deferror和deferror-group 。
deferror deferror是一个宏,它采用错误名称,类型和“通用”字符串,将前缀此类错误的所有错误。还可以选择地收集了必需的密钥。如果指定了必要的键,则在使用deferror定义的工具时,将抛出编译时错误,而无需在源代码中明确指定这些键。
示例用法:
( deferror storage-timeout
:storage/timeout
" A storage operation timed out "
[ :method-at-fault :timeout-ms ])在此示例中, deferror呼吁将定义两个新的宏, storage-timeout和raise-storage-timeout 。为了方便起见,任何有能力的IDE都将能够在这些新Vars上访问详细的Docstrings:
> ( 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}如果您尝试在数据图中使用其中的任何一种,则指定每个必需的键,则Clojure编译器将抛出一个例外:
> ( 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是一个宏,它定义了整个错误家族。它需要一个错误名称,基本类型的声明和一些子类型声明。
每个基本类型声明必须是代表此组错误的通用命名空间的关键字,或者其第一个元素是这样的关键字,其第二个元素是所需键的序列。每个子类型中都需要此处指定的键。
每个子类型声明由一个序列组成,其第一项是符号,第二项是误差的通用字符串,第三项(可选)项是该子类型的必需键序列。
例子:
( deferror-group parse-err
( :query.invalid [ :expression ])
( find-spec " Invalid find spec " )
( inputs " Invalid inputs " [ :invalid ]))在此示例中,定义了两种错误类型:: :query.invalid/find-spec和:query.invalid/inputs 。第一个需要:expression ,但第二个需要同时:expression和:invalid 。
与deferror一样,由deferror-group生产的公用事业也具有详细的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"}}如前所述,所需键在省略时会产生编译时错误。
> ( 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 如果我们使用关键字来指定错误类型,那么能够通过这些关键字catch错误将很有用。 RECIDE为此提供了try* 。 try*是一个扩展到Clojure的try的宏,这是Clojure的少数特殊形式之一。在大多数情况下, try*应该完全像try一样。它的不同之处在于它揭示了增强的catch功能。您可以抓住:
instance?检查。ErrorForm如何)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`.请注意,您可以使用形式的关键字:namespace/*作为通配符来捕获回收错误的家庭,例如由deferror-group定义的错误。
> ( try* ( raise :genus/species-1
" went extinct "
{ :year -1839421 })
( catch :genus/* e
( println ( :year ( ex-data e)))))
; ; -1839421 recide提供了一系列工具,用于获取一个例外的消毒版本,该版本应被认为是安全登录的(但结果可能没有用)。
该类包含一些静态实用方法:
getCurrentSanitizationLevel()等效于Deref'ing recide.sanex/*sanitization-level* 。
createSuppressionMap(...)使用与布尔Args相对应的适当关键字创建一个iPersistentMap。
sanitize(Throwable) , sanitize(Throwable, IPersistentMap)快捷方式到Clojure ifn recide.sanex/sanitize 。
ErrorForm默认情况下,该库提出的错误使用ex-info用作构造函数, recide.utils/serialize-throwable and recide.utils/deserialize-throwable用于(de)序列化,在地图形式中使用:recide/type ,:recide, :recide/msg , :recide/data ,以及:recide/cause作为其标准关键字。
通过定义新的ErrorForm ,您可以为自己的库更改所有这些行为。通过修改关键字,您可以“品牌”出现在库中。您可以将ex-info交换为同一Arity的另一个构造函数,这将期望返回IExceptionInfo 。例如,与Java Interop相关的问题可能会激励创建一个新的异常类,而Clojure Idioms可能会激发保留前INFO兼容性。
您可以通过recide.core/def-error-form ,仅覆盖您希望覆盖的方法;方法未指定默认值以恢复库默认值。
recide.core中的错误处理方法是对所使用的特定ErrorForm不可知的。要使用自定义ErrorForm创建错误,您可以轻松地生成一套完整的回收方法,专门针对您的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.