diff --git a/resources/signal-docstrings/signal-options.txt b/resources/signal-docstrings/signal-options.txt index ab74243..4b18b08 100644 --- a/resources/signal-docstrings/signal-options.txt +++ b/resources/signal-docstrings/signal-options.txt @@ -22,7 +22,7 @@ Signal options (shared by all signal creators): `:sample-rate` - ?rate ∈ℝ[0,1] for signal sampling (0.75 => allow 75% of signals, nil => allow all) `:when` -------- Arb ?form; when present, form must return truthy to allow signal `:rate-limit` -- ?spec as given to `taoensso.telemere/rate-limiter`, see its docstring for details -`:middleware` -- ?[(fn [signal])=>modified-signal ...] signal middleware +`:middleware` -- Optional (fn [signal]) => ?modified-signal to apply when signal is created `:trace?` ------ Should tracing be enabled for `:run` form? ---------- Other arb user-level ?kvs to incl. in signal. Typically NOT included in diff --git a/src/taoensso/telemere.cljc b/src/taoensso/telemere.cljc index ce5e10b..be7e39c 100644 --- a/src/taoensso/telemere.cljc +++ b/src/taoensso/telemere.cljc @@ -72,6 +72,7 @@ enc/chance enc/rate-limiter enc/newline + enc/comp-middleware impl/msg-splice impl/msg-skip @@ -143,14 +144,13 @@ (comment (with-ctx {:a :A1 :b :B1} (with-ctx+ {:a :A2} *ctx*))) -;;;; Middleware +;;;; Signal middleware (enc/defonce ^:dynamic *middleware* - "Optional vector of unary middleware fns to apply (sequentially/left-to-right) - to each signal before passing it to handlers. If any middleware fn returns nil, - aborts immediately without calling handlers. + "Optional (fn [signal]) => ?modified-signal to apply (once) when + signal is created. When middleware returns nil, skips all handlers. - Useful for transforming each signal before handling. + Compose multiple middleware fns together with `comp-middleware. Re/bind dynamic value using `with-middleware`, `binding`. Modify root (base) value using `set-middleware!`." @@ -159,13 +159,13 @@ #?(:clj (defmacro set-middleware! "Set `*middleware*` var's root (base) value. See `*middleware*` for details." - [root-val] `(enc/set-var-root! *middleware* ~root-val))) + [?root-middleware-fn] `(enc/set-var-root! *middleware* ~?root-middleware-fn))) #?(:clj (defmacro with-middleware "Evaluates given form with given `*middleware*` value. See `*middleware*` for details." - [init-val form] `(binding [*middleware* ~init-val] ~form))) + [?middleware-fn form] `(binding [*middleware* ~?middleware-fn] ~form))) ;;;; Signal creators ;; - signal! [ opts] ; => allowed? / run result (value or throw) diff --git a/src/taoensso/telemere/impl.cljc b/src/taoensso/telemere/impl.cljc index d9ef676..7247fb6 100644 --- a/src/taoensso/telemere/impl.cljc +++ b/src/taoensso/telemere/impl.cljc @@ -632,9 +632,9 @@ '~run-form ~'__run-result ~error-form)] ;; Final unwrapped signal value visible to users/handler-fns, allow to throw - (if-let [call-middleware# ~middleware-form] - ((sigs/get-middleware-fn call-middleware#) ~'__signal) ; Can throw - (do ~'__signal)))))] + (if-let [sig-middleware# ~middleware-form] + (sig-middleware# ~'__signal) ; Apply signal middleware, can throw + (do ~'__signal)))))] ;; Could avoid double `run-form` expansion with a fn wrap (>0 cost) ;; (let [run-fn-form (when run-form `(fn [] (~run-form)))] diff --git a/test/taoensso/telemere_tests.cljc b/test/taoensso/telemere_tests.cljc index 1fd8a55..6e09b0e 100644 --- a/test/taoensso/telemere_tests.cljc +++ b/test/taoensso/telemere_tests.cljc @@ -229,10 +229,10 @@ (testing "Call middleware" (let [c (enc/counter) - [[rv1 _] [sv1]] (with-sigs :raw nil (sig! {:level :info, :run (c), :middleware [#(assoc % :m1 (c)) #(assoc % :m2 (c))]})) - [[rv2 _] [sv2]] (with-sigs :raw nil (sig! {:level :info, :run (c), :middleware [#(assoc % :m1 (c)) #(assoc % :m2 (c))], :allow? false})) - [[rv3 _] [sv3]] (with-sigs :raw nil (sig! {:level :info, :run (c), :middleware [#(assoc % :m1 (c)) #(assoc % :m2 (c))]})) - [[rv4 _] [sv4]] (with-sigs :raw nil (sig! {:level :info, :middleware [(fn [_] "signal-value")]}))] + [[rv1 _] [sv1]] (with-sigs :raw nil (sig! {:level :info, :run (c), :middleware (tel/comp-middleware #(assoc % :m1 (c)) #(assoc % :m2 (c)))})) + [[rv2 _] [sv2]] (with-sigs :raw nil (sig! {:level :info, :run (c), :middleware (tel/comp-middleware #(assoc % :m1 (c)) #(assoc % :m2 (c))), :allow? false})) + [[rv3 _] [sv3]] (with-sigs :raw nil (sig! {:level :info, :run (c), :middleware (tel/comp-middleware #(assoc % :m1 (c)) #(assoc % :m2 (c)))})) + [[rv4 _] [sv4]] (with-sigs :raw nil (sig! {:level :info, :middleware (fn [_] "signal-value")}))] [(is (= rv1 0)) (is (sm? sv1 {:m1 1 :m2 2})) (is (= rv2 3)) (is (nil? sv2)) @@ -260,31 +260,31 @@ (let [c (enc/counter) sv-h1_ (atom nil) sv-h2_ (atom nil) - wh1 (sigs/wrap-handler :hid1 (fn [sv] (reset! sv-h1_ sv)) nil {:async nil, :middleware [#(assoc % :hm1 (c)) #(assoc % :hm2 (c))]}) - wh2 (sigs/wrap-handler :hid2 (fn [sv] (reset! sv-h2_ sv)) nil {:async nil, :middleware [#(assoc % :hm1 (c)) #(assoc % :hm2 (c))]})] + wh1 (sigs/wrap-handler :hid1 (fn [sv] (reset! sv-h1_ sv)) nil {:async nil, :middleware (tel/comp-middleware #(assoc % :hm1 (c)) #(assoc % :hm2 (c)))}) + wh2 (sigs/wrap-handler :hid2 (fn [sv] (reset! sv-h2_ sv)) nil {:async nil, :middleware (tel/comp-middleware #(assoc % :hm1 (c)) #(assoc % :hm2 (c)))})] ;; Note that call middleware output is cached and shared across all handlers (binding [impl/*sig-handlers* [wh1 wh2]] (let [;; 1x run + 4x handler middleware + 2x call middleware = 7x - rv1 (sig! {:level :info, :run (c), :middleware [#(assoc % :m1 (c)) #(assoc % :m2 (c))]}) + rv1 (sig! {:level :info, :run (c), :middleware (tel/comp-middleware #(assoc % :m1 (c)) #(assoc % :m2 (c)))}) sv1-h1 @sv-h1_ sv1-h2 @sv-h2_ c1 @c ;; 1x run - rv2 (sig! {:level :info, :run (c), :middleware [#(assoc % :m1 (c)) #(assoc % :m2 (c))], :allow? false}) + rv2 (sig! {:level :info, :run (c), :middleware (tel/comp-middleware #(assoc % :m1 (c)) #(assoc % :m2 (c))), :allow? false}) sv2-h1 @sv-h1_ sv2-h2 @sv-h2_ c2 @c ; 8 ;; 1x run + 4x handler middleware + 2x call middleware = 7x - rv3 (sig! {:level :info, :run (c), :middleware [#(assoc % :m1 (c)) #(assoc % :m2 (c))]}) + rv3 (sig! {:level :info, :run (c), :middleware (tel/comp-middleware #(assoc % :m1 (c)) #(assoc % :m2 (c)))}) sv3-h1 @sv-h1_ sv3-h2 @sv-h2_ c3 @c ; 15 ;; 4x handler middleware - rv4 (sig! {:level :info, :middleware [(fn [_] {:my-sig-val? true})]}) + rv4 (sig! {:level :info, :middleware (fn [_] {:my-sig-val? true})}) sv4-h1 @sv-h1_ sv4-h2 @sv-h2_ c4 @c] @@ -321,7 +321,7 @@ (tel/with-handler :hid1 (fn [sv] (force (:data sv)) (reset! sv_ sv)) {:async nil, :error-fn (fn [x] (reset! error_ x)), :rl-error nil, - :middleware [(fn [sv] (if *throwing-handler-middleware?* (ex1!) sv))]} + :middleware (fn [sv] (if *throwing-handler-middleware?* (ex1!) sv))} [(is (->> (sig! {:level :info, :when (ex1!)}) (throws? :ex-info "Ex1")) "`~filterable-expansion/allow` throws at call") (is (->> (sig! {:level :info, :inst (ex1!)}) (throws? :ex-info "Ex1")) "`~inst-form` throws at call") @@ -339,7 +339,7 @@ (testing "Throwing call middleware" (reset-state!) - [(is (true? (sig! {:level :info, :middleware [(fn [_] (ex1!))]}))) + [(is (true? (sig! {:level :info, :middleware (fn [_] (ex1!))}))) (is (= @sv_ :nx)) (is (sm? @error_ {:handler-id :hid1, :error pex1?}))])