From b5f840db229e20d6797edcd5f7b1ea9430d0eded Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Tue, 10 Oct 2017 16:22:58 -0700 Subject: [PATCH 01/14] Initial select implementation for Regexes. --- src/clj/com/rpl/specter.cljc | 4 ++++ src/clj/com/rpl/specter/navs.cljc | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/src/clj/com/rpl/specter.cljc b/src/clj/com/rpl/specter.cljc index a571e88..2a1a931 100644 --- a/src/clj/com/rpl/specter.cljc +++ b/src/clj/com/rpl/specter.cljc @@ -1170,6 +1170,10 @@ ImplicitNav (implicit-nav [this] (pred this))) +(extend-type #?(:clj java.util.regex.Pattern :cljs RegExp) + ImplicitNav + (implicit-nav [this] (n/regex* this))) + (defnav ^{:doc "Navigates to the provided val if the structure is nil. Otherwise it stays navigated at the structure."} diff --git a/src/clj/com/rpl/specter/navs.cljc b/src/clj/com/rpl/specter/navs.cljc index 5cd904f..f55524c 100644 --- a/src/clj/com/rpl/specter/navs.cljc +++ b/src/clj/com/rpl/specter/navs.cljc @@ -622,6 +622,14 @@ (do-keypath-transform vals structure key next-fn) )) +(defrichnav ^{:doc "Navigates to the regex applied to a string, navigating to nil if it does not exist or nothing is found."} + ;; Setting the value to NONE will remove it from the collection.} + regex* + [re] + (select* [this vals structure next-fn] + (next-fn vals (re-find re structure))) + #_(transform* [this vals structure next-fn] + (next-fn (clojure.string/replace structure re vals)))) (defrichnav ^{:doc "Navigates to the key only if it exists in the map. Setting the value to NONE From 6e90ceadea7b65269d0e52adc2b611931aa67483 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Wed, 11 Oct 2017 16:09:35 -0700 Subject: [PATCH 02/14] Use re-seq rather than re-find. --- src/clj/com/rpl/specter/navs.cljc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clj/com/rpl/specter/navs.cljc b/src/clj/com/rpl/specter/navs.cljc index f55524c..a9c83a3 100644 --- a/src/clj/com/rpl/specter/navs.cljc +++ b/src/clj/com/rpl/specter/navs.cljc @@ -622,12 +622,12 @@ (do-keypath-transform vals structure key next-fn) )) -(defrichnav ^{:doc "Navigates to the regex applied to a string, navigating to nil if it does not exist or nothing is found."} +(defrichnav ^{:doc "Navigates to the lazy sequence of successive matches when a regex is applied to a string, navigating to nil if it does not exist or nothing is found."} ;; Setting the value to NONE will remove it from the collection.} regex* [re] (select* [this vals structure next-fn] - (next-fn vals (re-find re structure))) + (next-fn vals (re-seq re structure))) #_(transform* [this vals structure next-fn] (next-fn (clojure.string/replace structure re vals)))) From dc9e2205c3eaaaebb687e70ed154e5ff3451e538 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Thu, 12 Oct 2017 09:40:38 -0700 Subject: [PATCH 03/14] Wrap re-seq in doseqres. This navigates to each match, not a sequence of matches. --- src/clj/com/rpl/specter/navs.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/clj/com/rpl/specter/navs.cljc b/src/clj/com/rpl/specter/navs.cljc index a9c83a3..20a90ee 100644 --- a/src/clj/com/rpl/specter/navs.cljc +++ b/src/clj/com/rpl/specter/navs.cljc @@ -627,7 +627,8 @@ regex* [re] (select* [this vals structure next-fn] - (next-fn vals (re-seq re structure))) + (doseqres NONE [s (re-seq re structure)] + (next-fn vals s))) #_(transform* [this vals structure next-fn] (next-fn (clojure.string/replace structure re vals)))) From 7dbe4dc524cdc52596d7c17f47f4490f55b827f9 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Thu, 19 Oct 2017 10:38:43 -0400 Subject: [PATCH 04/14] Use defnav with regex* rather than defrichnav. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nathan: vals is how collect / collect-one are implemented. You should use defnav rather than defrichnav since it handles that parameter in the background. defrichnav exists since the compiler doesn't do certain inlining optimizations, so defrichnav allows the handling of vals to be inlined manually. This is only important for navigators which do very little work (like keypath) – I think regexes are expensive enough that the inlining optimization won't be noticeable. --- src/clj/com/rpl/specter/navs.cljc | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/clj/com/rpl/specter/navs.cljc b/src/clj/com/rpl/specter/navs.cljc index 20a90ee..0f0069c 100644 --- a/src/clj/com/rpl/specter/navs.cljc +++ b/src/clj/com/rpl/specter/navs.cljc @@ -622,15 +622,12 @@ (do-keypath-transform vals structure key next-fn) )) -(defrichnav ^{:doc "Navigates to the lazy sequence of successive matches when a regex is applied to a string, navigating to nil if it does not exist or nothing is found."} - ;; Setting the value to NONE will remove it from the collection.} - regex* - [re] - (select* [this vals structure next-fn] +(defnav regex* [re] + (select* [this structure next-fn] (doseqres NONE [s (re-seq re structure)] - (next-fn vals s))) - #_(transform* [this vals structure next-fn] - (next-fn (clojure.string/replace structure re vals)))) + (next-fn s))) + (transform* [this structure next-fn] + (clojure.string/replace structure re next-fn))) (defrichnav ^{:doc "Navigates to the key only if it exists in the map. Setting the value to NONE From d7ee2f7c6a2c929c89dce3e7acb703afa7cdd98b Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Thu, 19 Oct 2017 10:42:13 -0400 Subject: [PATCH 05/14] Move regex* location to a better spot. ...since it is no longer a "defrichnav." --- src/clj/com/rpl/specter/navs.cljc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/clj/com/rpl/specter/navs.cljc b/src/clj/com/rpl/specter/navs.cljc index 0f0069c..a5fce3b 100644 --- a/src/clj/com/rpl/specter/navs.cljc +++ b/src/clj/com/rpl/specter/navs.cljc @@ -478,6 +478,13 @@ structure (updater structure next-fn)))) +(defnav regex* [re] + (select* [this structure next-fn] + (doseqres NONE [s (re-seq re structure)] + (next-fn s))) + (transform* [this structure next-fn] + (clojure.string/replace structure re next-fn))) + (defn- update-first-list [l afn] (let [newf (afn (first l)) restl (rest l)] @@ -622,12 +629,6 @@ (do-keypath-transform vals structure key next-fn) )) -(defnav regex* [re] - (select* [this structure next-fn] - (doseqres NONE [s (re-seq re structure)] - (next-fn s))) - (transform* [this structure next-fn] - (clojure.string/replace structure re next-fn))) (defrichnav ^{:doc "Navigates to the key only if it exists in the map. Setting the value to NONE From 5aed3b254e6989e610d970385c4838f88c626d64 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Thu, 19 Oct 2017 10:52:23 -0400 Subject: [PATCH 06/14] Add regex-navigation-test. --- test/com/rpl/specter/core_test.cljc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/com/rpl/specter/core_test.cljc b/test/com/rpl/specter/core_test.cljc index 58c015c..6353d61 100644 --- a/test/com/rpl/specter/core_test.cljc +++ b/test/com/rpl/specter/core_test.cljc @@ -1434,6 +1434,14 @@ (is (= "abq" (setval s/LAST "q" "abc"))) ) +(deftest regex-navigation-test + (is (= (select #"t" "test") ["t" "t"])) + (is (= (select [:a #"t"] {:a "test"}) ["t" "t"])) + (is (= (transform #"t" clojure.string/capitalize "test") "TesT")) + (is (= (transform [:a #"t"] clojure.string/capitalize {:a "test"}) {:a "TesT"})) + (is (= (setval #"t" "z" "test") "zesz")) + (is (= (setval [:a #"t"] "z" {:a "test"}) {:a "zesz"}))) + (deftest single-value-none-navigators-test (is (predand= vector? [1 2 3] (setval s/AFTER-ELEM 3 [1 2]))) (is (predand= list? '(1 2 3) (setval s/AFTER-ELEM 3 '(1 2)))) From 14dad51fd44cd55d07a76e8b882c4e87e2981abc Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Thu, 19 Oct 2017 11:02:46 -0400 Subject: [PATCH 07/14] Specify where NONE is in regex*. --- src/clj/com/rpl/specter/navs.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/com/rpl/specter/navs.cljc b/src/clj/com/rpl/specter/navs.cljc index a5fce3b..5bbde51 100644 --- a/src/clj/com/rpl/specter/navs.cljc +++ b/src/clj/com/rpl/specter/navs.cljc @@ -480,7 +480,7 @@ (defnav regex* [re] (select* [this structure next-fn] - (doseqres NONE [s (re-seq re structure)] + (doseqres i/NONE [s (re-seq re structure)] (next-fn s))) (transform* [this structure next-fn] (clojure.string/replace structure re next-fn))) From 0e88c57a873f221649fc549d642e4b9f1dc961a3 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Thu, 19 Oct 2017 11:17:28 -0400 Subject: [PATCH 08/14] Correctly refer to regex type in CLJS. Before it thought the type was com.rpl.specter/RegExp - but it's actually js/RegExp. --- src/clj/com/rpl/specter.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/com/rpl/specter.cljc b/src/clj/com/rpl/specter.cljc index 2a1a931..0339c1f 100644 --- a/src/clj/com/rpl/specter.cljc +++ b/src/clj/com/rpl/specter.cljc @@ -1170,7 +1170,7 @@ ImplicitNav (implicit-nav [this] (pred this))) -(extend-type #?(:clj java.util.regex.Pattern :cljs RegExp) +(extend-type #?(:clj java.util.regex.Pattern :cljs js/RegExp) ImplicitNav (implicit-nav [this] (n/regex* this))) From f593753a6811dc98cfb5cec07f88b292a72a3244 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Thu, 19 Oct 2017 11:29:41 -0400 Subject: [PATCH 09/14] Update CHANGES.md. --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 9db70c3..b23a548 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## 1.0.5-SNAPSHOT + +* Add implicit navigator for regexes, which navigate to every match in a string and support replacement with a new substring. + ## 1.0.4 * Add `indexed-vals` navigator, a variant of `INDEXED-VALS` that allows for a customized start index. From 4cf7ee965f97590e940d582fb3a43884d5db241d Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Thu, 19 Oct 2017 12:37:11 -0400 Subject: [PATCH 10/14] Rename regex* to regex-nav and move to specter.cljc. --- src/clj/com/rpl/specter.cljc | 9 ++++++++- src/clj/com/rpl/specter/navs.cljc | 6 ------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/clj/com/rpl/specter.cljc b/src/clj/com/rpl/specter.cljc index 0339c1f..5f6e6e4 100644 --- a/src/clj/com/rpl/specter.cljc +++ b/src/clj/com/rpl/specter.cljc @@ -1068,6 +1068,13 @@ (swap! structure next-fn) structure))) +(defnav regex-nav [re] + (select* [this structure next-fn] + (doseqres NONE [s (re-seq re structure)] + (next-fn s))) + (transform* [this structure next-fn] + (clojure.string/replace structure re next-fn))) + (defdynamicnav selected? "Filters the current value based on whether a path finds anything. e.g. (selected? :vals ALL even?) keeps the current element only if an @@ -1172,7 +1179,7 @@ (extend-type #?(:clj java.util.regex.Pattern :cljs js/RegExp) ImplicitNav - (implicit-nav [this] (n/regex* this))) + (implicit-nav [this] (regex-nav this))) (defnav ^{:doc "Navigates to the provided val if the structure is nil. Otherwise it stays diff --git a/src/clj/com/rpl/specter/navs.cljc b/src/clj/com/rpl/specter/navs.cljc index 5bbde51..b9a9552 100644 --- a/src/clj/com/rpl/specter/navs.cljc +++ b/src/clj/com/rpl/specter/navs.cljc @@ -478,12 +478,6 @@ structure (updater structure next-fn)))) -(defnav regex* [re] - (select* [this structure next-fn] - (doseqres i/NONE [s (re-seq re structure)] - (next-fn s))) - (transform* [this structure next-fn] - (clojure.string/replace structure re next-fn))) (defn- update-first-list [l afn] (let [newf (afn (first l)) From fdfaecd0d0ca1a91e91b294e5f351233f031895b Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Thu, 19 Oct 2017 15:09:51 -0400 Subject: [PATCH 11/14] Expand regex-nav test-suite. --- test/com/rpl/specter/core_test.cljc | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/com/rpl/specter/core_test.cljc b/test/com/rpl/specter/core_test.cljc index 6353d61..4cb8444 100644 --- a/test/com/rpl/specter/core_test.cljc +++ b/test/com/rpl/specter/core_test.cljc @@ -1439,8 +1439,13 @@ (is (= (select [:a #"t"] {:a "test"}) ["t" "t"])) (is (= (transform #"t" clojure.string/capitalize "test") "TesT")) (is (= (transform [:a #"t"] clojure.string/capitalize {:a "test"}) {:a "TesT"})) + (is (= (transform #"\s+\w" clojure.string/triml "Hello World!") "HelloWorld!")) (is (= (setval #"t" "z" "test") "zesz")) - (is (= (setval [:a #"t"] "z" {:a "test"}) {:a "zesz"}))) + (is (= (setval [:a #"t"] "z" {:a "test"}) {:a "zesz"})) + (is (= (transform #"aa*" (fn [s] (-> s count str)) "aadt") "2dt")) + (is (= (transform #"[Aa]+" (fn [s] (apply str (take (count s) (repeat "@")))) "Amsterdam Aardvarks") "@msterd@m @@rdv@rks")) + (is (= (select [#"(\S+):\ (\d+)" (s/nthpath 2)] "Mary: 1st George: 2nd Arthur: 3rd") ["1" "2" "3"])) + (is (= (transform (s/subselect #"\d\w+") reverse "Mary: 1st George: 2nd Arthur: 3rd")))) (deftest single-value-none-navigators-test (is (predand= vector? [1 2 3] (setval s/AFTER-ELEM 3 [1 2]))) @@ -1467,10 +1472,10 @@ (defspec map-keys-all-first-equivalence-transform (for-all+ - [m (limit-size 10 (gen/map gen/int gen/keyword))] - (= (transform s/MAP-KEYS inc m) - (transform [s/ALL s/FIRST] inc m ) - ))) + [m (limit-size 10 (gen/map gen/int gen/keyword))] + (= (transform s/MAP-KEYS inc m) + (transform [s/ALL s/FIRST] inc m ) + ))) (defspec map-keys-all-first-equivalence-select (for-all+ From 9a8f79774ce921492419b4206021b614002f3eb8 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Thu, 19 Oct 2017 20:33:07 -0400 Subject: [PATCH 12/14] Re-formatted test for consistent style. --- test/com/rpl/specter/core_test.cljc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/com/rpl/specter/core_test.cljc b/test/com/rpl/specter/core_test.cljc index 4cb8444..1c06f83 100644 --- a/test/com/rpl/specter/core_test.cljc +++ b/test/com/rpl/specter/core_test.cljc @@ -1445,7 +1445,8 @@ (is (= (transform #"aa*" (fn [s] (-> s count str)) "aadt") "2dt")) (is (= (transform #"[Aa]+" (fn [s] (apply str (take (count s) (repeat "@")))) "Amsterdam Aardvarks") "@msterd@m @@rdv@rks")) (is (= (select [#"(\S+):\ (\d+)" (s/nthpath 2)] "Mary: 1st George: 2nd Arthur: 3rd") ["1" "2" "3"])) - (is (= (transform (s/subselect #"\d\w+") reverse "Mary: 1st George: 2nd Arthur: 3rd")))) + (is (= (transform (s/subselect #"\d\w+") reverse "Mary: 1st George: 2nd Arthur: 3rd"))) + ) (deftest single-value-none-navigators-test (is (predand= vector? [1 2 3] (setval s/AFTER-ELEM 3 [1 2]))) From efaeff4fc5a9a8959b83f19bfc7fa368525cc027 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Fri, 20 Oct 2017 10:26:57 -0400 Subject: [PATCH 13/14] Add regex test result. --- test/com/rpl/specter/core_test.cljc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/com/rpl/specter/core_test.cljc b/test/com/rpl/specter/core_test.cljc index 1c06f83..55029af 100644 --- a/test/com/rpl/specter/core_test.cljc +++ b/test/com/rpl/specter/core_test.cljc @@ -1445,7 +1445,7 @@ (is (= (transform #"aa*" (fn [s] (-> s count str)) "aadt") "2dt")) (is (= (transform #"[Aa]+" (fn [s] (apply str (take (count s) (repeat "@")))) "Amsterdam Aardvarks") "@msterd@m @@rdv@rks")) (is (= (select [#"(\S+):\ (\d+)" (s/nthpath 2)] "Mary: 1st George: 2nd Arthur: 3rd") ["1" "2" "3"])) - (is (= (transform (s/subselect #"\d\w+") reverse "Mary: 1st George: 2nd Arthur: 3rd"))) + (is (= (transform (s/subselect #"\d\w+") reverse "Mary: 1st George: 2nd Arthur: 3rd") "Mary: 3rd George: 2nd Arthur: 1st")) ) (deftest single-value-none-navigators-test From 98c7510d1c30aec871779421aeb23fb39e119ba1 Mon Sep 17 00:00:00 2001 From: Michael Fogleman Date: Fri, 20 Oct 2017 10:44:59 -0400 Subject: [PATCH 14/14] Remove implicit regex functionality. --- CHANGES.md | 2 +- src/clj/com/rpl/specter.cljc | 4 ---- test/com/rpl/specter/core_test.cljc | 22 +++++++++++----------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b23a548..919fc36 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ ## 1.0.5-SNAPSHOT -* Add implicit navigator for regexes, which navigate to every match in a string and support replacement with a new substring. +* Add `regex-nav` navigator for regexes, which navigate to every match in a string and support replacement with a new substring. ## 1.0.4 diff --git a/src/clj/com/rpl/specter.cljc b/src/clj/com/rpl/specter.cljc index 5f6e6e4..a24871d 100644 --- a/src/clj/com/rpl/specter.cljc +++ b/src/clj/com/rpl/specter.cljc @@ -1177,10 +1177,6 @@ ImplicitNav (implicit-nav [this] (pred this))) -(extend-type #?(:clj java.util.regex.Pattern :cljs js/RegExp) - ImplicitNav - (implicit-nav [this] (regex-nav this))) - (defnav ^{:doc "Navigates to the provided val if the structure is nil. Otherwise it stays navigated at the structure."} diff --git a/test/com/rpl/specter/core_test.cljc b/test/com/rpl/specter/core_test.cljc index 55029af..6ab8dd6 100644 --- a/test/com/rpl/specter/core_test.cljc +++ b/test/com/rpl/specter/core_test.cljc @@ -1435,17 +1435,17 @@ ) (deftest regex-navigation-test - (is (= (select #"t" "test") ["t" "t"])) - (is (= (select [:a #"t"] {:a "test"}) ["t" "t"])) - (is (= (transform #"t" clojure.string/capitalize "test") "TesT")) - (is (= (transform [:a #"t"] clojure.string/capitalize {:a "test"}) {:a "TesT"})) - (is (= (transform #"\s+\w" clojure.string/triml "Hello World!") "HelloWorld!")) - (is (= (setval #"t" "z" "test") "zesz")) - (is (= (setval [:a #"t"] "z" {:a "test"}) {:a "zesz"})) - (is (= (transform #"aa*" (fn [s] (-> s count str)) "aadt") "2dt")) - (is (= (transform #"[Aa]+" (fn [s] (apply str (take (count s) (repeat "@")))) "Amsterdam Aardvarks") "@msterd@m @@rdv@rks")) - (is (= (select [#"(\S+):\ (\d+)" (s/nthpath 2)] "Mary: 1st George: 2nd Arthur: 3rd") ["1" "2" "3"])) - (is (= (transform (s/subselect #"\d\w+") reverse "Mary: 1st George: 2nd Arthur: 3rd") "Mary: 3rd George: 2nd Arthur: 1st")) + (is (= (select (s/regex-nav #"t") "test") ["t" "t"])) + (is (= (select [:a (s/regex-nav #"t")] {:a "test"}) ["t" "t"])) + (is (= (transform (s/regex-nav #"t") clojure.string/capitalize "test") "TesT")) + (is (= (transform [:a (s/regex-nav #"t")] clojure.string/capitalize {:a "test"}) {:a "TesT"})) + (is (= (transform (s/regex-nav #"\s+\w") clojure.string/triml "Hello World!") "HelloWorld!")) + (is (= (setval (s/regex-nav #"t") "z" "test") "zesz")) + (is (= (setval [:a (s/regex-nav #"t")] "z" {:a "test"}) {:a "zesz"})) + (is (= (transform (s/regex-nav #"aa*") (fn [s] (-> s count str)) "aadt") "2dt")) + (is (= (transform (s/regex-nav #"[Aa]+") (fn [s] (apply str (take (count s) (repeat "@")))) "Amsterdam Aardvarks") "@msterd@m @@rdv@rks")) + (is (= (select [(s/regex-nav #"(\S+):\ (\d+)") (s/nthpath 2)] "Mary: 1st George: 2nd Arthur: 3rd") ["1" "2" "3"])) + (is (= (transform (s/subselect (s/regex-nav #"\d\w+")) reverse "Mary: 1st George: 2nd Arthur: 3rd") "Mary: 3rd George: 2nd Arthur: 1st")) ) (deftest single-value-none-navigators-test