diff --git a/CHANGES.md b/CHANGES.md index 697c07e..909a866 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ * `walker` and `codewalker` can now be used with `NONE` to remove elements * Improve `walker` performance by 70% by replacing clojure.walk implementation with custom recursive path * Extend `ALL` to work on records (navigate to key/value pairs) +* Add ability to declare a function for end index of `srange-dynamic` that takes in the result of the start index fn. Use `end-fn` macro to declare this function (takes in 2 args of [collection, start-index]). Functions defined with normal mechanisms (e.g. `fn`) will still only take in the collection as an argument. * Workaround ClojureScript bug that emits warnings for vars named the same as a private var in cljs.core (in this case `NONE`, added as private var to cljs.core with 1.9.562) * For ALL transforms on maps, interpret transformed key/value pair of size < 2 as removal * Bug fix: Fix incorrect inline compilation when a dynamic function invocation is nested in a data structure within a parameter to a navigator builder diff --git a/src/clj/com/rpl/specter.cljc b/src/clj/com/rpl/specter.cljc index 2383b86..3eee0a6 100644 --- a/src/clj/com/rpl/specter.cljc +++ b/src/clj/com/rpl/specter.cljc @@ -452,7 +452,12 @@ embed (vec (for [[t p] extensions] [t `(quote ~p)]))] `(extend-protocolpath* ~(protpath-sym protpath) - ~embed))))) + ~embed))) + + (defmacro end-fn [& args] + `(n/->SrangeEndFunction (fn ~@args))) + + )) @@ -710,9 +715,11 @@ srange-dynamic [start-fn end-fn] (select* [this structure next-fn] - (n/srange-select structure (start-fn structure) (end-fn structure) next-fn)) + (let [s (start-fn structure)] + (n/srange-select structure s (n/invoke-end-fn end-fn structure s) next-fn))) (transform* [this structure next-fn] - (n/srange-transform structure (start-fn structure) (end-fn structure) next-fn))) + (let [s (start-fn structure)] + (n/srange-transform structure s (n/invoke-end-fn end-fn structure s) next-fn)))) (defnav diff --git a/src/clj/com/rpl/specter/navs.cljc b/src/clj/com/rpl/specter/navs.cljc index 3bb7a6e..5cd904f 100644 --- a/src/clj/com/rpl/specter/navs.cljc +++ b/src/clj/com/rpl/specter/navs.cljc @@ -659,3 +659,12 @@ [] [v]) )))))) + +(defrecord SrangeEndFunction [end-fn]) + +;; done this way to maintain backwards compatibility +(defn invoke-end-fn [end-fn structure start] + (if (instance? SrangeEndFunction end-fn) + ((:end-fn end-fn) structure start) + (end-fn structure) + )) diff --git a/test/com/rpl/specter/core_test.cljc b/test/com/rpl/specter/core_test.cljc index cea2603..bab6ca4 100644 --- a/test/com/rpl/specter/core_test.cljc +++ b/test/com/rpl/specter/core_test.cljc @@ -10,7 +10,7 @@ select-first transform setval replace-in select-any selected-any? collected? traverse multi-transform path dynamicnav recursive-path - defdynamicnav traverse-all satisfies-protpath?]])) + defdynamicnav traverse-all satisfies-protpath? end-fn]])) (:use #?(:clj [clojure.test :only [deftest is]]) #?(:clj [clojure.test.check.clojure-test :only [defspec]]) @@ -21,7 +21,7 @@ select-first transform setval replace-in select-any selected-any? collected? traverse multi-transform path dynamicnav recursive-path - defdynamicnav traverse-all satisfies-protpath?]])) + defdynamicnav traverse-all satisfies-protpath? end-fn]])) @@ -1561,6 +1561,22 @@ (is (= (assoc f :b 1 :c 2) (transform [(s/walker (complement record?)) s/FIRST] (fn [k] (if (= :a k) :b :c)) f))) )) +(def MIDDLE + (s/comp-paths + (s/srange-dynamic + (fn [aseq] (long (/ (count aseq) 2))) + (end-fn [aseq s] (if (empty? aseq) 0 (inc s)))) + s/FIRST + )) + +(deftest srange-dynamic-test + (is (= 2 (select-any MIDDLE [1 2 3]))) + (is (identical? s/NONE (select-any MIDDLE []))) + (is (= 1 (select-any MIDDLE [1]))) + (is (= 2 (select-any MIDDLE [1 2]))) + (is (= [1 3 3] (transform MIDDLE inc [1 2 3]))) + ) + #?(:clj (do (defprotocolpath FooPP)