恢復,第二人稱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.