Implemented #146, dynamic navigator enhancements
This commit is contained in:
parent
728926c4ea
commit
dfedd30b29
6 changed files with 72 additions and 30 deletions
|
|
@ -1,6 +1,9 @@
|
||||||
## 0.13.1-SNAPSHOT
|
## 0.13.1-SNAPSHOT
|
||||||
|
|
||||||
* Remove any? in com.rpl.specter.impl to avoid conflict with Clojure 1.9
|
* Remove any? in com.rpl.specter.impl to avoid conflict with Clojure 1.9
|
||||||
|
* Enhanced dynamic navigators to continue expanding if any other dynamic navs are returned
|
||||||
|
* Added `eachnav` to turn any 1-argument navigator into a navigator that accepts any number of arguments, navigating by each argument in order
|
||||||
|
* `keypath` and `must` enhanced to take in multiple arguments for concisely specifying multiple steps
|
||||||
* Bug fix: Fix regression from 0.13.0 where [ALL FIRST] on a PersistentArrayMap that created duplicate keys would create an invalid PersistentArrayMap
|
* Bug fix: Fix regression from 0.13.0 where [ALL FIRST] on a PersistentArrayMap that created duplicate keys would create an invalid PersistentArrayMap
|
||||||
* Bug fix: Fix problems with multi-path and if-path in latest versions of ClojureScript
|
* Bug fix: Fix problems with multi-path and if-path in latest versions of ClojureScript
|
||||||
* Bug fix: Inline compiler no longer flattens and changes the type of sequential params
|
* Bug fix: Inline compiler no longer flattens and changes the type of sequential params
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@
|
||||||
(defmacroalias richnav macros/richnav)
|
(defmacroalias richnav macros/richnav)
|
||||||
(defmacroalias nav macros/nav)
|
(defmacroalias nav macros/nav)
|
||||||
(defmacroalias defnav macros/defnav)
|
(defmacroalias defnav macros/defnav)
|
||||||
|
(defmacroalias defrichnav macros/defrichnav)
|
||||||
|
|
||||||
(defmacro collector [params [_ [_ structure-sym] & body]]
|
(defmacro collector [params [_ [_ structure-sym] & body]]
|
||||||
`(richnav ~params
|
`(richnav ~params
|
||||||
|
|
@ -42,9 +43,6 @@
|
||||||
(~'transform* [this# vals# ~structure-sym next-fn#]
|
(~'transform* [this# vals# ~structure-sym next-fn#]
|
||||||
(next-fn# (conj vals# (do ~@body)) ~structure-sym))))
|
(next-fn# (conj vals# (do ~@body)) ~structure-sym))))
|
||||||
|
|
||||||
(defmacro defrichnav [name params & impls]
|
|
||||||
`(def ~name (richnav ~params ~@impls)))
|
|
||||||
|
|
||||||
(defmacro defcollector [name & body]
|
(defmacro defcollector [name & body]
|
||||||
`(def ~name (collector ~@body)))
|
`(def ~name (collector ~@body)))
|
||||||
|
|
||||||
|
|
@ -543,6 +541,24 @@
|
||||||
|
|
||||||
(def late-path i/late-path)
|
(def late-path i/late-path)
|
||||||
(def dynamic-param? i/dynamic-param?)
|
(def dynamic-param? i/dynamic-param?)
|
||||||
|
(def late-resolved-fn i/late-resolved-fn)
|
||||||
|
|
||||||
|
|
||||||
|
(defdynamicnav
|
||||||
|
^{:doc "Turns a navigator that takes one argument into a navigator that takes
|
||||||
|
many arguments and uses the same navigator with each argument. There
|
||||||
|
is no performance cost to using this. See implementation of `keypath`"}
|
||||||
|
eachnav
|
||||||
|
[navfn]
|
||||||
|
(let [latenavfn (late-resolved-fn navfn)]
|
||||||
|
(dynamicnav [& args]
|
||||||
|
(if (= 1 (count args))
|
||||||
|
;; optimization if dynamicnav is used in a runtime situation (like
|
||||||
|
;; via a dynamic var)
|
||||||
|
;; also makes the resulting nav function semantically identical to original
|
||||||
|
;; nav by returning a RichNavigator instead of a sequence
|
||||||
|
(latenavfn (first args))
|
||||||
|
(map latenavfn args)))))
|
||||||
|
|
||||||
|
|
||||||
;; Helpers for making recursive or mutually recursive navs
|
;; Helpers for making recursive or mutually recursive navs
|
||||||
|
|
@ -752,28 +768,8 @@
|
||||||
next-val))
|
next-val))
|
||||||
structure)))))
|
structure)))))
|
||||||
|
|
||||||
(defrichnav
|
(def keypath (eachnav n/keypath*))
|
||||||
^{:doc "Navigates to the specified key, navigating to nil if it does not exist."}
|
(def must (eachnav n/must*))
|
||||||
keypath
|
|
||||||
[key]
|
|
||||||
(select* [this vals structure next-fn]
|
|
||||||
(next-fn vals (get structure key)))
|
|
||||||
(transform* [this vals structure next-fn]
|
|
||||||
(assoc structure key (next-fn vals (get structure key)))))
|
|
||||||
|
|
||||||
|
|
||||||
(defrichnav
|
|
||||||
^{:doc "Navigates to the key only if it exists in the map."}
|
|
||||||
must
|
|
||||||
[k]
|
|
||||||
(select* [this vals structure next-fn]
|
|
||||||
(if (contains? structure k)
|
|
||||||
(next-fn vals (get structure k))
|
|
||||||
NONE))
|
|
||||||
(transform* [this vals structure next-fn]
|
|
||||||
(if (contains? structure k)
|
|
||||||
(assoc structure k (next-fn vals (get structure k)))
|
|
||||||
structure)))
|
|
||||||
|
|
||||||
|
|
||||||
(defrichnav
|
(defrichnav
|
||||||
|
|
@ -881,7 +877,7 @@
|
||||||
|
|
||||||
(extend-type #?(:clj clojure.lang.Keyword :cljs cljs.core/Keyword)
|
(extend-type #?(:clj clojure.lang.Keyword :cljs cljs.core/Keyword)
|
||||||
ImplicitNav
|
ImplicitNav
|
||||||
(implicit-nav [this] (keypath this)))
|
(implicit-nav [this] (n/keypath* this)))
|
||||||
|
|
||||||
(extend-type #?(:clj clojure.lang.AFn :cljs function)
|
(extend-type #?(:clj clojure.lang.AFn :cljs function)
|
||||||
ImplicitNav
|
ImplicitNav
|
||||||
|
|
|
||||||
|
|
@ -569,7 +569,7 @@
|
||||||
:cljs (satisfies? RichNavigator o))
|
:cljs (satisfies? RichNavigator o))
|
||||||
o
|
o
|
||||||
|
|
||||||
(vector? o)
|
(sequential? o)
|
||||||
(comp-paths* o)
|
(comp-paths* o)
|
||||||
|
|
||||||
:else
|
:else
|
||||||
|
|
@ -602,9 +602,16 @@
|
||||||
(defn all-static? [params]
|
(defn all-static? [params]
|
||||||
(every? (complement dynamic-param?) params))
|
(every? (complement dynamic-param?) params))
|
||||||
|
|
||||||
|
(defn late-resolved-fn [afn]
|
||||||
|
(fn [& args]
|
||||||
|
(if (all-static? args)
|
||||||
|
(apply afn args)
|
||||||
|
(->DynamicFunction afn args)
|
||||||
|
)))
|
||||||
|
|
||||||
(defn- magic-precompilation* [o]
|
(defn- magic-precompilation* [o]
|
||||||
(cond (sequential? o)
|
(cond (sequential? o)
|
||||||
(if (list? o)
|
(if (or (list? o) (seq? o))
|
||||||
(map magic-precompilation* o)
|
(map magic-precompilation* o)
|
||||||
(into (empty o) (map magic-precompilation* o)))
|
(into (empty o) (map magic-precompilation* o)))
|
||||||
|
|
||||||
|
|
@ -631,7 +638,7 @@
|
||||||
params (doall (map magic-precompilation* (:params o)))]
|
params (doall (map magic-precompilation* (:params o)))]
|
||||||
(if (or (-> op meta :dynamicnav)
|
(if (or (-> op meta :dynamicnav)
|
||||||
(all-static? (conj params op)))
|
(all-static? (conj params op)))
|
||||||
(apply op params)
|
(magic-precompilation* (apply op params))
|
||||||
(->DynamicFunction op params)))
|
(->DynamicFunction op params)))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
|
|
|
||||||
|
|
@ -44,3 +44,6 @@
|
||||||
~@decls
|
~@decls
|
||||||
~@helpers
|
~@helpers
|
||||||
(def ~name (nav ~params ~@impls)))))
|
(def ~name (nav ~params ~@impls)))))
|
||||||
|
|
||||||
|
(defmacro defrichnav [name params & impls]
|
||||||
|
`(def ~name (richnav ~params ~@impls)))
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
[defnav]]
|
[defnav]]
|
||||||
[com.rpl.specter.util-macros :refer
|
[com.rpl.specter.util-macros :refer
|
||||||
[doseqres]]))
|
[doseqres]]))
|
||||||
(:use #?(:clj [com.rpl.specter.macros :only [defnav]])
|
(:use #?(:clj [com.rpl.specter.macros :only [defnav defrichnav]])
|
||||||
#?(:clj [com.rpl.specter.util-macros :only [doseqres]]))
|
#?(:clj [com.rpl.specter.util-macros :only [doseqres]]))
|
||||||
(:require [com.rpl.specter.impl :as i]
|
(:require [com.rpl.specter.impl :as i]
|
||||||
[clojure.walk :as walk]
|
[clojure.walk :as walk]
|
||||||
|
|
@ -409,3 +409,27 @@
|
||||||
(if (pred structure)
|
(if (pred structure)
|
||||||
(on-match-fn structure)
|
(on-match-fn structure)
|
||||||
(walk/walk (partial walk-until pred on-match-fn) identity structure)))
|
(walk/walk (partial walk-until pred on-match-fn) identity structure)))
|
||||||
|
|
||||||
|
|
||||||
|
(defrichnav
|
||||||
|
^{:doc "Navigates to the specified key, navigating to nil if it does not exist."}
|
||||||
|
keypath*
|
||||||
|
[key]
|
||||||
|
(select* [this vals structure next-fn]
|
||||||
|
(next-fn vals (get structure key)))
|
||||||
|
(transform* [this vals structure next-fn]
|
||||||
|
(assoc structure key (next-fn vals (get structure key)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defrichnav
|
||||||
|
^{:doc "Navigates to the key only if it exists in the map."}
|
||||||
|
must*
|
||||||
|
[k]
|
||||||
|
(select* [this vals structure next-fn]
|
||||||
|
(if (contains? structure k)
|
||||||
|
(next-fn vals (get structure k))
|
||||||
|
i/NONE))
|
||||||
|
(transform* [this vals structure next-fn]
|
||||||
|
(if (contains? structure k)
|
||||||
|
(assoc structure k (next-fn vals (get structure k)))
|
||||||
|
structure)))
|
||||||
|
|
|
||||||
|
|
@ -1305,3 +1305,12 @@
|
||||||
|
|
||||||
(deftest inline-caching-vector-params-test
|
(deftest inline-caching-vector-params-test
|
||||||
(is (= [10 [11]] (multi-transform (s/terminal-val [10 [11]]) :a))))
|
(is (= [10 [11]] (multi-transform (s/terminal-val [10 [11]]) :a))))
|
||||||
|
|
||||||
|
(defn eachnav-fn-test [akey data]
|
||||||
|
(select-any (s/keypath "a" akey) data))
|
||||||
|
|
||||||
|
(deftest eachnav-test
|
||||||
|
(let [data {"a" {"b" 1 "c" 2}}]
|
||||||
|
(is (= 1 (eachnav-fn-test "b" data)))
|
||||||
|
(is (= 2 (eachnav-fn-test "c" data)))
|
||||||
|
))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue