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
|
||||
|
||||
* 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 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
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
(defmacroalias richnav macros/richnav)
|
||||
(defmacroalias nav macros/nav)
|
||||
(defmacroalias defnav macros/defnav)
|
||||
(defmacroalias defrichnav macros/defrichnav)
|
||||
|
||||
(defmacro collector [params [_ [_ structure-sym] & body]]
|
||||
`(richnav ~params
|
||||
|
|
@ -42,9 +43,6 @@
|
|||
(~'transform* [this# vals# ~structure-sym next-fn#]
|
||||
(next-fn# (conj vals# (do ~@body)) ~structure-sym))))
|
||||
|
||||
(defmacro defrichnav [name params & impls]
|
||||
`(def ~name (richnav ~params ~@impls)))
|
||||
|
||||
(defmacro defcollector [name & body]
|
||||
`(def ~name (collector ~@body)))
|
||||
|
||||
|
|
@ -543,6 +541,24 @@
|
|||
|
||||
(def late-path i/late-path)
|
||||
(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
|
||||
|
|
@ -752,28 +768,8 @@
|
|||
next-val))
|
||||
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))
|
||||
NONE))
|
||||
(transform* [this vals structure next-fn]
|
||||
(if (contains? structure k)
|
||||
(assoc structure k (next-fn vals (get structure k)))
|
||||
structure)))
|
||||
(def keypath (eachnav n/keypath*))
|
||||
(def must (eachnav n/must*))
|
||||
|
||||
|
||||
(defrichnav
|
||||
|
|
@ -881,7 +877,7 @@
|
|||
|
||||
(extend-type #?(:clj clojure.lang.Keyword :cljs cljs.core/Keyword)
|
||||
ImplicitNav
|
||||
(implicit-nav [this] (keypath this)))
|
||||
(implicit-nav [this] (n/keypath* this)))
|
||||
|
||||
(extend-type #?(:clj clojure.lang.AFn :cljs function)
|
||||
ImplicitNav
|
||||
|
|
|
|||
|
|
@ -569,7 +569,7 @@
|
|||
:cljs (satisfies? RichNavigator o))
|
||||
o
|
||||
|
||||
(vector? o)
|
||||
(sequential? o)
|
||||
(comp-paths* o)
|
||||
|
||||
:else
|
||||
|
|
@ -602,9 +602,16 @@
|
|||
(defn all-static? [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]
|
||||
(cond (sequential? o)
|
||||
(if (list? o)
|
||||
(if (or (list? o) (seq? o))
|
||||
(map magic-precompilation* o)
|
||||
(into (empty o) (map magic-precompilation* o)))
|
||||
|
||||
|
|
@ -631,7 +638,7 @@
|
|||
params (doall (map magic-precompilation* (:params o)))]
|
||||
(if (or (-> op meta :dynamicnav)
|
||||
(all-static? (conj params op)))
|
||||
(apply op params)
|
||||
(magic-precompilation* (apply op params))
|
||||
(->DynamicFunction op params)))
|
||||
|
||||
:else
|
||||
|
|
|
|||
|
|
@ -44,3 +44,6 @@
|
|||
~@decls
|
||||
~@helpers
|
||||
(def ~name (nav ~params ~@impls)))))
|
||||
|
||||
(defmacro defrichnav [name params & impls]
|
||||
`(def ~name (richnav ~params ~@impls)))
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
[defnav]]
|
||||
[com.rpl.specter.util-macros :refer
|
||||
[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]]))
|
||||
(:require [com.rpl.specter.impl :as i]
|
||||
[clojure.walk :as walk]
|
||||
|
|
@ -409,3 +409,27 @@
|
|||
(if (pred structure)
|
||||
(on-match-fn 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
|
||||
(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