Merge branch 'master' into traverse

This commit is contained in:
Nathan Marz 2016-06-13 13:51:03 -04:00
commit 66d1ce65f3
6 changed files with 109 additions and 3 deletions

View file

@ -5,7 +5,9 @@
* Added `selected-any?` operation that returns true if any element is navigated to. * Added `selected-any?` operation that returns true if any element is navigated to.
* Huge performance improvements to `select`, `select-one`, `select-first`, and `select-one!` * Huge performance improvements to `select`, `select-one`, `select-first`, and `select-one!`
* Added META navigator (thanks @aengelberg) * Added META navigator (thanks @aengelberg)
* Bug fix: END, BEGINNING, FIRST, and LAST now work properly on nil * Added DISPENSE navigator to drop all collected values for subsequent navigation
* Added `collected?` macro to create a filter function which operates on the collected values
* Bug fix: END, BEGINNING, FIRST, LAST, and MAP-VALS now work properly on nil
## 0.11.2 ## 0.11.2
* Renamed com.rpl.specter.transient namespace to com.rpl.specter.transients to eliminate ClojureScript compiler warning about reserved keyword * Renamed com.rpl.specter.transient namespace to com.rpl.specter.transients to eliminate ClojureScript compiler warning about reserved keyword

View file

@ -109,6 +109,7 @@ The latest release version of Specter is hosted on [Clojars](https://clojars.org
- Introductory blog post: [Functional-navigational programming in Clojure(Script) with Specter](http://nathanmarz.com/blog/functional-navigational-programming-in-clojurescript-with-sp.html) - Introductory blog post: [Functional-navigational programming in Clojure(Script) with Specter](http://nathanmarz.com/blog/functional-navigational-programming-in-clojurescript-with-sp.html)
- Presentation about Specter: [Specter: Powerful and Simple Data Structure Manipulation](https://www.youtube.com/watch?v=VTCy_DkAJGk) - Presentation about Specter: [Specter: Powerful and Simple Data Structure Manipulation](https://www.youtube.com/watch?v=VTCy_DkAJGk)
- List of navigators with examples: [This wiki page](https://github.com/nathanmarz/specter/wiki/List-of-Navigators) provides a more comprehensive overview than the API docs about the behavior of specific navigators and includes many examples.
- [API docs](http://nathanmarz.github.io/specter/) - [API docs](http://nathanmarz.github.io/specter/)
- Performance guide: The [Specter 0.11.0 announcement post](https://github.com/nathanmarz/specter/wiki/Specter-0.11.0:-Performance-without-the-tradeoffs) provides a comprehensive overview of how Specter achieves its performance and what you need to know as a user to enable Specter to perform its optimizations. - Performance guide: The [Specter 0.11.0 announcement post](https://github.com/nathanmarz/specter/wiki/Specter-0.11.0:-Performance-without-the-tradeoffs) provides a comprehensive overview of how Specter achieves its performance and what you need to know as a user to enable Specter to perform its optimizations.

View file

@ -575,6 +575,11 @@
(collect-val [this structure] (collect-val [this structure]
val )) val ))
(def
^{:doc "Drops all collected values for subsequent navigation."}
DISPENSE i/DISPENSE*)
(defpathedfn if-path (defpathedfn if-path
"Like cond-path, but with if semantics." "Like cond-path, but with if semantics."
([cond-p then-path] ([cond-p then-path]

View file

@ -609,6 +609,36 @@
ret ret
)))) ))))
(def collected?*
(->ParamsNeededPath
(->TransformFunctions
RichPathExecutor
(fn [params params-idx vals structure next-fn]
(let [afn (aget ^objects params params-idx)]
(if (afn vals)
(next-fn params (inc params-idx) vals structure)
NONE
)))
(fn [params params-idx vals structure next-fn]
(let [afn (aget ^objects params params-idx)]
(if (afn vals)
(next-fn params (inc params-idx) vals structure)
structure
))))
1
))
(def DISPENSE*
(no-params-compiled-path
(->TransformFunctions
RichPathExecutor
(fn [params params-idx vals structure next-fn]
(next-fn params params-idx [] structure))
(fn [params params-idx vals structure next-fn]
(next-fn params params-idx [] structure))
)))
(defn transform-fns-field [^CompiledPath path] (defn transform-fns-field [^CompiledPath path]
(.-transform-fns path)) (.-transform-fns path))
@ -893,6 +923,11 @@
structure)) structure))
(extend-protocol MapValsTransformProtocol (extend-protocol MapValsTransformProtocol
nil
(map-vals-transform [structure next-fn]
nil
)
#+clj clojure.lang.PersistentArrayMap #+cljs cljs.core/PersistentArrayMap #+clj clojure.lang.PersistentArrayMap #+cljs cljs.core/PersistentArrayMap
(map-vals-transform [structure next-fn] (map-vals-transform [structure next-fn]
(map-vals-non-transient-transform structure {} next-fn) (map-vals-non-transient-transform structure {} next-fn)

View file

@ -620,3 +620,11 @@
[apath transform-fn structure & args] [apath transform-fn structure & args]
`(i/compiled-replace-in* (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))
)

View file

@ -8,7 +8,7 @@
:refer [paramsfn defprotocolpath defnav extend-protocolpath :refer [paramsfn 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 defnavconstructor select-first transform setval replace-in defnavconstructor
select-any selected-any?]]) select-any selected-any? collected?]])
(:use (:use
#+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]]
@ -17,7 +17,7 @@
:only [paramsfn defprotocolpath defnav extend-protocolpath :only [paramsfn 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 defnavconstructor select-first transform setval replace-in defnavconstructor
select-any selected-any?]] select-any selected-any? collected?]]
) )
@ -1201,3 +1201,58 @@
(is (empty? (select s/LAST nil))) (is (empty? (select s/LAST nil)))
(is (empty? (select s/ALL nil))) (is (empty? (select s/ALL nil)))
) )
(deftest map-vals-nil
(is (= nil (transform s/MAP-VALS inc nil)))
(is (empty? (select s/MAP-VALS nil)))
)
(defspec dispense-test
(for-all+
[k1 gen/int
k2 gen/int
k3 gen/int
m (gen-map-with-keys gen/int gen/int k1 k2 k3)]
(= (select [(s/collect-one (s/keypath k1))
(s/collect-one (s/keypath k2))
s/DISPENSE
(s/collect-one (s/keypath k2))
(s/keypath k3)]
m)
(select [(s/collect-one (s/keypath k2))
(s/keypath k3)]
m)
)))
(deftest collected?-test
(let [data {:active-id 1 :items [{:id 1 :name "a"} {:id 2 :name "b"}]}]
(is (= {:id 1 :name "a"}
(select-any [(s/collect-one :active-id)
:items
s/ALL
(s/collect-one :id)
(collected? [a i] (= a i))
s/DISPENSE
]
data)
(select-any [(s/collect-one :active-id)
:items
s/ALL
(s/collect-one :id)
(collected? v (apply = v))
s/DISPENSE
]
data)
)))
(let [data {:active 3 :items [{:id 1 :val 0} {:id 3 :val 11}]}]
(is (= (transform [:items s/ALL (s/selected? :id #(= % 3)) :val] inc data)
(transform [(s/collect-one :active)
:items
s/ALL
(s/collect-one :id)
(collected? [a i] (= a i))
s/DISPENSE
:val]
inc
data)
))))