merge macros namespace into core com.rpl.specter namespace
This commit is contained in:
parent
e18d2b3b34
commit
2e85cedcc4
11 changed files with 449 additions and 446 deletions
|
|
@ -134,7 +134,6 @@ You can also find help in the #specter channel on [Clojurians](http://clojurians
|
||||||
Increment all the values in maps of maps:
|
Increment all the values in maps of maps:
|
||||||
```clojure
|
```clojure
|
||||||
user> (use 'com.rpl.specter)
|
user> (use 'com.rpl.specter)
|
||||||
user> (use 'com.rpl.specter.macros)
|
|
||||||
user> (transform [MAP-VALS MAP-VALS]
|
user> (transform [MAP-VALS MAP-VALS]
|
||||||
inc
|
inc
|
||||||
{:a {:aa 1} :b {:ba -1 :bb 2}})
|
{:a {:aa 1} :b {:ba -1 :bb 2}})
|
||||||
|
|
@ -266,7 +265,6 @@ You can make an "AccountPath" that dynamically chooses its path based on the typ
|
||||||
|
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(use 'com.rpl.specter.macros)
|
|
||||||
(defprotocolpath AccountPath [])
|
(defprotocolpath AccountPath [])
|
||||||
(extend-protocolpath AccountPath
|
(extend-protocolpath AccountPath
|
||||||
User :account
|
User :account
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
:plugins [[lein-codox "0.9.5"]]
|
:plugins [[lein-codox "0.9.5"]]
|
||||||
:codox {:source-paths ["target/classes" "src/clj"]
|
:codox {:source-paths ["target/classes" "src/clj"]
|
||||||
:namespaces [com.rpl.specter
|
:namespaces [com.rpl.specter
|
||||||
com.rpl.specter.macros
|
|
||||||
com.rpl.specter.zipper
|
com.rpl.specter.zipper
|
||||||
com.rpl.specter.protocols
|
com.rpl.specter.protocols
|
||||||
com.rpl.specter.transients]
|
com.rpl.specter.transients]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
(ns com.rpl.specter
|
(ns com.rpl.specter
|
||||||
#?(:cljs (:require-macros
|
#?(:cljs (:require-macros
|
||||||
[com.rpl.specter.macros
|
[com.rpl.specter
|
||||||
:refer
|
:refer
|
||||||
[late-bound-nav
|
[late-bound-nav
|
||||||
late-bound-richnav
|
late-bound-richnav
|
||||||
|
|
@ -14,23 +14,413 @@
|
||||||
[com.rpl.specter.util-macros :refer
|
[com.rpl.specter.util-macros :refer
|
||||||
[doseqres]]))
|
[doseqres]]))
|
||||||
|
|
||||||
(:use [com.rpl.specter.protocols :only [ImplicitNav]]
|
(:use [com.rpl.specter.protocols :only [ImplicitNav RichNavigator]]
|
||||||
#?(:clj [com.rpl.specter.macros :only
|
|
||||||
[late-bound-nav
|
|
||||||
late-bound-richnav
|
|
||||||
late-bound-collector
|
|
||||||
defcollector
|
|
||||||
defnav
|
|
||||||
defdynamicnav
|
|
||||||
richnav
|
|
||||||
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]
|
||||||
[com.rpl.specter.navs :as n]
|
[com.rpl.specter.navs :as n]
|
||||||
|
#?(:clj [clojure.walk :as cljwalk])
|
||||||
|
#?(:clj [com.rpl.specter.macros :as macros])
|
||||||
[clojure.set :as set]))
|
[clojure.set :as set]))
|
||||||
|
|
||||||
|
|
||||||
|
#?(:clj
|
||||||
|
(do
|
||||||
|
|
||||||
|
(defmacro defmacroalias [name target]
|
||||||
|
`(do
|
||||||
|
(def ~name (var ~target))
|
||||||
|
(alter-meta! (var ~name) merge {:macro true})))
|
||||||
|
|
||||||
|
(defmacroalias richnav macros/richnav)
|
||||||
|
(defmacroalias nav macros/nav)
|
||||||
|
(defmacroalias defnav macros/defnav)
|
||||||
|
|
||||||
|
(defmacro collector [params [_ [_ structure-sym] & body]]
|
||||||
|
`(richnav ~params
|
||||||
|
(~'select* [this# vals# ~structure-sym next-fn#]
|
||||||
|
(next-fn# (conj vals# (do ~@body)) ~structure-sym))
|
||||||
|
(~'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)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- late-bound-operation [bindings builder-op impls]
|
||||||
|
(let [bindings (partition 2 bindings)
|
||||||
|
params (map first bindings)
|
||||||
|
curr-params (map second bindings)]
|
||||||
|
`(let [builder# (~builder-op [~@params] ~@impls)
|
||||||
|
curr-params# [~@curr-params]]
|
||||||
|
(if (every? (complement i/dynamic-param?) curr-params#)
|
||||||
|
(apply builder# curr-params#)
|
||||||
|
(com.rpl.specter.impl/->DynamicFunction builder# curr-params#)))))
|
||||||
|
|
||||||
|
(defmacro late-bound-nav [bindings & impls]
|
||||||
|
(late-bound-operation bindings `nav impls))
|
||||||
|
|
||||||
|
(defmacro late-bound-collector [bindings impl]
|
||||||
|
(late-bound-operation bindings `collector [impl]))
|
||||||
|
|
||||||
|
(defmacro late-bound-richnav [bindings & impls]
|
||||||
|
(late-bound-operation bindings `richnav impls))
|
||||||
|
|
||||||
|
(defmacro with-inline-debug [& body]
|
||||||
|
`(binding [i/*DEBUG-INLINE-CACHING* true]
|
||||||
|
~@body))
|
||||||
|
|
||||||
|
(defmacro declarepath [name]
|
||||||
|
`(def ~name (i/local-declarepath)))
|
||||||
|
|
||||||
|
(defmacro providepath [name apath]
|
||||||
|
`(i/providepath* ~name (path ~apath)))
|
||||||
|
|
||||||
|
(defmacro recursive-path [params self-sym path]
|
||||||
|
(if (empty? params)
|
||||||
|
`(let [~self-sym (i/local-declarepath)]
|
||||||
|
(providepath ~self-sym ~path)
|
||||||
|
~self-sym)
|
||||||
|
`(i/direct-nav-obj
|
||||||
|
(fn ~params
|
||||||
|
(let [~self-sym (i/local-declarepath)]
|
||||||
|
(providepath ~self-sym ~path)
|
||||||
|
~self-sym)))))
|
||||||
|
|
||||||
|
;; copied from tools.macro to avoid the dependency
|
||||||
|
(defn- name-with-attributes
|
||||||
|
"To be used in macro definitions.
|
||||||
|
Handles optional docstrings and attribute maps for a name to be defined
|
||||||
|
in a list of macro arguments. If the first macro argument is a string,
|
||||||
|
it is added as a docstring to name and removed from the macro argument
|
||||||
|
list. If afterwards the first macro argument is a map, its entries are
|
||||||
|
added to the name's metadata map and the map is removed from the
|
||||||
|
macro argument list. The return value is a vector containing the name
|
||||||
|
with its extended metadata map and the list of unprocessed macro
|
||||||
|
arguments."
|
||||||
|
[name macro-args]
|
||||||
|
(let [[docstring macro-args] (if (string? (first macro-args))
|
||||||
|
[(first macro-args) (next macro-args)]
|
||||||
|
[nil macro-args])
|
||||||
|
[attr macro-args] (if (map? (first macro-args))
|
||||||
|
[(first macro-args) (next macro-args)]
|
||||||
|
[{} macro-args])
|
||||||
|
attr (if docstring
|
||||||
|
(assoc attr :doc docstring)
|
||||||
|
attr)
|
||||||
|
attr (if (meta name)
|
||||||
|
(conj (meta name) attr)
|
||||||
|
attr)]
|
||||||
|
[(with-meta name attr) macro-args]))
|
||||||
|
|
||||||
|
(defmacro dynamicnav [& args]
|
||||||
|
`(vary-meta (fn ~@args) assoc :dynamicnav true))
|
||||||
|
|
||||||
|
(defmacro defdynamicnav
|
||||||
|
"Defines a function that can choose what navigator to use at runtime based on
|
||||||
|
the dynamic context. The arguments will either be static values or
|
||||||
|
objects satisfying `dynamic-param?`. Use `late-bound-nav` to produce a runtime
|
||||||
|
navigator that uses the values of the dynamic params. See `selected?` for
|
||||||
|
an illustrative example of dynamic navs."
|
||||||
|
[name & args]
|
||||||
|
(let [[name args] (name-with-attributes name args)]
|
||||||
|
`(def ~name (dynamicnav ~@args))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- ic-prepare-path [locals-set path]
|
||||||
|
(cond
|
||||||
|
(vector? path)
|
||||||
|
(mapv #(ic-prepare-path locals-set %) path)
|
||||||
|
|
||||||
|
(symbol? path)
|
||||||
|
(if (contains? locals-set path)
|
||||||
|
(let [s (get locals-set path)
|
||||||
|
embed (i/maybe-direct-nav path (-> s meta :direct-nav))]
|
||||||
|
`(com.rpl.specter.impl/->LocalSym ~path (quote ~embed)))
|
||||||
|
;; var-get doesn't work in cljs, so capture the val in the macro instead
|
||||||
|
`(com.rpl.specter.impl/->VarUse ~path (var ~path) (quote ~path)))
|
||||||
|
|
||||||
|
|
||||||
|
(i/fn-invocation? path)
|
||||||
|
(let [[op & params] path]
|
||||||
|
;; need special case for 'fn since macroexpand does NOT
|
||||||
|
;; expand fn when run on cljs code, but it's also not considered a special symbol
|
||||||
|
(if (or (= 'fn op) (special-symbol? op))
|
||||||
|
`(com.rpl.specter.impl/->SpecialFormUse ~path (quote ~path))
|
||||||
|
`(com.rpl.specter.impl/->FnInvocation
|
||||||
|
~(ic-prepare-path locals-set op)
|
||||||
|
~(mapv #(ic-prepare-path locals-set %) params)
|
||||||
|
(quote ~path))))
|
||||||
|
|
||||||
|
|
||||||
|
:else
|
||||||
|
(if (empty? (i/used-locals locals-set path))
|
||||||
|
path
|
||||||
|
`(com.rpl.specter.impl/->DynamicVal (quote ~path)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- ic-possible-params [path]
|
||||||
|
(do
|
||||||
|
(mapcat
|
||||||
|
(fn [e]
|
||||||
|
(cond (or (set? e)
|
||||||
|
(map? e)
|
||||||
|
(symbol? e)
|
||||||
|
(and (i/fn-invocation? e)
|
||||||
|
(or (contains? #{'fn* 'fn} (first e))
|
||||||
|
(special-symbol? (first e)))))
|
||||||
|
[e]
|
||||||
|
|
||||||
|
(sequential? e)
|
||||||
|
(ic-possible-params e)))
|
||||||
|
|
||||||
|
|
||||||
|
path)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- cljs-macroexpand [env form]
|
||||||
|
(let [expand-fn (i/cljs-analyzer-macroexpand-1)
|
||||||
|
mform (expand-fn env form)]
|
||||||
|
(cond (identical? form mform) mform
|
||||||
|
(and (seq? mform) (#{'js*} (first mform))) form
|
||||||
|
:else (cljs-macroexpand env mform))))
|
||||||
|
|
||||||
|
(defn- cljs-macroexpand-all* [env form]
|
||||||
|
(if (and (seq? form)
|
||||||
|
(#{'fn 'fn* 'cljs.core/fn} (first form)))
|
||||||
|
form
|
||||||
|
(let [expanded (if (seq? form) (cljs-macroexpand env form) form)]
|
||||||
|
(cljwalk/walk #(cljs-macroexpand-all* env %) identity expanded))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- cljs-macroexpand-all [env form]
|
||||||
|
(let [ret (cljs-macroexpand-all* env form)]
|
||||||
|
ret))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro path
|
||||||
|
"Same as calling comp-paths, except it caches the composition of the static parts
|
||||||
|
of the path for later re-use (when possible). For almost all idiomatic uses
|
||||||
|
of Specter provides huge speedup. This macro is automatically used by the
|
||||||
|
select/transform/setval/replace-in/etc. macros."
|
||||||
|
[& path]
|
||||||
|
(let [;;this is a hack, but the composition of &env is considered stable for cljs
|
||||||
|
platform (if (contains? &env :locals) :cljs :clj)
|
||||||
|
local-syms (if (= platform :cljs)
|
||||||
|
(-> &env :locals keys set) ;cljs
|
||||||
|
(-> &env keys set)) ;clj
|
||||||
|
|
||||||
|
used-locals (i/used-locals local-syms path)
|
||||||
|
|
||||||
|
;; note: very important to use riddley's macroexpand-all here, so that
|
||||||
|
;; &env is preserved in any potential nested calls to select (like via
|
||||||
|
;; a view function)
|
||||||
|
expanded (if (= platform :clj)
|
||||||
|
(i/clj-macroexpand-all (vec path))
|
||||||
|
(cljs-macroexpand-all &env (vec path)))
|
||||||
|
|
||||||
|
prepared-path (ic-prepare-path local-syms expanded)
|
||||||
|
possible-params (vec (ic-possible-params expanded))
|
||||||
|
|
||||||
|
cache-sym (vary-meta
|
||||||
|
(gensym "pathcache")
|
||||||
|
assoc :cljs.analyzer/no-resolve true)
|
||||||
|
|
||||||
|
info-sym (gensym "info")
|
||||||
|
|
||||||
|
get-cache-code (if (= platform :clj)
|
||||||
|
`(try (i/get-cell ~cache-sym)
|
||||||
|
(catch ClassCastException e#
|
||||||
|
(if (bound? (var ~cache-sym))
|
||||||
|
(throw e#)
|
||||||
|
(do
|
||||||
|
(alter-var-root
|
||||||
|
(var ~cache-sym)
|
||||||
|
(fn [_#] (i/mutable-cell)))
|
||||||
|
nil))))
|
||||||
|
cache-sym)
|
||||||
|
|
||||||
|
add-cache-code (if (= platform :clj)
|
||||||
|
`(i/set-cell! ~cache-sym ~info-sym)
|
||||||
|
`(def ~cache-sym ~info-sym))
|
||||||
|
|
||||||
|
precompiled-sym (gensym "precompiled")
|
||||||
|
|
||||||
|
handle-params-code
|
||||||
|
(if (= platform :clj)
|
||||||
|
`(~precompiled-sym ~@used-locals)
|
||||||
|
`(~precompiled-sym ~possible-params))]
|
||||||
|
(if (= platform :clj)
|
||||||
|
(i/intern* *ns* cache-sym (i/mutable-cell)))
|
||||||
|
`(let [info# ~get-cache-code
|
||||||
|
|
||||||
|
^com.rpl.specter.impl.CachedPathInfo info#
|
||||||
|
(if (nil? info#)
|
||||||
|
(let [~info-sym (i/magic-precompilation
|
||||||
|
~prepared-path
|
||||||
|
~(str *ns*)
|
||||||
|
(quote ~used-locals)
|
||||||
|
(quote ~possible-params))]
|
||||||
|
~add-cache-code
|
||||||
|
~info-sym)
|
||||||
|
info#)
|
||||||
|
|
||||||
|
~precompiled-sym (.-precompiled info#)
|
||||||
|
dynamic?# (.-dynamic? info#)]
|
||||||
|
(if dynamic?#
|
||||||
|
~handle-params-code
|
||||||
|
~precompiled-sym))))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro select
|
||||||
|
"Navigates to and returns a sequence of all the elements specified by the path.
|
||||||
|
This macro will do inline caching of the path."
|
||||||
|
[apath structure]
|
||||||
|
`(i/compiled-select* (path ~apath) ~structure))
|
||||||
|
|
||||||
|
(defmacro select-one!
|
||||||
|
"Returns exactly one element, throws exception if zero or multiple elements found.
|
||||||
|
This macro will do inline caching of the path."
|
||||||
|
[apath structure]
|
||||||
|
`(i/compiled-select-one!* (path ~apath) ~structure))
|
||||||
|
|
||||||
|
(defmacro select-one
|
||||||
|
"Like select, but returns either one element or nil. Throws exception if multiple elements found.
|
||||||
|
This macro will do inline caching of the path."
|
||||||
|
[apath structure]
|
||||||
|
`(i/compiled-select-one* (path ~apath) ~structure))
|
||||||
|
|
||||||
|
(defmacro select-first
|
||||||
|
"Returns first element found.
|
||||||
|
This macro will do inline caching of the path."
|
||||||
|
[apath structure]
|
||||||
|
`(i/compiled-select-first* (path ~apath) ~structure))
|
||||||
|
|
||||||
|
(defmacro select-any
|
||||||
|
"Returns any element found or [[NONE]] if nothing selected. This is the most
|
||||||
|
efficient of the various selection operations.
|
||||||
|
This macro will do inline caching of the path."
|
||||||
|
[apath structure]
|
||||||
|
`(i/compiled-select-any* (path ~apath) ~structure))
|
||||||
|
|
||||||
|
(defmacro selected-any?
|
||||||
|
"Returns true if any element was selected, false otherwise.
|
||||||
|
This macro will do inline caching of the path."
|
||||||
|
[apath structure]
|
||||||
|
`(i/compiled-selected-any?* (path ~apath) ~structure))
|
||||||
|
|
||||||
|
(defmacro transform
|
||||||
|
"Navigates to each value specified by the path and replaces it by the result of running
|
||||||
|
the transform-fn on it.
|
||||||
|
This macro will do inline caching of the path."
|
||||||
|
[apath transform-fn structure]
|
||||||
|
`(i/compiled-transform* (path ~apath) ~transform-fn ~structure))
|
||||||
|
|
||||||
|
(defmacro multi-transform
|
||||||
|
"Just like `transform` but expects transform functions to be specified
|
||||||
|
inline in the path using `terminal`. Error is thrown if navigation finishes
|
||||||
|
at a non-`terminal` navigator. `terminal-val` is a wrapper around `terminal` and is
|
||||||
|
the `multi-transform` equivalent of `setval`.
|
||||||
|
This macro will do inline caching of the path."
|
||||||
|
[apath structure]
|
||||||
|
`(i/compiled-multi-transform* (path ~apath) ~structure))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro setval
|
||||||
|
"Navigates to each value specified by the path and replaces it by `aval`.
|
||||||
|
This macro will do inline caching of the path."
|
||||||
|
[apath aval structure]
|
||||||
|
`(i/compiled-setval* (path ~apath) ~aval ~structure))
|
||||||
|
|
||||||
|
(defmacro traverse
|
||||||
|
"Return a reducible object that traverses over `structure` to every element
|
||||||
|
specified by the path.
|
||||||
|
This macro will do inline caching of the path."
|
||||||
|
[apath structure]
|
||||||
|
`(i/do-compiled-traverse (path ~apath) ~structure))
|
||||||
|
|
||||||
|
(defmacro replace-in
|
||||||
|
"Similar to transform, except returns a pair of [transformed-structure sequence-of-user-ret].
|
||||||
|
The transform-fn in this case is expected to return [ret user-ret]. ret is
|
||||||
|
what's used to transform the data structure, while user-ret will be added to the user-ret sequence
|
||||||
|
in the final return. replace-in is useful for situations where you need to know the specific values
|
||||||
|
of what was transformed in the data structure.
|
||||||
|
This macro will do inline caching of the path."
|
||||||
|
[apath transform-fn structure & args]
|
||||||
|
`(i/compiled-replace-in* (path ~apath) ~transform-fn ~structure ~@args))
|
||||||
|
|
||||||
|
(defmacro collected?
|
||||||
|
"Creates a filter function navigator that takes in all the collected values
|
||||||
|
as input. For arguments, can use `(collected? [a b] ...)` syntax to look
|
||||||
|
at each collected value as individual arguments, or `(collected? v ...)` syntax
|
||||||
|
to capture all the collected values as a single vector."
|
||||||
|
[params & body]
|
||||||
|
`(i/collected?* (~'fn [~params] ~@body)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- protpath-sym [name]
|
||||||
|
(-> name (str "-prot") symbol))
|
||||||
|
|
||||||
|
(defn- protpath-meth-sym [name]
|
||||||
|
(-> name (str "-retrieve") symbol))
|
||||||
|
|
||||||
|
|
||||||
|
(defmacro defprotocolpath
|
||||||
|
"Defines a navigator that chooses the path to take based on the type
|
||||||
|
of the value at the current point. May be specified with parameters to
|
||||||
|
specify that all extensions must require that number of parameters.
|
||||||
|
|
||||||
|
Currently not available for ClojureScript.
|
||||||
|
|
||||||
|
Example of usage:
|
||||||
|
(defrecord SingleAccount [funds])
|
||||||
|
(defrecord FamilyAccount [single-accounts])
|
||||||
|
|
||||||
|
(defprotocolpath FundsPath)
|
||||||
|
(extend-protocolpath FundsPath
|
||||||
|
SingleAccount :funds
|
||||||
|
FamilyAccount [ALL FundsPath]
|
||||||
|
)
|
||||||
|
"
|
||||||
|
([name]
|
||||||
|
`(defprotocolpath ~name []))
|
||||||
|
([name params]
|
||||||
|
(let [prot-name (protpath-sym name)
|
||||||
|
m (protpath-meth-sym name)
|
||||||
|
num-params (count params)
|
||||||
|
ssym (gensym "structure")
|
||||||
|
rargs [(gensym "vals") ssym (gensym "next-fn")]
|
||||||
|
retrieve `(~m ~ssym ~@params)]
|
||||||
|
`(do
|
||||||
|
(defprotocol ~prot-name (~m [structure# ~@params]))
|
||||||
|
(defrichnav ~name ~params
|
||||||
|
(~'select* [this# ~@rargs]
|
||||||
|
(let [inav# ~retrieve]
|
||||||
|
(i/exec-select* inav# ~@rargs)))
|
||||||
|
(~'transform* [this# ~@rargs]
|
||||||
|
(let [inav# ~retrieve]
|
||||||
|
(i/exec-transform* inav# ~@rargs))))))))
|
||||||
|
|
||||||
|
(defn extend-protocolpath* [protpath-prot extensions]
|
||||||
|
(let [m (-> protpath-prot :sigs keys first)
|
||||||
|
params (-> protpath-prot :sigs first last :arglists first)]
|
||||||
|
(doseq [[atype path-code] extensions]
|
||||||
|
(extend atype protpath-prot
|
||||||
|
{m (eval `(fn ~params (path ~path-code)))}))))
|
||||||
|
|
||||||
|
(defmacro extend-protocolpath
|
||||||
|
"Used in conjunction with `defprotocolpath`. See [[defprotocolpath]]."
|
||||||
|
[protpath & extensions]
|
||||||
|
(let [extensions (partition 2 extensions)
|
||||||
|
embed (vec (for [[t p] extensions] [t `(quote ~p)]))]
|
||||||
|
`(extend-protocolpath*
|
||||||
|
~(protpath-sym protpath)
|
||||||
|
~embed)))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defn comp-paths
|
(defn comp-paths
|
||||||
"Returns a compiled version of the given path for use with
|
"Returns a compiled version of the given path for use with
|
||||||
compiled-{select/transform/setval/etc.} functions. This can compile navigators
|
compiled-{select/transform/setval/etc.} functions. This can compile navigators
|
||||||
|
|
|
||||||
|
|
@ -1,430 +1,46 @@
|
||||||
(ns com.rpl.specter.macros
|
(ns com.rpl.specter.macros
|
||||||
(:use [com.rpl.specter.protocols :only [RichNavigator]])
|
(:use [com.rpl.specter.protocols :only [RichNavigator]])
|
||||||
(:require [com.rpl.specter.impl :as i]
|
(:require [com.rpl.specter.impl :as i]))
|
||||||
[clojure.walk :as cljwalk]))
|
|
||||||
|
|
||||||
(defn ^:no-doc determine-params-impls [impls]
|
|
||||||
(let [grouped (->> impls (map (fn [[n & body]] [n body])) (into {}))]
|
(defn- determine-params-impls [impls]
|
||||||
(if-not (= #{'select* 'transform*} (-> grouped keys set))
|
(let [grouped (->> impls (map (fn [[n & body]] [n body])) (into {}))]
|
||||||
(i/throw-illegal "defnav must implement select* and transform*, instead got "
|
(if-not (= #{'select* 'transform*} (-> grouped keys set))
|
||||||
(keys grouped)))
|
(i/throw-illegal "defnav must implement select* and transform*, instead got "
|
||||||
grouped))
|
(keys grouped)))
|
||||||
|
grouped))
|
||||||
|
|
||||||
|
|
||||||
(defmacro richnav [params & impls]
|
(defmacro richnav [params & impls]
|
||||||
(if (empty? params)
|
(if (empty? params)
|
||||||
`(reify RichNavigator ~@impls)
|
`(reify RichNavigator ~@impls)
|
||||||
`(i/direct-nav-obj
|
`(i/direct-nav-obj
|
||||||
(fn ~params
|
(fn ~params
|
||||||
(reify RichNavigator
|
(reify RichNavigator
|
||||||
~@impls)))))
|
~@impls)))))
|
||||||
|
|
||||||
|
|
||||||
(defmacro nav [params & impls]
|
(defmacro nav [params & impls]
|
||||||
(let [{[[_ s-structure-sym s-next-fn-sym] & s-body] 'select*
|
(let [{[[_ s-structure-sym s-next-fn-sym] & s-body] 'select*
|
||||||
[[_ t-structure-sym t-next-fn-sym] & t-body] 'transform*} (determine-params-impls impls)]
|
[[_ t-structure-sym t-next-fn-sym] & t-body] 'transform*} (determine-params-impls impls)]
|
||||||
`(richnav ~params
|
`(richnav ~params
|
||||||
(~'select* [this# vals# ~s-structure-sym next-fn#]
|
(~'select* [this# vals# ~s-structure-sym next-fn#]
|
||||||
(let [~s-next-fn-sym (fn [s#] (next-fn# vals# s#))]
|
(let [~s-next-fn-sym (fn [s#] (next-fn# vals# s#))]
|
||||||
~@s-body))
|
~@s-body))
|
||||||
(~'transform* [this# vals# ~t-structure-sym next-fn#]
|
(~'transform* [this# vals# ~t-structure-sym next-fn#]
|
||||||
(let [~t-next-fn-sym (fn [s#] (next-fn# vals# s#))]
|
(let [~t-next-fn-sym (fn [s#] (next-fn# vals# s#))]
|
||||||
~@t-body)))))
|
~@t-body)))))
|
||||||
|
|
||||||
(defmacro collector [params [_ [_ structure-sym] & body]]
|
|
||||||
`(richnav ~params
|
|
||||||
(~'select* [this# vals# ~structure-sym next-fn#]
|
|
||||||
(next-fn# (conj vals# (do ~@body)) ~structure-sym))
|
|
||||||
(~'transform* [this# vals# ~structure-sym next-fn#]
|
|
||||||
(next-fn# (conj vals# (do ~@body)) ~structure-sym))))
|
|
||||||
|
|
||||||
(defn- helper-name [name method-name]
|
(defn- helper-name [name method-name]
|
||||||
(symbol (str name "-" method-name)))
|
(symbol (str name "-" method-name)))
|
||||||
|
|
||||||
(defmacro defnav [name params & impls]
|
(defmacro defnav [name params & impls]
|
||||||
;; remove the "this" param for the helper
|
;; remove the "this" param for the helper
|
||||||
(let [helpers (for [[mname [_ & mparams] & mbody] impls]
|
(let [helpers (for [[mname [_ & mparams] & mbody] impls]
|
||||||
`(defn ~(helper-name name mname) [~@params ~@mparams] ~@mbody))
|
`(defn ~(helper-name name mname) [~@params ~@mparams] ~@mbody))
|
||||||
decls (for [[mname & _] impls]
|
decls (for [[mname & _] impls]
|
||||||
`(declare ~(helper-name name mname)))]
|
`(declare ~(helper-name name mname)))]
|
||||||
`(do
|
`(do
|
||||||
~@decls
|
~@decls
|
||||||
~@helpers
|
~@helpers
|
||||||
(def ~name (nav ~params ~@impls)))))
|
(def ~name (nav ~params ~@impls)))))
|
||||||
|
|
||||||
(defmacro defrichnav [name params & impls]
|
|
||||||
`(def ~name (richnav ~params ~@impls)))
|
|
||||||
|
|
||||||
(defmacro defcollector [name & body]
|
|
||||||
`(def ~name (collector ~@body)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn- late-bound-operation [bindings builder-op impls]
|
|
||||||
(let [bindings (partition 2 bindings)
|
|
||||||
params (map first bindings)
|
|
||||||
curr-params (map second bindings)]
|
|
||||||
`(let [builder# (~builder-op [~@params] ~@impls)
|
|
||||||
curr-params# [~@curr-params]]
|
|
||||||
(if (every? (complement i/dynamic-param?) curr-params#)
|
|
||||||
(apply builder# curr-params#)
|
|
||||||
(com.rpl.specter.impl/->DynamicFunction builder# curr-params#)))))
|
|
||||||
|
|
||||||
(defmacro late-bound-nav [bindings & impls]
|
|
||||||
(late-bound-operation bindings `nav impls))
|
|
||||||
|
|
||||||
(defmacro late-bound-collector [bindings impl]
|
|
||||||
(late-bound-operation bindings `collector [impl]))
|
|
||||||
|
|
||||||
(defmacro late-bound-richnav [bindings & impls]
|
|
||||||
(late-bound-operation bindings `richnav impls))
|
|
||||||
|
|
||||||
(defmacro with-inline-debug [& body]
|
|
||||||
`(binding [i/*DEBUG-INLINE-CACHING* true]
|
|
||||||
~@body))
|
|
||||||
|
|
||||||
(defmacro declarepath [name]
|
|
||||||
`(def ~name (i/local-declarepath)))
|
|
||||||
|
|
||||||
(defmacro providepath [name apath]
|
|
||||||
`(i/providepath* ~name (path ~apath)))
|
|
||||||
|
|
||||||
(defmacro recursive-path [params self-sym path]
|
|
||||||
(if (empty? params)
|
|
||||||
`(let [~self-sym (i/local-declarepath)]
|
|
||||||
(providepath ~self-sym ~path)
|
|
||||||
~self-sym)
|
|
||||||
`(i/direct-nav-obj
|
|
||||||
(fn ~params
|
|
||||||
(let [~self-sym (i/local-declarepath)]
|
|
||||||
(providepath ~self-sym ~path)
|
|
||||||
~self-sym)))))
|
|
||||||
|
|
||||||
;; copied from tools.macro to avoid the dependency
|
|
||||||
(defn ^:no-doc name-with-attributes
|
|
||||||
"To be used in macro definitions.
|
|
||||||
Handles optional docstrings and attribute maps for a name to be defined
|
|
||||||
in a list of macro arguments. If the first macro argument is a string,
|
|
||||||
it is added as a docstring to name and removed from the macro argument
|
|
||||||
list. If afterwards the first macro argument is a map, its entries are
|
|
||||||
added to the name's metadata map and the map is removed from the
|
|
||||||
macro argument list. The return value is a vector containing the name
|
|
||||||
with its extended metadata map and the list of unprocessed macro
|
|
||||||
arguments."
|
|
||||||
[name macro-args]
|
|
||||||
(let [[docstring macro-args] (if (string? (first macro-args))
|
|
||||||
[(first macro-args) (next macro-args)]
|
|
||||||
[nil macro-args])
|
|
||||||
[attr macro-args] (if (map? (first macro-args))
|
|
||||||
[(first macro-args) (next macro-args)]
|
|
||||||
[{} macro-args])
|
|
||||||
attr (if docstring
|
|
||||||
(assoc attr :doc docstring)
|
|
||||||
attr)
|
|
||||||
attr (if (meta name)
|
|
||||||
(conj (meta name) attr)
|
|
||||||
attr)]
|
|
||||||
[(with-meta name attr) macro-args]))
|
|
||||||
|
|
||||||
(defmacro dynamicnav [& args]
|
|
||||||
`(vary-meta (fn ~@args) assoc :dynamicnav true))
|
|
||||||
|
|
||||||
(defmacro defdynamicnav
|
|
||||||
"Defines a function that can choose what navigator to use at runtime based on
|
|
||||||
the dynamic context. The arguments will either be static values or
|
|
||||||
objects satisfying `dynamic-param?`. Use `late-bound-nav` to produce a runtime
|
|
||||||
navigator that uses the values of the dynamic params. See `selected?` for
|
|
||||||
an illustrative example of dynamic navs."
|
|
||||||
[name & args]
|
|
||||||
(let [[name args] (name-with-attributes name args)]
|
|
||||||
`(def ~name (dynamicnav ~@args))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn ^:no-doc ic-prepare-path [locals-set path]
|
|
||||||
(cond
|
|
||||||
(vector? path)
|
|
||||||
(mapv #(ic-prepare-path locals-set %) path)
|
|
||||||
|
|
||||||
(symbol? path)
|
|
||||||
(if (contains? locals-set path)
|
|
||||||
(let [s (get locals-set path)
|
|
||||||
embed (i/maybe-direct-nav path (-> s meta :direct-nav))]
|
|
||||||
`(com.rpl.specter.impl/->LocalSym ~path (quote ~embed)))
|
|
||||||
;; var-get doesn't work in cljs, so capture the val in the macro instead
|
|
||||||
`(com.rpl.specter.impl/->VarUse ~path (var ~path) (quote ~path)))
|
|
||||||
|
|
||||||
|
|
||||||
(i/fn-invocation? path)
|
|
||||||
(let [[op & params] path]
|
|
||||||
;; need special case for 'fn since macroexpand does NOT
|
|
||||||
;; expand fn when run on cljs code, but it's also not considered a special symbol
|
|
||||||
(if (or (= 'fn op) (special-symbol? op))
|
|
||||||
`(com.rpl.specter.impl/->SpecialFormUse ~path (quote ~path))
|
|
||||||
`(com.rpl.specter.impl/->FnInvocation
|
|
||||||
~(ic-prepare-path locals-set op)
|
|
||||||
~(mapv #(ic-prepare-path locals-set %) params)
|
|
||||||
(quote ~path))))
|
|
||||||
|
|
||||||
|
|
||||||
:else
|
|
||||||
(if (empty? (i/used-locals locals-set path))
|
|
||||||
path
|
|
||||||
`(com.rpl.specter.impl/->DynamicVal (quote ~path)))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn ^:no-doc ic-possible-params [path]
|
|
||||||
(do
|
|
||||||
(mapcat
|
|
||||||
(fn [e]
|
|
||||||
(cond (or (set? e)
|
|
||||||
(map? e)
|
|
||||||
(symbol? e)
|
|
||||||
(and (i/fn-invocation? e)
|
|
||||||
(or (contains? #{'fn* 'fn} (first e))
|
|
||||||
(special-symbol? (first e)))))
|
|
||||||
[e]
|
|
||||||
|
|
||||||
(sequential? e)
|
|
||||||
(ic-possible-params e)))
|
|
||||||
|
|
||||||
|
|
||||||
path)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn cljs-macroexpand [env form]
|
|
||||||
(let [expand-fn (i/cljs-analyzer-macroexpand-1)
|
|
||||||
mform (expand-fn env form)]
|
|
||||||
(cond (identical? form mform) mform
|
|
||||||
(and (seq? mform) (#{'js*} (first mform))) form
|
|
||||||
:else (cljs-macroexpand env mform))))
|
|
||||||
|
|
||||||
(defn cljs-macroexpand-all* [env form]
|
|
||||||
(if (and (seq? form)
|
|
||||||
(#{'fn 'fn* 'cljs.core/fn} (first form)))
|
|
||||||
form
|
|
||||||
(let [expanded (if (seq? form) (cljs-macroexpand env form) form)]
|
|
||||||
(cljwalk/walk #(cljs-macroexpand-all* env %) identity expanded))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn cljs-macroexpand-all [env form]
|
|
||||||
(let [ret (cljs-macroexpand-all* env form)]
|
|
||||||
ret))
|
|
||||||
|
|
||||||
|
|
||||||
(defmacro path
|
|
||||||
"Same as calling comp-paths, except it caches the composition of the static parts
|
|
||||||
of the path for later re-use (when possible). For almost all idiomatic uses
|
|
||||||
of Specter provides huge speedup. This macro is automatically used by the
|
|
||||||
select/transform/setval/replace-in/etc. macros."
|
|
||||||
[& path]
|
|
||||||
(let [;;this is a hack, but the composition of &env is considered stable for cljs
|
|
||||||
platform (if (contains? &env :locals) :cljs :clj)
|
|
||||||
local-syms (if (= platform :cljs)
|
|
||||||
(-> &env :locals keys set) ;cljs
|
|
||||||
(-> &env keys set)) ;clj
|
|
||||||
|
|
||||||
used-locals (i/used-locals local-syms path)
|
|
||||||
|
|
||||||
;; note: very important to use riddley's macroexpand-all here, so that
|
|
||||||
;; &env is preserved in any potential nested calls to select (like via
|
|
||||||
;; a view function)
|
|
||||||
expanded (if (= platform :clj)
|
|
||||||
(i/clj-macroexpand-all (vec path))
|
|
||||||
(cljs-macroexpand-all &env (vec path)))
|
|
||||||
|
|
||||||
prepared-path (ic-prepare-path local-syms expanded)
|
|
||||||
possible-params (vec (ic-possible-params expanded))
|
|
||||||
|
|
||||||
cache-sym (vary-meta
|
|
||||||
(gensym "pathcache")
|
|
||||||
assoc :cljs.analyzer/no-resolve true)
|
|
||||||
|
|
||||||
info-sym (gensym "info")
|
|
||||||
|
|
||||||
get-cache-code (if (= platform :clj)
|
|
||||||
`(try (i/get-cell ~cache-sym)
|
|
||||||
(catch ClassCastException e#
|
|
||||||
(if (bound? (var ~cache-sym))
|
|
||||||
(throw e#)
|
|
||||||
(do
|
|
||||||
(alter-var-root
|
|
||||||
(var ~cache-sym)
|
|
||||||
(fn [_#] (i/mutable-cell)))
|
|
||||||
nil))))
|
|
||||||
cache-sym)
|
|
||||||
|
|
||||||
add-cache-code (if (= platform :clj)
|
|
||||||
`(i/set-cell! ~cache-sym ~info-sym)
|
|
||||||
`(def ~cache-sym ~info-sym))
|
|
||||||
|
|
||||||
precompiled-sym (gensym "precompiled")
|
|
||||||
|
|
||||||
handle-params-code
|
|
||||||
(if (= platform :clj)
|
|
||||||
`(~precompiled-sym ~@used-locals)
|
|
||||||
`(~precompiled-sym ~possible-params))]
|
|
||||||
(if (= platform :clj)
|
|
||||||
(i/intern* *ns* cache-sym (i/mutable-cell)))
|
|
||||||
`(let [info# ~get-cache-code
|
|
||||||
|
|
||||||
^com.rpl.specter.impl.CachedPathInfo info#
|
|
||||||
(if (nil? info#)
|
|
||||||
(let [~info-sym (i/magic-precompilation
|
|
||||||
~prepared-path
|
|
||||||
~(str *ns*)
|
|
||||||
(quote ~used-locals)
|
|
||||||
(quote ~possible-params))]
|
|
||||||
~add-cache-code
|
|
||||||
~info-sym)
|
|
||||||
info#)
|
|
||||||
|
|
||||||
~precompiled-sym (.-precompiled info#)
|
|
||||||
dynamic?# (.-dynamic? info#)]
|
|
||||||
(if dynamic?#
|
|
||||||
~handle-params-code
|
|
||||||
~precompiled-sym))))
|
|
||||||
|
|
||||||
|
|
||||||
(defmacro select
|
|
||||||
"Navigates to and returns a sequence of all the elements specified by the path.
|
|
||||||
This macro will do inline caching of the path."
|
|
||||||
[apath structure]
|
|
||||||
`(i/compiled-select* (path ~apath) ~structure))
|
|
||||||
|
|
||||||
(defmacro select-one!
|
|
||||||
"Returns exactly one element, throws exception if zero or multiple elements found.
|
|
||||||
This macro will do inline caching of the path."
|
|
||||||
[apath structure]
|
|
||||||
`(i/compiled-select-one!* (path ~apath) ~structure))
|
|
||||||
|
|
||||||
(defmacro select-one
|
|
||||||
"Like select, but returns either one element or nil. Throws exception if multiple elements found.
|
|
||||||
This macro will do inline caching of the path."
|
|
||||||
[apath structure]
|
|
||||||
`(i/compiled-select-one* (path ~apath) ~structure))
|
|
||||||
|
|
||||||
(defmacro select-first
|
|
||||||
"Returns first element found.
|
|
||||||
This macro will do inline caching of the path."
|
|
||||||
[apath structure]
|
|
||||||
`(i/compiled-select-first* (path ~apath) ~structure))
|
|
||||||
|
|
||||||
(defmacro select-any
|
|
||||||
"Returns any element found or [[NONE]] if nothing selected. This is the most
|
|
||||||
efficient of the various selection operations.
|
|
||||||
This macro will do inline caching of the path."
|
|
||||||
[apath structure]
|
|
||||||
`(i/compiled-select-any* (path ~apath) ~structure))
|
|
||||||
|
|
||||||
(defmacro selected-any?
|
|
||||||
"Returns true if any element was selected, false otherwise.
|
|
||||||
This macro will do inline caching of the path."
|
|
||||||
[apath structure]
|
|
||||||
`(i/compiled-selected-any?* (path ~apath) ~structure))
|
|
||||||
|
|
||||||
(defmacro transform
|
|
||||||
"Navigates to each value specified by the path and replaces it by the result of running
|
|
||||||
the transform-fn on it.
|
|
||||||
This macro will do inline caching of the path."
|
|
||||||
[apath transform-fn structure]
|
|
||||||
`(i/compiled-transform* (path ~apath) ~transform-fn ~structure))
|
|
||||||
|
|
||||||
(defmacro multi-transform
|
|
||||||
"Just like `transform` but expects transform functions to be specified
|
|
||||||
inline in the path using `terminal`. Error is thrown if navigation finishes
|
|
||||||
at a non-`terminal` navigator. `terminal-val` is a wrapper around `terminal` and is
|
|
||||||
the `multi-transform` equivalent of `setval`.
|
|
||||||
This macro will do inline caching of the path."
|
|
||||||
[apath structure]
|
|
||||||
`(i/compiled-multi-transform* (path ~apath) ~structure))
|
|
||||||
|
|
||||||
|
|
||||||
(defmacro setval
|
|
||||||
"Navigates to each value specified by the path and replaces it by `aval`.
|
|
||||||
This macro will do inline caching of the path."
|
|
||||||
[apath aval structure]
|
|
||||||
`(i/compiled-setval* (path ~apath) ~aval ~structure))
|
|
||||||
|
|
||||||
(defmacro traverse
|
|
||||||
"Return a reducible object that traverses over `structure` to every element
|
|
||||||
specified by the path.
|
|
||||||
This macro will do inline caching of the path."
|
|
||||||
[apath structure]
|
|
||||||
`(i/do-compiled-traverse (path ~apath) ~structure))
|
|
||||||
|
|
||||||
(defmacro replace-in
|
|
||||||
"Similar to transform, except returns a pair of [transformed-structure sequence-of-user-ret].
|
|
||||||
The transform-fn in this case is expected to return [ret user-ret]. ret is
|
|
||||||
what's used to transform the data structure, while user-ret will be added to the user-ret sequence
|
|
||||||
in the final return. replace-in is useful for situations where you need to know the specific values
|
|
||||||
of what was transformed in the data structure.
|
|
||||||
This macro will do inline caching of the path."
|
|
||||||
[apath transform-fn structure & args]
|
|
||||||
`(i/compiled-replace-in* (path ~apath) ~transform-fn ~structure ~@args))
|
|
||||||
|
|
||||||
(defmacro collected?
|
|
||||||
"Creates a filter function navigator that takes in all the collected values
|
|
||||||
as input. For arguments, can use `(collected? [a b] ...)` syntax to look
|
|
||||||
at each collected value as individual arguments, or `(collected? v ...)` syntax
|
|
||||||
to capture all the collected values as a single vector."
|
|
||||||
[params & body]
|
|
||||||
`(i/collected?* (~'fn [~params] ~@body)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn- protpath-sym [name]
|
|
||||||
(-> name (str "-prot") symbol))
|
|
||||||
|
|
||||||
(defn- protpath-meth-sym [name]
|
|
||||||
(-> name (str "-retrieve") symbol))
|
|
||||||
|
|
||||||
|
|
||||||
(defmacro defprotocolpath
|
|
||||||
"Defines a navigator that chooses the path to take based on the type
|
|
||||||
of the value at the current point. May be specified with parameters to
|
|
||||||
specify that all extensions must require that number of parameters.
|
|
||||||
|
|
||||||
Currently not available for ClojureScript.
|
|
||||||
|
|
||||||
Example of usage:
|
|
||||||
(defrecord SingleAccount [funds])
|
|
||||||
(defrecord FamilyAccount [single-accounts])
|
|
||||||
|
|
||||||
(defprotocolpath FundsPath)
|
|
||||||
(extend-protocolpath FundsPath
|
|
||||||
SingleAccount :funds
|
|
||||||
FamilyAccount [ALL FundsPath]
|
|
||||||
)
|
|
||||||
"
|
|
||||||
([name]
|
|
||||||
`(defprotocolpath ~name []))
|
|
||||||
([name params]
|
|
||||||
(let [prot-name (protpath-sym name)
|
|
||||||
m (protpath-meth-sym name)
|
|
||||||
num-params (count params)
|
|
||||||
ssym (gensym "structure")
|
|
||||||
rargs [(gensym "vals") ssym (gensym "next-fn")]
|
|
||||||
retrieve `(~m ~ssym ~@params)]
|
|
||||||
`(do
|
|
||||||
(defprotocol ~prot-name (~m [structure# ~@params]))
|
|
||||||
(defrichnav ~name ~params
|
|
||||||
(~'select* [this# ~@rargs]
|
|
||||||
(let [inav# ~retrieve]
|
|
||||||
(i/exec-select* inav# ~@rargs)))
|
|
||||||
(~'transform* [this# ~@rargs]
|
|
||||||
(let [inav# ~retrieve]
|
|
||||||
(i/exec-transform* inav# ~@rargs))))))))
|
|
||||||
|
|
||||||
(defn extend-protocolpath* [protpath-prot extensions]
|
|
||||||
(let [m (-> protpath-prot :sigs keys first)
|
|
||||||
params (-> protpath-prot :sigs first last :arglists first)]
|
|
||||||
(doseq [[atype path-code] extensions]
|
|
||||||
(extend atype protpath-prot
|
|
||||||
{m (eval `(fn ~params (path ~path-code)))}))))
|
|
||||||
|
|
||||||
(defmacro extend-protocolpath
|
|
||||||
"Used in conjunction with `defprotocolpath`. See [[defprotocolpath]]."
|
|
||||||
[protpath & extensions]
|
|
||||||
(let [extensions (partition 2 extensions)
|
|
||||||
embed (vec (for [[t p] extensions] [t `(quote ~p)]))]
|
|
||||||
`(extend-protocolpath*
|
|
||||||
~(protpath-sym protpath)
|
|
||||||
~embed)))
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
(ns com.rpl.specter.navs
|
(ns com.rpl.specter.navs
|
||||||
#?(:cljs (:require-macros
|
#?(:cljs (:require-macros
|
||||||
[com.rpl.specter.macros
|
[com.rpl.specter
|
||||||
:refer
|
:refer
|
||||||
[defnav]]
|
[defnav]]
|
||||||
[com.rpl.specter.util-macros :refer
|
[com.rpl.specter.util-macros :refer
|
||||||
[doseqres]]))
|
[doseqres]]))
|
||||||
(:use #?(:clj [com.rpl.specter macros])
|
(:use #?(:clj [com.rpl.specter.macros :only [defnav]])
|
||||||
#?(: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]
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
(ns com.rpl.specter.protocols)
|
(ns com.rpl.specter.protocols)
|
||||||
|
|
||||||
(defprotocol RichNavigator
|
(defprotocol RichNavigator
|
||||||
"Do not use this protocol directly. All navigators must be created using
|
"Do not use this protocol directly. All navigators must be created using macros
|
||||||
com.rpl.specter.macros namespace."
|
in com.rpl.specter namespace."
|
||||||
(select* [this vals structure next-fn]
|
(select* [this vals structure next-fn]
|
||||||
"An implementation of `select*` must call `next-fn` on each
|
"An implementation of `select*` must call `next-fn` on each
|
||||||
subvalue of `structure`. The result of `select*` is specified
|
subvalue of `structure`. The result of `select*` is specified
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
(defprotocol Collector
|
(defprotocol Collector
|
||||||
"Do not use this protocol directly. All navigators must be created using
|
"Do not use this protocol directly. All navigators must be created using
|
||||||
com.rpl.specter.macros namespace."
|
macros in com.rpl.specter namespace."
|
||||||
(collect-val [this structure]))
|
(collect-val [this structure]))
|
||||||
|
|
||||||
(defprotocol ImplicitNav
|
(defprotocol ImplicitNav
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
(ns com.rpl.specter.transients
|
(ns com.rpl.specter.transients
|
||||||
#?(:cljs
|
#?(:cljs
|
||||||
(:require-macros [com.rpl.specter.macros
|
(:require-macros [com.rpl.specter
|
||||||
:refer
|
:refer
|
||||||
[defnav]]))
|
[defnav]]))
|
||||||
(:use #?(:clj
|
(:use #?(:clj
|
||||||
[com.rpl.specter.macros :only
|
[com.rpl.specter :only
|
||||||
[defnav]]))
|
[defnav]]))
|
||||||
(:require [com.rpl.specter.navs :as n]
|
(:require [com.rpl.specter.navs :as n]
|
||||||
[com.rpl.specter :refer [subselect selected?]]))
|
[com.rpl.specter :refer [subselect selected?]]))
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
(ns com.rpl.specter.zipper
|
(ns com.rpl.specter.zipper
|
||||||
#?(:cljs (:require-macros
|
#?(:cljs (:require-macros
|
||||||
[com.rpl.specter.macros
|
[com.rpl.specter
|
||||||
:refer [defnav nav declarepath providepath recursive-path]]))
|
:refer [defnav nav declarepath providepath recursive-path]]))
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(:use
|
(:use
|
||||||
[com.rpl.specter.macros :only [defnav nav declarepath providepath
|
[com.rpl.specter :only [defnav nav declarepath providepath
|
||||||
recursive-path]]))
|
recursive-path]]))
|
||||||
(:require [com.rpl.specter :as s]
|
(:require [com.rpl.specter :as s]
|
||||||
[clojure.zip :as zip]))
|
[clojure.zip :as zip]))
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
[cljs.test.check.cljs-test :refer [defspec]]
|
[cljs.test.check.cljs-test :refer [defspec]]
|
||||||
[com.rpl.specter.cljs-test-helpers :refer [for-all+]]
|
[com.rpl.specter.cljs-test-helpers :refer [for-all+]]
|
||||||
[com.rpl.specter.test-helpers :refer [ic-test]]
|
[com.rpl.specter.test-helpers :refer [ic-test]]
|
||||||
[com.rpl.specter.macros
|
[com.rpl.specter
|
||||||
:refer [defprotocolpath defnav extend-protocolpath
|
:refer [defprotocolpath defnav extend-protocolpath
|
||||||
nav declarepath providepath select select-one select-one!
|
nav declarepath providepath select select-one select-one!
|
||||||
select-first transform setval replace-in
|
select-first transform setval replace-in
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
#?(:clj [clojure.test :only [deftest is]])
|
#?(:clj [clojure.test :only [deftest is]])
|
||||||
#?(:clj [clojure.test.check.clojure-test :only [defspec]])
|
#?(:clj [clojure.test.check.clojure-test :only [defspec]])
|
||||||
#?(:clj [com.rpl.specter.test-helpers :only [for-all+ ic-test]])
|
#?(:clj [com.rpl.specter.test-helpers :only [for-all+ ic-test]])
|
||||||
#?(:clj [com.rpl.specter.macros
|
#?(:clj [com.rpl.specter
|
||||||
:only [defprotocolpath defnav extend-protocolpath
|
:only [defprotocolpath defnav extend-protocolpath
|
||||||
nav declarepath providepath select select-one select-one!
|
nav declarepath providepath select select-one select-one!
|
||||||
select-first transform setval replace-in
|
select-first transform setval replace-in
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
[properties :as prop]]
|
[properties :as prop]]
|
||||||
[clojure.test])
|
[clojure.test])
|
||||||
|
|
||||||
(:use [com.rpl.specter.macros :only [select transform]]
|
(:use [com.rpl.specter :only [select transform]]
|
||||||
[com.rpl.specter :only [select* transform*]]))
|
[com.rpl.specter :only [select* transform*]]))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
[cljs.test :refer [is deftest]]
|
[cljs.test :refer [is deftest]]
|
||||||
[cljs.test.check.cljs-test :refer [defspec]]
|
[cljs.test.check.cljs-test :refer [defspec]]
|
||||||
[com.rpl.specter.cljs-test-helpers :refer [for-all+]]
|
[com.rpl.specter.cljs-test-helpers :refer [for-all+]]
|
||||||
[com.rpl.specter.macros
|
[com.rpl.specter
|
||||||
:refer [declarepath providepath select select-one select-one!
|
:refer [declarepath providepath select select-one select-one!
|
||||||
select-first transform setval replace-in]]))
|
select-first transform setval replace-in]]))
|
||||||
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
#?(:clj [clojure.test :only [deftest is]])
|
#?(:clj [clojure.test :only [deftest is]])
|
||||||
#?(:clj [clojure.test.check.clojure-test :only [defspec]])
|
#?(:clj [clojure.test.check.clojure-test :only [defspec]])
|
||||||
#?(:clj [com.rpl.specter.test-helpers :only [for-all+]])
|
#?(:clj [com.rpl.specter.test-helpers :only [for-all+]])
|
||||||
#?(:clj [com.rpl.specter.macros
|
#?(:clj [com.rpl.specter
|
||||||
:only [declarepath providepath select select-one select-one!
|
:only [declarepath providepath select select-one select-one!
|
||||||
select-first transform setval replace-in]]))
|
select-first transform setval replace-in]]))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue