Recide, единственный настоящий настоящий императив второго recidere : - чтобы отступить, вернуться в чуть; чтобы ограничить
assoc / update в бывшую дату ошибок Recidedeferrordeferror-groupgetCurrentSanitizationLevel()createSuppressionMap(...)sanitize(Throwable) , sanitize(Throwable, IPersistentMap)ErrorForm ex-info 's Clojure-очень полезная конструкция: вы можете прикрепить карту произвольных данных к исключению, позволяющему коду, который привлек исключение для проверки, журнала или иного обработки этой ex-data .
Например, заявления assert предоставляют ценные проверки здравомыслия в бизнес -логике, но когда они терпят неудачу, обычно очень желательно узнать, как они потерпели неудачу. Можно попытаться вставить эту информацию в строку исключений, но иногда соответствующие данные слишком велики для отличного сообщения исключения. Вместо этого проверка желаемого свойства и бросить ex-info со всеми соответствующими прикрепленными данными, может сохранить краткость сообщений об ошибках при сохранении огромного времени разработчика, особенно при отладке в реплике.
Одна из основных недостатков ex-info заключается в том, что его использование может поощрять специальные исключения без стандартов: если вы поймаете бывшую инфу, его тип такой же, как и все другие бывшие инфос, его строка произвольная, а не Только вы не можете рассчитывать на какой-либо конкретный ключ, появляющийся в ex-data , вполне возможно, что карта полностью пуста.
Если вы хотите пользоваться преимуществами использования ex-info широко в большом проекте, но вы также хотите сохранить мера здравомыслия Преимущества четко определенных ошибок, вполне вероятно, что в конечном итоге вы прибегаете, в каждом логическом компоненте вашего приложения, либо к разрыву с универсальными идиомами Clojure, либо к определению вашего собственного определения стандартного набора «функций броска», которые используют ex-info , но гарантирует определенную жесткость: возможно, общий префикс для строк исключений, возможно, определенные гарантированные ключи на картах ex-data .
Основная цель этой библиотеки, Recide , состоит в том, чтобы предоставить инструменты для облегчения этого процесса. Он предоставляет утилиты для определения стандартных форм ex-info, а также способность проверять во время компиляции , которые они используются в соответствии с задумами.
Все карты ex-info , сгенерированные инструментами в recide содержат как минимум два ключа:
:recide/error , значение которого является экземпляром ErrorForm .ErrorForm (по умолчанию в Recide :recide/type ). Документация API Clojure можно найти здесь. Документация Java API можно найти здесь.
recide.core/insist аналогично assert . Его подпись такая же, и точно так же, как assert что она выполняется только тогда, когда clojure.core/*assert* TRUE.
Но вместо того, чтобы бросить AssertionError , он бросает ex-info с объяснительной строкой формы: «Утверждение не удалось: <утвержденное выражение или предоставленное сообщение>». Тип в бывшей дате :recide/assertion . Настаивают два других ключа: insist :
:expression , значение которого является фактическим выражением, содержащимся в insist:values , значение которой является карта из каждой переменной, используемой в выражении к его значению во время отказа. :values присутствуют только всякий раз, когда recide.impl/*capture-insists* -это истинно, но по умолчанию это ложно. При времени загрузки библиотеки он установлен на 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 имеет два арвиата: ([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 имеет одинаковые два арнина. raise исключение, построенное по error .
assoc / update в бывшую дату ошибок Recide Удобные функции: 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 сможет получить доступ к подробным DocStrings на этих новых VAR:
> ( 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* - это макрос, который расширяется до 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/* в качестве подстановочных знаков, чтобы поймать семейства ошибок рецидива, такие как те, которые определены 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 с соответствующими ключевыми словами, соответствующими логическим аргументам.
sanitize(Throwable) , sanitize(Throwable, IPersistentMap) Ярлык до Clojure ifn recide.sanex/sanitize .
ErrorForm По умолчанию ошибки, выраженные этой библиотекой, используют ex-info в качестве конструктора, recide.utils/serialize-throwable и recide.utils/deserialize-throwable для (de) сериализации, а в форме карты они используют :recide/type , :recide/msg , :recide/data и :recide/cause в качестве их стандартных ключевых слов.
Определяя новую ErrorForm , вы можете изменить все это поведение для своей собственной библиотеки. Изменение ключевых слов, вы можете «маркировать» ошибки, выходящие из вашей библиотеки. Вы можете поменять ex-info на другой конструктор того же богатства, который, как ожидается, вернет IExceptionInfo . Например, проблемы, связанные с Java Interop, могут мотивировать создание нового класса исключений, в то время как идиомы Clojure могут мотивировать сохранение совместимости ex-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.