recide, บุคคลที่สองที่เป็นเอกพจน์ในปัจจุบันที่จำเป็นของ recidere : - ที่จะถอยกลับมาที่ไม่มี; เพื่อลดทอน
assoc / update เป็น ex-data ของ Recide Errorsdeferrordeferror-groupgetCurrentSanitizationLevel()createSuppressionMap(...)sanitize(Throwable) , sanitize(Throwable, IPersistentMap)ErrorForm ที่กำหนดเอง ex-info ของ Clojure เป็นโครงสร้างที่มีประโยชน์มาก: คุณสามารถแนบแผนที่ของข้อมูลโดยพลการกับข้อยกเว้นที่ถูกโยนทิ้งไว้เพื่อให้รหัสที่ได้รับการยกเว้นในการตรวจสอบบันทึกหรือประมวลผล ex-data นี้
ตัวอย่างเช่นคำสั่ง assert ให้การตรวจสอบสติที่มีคุณค่าในตรรกะทางธุรกิจ แต่เมื่อพวกเขาล้มเหลวมักจะเป็นที่พึงปรารถนาอย่างมากที่จะรู้ว่าพวกเขาล้มเหลว อย่างไร หนึ่งสามารถพยายามแทรกข้อมูลนี้ลงในสตริงข้อยกเว้น แต่บางครั้งข้อมูลที่เกี่ยวข้องมีขนาดใหญ่เกินไปสำหรับข้อความข้อยกเว้นที่ไม่สม่ำเสมอ แทนที่จะตรวจสอบคุณสมบัติที่ต้องการและการขว้าง ex-info ด้วยข้อมูลที่เกี่ยวข้องทั้งหมดที่แนบมาสามารถรักษาความกระชับของข้อความแสดงข้อผิดพลาดในขณะที่ประหยัดเวลาจำนวนมหาศาลโดยเฉพาะอย่างยิ่งเมื่อทำการดีบักที่ Repl
หนึ่งในจุดอ่อนหลักของ ex-info คือการใช้งานสามารถส่งเสริมข้อยกเว้นแบบ Ad-Hoc โดยไม่มีมาตรฐาน: หากคุณจับ Ex-Info ประเภทของมันจะเหมือนกับ Ex-infos อื่น ๆ ทั้งหมด คุณไม่สามารถนับได้ในคีย์ใด ๆ ที่ปรากฏใน ex-data แต่เป็นไปได้ที่แผนที่จะว่างเปล่าทั้งหมด
หากคุณต้องการเพลิดเพลินไปกับประโยชน์ของการใช้ ex-info อย่างกว้างขวางในโครงการขนาดใหญ่ แต่คุณต้องการรักษา การวัดความมีสติ ประโยชน์ของข้อผิดพลาดที่กำหนดไว้อย่างดีเป็นไปได้ว่าคุณจะหันไปใช้ในแต่ละองค์ประกอบเชิงตรรกะของแอปพลิเคชันของคุณ ex-info ว่า ex-info แต่รับประกันความแข็งแกร่งบางอย่าง: อาจเป็นคำนำหน้าทั่วไปของสตริงข้อยกเว้นบางทีคีย์ที่รับประกันบางอย่างในแผนที่ ex-data
วัตถุประสงค์หลักของ ไลบรารี นี้คือการจัดหาเครื่องมือเพื่อบรรเทากระบวนการนี้ มันให้บริการสาธารณูปโภคสำหรับการกำหนดแบบฟอร์ม EX-INFO มาตรฐานรวมถึงความสามารถในการตรวจสอบ ในเวลาคอมไพล์ ที่ใช้ตามที่ตั้งใจไว้
แผนที่ ex-info ทั้งหมดที่สร้างโดยเครื่องมือใน recide มีอย่างน้อยสองปุ่ม:
:recide/error ซึ่งค่าเป็นอินสแตนซ์ของ ErrorFormErrorForm (ค่าเริ่มต้นใน recide คือ :recide/type ) เอกสาร CLOJURE API สามารถพบได้ที่นี่ เอกสาร Java API สามารถพบได้ที่นี่
recide.core/insist นั้นคล้ายคลึงกับ assert ลายเซ็นของมันเหมือนกันและเช่นเดียวกับ assert มันจะดำเนินการเฉพาะเมื่อ clojure.core/*assert* เป็นจริง
แต่แทนที่จะขว้าง AssertionError มันจะโยน ex-info ด้วยสตริงอธิบายของแบบฟอร์ม: "การยืนยันล้มเหลว: <การแสดงออกที่ถูกยืนยันหรือข้อความที่ให้มา>" ประเภทใน ex-data คือ :recide/assertion มีอีกสองปุ่มที่ใช้โดย insist :
:expression ซึ่งมีค่าเป็นนิพจน์จริงที่มีอยู่ใน insist:values ที่มีค่าเป็นแผนที่จากแต่ละตัวแปรที่ใช้ในนิพจน์กับค่าของมันในเวลาที่เกิดความล้มเหลว :values อยู่เฉพาะเมื่อใดก็ตามที่ recide.impl/*capture-insists* เป็นจริง แต่มันเป็นเท็จโดยค่าเริ่มต้น ที่เวลาโหลดไลบรารีจะถูกตั้งค่าเป็นจริงถ้าอย่างน้อยหนึ่งในต่อไปนี้เป็นจริง:
ลายเซ็นของ insist คือ [[expr] [expr message]] และ ex-data ใน 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 มีสอง arities: ([type msg data] [type msg data cause]) error สร้าง ex-info พร้อม data แผนที่ที่แนบมาซึ่งมีประเภท (อีกครั้งซึ่งมีความหมายโดย :recide/type โดยค่าเริ่มต้น) เป็น type การจัดหา cause เพียงให้ข้อยกเว้นเป็นสาเหตุตามสำนวน 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 มีสอง arities เดียวกัน raise การโยนข้อยกเว้นที่สร้างขึ้นโดย error
assoc / update เป็น ex-data ของ Recide Errors ฟังก์ชั่นความสะดวกสบาย: recide.core/assoc-error และ recide.core/update-error แต่ละครั้งส่งคืนข้อยกเว้นใหม่ของประเภทดั้งเดิมโดยมีการแก้ไขแบบเก่าเช่นเดียวกับ assoc และ update
บางครั้งมันก็สะดวกที่จะผ่านไปรอบ ๆ และจัดการกับการแสดงแผนที่ของข้อผิดพลาดก่อนที่จะเปลี่ยนกลับเป็นข้อยกเว้นและโยนมัน ด้วยเหตุนี้เราให้ recide.core/error->map และ recide.core/error-map->throwable ซึ่งทำตามที่คุณคาดหวัง
นอกจากนี้เรายังมี error-map ทางเลือก "ตัวสร้าง" สำหรับข้อผิดพลาดที่ส่งคืนแบบฟอร์มแผนที่และ error-map? -
สำหรับข้อผิดพลาดใด ๆ ของ type ประเภทที่มี msg ข้อความ, data อดีตข้อมูลและสาเหตุ cause แผนที่ถาวรมีลักษณะเช่นนี้:
{ :recide/error <ErrorForm>,
:recide/type type,
:recide/msg msg,
:recide/cause cause,
:recide/data data} ปุ่มทั้งหมดเหล่านี้ยกเว้น :recide/error สามารถแก้ไขได้โดยการจัดหา ErrorForm ที่กำหนดเอง (ดูรายละเอียดด้านล่าง)
raise และ error ทำน้อยมากเพื่อให้ประเภทข้อยกเว้นมาตรฐาน เพื่อแก้ไขปัญหานี้เพิ่มเติมให้ Recide ให้ 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 ใหม่เหล่านี้:
> ( 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 เป็นแมโครที่กำหนดข้อผิดพลาดทั้ง ครอบครัว ต้องใช้ชื่อข้อผิดพลาดการประกาศประเภทฐานและการประกาศชนิดย่อยจำนวนมาก
การประกาศประเภทฐาน แต่ละครั้งจะต้อง เป็น คำหลักที่แสดงถึงเนมสเปซทั่วไปสำหรับกลุ่มของข้อผิดพลาดนี้ หรือ tuple ที่องค์ประกอบแรกเป็นคำหลักและองค์ประกอบที่สองเป็นลำดับของคีย์ที่ต้องการ คีย์ที่ระบุไว้ที่นี่จะต้องใช้ในทุกประเภทย่อย
การประกาศชนิดย่อย แต่ละครั้งประกอบด้วยลำดับที่มีเทอมแรกเป็นสัญลักษณ์เทอมที่สองเป็นสตริงทั่วไปสำหรับข้อผิดพลาดและคำที่สาม (ไม่บังคับ) เป็นลำดับของคีย์ที่ต้องการสำหรับชนิดย่อยนั้น
ตัวอย่าง:
( 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 มีเอกสารรายละเอียด:
> ( 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* เป็นแมโครที่ขยายไปสู่ try ของ Clojure ซึ่งเป็นหนึ่งในรูปแบบพิเศษไม่กี่รูปแบบของ 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/* เป็นไวด์การ์ดเพื่อจับครอบครัวที่มีข้อผิดพลาด recide เช่นที่กำหนดโดย 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(...)สร้าง ipersistentMap ด้วยคำหลักที่เหมาะสมที่สอดคล้องกับบูลีน args
sanitize(Throwable) , sanitize(Throwable, IPersistentMap) ทางลัดเป็น Clojure IFN recide.sanex/sanitize
ErrorForm ที่กำหนดเอง โดยค่าเริ่มต้นข้อผิดพลาดที่เกิดขึ้นจากไลบรารีนี้ใช้ ex-info เป็นตัวสร้าง recide.utils/serialize-throwable และ recide.utils/deserialize-throwable สำหรับ (de) serialization และในรูปแบบแผนที่ที่พวกเขาใช้: :recide/msg :recide/type :recide/msg , :recide/data และ :recide/cause เป็นคำหลักมาตรฐาน
โดยการกำหนด ErrorForm ใหม่คุณสามารถเปลี่ยนพฤติกรรมทั้งหมดนี้สำหรับห้องสมุดของคุณเอง โดยการแก้ไขคำหลักคุณสามารถ "ข้อผิดพลาดของแบรนด์" ที่ออกมาจากห้องสมุดของคุณ คุณสามารถแลกเปลี่ยน ex-info สำหรับตัวสร้างรายอื่นของ Arity เดียวกันซึ่งคาดว่าจะคืน IExceptionInfo ตัวอย่างเช่นข้อกังวลที่เกี่ยวข้องกับ Java Interop อาจกระตุ้นให้เกิดการสร้างคลาสข้อยกเว้นใหม่ในขณะที่สำนวน Clojure อาจกระตุ้นให้เกิดความเข้ากันได้ในอดีต
คุณสามารถกำหนดการปรับแต่งของคุณด้วย 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.