Recide, present singular orang kedua kehadiran recidere : - untuk mundur, menjadi sia -sia; untuk mengurangi
assoc / update menjadi Ex-Data Errors 'Recidedeferrordeferror-groupgetCurrentSanitizationLevel()createSuppressionMap(...)sanitize(Throwable) , sanitize(Throwable, IPersistentMap)ErrorForm yang disesuaikan ex-info Clojure adalah konstruk yang sangat berguna: Anda dapat melampirkan peta data sewenang-wenang ke pengecualian yang dilemparkan, memungkinkan kode yang telah menangkap pengecualian untuk memeriksa, log, atau memproses ex-data ini.
Misalnya, pernyataan assert memberikan pemeriksaan kewarasan yang berharga dalam logika bisnis, tetapi ketika gagal biasanya sangat diinginkan untuk mengetahui bagaimana mereka gagal. Seseorang dapat mencoba memasukkan informasi ini ke dalam string pengecualian, tetapi kadang -kadang data yang relevan terlalu besar untuk pesan pengecualian yang bergantung. Alih-alih memeriksa properti yang diinginkan dan melempar ex-info dengan semua data yang relevan yang terlampir dapat menjaga ringkasan pesan kesalahan sambil menghemat banyak waktu pengembang, terutama ketika debugging di repl.
Salah satu kelemahan utama ex-info adalah bahwa penggunaannya dapat mendorong pengecualian ad-hoc tanpa standar: jika Anda menangkap mantan info, jenisnya sama dengan semua mantan infos lainnya, stringnya sewenang-wenang, dan bukan Hanya Anda yang tidak dapat mengandalkan kunci tertentu yang muncul di ex-data , sangat mungkin bahwa peta itu sepenuhnya kosong.
Jika Anda ingin menikmati manfaat menggunakan ex-info secara luas dalam proyek besar, tetapi Anda juga ingin mempertahankan Ukuran kewarasan Manfaat dari kesalahan yang terdefinisi dengan baik, ada kemungkinan bahwa Anda pada akhirnya akan menggunakan, di setiap komponen logis aplikasi Anda, baik untuk melanggar dengan idiom clojure universal atau untuk mendefinisikan Anda sendiri mendefinisikan serangkaian standar "fungsi lemparan" yang menggunakan ex-info tetapi menjamin kekakuan tertentu: mungkin awalan umum untuk string pengecualian, mungkin kunci yang dijamin tertentu di peta ex-data .
Tujuan utama perpustakaan ini, reside , adalah untuk menyediakan alat untuk memudahkan proses ini. Ini memberikan utilitas untuk mendefinisikan formulir mantan info standar, serta kapasitas untuk memeriksa pada waktu kompilasi bahwa mereka digunakan sebagaimana dimaksud.
Semua mantan peta ex-info yang dihasilkan oleh alat-alat dalam recide mengandung setidaknya dua tombol:
:recide/error , yang nilainya merupakan instance dari ErrorForm .ErrorForm (default di Recide adalah :recide/type ). Dokumentasi API Clojure dapat ditemukan di sini. Dokumentasi API Java dapat ditemukan di sini.
recide.core/insist analog untuk assert . Tanda tangannya sama, dan sama seperti assert hanya dieksekusi ketika clojure.core/*assert* adalah benar.
Tetapi alih-alih melempar AssertionError , ia melempar ex-info dengan string penjelasan dari formulir: "Pernyataan gagal: <ekspresi yang ditegaskan atau disediakan pesan>". Jenis ex-data adalah :recide/assertion . Ada dua kunci lain yang digunakan oleh insist :
:expression , yang nilainya adalah ekspresi aktual yang terkandung dalam insist:values , yang nilainya merupakan peta dari setiap variabel yang digunakan dalam ekspresi ke nilainya pada saat kegagalan. :values hanya ada setiap kali recide.impl/*capture-insists* benar, tetapi salah secara default. Di perpustakaan waktu pemuatan, diatur ke true jika setidaknya satu dari yang berikut ini benar:
Tanda tangan insist adalah [[expr] [expr message]] , dan ex-data pada ex-info yang dihasilkan memiliki bentuk berikut:
{ :recide/type :recide/assertion ,
:expression <expr>
:values { ... }}Contoh yang digunakan:
( 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 memiliki dua arities: ([type msg data] [type msg data cause]) . error membangun ex-info dengan data peta terlampir, yang tipenya (sekali lagi, ditandai oleh :recide/type secara default) adalah type . Memasok cause hanya memberi pengecualian penyebab sesuai dengan Idiom Java.
( 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 memiliki dua arities yang sama. raise lemparan pengecualian yang dibangun berdasarkan error .
assoc / update menjadi Ex-Data Errors 'Recide Fungsi Kenyamanan: recide.core/assoc-error dan recide.core/update-error Setiap pengembalian pengecualian baru dari jenis asli, dengan ex-data dimodifikasi seperti assoc dan update .
Kadang -kadang bisa lebih mudah untuk melewati dan memanipulasi representasi peta dari kesalahan sebelum mengubahnya kembali menjadi pengecualian dan melemparkannya. Untuk tujuan ini, kami menyediakan recide.core/error->map dan recide.core/error-map->throwable yang melakukan seperti yang Anda harapkan.
Selain itu, kami memberikan "konstruktor" error-map alternatif untuk kesalahan yang mengembalikan formulir peta, dan error-map? .
Untuk kesalahan jenis type dengan msg pesan, data ex-data, dan cause , peta persisten terlihat seperti ini:
{ :recide/error <ErrorForm>,
:recide/type type,
:recide/msg msg,
:recide/cause cause,
:recide/data data} Semua kunci ini kecuali :recide/error dapat dimodifikasi dengan menyediakan ErrorForm yang disesuaikan (lihat di bawah untuk detailnya).
raise dan error tidak banyak memberikan jenis pengecualian standar. Untuk mengatasi ini lebih lanjut, Recide menyediakan deferror dan deferror-group .
deferror deferror adalah makro yang mengambil nama kesalahan, tipe, dan string "generik" yang akan mengawali pesan dari semua kesalahan jenis ini. Itu juga, secara opsional, mengambil koleksi kunci yang diperlukan. Jika tombol yang diperlukan ditentukan, maka kesalahan waktu kompilasi akan dilemparkan kapan saja alat yang ditentukan oleh deferror digunakan tanpa menentukan kunci tersebut secara eksplisit dalam kode sumber.
Contoh Penggunaan:
( deferror storage-timeout
:storage/timeout
" A storage operation timed out "
[ :method-at-fault :timeout-ms ]) Dalam contoh ini, panggilan untuk deferror ini akan mendefinisikan dua makro baru, storage-timeout , dan raise-storage-timeout . Untuk kenyamanan Anda, IDE yang kompeten akan dapat mengakses Docstring terperinci di VARS baru ini:
> ( 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}Jika Anda mencoba menggunakan salah satu dari ini tanpa, di peta data Anda, menentukan masing -masing tombol yang diperlukan, kompiler clojure akan melempar pengecualian:
> ( 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 adalah makro yang mendefinisikan seluruh keluarga kesalahan. Dibutuhkan nama kesalahan, deklarasi tipe dasar, dan sejumlah deklarasi subtipe.
Setiap deklarasi tipe dasar harus berupa kata kunci yang mewakili namespace umum untuk kelompok kesalahan ini, atau tuple yang elemen pertamanya adalah kata kunci seperti itu dan yang elemen kedua adalah urutan kunci yang diperlukan. Kunci yang ditentukan di sini akan diperlukan di setiap subtipe.
Setiap deklarasi subtipe terdiri dari urutan yang istilah pertama adalah simbol, istilah kedua adalah string generik untuk kesalahan, dan istilah ketiga (opsional) adalah urutan kunci yang diperlukan untuk subtipe tersebut.
Contoh:
( deferror-group parse-err
( :query.invalid [ :expression ])
( find-spec " Invalid find spec " )
( inputs " Invalid inputs " [ :invalid ])) Dalam contoh ini, ada dua jenis kesalahan yang ditentukan :: :query.invalid/find-spec dan :query.invalid/inputs . Yang pertama membutuhkan :expression dalam peta datanya, tetapi yang kedua membutuhkan keduanya :expression dan :invalid .
Seperti halnya deferror , utilitas yang diproduksi oleh deferror-group memiliki docstrings terperinci:
> ( 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"}}Seperti yang terlihat sebelumnya, tombol yang diperlukan menghasilkan kesalahan waktu kompilasi saat dihilangkan.
> ( 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 Jika kami menggunakan kata kunci untuk menunjuk tipe kesalahan, akan berguna untuk dapat catch kesalahan melalui kata kunci ini. Recide memberikan try* untuk tujuan ini. try* adalah makro yang mengembang untuk try Clojure, yang merupakan salah satu dari beberapa bentuk khusus Clojure. Dalam kebanyakan kasus, try* lalu harus berperilaku persis seperti try . Ini berbeda karena memaparkan fungsionalitas catch yang ditingkatkan. Anda dapat menangkap:
instance? Periksa.ErrorForm yang digunakan untuk membangunnya)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`. Perhatikan bahwa Anda dapat menggunakan kata kunci dari formulir :namespace/* sebagai wildcard untuk menangkap keluarga kesalahan reside, seperti yang ditentukan oleh deferror-group .
> ( try* ( raise :genus/species-1
" went extinct "
{ :year -1839421 })
( catch :genus/* e
( println ( :year ( ex-data e)))))
; ; -1839421 Recide menyediakan kumpulan alat untuk memperoleh versi sanitasi dari pengecualian yang harus dianggap aman untuk dicatat (tetapi mungkin tidak berguna sebagai hasilnya).
Kelas ini berisi beberapa metode utilitas statis:
getCurrentSanitizationLevel() Setara dengan recide.sanex/*sanitization-level* .
createSuppressionMap(...)Membuat IpersistentMap dengan kata kunci yang sesuai yang sesuai dengan arg boolean.
sanitize(Throwable) , sanitize(Throwable, IPersistentMap) Pintasan ke clojure ifn recide.sanex/sanitize .
ErrorForm yang disesuaikan Secara default, kesalahan yang diangkat oleh perpustakaan ini Gunakan ex-info sebagai konstruktor, recide.utils/serialize-throwable dan recide.utils/deserialize-throwable for (de) serialisasi, dan dalam bentuk peta mereka menggunakan :recide/type , :recide/msg , :recide/data , dan :recide/cause sebagai kata kunci standar mereka.
Dengan mendefinisikan ErrorForm baru, Anda dapat mengubah semua perilaku ini untuk perpustakaan Anda sendiri. Dengan memodifikasi kata kunci, Anda dapat "merek" kesalahan yang keluar dari perpustakaan Anda. Anda dapat menukar ex-info dengan konstruktor lain dari arity yang sama, yang diharapkan untuk mengembalikan IExceptionInfo . Misalnya, kekhawatiran yang terkait dengan Java Interop dapat memotivasi membuat kelas pengecualian baru, sementara idiom Clojure mungkin memotivasi mempertahankan kompatibilitas mantan info.
Anda dapat dengan mudah mendefinisikan kustomisasi Anda dengan recide.core/def-error-form Metode tidak ditentukan default untuk menyatukan pustaka default.
Metode penanganan kesalahan di recide.core adalah agnostik untuk ErrorForm spesifik yang digunakan. Untuk membuat kesalahan menggunakan ErrorForm khusus, Anda dapat dengan mudah menghasilkan serangkaian metode lengkap yang disesuaikan secara khusus untuk kustomisasi Anda dengan 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.