Skip to content

lopalghost/doh

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

D'oh!

D'oh! is a simple, minimal library for handling errors in Clojure. Its only dependency is Clojure 1.9.

Quick Start

Insert an error handler into a function, with a handler type (namespaced keyword) and a context map:

(require '[doh.core :refer [handle-error def-err]])

(defn my-fn
  [x]
  (let [result (do-stuff x)]
    (if (valid? result)
      result
      (handle-error ::bad-result {:x x}))))

handle-error always returns nil if it does not produce a valid result, so we can always treat non-nil evaluations of my-fn as valid.

30 minutes in the hammock later

(def-err ::bad-result
  [ctx]
  ::retry (do-stuff (fix-x (:x ctx)))
  ::ret-spec ::x-spec
  ::on-fail (println "x cannot be fixed"))

Usage

Often, I find myself writing a function that is likely to fail. I'd like to include error handling, but I haven't exactly thought out how I'd like to do that yet.

(defn get-dog
  [name]
  (let [req (http/get (str "/dogs/" name))]
    (if (get-in req [:body :success])
      (:body req)
      ;; TODO: handle error!
      )))

D'oh! uses clojure.spec to let you put in a generic error handler and implement it later. The handler is guaranteed to either return valid data or nil.

(require '[doh.core :refer [handle-error def-err]])
(require '[clojure.spec :as s])
(s/def ::dog (s/keys :req-un [::name ::breed ::age]))

(defn get-dog
  [name]
  (let [url (str "/dogs/" name)
        res (http/get url)]
    (if (get-in res [:body :success])
      (:body res)
      (handle-error ::failed-request {:url url :res res)))

(def-err ::failed-request
  [{:keys [url res]}]
  :retry (http/get url) ;; try again--you could do this in a loop as well
  :ret-spec ::dog ;; only return the result if it fits the spec
  :on-fail (log (format "Failed Requst: %s %s" url (:status res))))
  
(let [dog (or (get-dog "Avon_Barksdale")
              (throw (ex-info "Could not get dog" {...})))]
  ;; will always throw unless you have a valid dog
  ;; alternatively, you could throw an exception from the handler
  (do-stuff dog))

This is working from inside a function. If you'd rather handle errors outside of a function, the handle-exception macro will wrap your code in a try and inject your error handler as necessary:

(require '[doh.core :refer [handle-exception]])
(def n nil)
(handle-exception {NullPointerException ::null-pointer
                   Throwable ::other}
                  {:n n}
                  (do-stuff (inc n)))
                  
(def-err ::null-pointer
  [{n :n}]
  ::retry (do-stuff (inc (or n 0)))
  ::ret-spec (s/spec number?)
  ;; feel free to leave out unneeded args
  ;; however, if you leave out ::ret-spec, (s/spec any?) will be substituted
  )

(def-err ::other
  [{e :doh/exception}] ;; the exception is automatically passed into the context map
  ::on-fail (println "Unexpected exception" (Throwable->map e)))

def-err and handle-exception are spec'd and should be easy to use. I hope you find this library useful. Suggestions and contributions are welcome.

License

Copyright © 2016 Gary Nalven

Distributed under the Eclipse Public License version 1.0

About

Simple error handling for Clojure

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published