From 587692750554b618f978f24b5b3ce5930f101f8a Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Tue, 20 Oct 2020 17:24:26 +0200 Subject: [PATCH] :tada: Add function instrumentation helper and improve assertion exceptions. --- common/app/common/spec.cljc | 53 +++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/common/app/common/spec.cljc b/common/app/common/spec.cljc index e9d81eae14..b9ab80199b 100644 --- a/common/app/common/spec.cljc +++ b/common/app/common/spec.cljc @@ -12,11 +12,16 @@ (:refer-clojure :exclude [assert]) #?(:cljs (:require-macros [app.common.spec :refer [assert]])) (:require - #?(:clj [clojure.spec.alpha :as s] + #?(:clj [clojure.spec.alpha :as s] :cljs [cljs.spec.alpha :as s]) + + #?(:clj [clojure.spec.test.alpha :as stest] + :cljs [cljs.spec.test.alpha :as stest]) + [expound.alpha :as expound] [app.common.uuid :as uuid] [app.common.exceptions :as ex] + [app.common.geom.point :as gpt] [cuerdas.core :as str])) (s/check-asserts true) @@ -113,27 +118,54 @@ (s/def ::not-empty-string (s/and string? #(not (str/empty? %)))) (s/def ::url string?) (s/def ::fn fn?) +(s/def ::point gpt/point?) ;; --- Macros (defn spec-assert - [spec x] + [spec x message] (if (s/valid? spec x) x (ex/raise :type :assertion :data (s/explain-data spec x) - #?@(:cljs [:stack (.-stack (ex-info "assertion" {}))])))) + :message message + #?@(:cljs [:stack (.-stack (ex-info message {}))])))) + +(defn spec-assert* + [spec x message context] + (if (s/valid? spec x) + x + (ex/raise :type :assertion + :data (s/explain-data spec x) + :context context + :message message + #?@(:cljs [:stack (.-stack (ex-info message {}))])))) + (defmacro assert "Development only assertion macro." [spec x] (when *assert* - `(spec-assert ~spec ~x))) + (let [nsdata (:ns &env) + context (when nsdata + {:ns (str (:name nsdata)) + :name (pr-str spec) + :line (:line &env) + :file (:file (:meta nsdata))}) + message (str "Spec Assertion: '" (pr-str spec) "'")] + `(spec-assert* ~spec ~x ~message ~context)))) (defmacro verify "Always active assertion macro (does not obey to :elide-asserts)" [spec x] - `(spec-assert ~spec ~x)) + (let [nsdata (:ns &env) + context (when nsdata + {:ns (str (:name nsdata)) + :name (pr-str spec) + :line (:line &env) + :file (:file (:meta nsdata))}) + message (str "Spec Assertion: '" (pr-str spec) "'")] + `(spec-assert* ~spec ~x ~message ~context))) ;; --- Public Api @@ -148,3 +180,14 @@ (expound/printer edata)) :data (::s/problems edata))))) result)) + +(defmacro instrument! + [& {:keys [sym spec]}] + (when *assert* + (let [message (str "Spec failed on: " sym)] + `(let [origf# ~sym + mdata# (meta (var ~sym))] + (set! ~sym (fn [& params#] + (spec-assert* ~spec params# ~message mdata#) + (apply origf# params#))))))) +