diff --git a/README.md b/README.md index 0cc0661..eff8cce 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,5 @@ -[![CI](https://github.com/day8/re-frame-async-flow-fx/workflows/ci/badge.svg)](https://github.com/day8/re-frame-async-flow-fx/actions?workflow=ci) -[![CD](https://github.com/day8/re-frame-async-flow-fx/workflows/cd/badge.svg)](https://github.com/day8/re-frame-async-flow-fx/actions?workflow=cd) -[![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/day8/re-frame-async-flow-fx?style=flat)](https://github.com/day8/re-frame-async-flow-fx/tags) +[![GitHub tag (latest by date)](https://img.shields.io/github/v/tag/lumanu/re-frame-async-flow-fx?style=flat)](https://github.com/lumanu/re-frame-async-flow-fx/tags) [![Clojars Project](https://img.shields.io/clojars/v/day8.re-frame/async-flow-fx.svg)](https://clojars.org/day8.re-frame/async-flow-fx) -[![GitHub issues](https://img.shields.io/github/issues-raw/day8/re-frame-async-flow-fx?style=flat)](https://github.com/day8/re-frame-async-flow-fx/issues) -[![GitHub pull requests](https://img.shields.io/github/issues-pr/day8/re-frame-async-flow-fx)](https://github.com/day8/re-frame-async-flow-fx/pulls) [![License](https://img.shields.io/github/license/day8/re-frame-async-flow-fx.svg)](LICENSE) ## Async Control Flow In re-frame @@ -381,6 +377,7 @@ A `rule` is a map with the following fields: - `:dispatch-n` to dispatch multiple events, must be a coll where each elem represents one event to dispatch. - `:dispatch-fn` can be a function that accepts the seen event, and returns a coll where each elem represents one event to dispatch. - `:halt?` optional boolean. If true, the flow enters teardown and stops. + - `:transient?` optional boolean. If true, the flow will allow the rule to be executed multiple times. ### Under The Covers @@ -426,7 +423,11 @@ Or, to dispatch a server error event if a status of 500 or above has been seen ```clj {:when :seen? :events (fn [[e status]] (and (= e :http/response-received) (>= status 500))) :dispatch [:server/error])) -``` +``` + +Furthermore, sometimes it might be helpful to allow rules to be executed more than once. For these cases set +`:transient?` to `true`. This way the system will not track that the rule has fired and will allow it to execute +any number of times. ## Design Philosophy diff --git a/project.clj b/project.clj index 372d95e..a17b236 100644 --- a/project.clj +++ b/project.clj @@ -1,6 +1,6 @@ -(defproject day8.re-frame/async-flow-fx "lein-git-inject/version" +(defproject lumanu/async-flow-fx "lein-git-inject/version" :description "A re-frame effects handler for coordinating the kind of async control flow which often happens on app startup." - :url "https://github.com/day8/re-frame-async-flow-fx.git" + :url "https://github.com/lumanu/re-frame-async-flow-fx.git" :license {:name "MIT"} :min-lein-version "2.9.0" diff --git a/src/day8/re_frame/async_flow_fx.cljs b/src/day8/re_frame/async_flow_fx.cljs index cf288b9..3132944 100644 --- a/src/day8/re_frame/async_flow_fx.cljs +++ b/src/day8/re_frame/async_flow_fx.cljs @@ -63,12 +63,13 @@ - add a unique :id, if one not already present" [rules] (->> rules - (map-indexed (fn [index {:as rule :keys [id when events dispatch dispatch-n dispatch-fn halt?]}] + (map-indexed (fn [index {:as rule :keys [id when events transient? dispatch dispatch-n dispatch-fn halt?]}] (if (< 1 (count (remove nil? [dispatch dispatch-n dispatch-fn]))) (re-frame/console :error "async-flow: rule can only specify one of :dispatch, :dispatch-n and :dispatch-fn. Got more than one: " rule) (cond-> {:id (or id index) :halt? (or halt? false) + :transient? (or transient? false) :when (when->fn when) :events (if (coll? events) (set events) (hash-set events))} dispatch-fn (assoc :dispatch-fn dispatch-fn) @@ -149,7 +150,7 @@ new-seen-events (conj seen-events forwarded-event) ready-rules (startable-rules rules new-seen-events rules-fired) halt? (some :halt? ready-rules) - ready-rules-ids (->> ready-rules (map :id) set) + ready-rules-ids (->> ready-rules (filter #(not (:transient? %))) (map :id) set) new-rules-fired (set/union rules-fired ready-rules-ids) new-dispatches (rules->dispatches ready-rules forwarded-event) new-db (set-state db new-seen-events new-rules-fired)] diff --git a/test/day8/re_frame/async_flow_fx_test.cljs b/test/day8/re_frame/async_flow_fx_test.cljs index 3a3c80d..1ceff89 100644 --- a/test/day8/re_frame/async_flow_fx_test.cljs +++ b/test/day8/re_frame/async_flow_fx_test.cljs @@ -58,13 +58,13 @@ (deftest test-massage-rules (is (= (core/massage-rules [{:when :seen? :events :1 :dispatch [:2]}]) - (list {:id 0 :when core/seen-all-of? :events #{:1} :halt? false :dispatch-n (list [:2])}))) + (list {:id 0 :when core/seen-all-of? :events #{:1} :transient? false :halt? false :dispatch-n (list [:2])}))) (is (= (core/massage-rules [{:when :seen-both? :events [:1 :2] :halt? true}]) - (list {:id 0 :when core/seen-all-of? :events #{:1 :2} :halt? true :dispatch-n '()}))) + (list {:id 0 :when core/seen-all-of? :events #{:1 :2} :transient? false :halt? true :dispatch-n '()}))) (is (= (core/massage-rules [{:when :seen-any-of? :events #{:1 :2} :dispatch [:2] :halt? true}]) - (list {:id 0 :when core/seen-any-of? :events #{:1 :2} :halt? true :dispatch-n (list [:2])})))) + (list {:id 0 :when core/seen-any-of? :events #{:1 :2} :transient? false :halt? true :dispatch-n (list [:2])})))) (deftest test-setup @@ -185,6 +185,23 @@ {:db {:p {:seen-events #{[:4 :b]} :rules-fired #{2}}} :dispatch-n [[:6]]})))) +(deftest test-transient-handling + (let [flow {:first-dispatch [:start] + :id :test-id + :db-path [:p] + :rules [{:id 0 :when :seen? :transient? true :events [[:1 :a]] :dispatch [:2]} + {:id 2 :when :seen-any-of? :events [[:4 :b] :5] :dispatch [:6]} + ]} + handler-fn (core/make-flow-event-handler flow)] + + ;; new event should cause a dispatch and not track rules-fired + (is (= (handler-fn + {:db {:p {:seen-events #{} + :rules-fired #{}}}} + [:test-id [:1 :a]]) + {:db {:p {:seen-events #{[:1 :a]} :rules-fired #{}}} + :dispatch-n [[:2]]})))) + (deftest test-halt1 (let [flow {:first-dispatch [:start] :id :test-id