Compare commits

..

844 commits

Author SHA1 Message Date
tommy
9a615ff22c
Implement ALL for strings (#333) 2024-09-16 05:10:52 -10:00
Nathan Marz
67e8680602 update changelog 2022-03-18 07:45:26 -10:00
Nathan Marz
05ae730896 1.1.5-SNAPSHOT 2022-03-18 07:42:58 -10:00
Nathan Marz
a91aaaa34a 1.1.4 2022-03-18 07:41:05 -10:00
Michiel Borkent
efeefdfacd
Update CHANGES.md (#324) 2022-03-14 09:46:29 -10:00
Michiel Borkent
a64209d582
Make babashka-compatible (#323) 2022-03-14 07:11:40 -10:00
Joshua Suskalo
d0d6fdf581
Add section to the readme with clj-kondo configuration snippet. (#310) (#311) 2021-06-02 13:47:44 -10:00
Jeff Evans
e8225f0e58
Make it possible to run individual benchmark(s) (#298)
Parse command line args to benchmarks.clj and treat them as benchmark names to run

Add information on running benchmarks to DEVELOPER.md
2020-10-22 17:51:04 -04:00
Nathan Marz
a379893598 BEFORE-ELEM, AFTER-ELEM, FIRST, LAST, BEGINNING, and END on subvecs now produce vector type in cljs 2020-09-17 15:47:26 -04:00
Nathan Marz
efaf35558a replace throw-illegal with ex-info 2020-09-17 15:36:16 -04:00
Nathan Marz
083376f904 update changelog 2020-09-17 15:26:28 -04:00
Nathan Marz
de48f24d70 update changelog 2020-09-17 15:25:22 -04:00
Nathan Marz
c54a46c686 before-index 0 on nil produces list, fix flaws in tests regarding predand= 2020-09-17 15:24:11 -04:00
Jeff Evans
2e002c1270
Improve before-index performance #223 (#291)
Adding new protocol for performing the insert-before-idx operation, with
implementations for core collection types

Adding new functional test to confirm behavior when operating on a string

Adding benchmarks to compare new performance vs old implementation vs
core Clojure in a couple of cases
2020-09-17 15:15:42 -04:00
Jeff Evans
789881b3ad
Fix instructions for running tests (#295) 2020-09-14 15:37:16 -04:00
Nathan Marz
edd7429281 update changelog 2020-08-16 19:40:59 -04:00
Adrian
e222ba2a6c
Add arglist metadata to navs. (#290) 2020-08-16 19:40:02 -04:00
Nathan Marz
40add561b6
Update README.md 2020-01-02 21:45:33 -05:00
Nathan Marz
123af0937e update release date 2019-10-13 23:07:26 -04:00
Nathan Marz
6e7d772755 update codox 2019-10-13 23:04:36 -04:00
Nathan Marz
134beac2a7 begin 1.1.4 2019-10-13 22:56:15 -04:00
Nathan Marz
86f8412ee4 update deploy 2019-10-13 22:56:04 -04:00
Nathan Marz
28ef42152a 1.1.3 2019-10-13 22:22:18 -04:00
Nathan Marz
ffe8130bf0 Merge branch 'master' of github.com:redplanetlabs/specter 2019-09-10 18:19:08 -04:00
Nathan Marz
9c7f6fb65e don't write class files for eval'd functions for inline path functions or protpath extensions. 2019-09-10 18:18:56 -04:00
Nathan Marz
029a33427f
Update README.md
Update links in README
2019-08-29 13:39:20 -04:00
nathanmarz
925e2e91d6 Merge branch 'master' of github.com:nathanmarz/specter 2019-08-27 20:48:58 -04:00
nathanmarz
29ee91d01e add CONTRIBUTING.md 2019-08-27 20:48:49 -04:00
Nathan Marz
b7ff05bf54
Update .travis.yml 2019-08-24 19:19:34 -04:00
Nathan Marz
ae24856ec3
Update travis link 2019-08-24 19:14:08 -04:00
Nathan Marz
23f4e1a370
Update README.md 2019-01-29 14:25:15 -05:00
Nathan Marz
e38a2561d8
Merge pull request #275 from xificurC/master
use criterium for benchmarks
2018-12-13 21:23:03 -05:00
Peter Nagy (NPE)
fe1deb5195 use criterium for benchmarks, output JVM and clojure version 2018-12-13 12:54:28 +01:00
nathanmarz
7790213b16 update changelog 2018-12-03 14:30:28 -05:00
nathanmarz
8764a4b2b6 fix throw-illegal in cljs code 2018-12-03 14:29:58 -05:00
nathanmarz
c65b31181a update changelog 2018-11-01 21:40:57 -04:00
nathanmarz
f13b5db08a begin 1.1.3 2018-11-01 21:39:09 -04:00
nathanmarz
6c3253f16a 1.1.2 2018-11-01 21:38:17 -04:00
nathanmarz
e7abb2b538 fix handling of subvector paths in cljs 2018-06-26 08:39:28 -04:00
nathanmarz
3536e3c461 update class-constant-test for cljs 2018-06-12 10:48:56 -04:00
nathanmarz
350c8b857f Fix inline compiler symbol handling so class references can be used as constants within paths 2018-06-12 10:09:11 -04:00
nathanmarz
798cda211f eliminate reflection warning 2018-05-18 13:45:20 -04:00
nathanmarz
4580de8cc6 1.1.1 2018-04-23 11:28:23 -04:00
nathanmarz
79a3610a64 remove usage of pprint in cljs 2018-04-21 10:19:05 -04:00
nathanmarz
1590b2c2d4 update changelog 2018-04-09 14:57:45 -04:00
Nathan Marz
efe94b6539
Merge pull request #253 from gnl/master
Fix ns form to comply with cljs.core.specs.alpha
2018-04-09 14:55:46 -04:00
gnl
eea5fcb48b Fix ns form to comply with cljs.core.specs.alpha
When requiring the cljs.core.specs.alpha namespace with ClojureScript
1.10, the ns form in com.rpl.specter.navs fails spec validation and
produces a compile error, because the nested :clj reader conditionals
result in an empty :use clause.

Moving the reader conditional up to enclose :use fixes this.

Relevant spec:
b11cbeefa5/src/main/cljs/cljs/core/specs/alpha.cljc (L170)

Additional info:
https://clojurescript.org/news/2018-03-26-release#_core_specs_alpha
2018-04-07 16:54:11 +03:00
nathanmarz
ac48127871 fix 2018-03-23 18:24:01 -04:00
nathanmarz
01617b6264 fix build 2018-03-22 16:01:43 -04:00
nathanmarz
38a27ecc43 Merge branch 'master' of github.com:nathanmarz/specter 2018-03-22 15:44:11 -04:00
nathanmarz
d4887c2090 fix issue with walker caused by change in cljs 1.10 2018-03-22 15:43:53 -04:00
Nathan Marz
4a589c3074
add cheat sheet to readme 2018-02-13 09:09:38 -05:00
nathanmarz
1a05546f27 Merge branch 'master' of github.com:nathanmarz/specter 2018-02-08 22:27:06 -05:00
nathanmarz
d5143f136c update readme 2018-02-08 22:26:58 -05:00
Nathan Marz
f7b7b0cba2
Merge pull request #245 from DjebbZ/master
Add dates to all changelog entries
2018-02-08 10:53:44 -05:00
DjebbZ
05e354a9a2 Add dates to all changelog entries 2018-02-08 15:47:20 +01:00
nathanmarz
b4a3c4f601 begin 1.1.1 2018-01-02 09:30:22 -05:00
nathanmarz
c81a4b1a7b 1.1.0 2018-01-02 09:29:28 -05:00
nathanmarz
e92fd674b1 add examples 2017-12-24 18:39:01 -05:00
nathanmarz
c2f669db71 update example 2017-12-24 17:57:26 -05:00
nathanmarz
09d0d071ef improve compact implementation 2017-12-24 10:50:22 -05:00
nathanmarz
70d9fef5cc improve subselect fix 2017-12-21 23:28:00 -05:00
nathanmarz
b1050b910c improve compact test 2017-12-21 22:32:30 -05:00
nathanmarz
7b646ca566 fix subselect changing first matched element to nil when transformed to empty sequence 2017-12-21 22:24:37 -05:00
nathanmarz
6a5054feea add compact navigator 2017-12-21 13:15:22 -05:00
nathanmarz
632e710b07 make terminal and vterminal select codepath no-ops 2017-12-21 13:04:26 -05:00
nathanmarz
7bd119aa52 fix build 2017-12-04 10:47:42 -05:00
nathanmarz
0ceda21151 add vtransform 2017-12-04 10:06:43 -05:00
nathanmarz
9515582a19 add vterminal 2017-12-04 09:57:43 -05:00
nathanmarz
680c36ae5b update readme 2017-11-17 15:18:49 -05:00
nathanmarz
d54aa28d49 begin 1.0.6 2017-11-16 21:53:00 -05:00
nathanmarz
3478e5b6d7 1.0.5 2017-11-16 14:23:25 -05:00
nathanmarz
5b60eb17e3 extend ImplicitNav for strings, numbers, characters, booleans, symbols, and regexes 2017-11-08 13:47:40 -05:00
nathanmarz
349e03342f formatting 2017-10-22 12:23:25 -04:00
nathanmarz
844050545a update changelog 2017-10-20 11:06:25 -04:00
Nathan Marz
5efafd2d9b Merge pull request #231 from mwfogleman/regex
Regex navigator.
2017-10-20 11:05:15 -04:00
Michael Fogleman
98c7510d1c Remove implicit regex functionality. 2017-10-20 10:50:16 -04:00
Michael Fogleman
efaeff4fc5 Add regex test result. 2017-10-20 10:26:57 -04:00
Michael Fogleman
9a8f79774c Re-formatted test for consistent style. 2017-10-19 20:33:07 -04:00
Michael Fogleman
fdfaecd0d0 Expand regex-nav test-suite. 2017-10-19 19:08:44 -04:00
Michael Fogleman
4cf7ee965f Rename regex* to regex-nav and move to specter.cljc. 2017-10-19 12:42:14 -04:00
Michael Fogleman
f593753a68 Update CHANGES.md. 2017-10-19 11:29:41 -04:00
Michael Fogleman
0e88c57a87 Correctly refer to regex type in CLJS.
Before it thought the type was com.rpl.specter/RegExp - but it's
actually js/RegExp.
2017-10-19 11:25:42 -04:00
Michael Fogleman
14dad51fd4 Specify where NONE is in regex*. 2017-10-19 11:25:42 -04:00
Michael Fogleman
5aed3b254e Add regex-navigation-test. 2017-10-19 11:25:42 -04:00
Michael Fogleman
d7ee2f7c6a Move regex* location to a better spot.
...since it is no longer a "defrichnav."
2017-10-19 11:25:42 -04:00
Michael Fogleman
7dbe4dc524 Use defnav with regex* rather than defrichnav.
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.
2017-10-19 11:25:42 -04:00
Michael Fogleman
dc9e2205c3 Wrap re-seq in doseqres.
This navigates to each match, not a sequence of matches.
2017-10-19 11:25:42 -04:00
Michael Fogleman
6e90ceadea Use re-seq rather than re-find. 2017-10-19 11:25:42 -04:00
Michael Fogleman
b5f840db22 Initial select implementation for Regexes. 2017-10-19 11:25:42 -04:00
nathanmarz
796ad700f7 begin 1.0.5 2017-10-17 12:14:52 -04:00
nathanmarz
3a6d8620d7 1.0.4 2017-10-17 12:13:35 -04:00
nathanmarz
92caabbb32 add screencast link 2017-10-07 14:39:49 -04:00
nathanmarz
49eed0079c update readme 2017-10-02 08:51:18 -04:00
nathanmarz
915c0a9471 Merge branch 'master' of github.com:nathanmarz/specter 2017-09-20 18:58:23 -04:00
nathanmarz
86033f3df1 link new wiki page 2017-09-20 18:58:17 -04:00
Nathan Marz
ac3fe713ac Merge pull request #230 from mwfogleman/space
Add a space to the nthpath documentation.
2017-09-11 16:14:46 -04:00
Michael Fogleman
efd123d17e Add a space to the nthpath documentation. 2017-09-11 10:21:42 -04:00
nathanmarz
c233fb7e9d add indexed-vals 2017-08-16 07:28:05 -04:00
nathanmarz
8d5f39a861 fix INDEXED-VALS 2017-08-15 01:32:43 -04:00
nathanmarz
09d00ac7e7 typo 2017-08-14 06:48:54 -04:00
nathanmarz
e2308e0cda begin 1.0.4 2017-08-14 06:30:44 -04:00
nathanmarz
31935fca2e 1.0.3 2017-08-14 06:25:24 -04:00
nathanmarz
85d3f14de9 fix docstring 2017-08-13 12:16:49 -04:00
nathanmarz
43fd7ab2e2 add INDEXED-VALS tests 2017-08-13 11:50:42 -04:00
nathanmarz
1805094df8 update changelog 2017-08-13 07:51:02 -04:00
nathanmarz
1b4a7d3d59 add INDEXED-VALS navigator 2017-08-13 07:49:08 -04:00
nathanmarz
1b7f987eaa fix outdated docstrings 2017-07-29 12:35:33 -04:00
nathanmarz
03e686cd9c tighten code 2017-07-24 13:02:19 -04:00
nathanmarz
dbae629472 cljs tests fix 2017-07-24 10:46:12 -04:00
nathanmarz
40c883c1f3 update changelog 2017-07-24 10:32:14 -04:00
nathanmarz
b7c62e444c add before-index and index-nav navigators 2017-07-24 10:29:11 -04:00
nathanmarz
179b705211 changes 2017-06-27 08:26:15 -04:00
nathanmarz
b18733b14e update changelog 2017-06-27 08:23:11 -04:00
nathanmarz
179d818503 fix regression in cljs that causes warning for record fields named var and other reserved words 2017-06-27 08:22:20 -04:00
nathanmarz
cfb191e1cd add note about value collection in subselect 2017-06-14 12:11:09 -04:00
nathanmarz
3fffee336e update .gitignore 2017-06-12 12:30:09 -04:00
nathanmarz
d36b4340c3 move repl.clj to scripts 2017-06-12 12:29:49 -04:00
nathanmarz
e8ba45e22b begin 1.0.3 2017-06-12 12:03:34 -04:00
nathanmarz
891a067bbd 1.0.2 2017-06-12 12:02:20 -04:00
nathanmarz
4325abab9d typo 2017-06-09 15:47:35 -04:00
Nathan Marz
abc5d2bf63 Merge pull request #213 from mwfogleman/cljs-documentation
Document ClojureScript usage.
2017-06-09 15:46:31 -04:00
Michael Fogleman
cfe5780ada Document ClojureScript usage. 2017-06-09 14:06:51 -04:00
nathanmarz
5259e5df55 add cljs repl script 2017-06-08 17:58:38 -04:00
nathanmarz
c2e3b779d9 typo 2017-06-07 17:54:56 -04:00
nathanmarz
7523e43823 improve doc for srange-dynamic 2017-06-07 17:17:13 -04:00
nathanmarz
3e4730b44f add ability to declare end-fn for srange-dynamic that takes in result of start-fn 2017-06-07 17:14:20 -04:00
nathanmarz
8f30918d59 add transducer cases to map vals benchmarks 2017-06-06 15:12:57 -04:00
nathanmarz
4b23bd9392 update changelog 2017-06-03 18:24:11 -04:00
nathanmarz
dbf3f60167 add walker vs. old clojure.walk version benchmark 2017-06-03 18:23:13 -04:00
nathanmarz
edf9d8c544 rename test 2017-06-03 06:11:23 -04:00
nathanmarz
0b88411f88 rename walker param to be consistent with doc 2017-06-02 13:46:47 -04:00
nathanmarz
b2589e00a6 fix handling of records with ALL in cljs 2017-06-02 10:17:35 -04:00
nathanmarz
b66db48a84 walker tests 2017-06-02 09:31:16 -04:00
nathanmarz
0841aa1587 add another string nav test 2017-06-01 06:18:56 -04:00
nathanmarz
d0ff9bbc35 reimplement codewalker so it works with NONE 2017-06-01 02:38:20 -04:00
nathanmarz
340d6d3065 reimplement walker in terms of recursive-path to support NONE removal 2017-06-01 01:38:12 -04:00
nathanmarz
e8f15c4137 extend ALL to records (for walker reimplementation) 2017-06-01 01:27:24 -04:00
nathanmarz
550d486954 update changelog 2017-05-31 21:01:31 -04:00
nathanmarz
5d185ec9f4 for ALL on maps, interpret transformed key value pair of < size 2 as removal 2017-05-31 21:00:23 -04:00
nathanmarz
1411ac495e add comments about workaround for cljs bug 2017-05-29 21:52:40 -04:00
nathanmarz
af029ac149 update changelog 2017-05-29 21:48:12 -04:00
nathanmarz
a924c75ac6 workaround to cljs bug where private vars in cljs.core can cause warnings with same named var (in this case, NONE) 2017-05-29 21:46:59 -04:00
nathanmarz
4f5376450a update changelog 2017-05-08 18:49:41 -04:00
nathanmarz
0608ca6396 fix bug with nested dynamic params with dynamic function invocations 2017-05-08 18:48:46 -04:00
nathanmarz
426873da98 support transforms to NONE for set-elem and map-key 2017-05-07 20:30:50 -04:00
nathanmarz
2dd6432c69 add set-elem navigator and change semantics of map-key to only navigate if the key exists (for consistency with set-elem) 2017-05-07 19:36:50 -04:00
nathanmarz
82314f50ba add test case for map-key 2017-05-05 18:43:20 -04:00
nathanmarz
2423127877 add map-key 2017-05-05 18:41:46 -04:00
nathanmarz
5dea8919be add helper pred navs for common comparisons 2017-04-24 20:35:41 -04:00
nathanmarz
af17c4617e begin 1.0.2 2017-04-17 16:22:35 -04:00
nathanmarz
d3010c0af8 1.0.1 2017-04-17 16:21:28 -04:00
nathanmarz
e25c400778 fix typo 2017-04-15 15:46:42 -04:00
nathanmarz
e3abd1ef9a better doc for filterer 2017-04-15 15:45:22 -04:00
nathanmarz
be30e46960 allow subselect/filterer to remove subvals 2017-04-13 10:20:24 -04:00
nathanmarz
ee56ddc1ab force ALL on lists to realize lazy sequence, fix benchmark 2017-04-11 11:58:52 -04:00
nathanmarz
a113946076 update changelog 2017-04-11 11:45:04 -04:00
nathanmarz
b18a249623 improve performance of ALL transform on lists, add benchmark 2017-04-11 11:44:39 -04:00
nathanmarz
a46ff5e8d9 fix tests for cljs 2017-04-03 14:11:23 -04:00
nathanmarz
c52f7671a7 make satisfies-protpath test clojure only 2017-04-03 14:03:10 -04:00
nathanmarz
c3164d722d add satisfies-protpath? 2017-04-03 13:46:08 -04:00
nathanmarz
0bb3ac8440 update cljs test instructions 2017-04-01 04:46:02 -04:00
nathanmarz
06d3ba548e fix nested dynamic arg issue for cljs 2017-04-01 04:45:50 -04:00
nathanmarz
6f6fa98680 update changelog 2017-04-01 04:29:18 -04:00
nathanmarz
b6ef861338 Fix #199 2017-04-01 04:27:57 -04:00
nathanmarz
3916c0dcf4 mark pred as direct-nav 2017-03-29 16:38:13 -04:00
nathanmarz
37441c149d update changelog 2017-03-22 13:31:28 -04:00
nathanmarz
fedef396eb change dynamic navs to return single navigator instead of sequence of one nav, helps inline compiler when a nav takes a parameterized navs as input 2017-03-22 13:30:35 -04:00
nathanmarz
468dcb05f9 update changelog 2017-03-15 11:53:16 -04:00
nathanmarz
70f41a635a make inline cache vars private 2017-03-15 11:52:35 -04:00
nathanmarz
e32ec1c687 remove dead code 2017-03-14 12:21:18 -04:00
Nathan Marz
3be05f2f0a Merge pull request #192 from firesofmay/switch-to-nodejs
Switch to node-js build engine for cljs tests
2017-03-11 14:09:12 -05:00
Mayank Jain
10e80e47ca Switch to node-js build engine for cljs tests 2017-03-11 23:02:12 +05:30
nathanmarz
4f56c6e5e9 revert previous travis fix attempt 2017-03-11 12:24:56 -05:00
nathanmarz
3cdaf2b358 try forcing v2.0 of phantomjs in build 2017-03-11 12:12:46 -05:00
nathanmarz
f426b0db61 disable travis cljs build for now 2017-03-11 08:36:18 -05:00
nathanmarz
cbc6520dbf update cljs testing instructions 2017-03-10 17:09:21 -05:00
Nathan Marz
4fb76c01c3 Merge pull request #189 from firesofmay/add-cljs-to-travis
Add cljs tests to travis
2017-03-10 16:49:07 -05:00
Mayank Jain
4695c96998 Add cljs tests to travis
* Add lein-doo plugin to run cljs tests. This is the new recommended
  approach.
* Update project.clj to add cljsbuild config
* Update .travis.yml file to run cljs tests as well.
* Also to speed up travis builds add ~/.m2 dir in cache

To run do:
$ lein do javac, doo phantom test-build once

Note that you'll need phantomjs installed for above to work.
2017-03-11 02:31:10 +05:30
nathanmarz
6b080a2575 update test.check to 0.9.0 2017-03-10 12:32:02 -05:00
nathanmarz
acf2801cbc update readme 2017-03-01 16:07:21 -05:00
nathanmarz
8afa602a40 update readme 2017-03-01 12:45:23 -05:00
nathanmarz
f4d021f87f update changelog 2017-03-01 12:40:56 -05:00
nathanmarz
b069265866 begin 1.0.1 2017-03-01 12:40:33 -05:00
nathanmarz
b5d65fda55 1.0.0 2017-03-01 12:33:18 -05:00
nathanmarz
35c8a9380e Add with-fresh-collected docstring 2017-03-01 10:46:28 -05:00
nathanmarz
b13b8d3c35 add remove with NONE functionality to FIRST and LAST 2017-03-01 10:40:17 -05:00
nathanmarz
5e56a99163 add more benchmark cases 2017-02-28 16:23:10 -05:00
nathanmarz
f169813b2d update changelog 2017-02-28 15:55:54 -05:00
nathanmarz
2504b7849a add specialized MAP-KEYS navigator 2017-02-28 15:55:25 -05:00
nathanmarz
cdbbd13939 add benchmark for prepending to a vector 2017-02-21 11:18:32 -05:00
nathanmarz
e7b595c4ca fix issue with NONE-ELEM on nil value 2017-02-17 17:54:11 -05:00
nathanmarz
84fdc7f1b4 update readme 2017-02-17 12:50:05 -05:00
nathanmarz
6bb5f5cab4 update changelog 2017-02-17 12:08:32 -05:00
nathanmarz
ef5ad1de6d fix transforms on subvectors to maintain the type as a vector type 2017-02-17 12:05:12 -05:00
nathanmarz
d8cfb649ad clarify changelog 2017-02-16 15:35:20 -05:00
nathanmarz
036144fdaf update changelog 2017-02-15 23:08:49 -05:00
nathanmarz
b79a71decd Add BEFORE-ELEM, AFTER-ELEM, and NONE-ELEM navigators 2017-02-15 20:34:44 -05:00
nathanmarz
7c798c1e3b extend srange, BEGINNING, END, FIRST, and LAST to strings 2017-02-15 18:30:44 -05:00
nathanmarz
ffcba01df7 fix NAMESPACE select 2017-02-15 10:12:08 -05:00
nathanmarz
d595b9f26b change nthpath to richnav for performance 2017-02-14 14:33:46 -05:00
nathanmarz
71ed0ffae9 Add docstrings for keypath, must, and nthpath 2017-02-14 10:46:02 -05:00
nathanmarz
16310d6008 fix docstring 2017-02-14 10:42:54 -05:00
nathanmarz
0855af39e7 update changelog 2017-02-14 08:48:36 -05:00
nathanmarz
48efea55ab Added NAME and NAMESPACE navigators 2017-02-14 08:47:19 -05:00
nathanmarz
3dbc775334 fix if-path/selected?/not-selected? so that vals are passed along to condition paths 2017-02-14 08:25:54 -05:00
nathanmarz
baf658365e add test for select-any with value collection 2017-02-14 08:11:39 -05:00
nathanmarz
646e03a227 Add nthpath navigator 2017-02-12 21:13:08 -05:00
nathanmarz
ebdfc80d8b update changelog 2017-02-12 21:03:16 -05:00
nathanmarz
3f71163454 extend ALL to PersistentHashSet 2017-02-12 21:02:38 -05:00
nathanmarz
e2b6f63f99 fix traverse with early termination to unwrap reduced vals 2017-02-12 15:24:13 -05:00
nathanmarz
76682e64d4 update README 2017-02-12 12:22:05 -05:00
nathanmarz
6d154799f9 add benchmark for getting first element of a vector 2017-02-12 12:13:13 -05:00
nathanmarz
3f3fad0eb7 update changelog 2017-02-12 11:44:36 -05:00
nathanmarz
41ac7790e2 Update changelog 2017-02-12 11:39:41 -05:00
nathanmarz
28ecb90489 add docstrings 2017-02-12 11:38:45 -05:00
nathanmarz
3693d307fc fix traverse-all to properly propagate reduced vals, add tests 2017-02-12 11:37:14 -05:00
nathanmarz
62c998a472 implement traverse-all 2017-02-12 09:38:56 -05:00
nathanmarz
80b3857b08 implement early termination using reduced, re-implement select-any/select-first in terms of it 2017-02-11 21:26:30 -05:00
nathanmarz
f521409482 add with-fresh-collected higher order navigator 2017-02-11 08:56:45 -05:00
nathanmarz
a1929d9eb7 add note on mutli-path about potential future enhancement 2017-02-10 17:29:49 -05:00
nathanmarz
a1073275c3 add assoc-in vs setval benchmark 2017-01-27 10:39:50 -05:00
nathanmarz
c48ab3bf8e update readme 2017-01-24 10:44:00 -05:00
nathanmarz
d966ed8ca1 update readme example 2017-01-17 17:02:31 -05:00
nathanmarz
15600981fd update changelog 2017-01-10 09:32:30 -05:00
nathanmarz
39e8c758fc fix missing wrap-dynamic-nav in cljs 2017-01-10 09:30:34 -05:00
Max Penet
f8d3ad5167 kill jvm reflection 2017-01-10 11:40:53 +01:00
nathanmarz
66ebd8c536 add example to readme 2017-01-08 20:43:56 -05:00
nathanmarz
9505ac6dd1 Implement #165 2017-01-08 19:15:14 -05:00
nathanmarz
1346cbc222 update changelog 2017-01-08 17:53:01 -05:00
nathanmarz
3dfda1e293 finish unit tests for #117 2017-01-08 17:48:49 -05:00
nathanmarz
ef9d82e61d fix tests 2017-01-08 17:41:56 -05:00
nathanmarz
b127a6c428 add basic NONE removal tests 2017-01-08 17:39:15 -05:00
nathanmarz
00ab106dd6 finish implementation of #117 2017-01-08 17:32:15 -05:00
nathanmarz
6b500a6aef implement NONE removal for ALL and MAP-VALS on PersistentArrayMap 2017-01-08 14:01:58 -05:00
nathanmarz
9617aa1931 implement ALL NONE removal for some of default cases 2017-01-08 13:30:14 -05:00
nathanmarz
fca11410b4 implemented NONE removal for keypath, must, MAP-VALS (except PersistentArrayMap), ALL (except lists and PersistentArrayMap) 2017-01-08 12:52:54 -05:00
nathanmarz
4774c2e30a update changelog 2016-12-26 12:56:40 -05:00
nathanmarz
6a509ca478 fix tests 2016-12-26 12:55:14 -05:00
nathanmarz
72e004df23 dynamic navs automatically compile sequence returns if completely static 2016-12-26 12:54:12 -05:00
nathanmarz
e2ec017c6b begin 0.13.3 2016-12-21 14:10:40 -05:00
nathanmarz
655e1b5e7a 0.13.2 2016-12-21 13:59:13 -05:00
nathanmarz
4ad0f79643 fix #160 2016-12-03 11:05:06 -05:00
Nathan Marz
3766a3fcc1 update changelog 2016-11-18 16:24:34 -05:00
Nathan Marz
23e58ae223 don't convert lists to vectors in LAST 2016-11-18 16:24:09 -05:00
Nathan Marz
9d859e0fae update changelog 2016-11-09 06:20:38 -05:00
Nathan Marz
9178f38465 fix race condition relating to retrieving path from cache and aot compilation 2016-11-09 06:20:10 -05:00
Nathan Marz
b19ceb8c0c begin 0.13.2 2016-11-07 11:28:59 -05:00
Nathan Marz
fdd74ea224 0.13.1 2016-11-07 11:25:35 -05:00
Nathan Marz
013c4d6d0a cljs compatibility 2016-11-07 11:25:12 -05:00
Nathan Marz
0ddb811326 add traversed test 2016-11-07 11:17:36 -05:00
Nathan Marz
390f00063c add benchmark case for multi-arity keypath 2016-11-07 07:47:56 -05:00
Nathan Marz
508e1d08ed added traversed 2016-11-07 07:47:36 -05:00
Nathan Marz
dfedd30b29 Implemented #146, dynamic navigator enhancements 2016-10-31 23:57:12 -04:00
Nathan Marz
728926c4ea update changelog 2016-10-29 16:03:39 -04:00
Nathan Marz
ce38883e64 fix flattening/type-conversion of sequential params during inline caching 2016-10-29 16:02:56 -04:00
Nathan Marz
8199c0ee36 update changelog 2016-10-14 09:54:40 -04:00
Nathan Marz
6b3ea32799 update versions of clj and cljs for tests, new repl script for cljs 2016-10-14 09:52:55 -04:00
Nathan Marz
d9d6c8772e fix #152 2016-10-14 09:52:12 -04:00
Nathan Marz
ea1f851d94 Fix #148 2016-10-02 07:48:53 -04:00
Nathan Marz
2a6ef8d0c0 fix doc on comp-paths 2016-09-30 06:57:12 -04:00
Nathan Marz
03493cf9ca replace any-pred? with core function some-fn 2016-09-07 15:19:50 -04:00
Nathan Marz
5162a23607 add benchmark for specialized select-any style composition 2016-09-07 10:01:30 -04:00
Nathan Marz
a06cb47bd8 update changelog 2016-09-07 07:54:54 -04:00
Nathan Marz
6a5d0f7560 rename any? to avoid conflict with clojure 1.9 2016-09-07 07:54:10 -04:00
Nathan Marz
2673004b5b suppress cache vars from docs 2016-09-06 19:37:35 -04:00
Nathan Marz
c39b41fea1 suppress docs for generated functions 2016-09-06 19:34:37 -04:00
Nathan Marz
bafab264f5 more benchmark cases 2016-09-06 19:32:17 -04:00
Nathan Marz
0901a24377 Merge branch 'master' of github.com:nathanmarz/specter 2016-09-06 10:44:45 -04:00
Nathan Marz
2b9655b382 fix benchmark script 2016-09-06 10:44:36 -04:00
Nathan Marz
472952e21c Update README.md 2016-09-06 10:31:56 -04:00
Nathan Marz
97e8341fe5 update changelog 2016-09-06 10:21:56 -04:00
Nathan Marz
d7b20f6d0b update changelog 2016-09-06 10:20:43 -04:00
Nathan Marz
e87551e2d5 update changelog 2016-09-06 08:25:47 -04:00
Nathan Marz
7ee838319c bump version 2016-09-06 08:18:46 -04:00
Nathan Marz
e2d927f274 0.13.0 2016-09-06 07:49:24 -04:00
Nathan Marz
843a569c25 update changelog 2016-09-06 07:48:54 -04:00
Nathan Marz
ec43a1b28e updated README 2016-09-06 06:33:06 -04:00
Nathan Marz
aa1c68901e updated README 2016-09-06 06:24:35 -04:00
Nathan Marz
ef41cd341e updated README 2016-09-06 06:19:21 -04:00
Nathan Marz
55e30b3420 update readme 2016-09-05 08:33:35 -04:00
Nathan Marz
2235b593b4 Merge branch 'master' of github.com:nathanmarz/specter 2016-09-05 08:17:43 -04:00
Nathan Marz
3afbafd38b greatly speed up compilation (for select*, transform*, etc) 2016-09-05 08:17:33 -04:00
Nathan Marz
b4ddfd776f Update CHANGES.md 2016-09-05 07:51:31 -04:00
Nathan Marz
23eb18826b update changelog 2016-09-05 07:48:58 -04:00
Nathan Marz
bbab9f8e92 Merge branch 'master' of github.com:nathanmarz/specter 2016-09-05 07:48:13 -04:00
Nathan Marz
2e85cedcc4 merge macros namespace into core com.rpl.specter namespace 2016-09-05 07:48:05 -04:00
Nathan Marz
d3e5463f1c Update README.md
fix typo
2016-09-04 19:50:30 -04:00
Nathan Marz
e18d2b3b34 update readme 2016-09-04 15:03:52 -04:00
Nathan Marz
e33daed812 update readme examples 2016-09-04 15:00:54 -04:00
Nathan Marz
18d7cc0f3d update readme 2016-09-04 11:29:09 -04:00
Nathan Marz
c44a380092 fix so direct-nav metadata on vars gets translated appropriately when that var is called as a function 2016-09-03 22:07:45 -04:00
Nathan Marz
18791c6b82 inline caching working for cljs, all tests passing 2016-09-03 19:58:10 -04:00
Nathan Marz
d7d1d264ef basic cljs inline caching working 2016-09-03 17:35:13 -04:00
Nathan Marz
8a71d5241d refactor to unify cljs and clj inline caching code 2016-09-03 16:09:02 -04:00
Nathan Marz
5400e3fd65 cleaner inline caching implementation 2016-09-03 09:29:01 -04:00
Nathan Marz
16446373f1 make terminal-val return a direct-nav, add another benchmark case 2016-09-02 20:20:49 -04:00
Nathan Marz
194396f0f4 remove todo 2016-09-02 10:45:50 -04:00
Nathan Marz
bb77fb8f9d moved mk-comp-navs macro into clj namespace 2016-09-02 09:17:32 -04:00
Nathan Marz
e27deb071a update changelog 2016-09-02 09:09:41 -04:00
Nathan Marz
9d2ffb907b update changelog 2016-09-02 09:09:16 -04:00
Nathan Marz
0046e23bfe added with-inline-debug helper and updated comment on dynamicnavs 2016-09-02 09:02:09 -04:00
Nathan Marz
37d985e5f0 added test for nested dynamic navs 2016-09-02 08:47:24 -04:00
Nathan Marz
0c50e1859d fix + test for when dynamic navs ignore dynamic params 2016-09-02 08:41:02 -04:00
Nathan Marz
f925e1814c clarify changelog 2016-09-01 23:44:35 -04:00
Nathan Marz
0a45b45e68 added specter dynamic nested get benchmark 2016-09-01 23:41:27 -04:00
Nathan Marz
984e3cdcd2 update changelog 2016-09-01 23:37:36 -04:00
Nathan Marz
1d52e970a0 another perf enhancement 2016-09-01 23:20:53 -04:00
Nathan Marz
dc6740d9dc fix accidental reflection 2016-09-01 23:15:42 -04:00
Nathan Marz
939378fcd1 fix transformed, all tests passing 2016-09-01 22:16:39 -04:00
Nathan Marz
23299457db fixes 2016-09-01 21:46:04 -04:00
Nathan Marz
e910f32931 fixes 2016-09-01 21:13:20 -04:00
Nathan Marz
e33ecb2c33 some fixes 2016-09-01 21:07:01 -04:00
Nathan Marz
c80bf573a5 updated tests, lots of errors 2016-09-01 20:32:38 -04:00
Nathan Marz
6549be1be5 protpaths and parameterized protpaths working 2016-09-01 17:35:19 -04:00
Nathan Marz
e057ee8d22 add note about original-obj 2016-09-01 17:04:58 -04:00
Nathan Marz
e571df5832 add code to avoid embedding functions with metadata on them (which kills perf) 2016-09-01 16:59:40 -04:00
Nathan Marz
f8d74d5884 add direct nav hints 2016-09-01 16:31:00 -04:00
Nathan Marz
479cb7d023 re-implemented declarepath and providepath, added local-declarepath for making recursive/mutually-recursive navigators locally 2016-09-01 13:56:51 -04:00
Nathan Marz
4c570e5de4 fixes + manual inlining for keypath, must, view 2016-09-01 11:24:15 -04:00
Nathan Marz
c80a2d3c50 fixes 2016-09-01 10:04:51 -04:00
Nathan Marz
eb5de0bdd5 lots of progress 2016-08-31 22:30:28 -04:00
Nathan Marz
f511cd4fca initial rewriting 2016-08-29 22:01:17 -04:00
Nathan Marz
a7b743c75d update changelog 2016-08-17 09:44:20 -04:00
Nathan Marz
a35cacae67 have defnav generate every method as a helper function as well (with name <name>-<method-name>, takes params as initial arguments followed by regular method arguments (except for 'this') 2016-08-17 09:42:59 -04:00
Nathan Marz
a765d1af50 update readme 2016-08-15 06:11:04 -04:00
Nathan Marz
21f117503e allow non-dynamic vars to be treated as constants and add test for constant detection 2016-08-13 15:59:38 -04:00
Nathan Marz
1b5b19c7c8 fix accidental coercion to rich nav 2016-08-13 09:23:23 -04:00
Nathan Marz
bf3d8a826d Merge branch 'master' of github.com:nathanmarz/specter 2016-08-11 10:13:38 -04:00
Nathan Marz
ac3f604211 reformat code with parinfer 2016-08-11 10:13:27 -04:00
Nathan Marz
5393b8a8be Merge pull request #139 from jstaffans/master
Fix links in readme
2016-08-10 15:45:55 -04:00
Nathan Marz
87137c633d minor refactoring of macros 2016-08-10 15:45:13 -04:00
jstaffans
6ddc237632 Fix links in readme 2016-08-10 21:36:01 +02:00
Nathan Marz
e08f12d944 update changelog 2016-08-09 12:29:25 -04:00
Nathan Marz
9db244ebe5 always preserve persistentarraymap for ALL and MAP-VALS, also improve performance of those cases by almost 2x 2016-08-09 12:27:46 -04:00
Nathan Marz
9a35c0666d define pred using defnav so it uses lean navigation when possible 2016-08-08 10:16:32 -04:00
Nathan Marz
6024195229 update changelog 2016-08-06 22:20:53 -04:00
Nathan Marz
0ab72d62ff update travis build 2016-08-06 10:43:59 -04:00
Nathan Marz
ea12393efa update cljs test instructions 2016-08-06 10:43:46 -04:00
Nathan Marz
74f38d93a9 switch from cljx to cljc 2016-08-06 10:40:45 -04:00
Nathan Marz
fb6cd38b1d fix typo 2016-08-06 02:30:36 -04:00
Nathan Marz
c4f5a1b02b if set is constant make a static navigator for it 2016-08-06 02:23:11 -04:00
Nathan Marz
5e1f596e60 minor improvements to benchmarks, add benchmark running script 2016-08-06 02:22:51 -04:00
Nathan Marz
645ea6f1aa update changelog 2016-08-06 00:57:04 -04:00
Nathan Marz
d45f3eb34c changelog and version 2016-08-06 00:55:29 -04:00
Nathan Marz
23a501f4ac Merge branch 'prot-redesign' 2016-08-06 00:48:01 -04:00
Nathan Marz
b9cd024c38 parameterize navigators immediately if all params are constant (rather than factor) 2016-08-06 00:44:40 -04:00
Nathan Marz
56da47aca5 generate a real higher order function from defnav and defcollector that returns a lean compiled path when invoked but coerces to equivalent paramsneededpath when composed, change nav constructors to recognize this and use a lean proxy for this case 2016-08-05 17:59:12 -04:00
Nathan Marz
7bfad80e00 fix for cljs 2016-08-05 16:34:58 -04:00
Nathan Marz
073dca1e98 add missing file 2016-08-05 16:21:49 -04:00
Nathan Marz
cbd3db5a75 all tests passing 2016-08-05 16:18:10 -04:00
Nathan Marz
e7db1803b1 fixes 2016-08-05 15:48:55 -04:00
Nathan Marz
e7dc940cd0 completely re-implement nav, collector, fixed-pathed-nav, and pathed-collector in cleaner and more flexible way 2016-08-05 14:10:38 -04:00
Nathan Marz
ff2f36ea17 improve readme example 2016-08-05 06:46:29 -04:00
Nathan Marz
184c14df4c 0.12.0 2016-08-05 06:11:44 -04:00
Nathan Marz
33d19ebd1d cljs tests passing 2016-08-04 22:40:53 -04:00
Nathan Marz
4005aca92e get cljs compiling 2016-08-04 16:32:54 -04:00
Nathan Marz
4460ee313e all tests passing 2016-08-04 16:20:23 -04:00
Nathan Marz
53682de1eb more fixes 2016-08-04 16:03:59 -04:00
Nathan Marz
423da1e03f more fixes 2016-08-04 15:03:00 -04:00
Nathan Marz
73312bffd7 fixes 2016-08-04 13:47:54 -04:00
Nathan Marz
3ba0926251 refactor codebase to use reified navigator objects instead of individual functions, force all navigators to be defined using defnav, add ImplicitNav protocol 2016-08-04 11:28:47 -04:00
Nathan Marz
9c9b19af26 fix error when using specter with aot 2016-06-27 17:32:02 -04:00
Nathan Marz
b7191b11c2 update docs and changelog 2016-06-23 12:40:39 -04:00
Nathan Marz
9a36f9ff00 update changelog 2016-06-23 12:36:36 -04:00
Nathan Marz
b060339573 major optimization to multi-path by removing sequence operations at runtime 2016-06-23 12:35:47 -04:00
Nathan Marz
930724b85b add benchmark for multi-transform 2016-06-23 12:30:50 -04:00
Nathan Marz
1497aacf59 fix tests for cljs 2016-06-23 12:30:17 -04:00
Nathan Marz
7dcc589495 added multi-transform tests 2016-06-23 11:56:29 -04:00
Nathan Marz
e3a259c2b4 merge 2016-06-22 21:19:13 -04:00
Nathan Marz
18680437aa optimize terminal-val with fast-constantly 2016-06-22 21:18:38 -04:00
Nathan Marz
898d7489a6 add terminal-val 2016-06-22 21:17:14 -04:00
Nathan Marz
2f5d601ccf implemented multi-transform and terminal 2016-06-22 21:17:13 -04:00
Nathan Marz
a8f48baced add vals collection + setval test, update changelog 2016-06-22 21:17:02 -04:00
Nathan Marz
d1e32be13b fix exception when using val collection with setval 2016-06-22 21:14:35 -04:00
Nathan Marz
50d2aa48f5 add terminal-val 2016-06-22 21:00:31 -04:00
Nathan Marz
858b0b488d implemented multi-transform and terminal 2016-06-22 14:20:50 -04:00
Nathan Marz
9ff9ef6650 Fix #128 2016-06-21 18:38:00 -04:00
Nathan Marz
96ad0ff68c fix build badge 2016-06-21 16:15:03 -04:00
Nathan Marz
d0331c9afe new benchmark cases, reduce benchmark iterations 2016-06-17 12:56:33 -04:00
Nathan Marz
4c9c4b0001 update changelog 2016-06-16 09:59:29 -04:00
Nathan Marz
ba908284b9 tests for multi-path and if-path with vals collection 2016-06-16 09:52:41 -04:00
Nathan Marz
e4ee703a09 update richnav comment 2016-06-16 09:44:30 -04:00
Nathan Marz
66f555ab73 fix multi-path + val collection and minor problem in if-path 2016-06-16 09:42:24 -04:00
Nathan Marz
36f0e63d56 added richnav and fixed if-path with value collection 2016-06-16 09:09:29 -04:00
Nathan Marz
bb058a24b9 update changelog 2016-06-15 17:20:45 -04:00
Nathan Marz
c709b16eab ALL and MAP-VALS perf improvement for small maps 2016-06-15 17:18:58 -04:00
Nathan Marz
e8f0a873fa update changelog 2016-06-15 12:46:33 -04:00
Nathan Marz
4778500e03 more benchmarks for map-vals 2016-06-15 12:23:03 -04:00
Nathan Marz
ef8039cacc error if pathed fn used where navigator expected 2016-06-15 11:41:12 -04:00
Nathan Marz
356dd1d03e improve map-vals benchmark 2016-06-15 11:02:07 -04:00
Nathan Marz
8018d0e330 link to new wiki page 2016-06-15 10:53:00 -04:00
Nathan Marz
f5baf819c0 fix typo 2016-06-15 10:32:50 -04:00
Nathan Marz
6707ae33c9 benchmark updates 2016-06-15 10:31:59 -04:00
Nathan Marz
db08499eaf include #iters in benchmark output 2016-06-15 09:31:24 -04:00
Nathan Marz
85b91d19c4 rearrange code for bootstrap compatibility 2016-06-14 08:45:11 -04:00
Nathan Marz
ecda5e2cff rearrange code to hide riddley usage from bootstrap 2016-06-13 19:49:35 -04:00
Nathan Marz
aedb7235f9 add note on implementation detail of using cljs.analyzer 2016-06-13 17:25:29 -04:00
Nathan Marz
2dff143070 update changelog 2016-06-13 17:23:40 -04:00
Nathan Marz
8c128816f5 get macroexpansion for clojurescript working correctly so that collected? works inside inline paths 2016-06-13 17:23:23 -04:00
Nathan Marz
1f21fa5a9d update changelog 2016-06-13 15:26:42 -04:00
Nathan Marz
c82c3b71d8 add note about not being able to use collected? inline in a path when using cljs, disable that test for cljs 2016-06-13 15:26:08 -04:00
Nathan Marz
1697938587 update changelog 2016-06-13 14:52:35 -04:00
Nathan Marz
a687f4a0bc test for traverse + make it work in cljs 2016-06-13 14:51:20 -04:00
Nathan Marz
66d1ce65f3 Merge branch 'master' into traverse 2016-06-13 13:51:03 -04:00
Nathan Marz
fd180d2e2c link to new wiki page 2016-06-12 09:44:49 -04:00
Nathan Marz
eaa1220af9 update changelog 2016-06-11 09:51:52 -04:00
Nathan Marz
f26aa001eb make MAP-VALS work on nil 2016-06-11 09:51:28 -04:00
Nathan Marz
589e6aa471 added a collected?/DISPENSE test case for transform path 2016-06-10 08:09:11 -04:00
Nathan Marz
59d6e4d319 fix conflict? 2016-06-10 07:58:46 -04:00
Nathan Marz
3dc7ad25ff added collected? and DISPENSE navigators 2016-06-10 07:57:18 -04:00
Nathan Marz
e20278cf97 fix typo in docstring 2016-06-09 23:19:34 -04:00
Nathan Marz
911dab5e52 add non-init reduce case to traverse 2016-06-09 23:19:12 -04:00
Nathan Marz
f9e6b621a9 upgrade to clojure 1.7.0 for reducers 2016-06-09 23:19:12 -04:00
Nathan Marz
f605167a53 implement traverse 2016-06-09 23:19:11 -04:00
Nathan Marz
3af11575d7 fix typos in docstrings 2016-06-09 23:19:03 -04:00
Nathan Marz
9ac9771a0e Merge branch 'master' into traverse 2016-06-09 17:32:46 -04:00
Nathan Marz
0c4c4369a2 eliminate compiler performance warning regarding case 2016-06-09 17:32:40 -04:00
Nathan Marz
e7aae0cb0f add non-init reduce case to traverse 2016-06-09 17:21:11 -04:00
Nathan Marz
02a4bf09ca upgrade to clojure 1.7.0 for reducers 2016-06-09 16:43:03 -04:00
Nathan Marz
ff03dbc834 implement traverse 2016-06-09 16:36:20 -04:00
Nathan Marz
14c404c552 more tests for all/last/first on nil 2016-06-09 10:33:14 -04:00
Nathan Marz
b0d8fe700c update doc gen 2016-06-09 09:24:05 -04:00
Nathan Marz
615553cf3e update readme 2016-06-09 09:23:41 -04:00
Nathan Marz
c6781ae5fc update changelog 2016-06-09 08:55:58 -04:00
Nathan Marz
bd3162f19b update changelog 2016-06-09 08:53:53 -04:00
Nathan Marz
2a5782bde6 0.11.2 notes 2016-06-09 08:52:35 -04:00
Nathan Marz
b22ff2bb2a add in transients namespace 2016-06-09 08:46:29 -04:00
Nathan Marz
11029a4285 update changelog 2016-06-09 08:06:52 -04:00
Nathan Marz
4565a7e7d6 fail cljs build on any warnings, eliminate warning about com.rpl.specter.transient namespace by renaming to transients 2016-06-09 08:06:17 -04:00
Nathan Marz
82144f6e4c fix typo 2016-06-09 07:33:12 -04:00
Nathan Marz
90bbc2c39f fix typo 2016-06-08 22:01:46 -04:00
Nathan Marz
98343784bf fix end, last, beginning, and first to work on nil 2016-06-08 14:42:24 -04:00
Nathan Marz
9045e13386 merge 2016-06-08 13:58:15 -04:00
Nathan Marz
65da1056d5 increase benchmark iterations 2016-06-08 13:43:01 -04:00
Nathan Marz
7fa477f197 update changelog 2016-06-08 13:35:29 -04:00
Nathan Marz
29c3e1dc06 Merge pull request #116 from aengelberg/meta-navigator
Add META navigator
2016-06-08 13:30:27 -04:00
Alex Engelberg
21e6289c64 Add benchmark to compare to vary-meta 2016-06-08 08:25:16 -07:00
Alex Engelberg
fbb7a17197 Add META navigator, test case, and benchmarks 2016-06-08 08:18:10 -07:00
Nathan Marz
294582e589 fix credit on changelog 2016-06-08 10:24:04 -04:00
Nathan Marz
779fd72226 formatting 2016-06-08 10:09:39 -04:00
Nathan Marz
9b70adb07b formatting 2016-06-08 09:59:23 -04:00
Nathan Marz
bd131246d8 improve readme 2016-06-08 09:58:22 -04:00
Nathan Marz
ab38b2db62 Merge branch 'master' into new-readme 2016-06-08 09:44:12 -04:00
Nathan Marz
3d84d288d1 fix typo in readme 2016-06-08 09:40:53 -04:00
Nathan Marz
85f14e8398 update README 2016-06-08 07:06:04 -04:00
Nathan Marz
839d92da14 update changelog 2016-06-08 07:01:17 -04:00
Nathan Marz
3a9de5b70f 0.11.1 2016-06-08 06:57:32 -04:00
Nathan Marz
50c176ba48 reinstate one-at-at-time vector append benchmark 2016-06-08 06:57:15 -04:00
Nathan Marz
8231cd654f benchmark keypath alongside direct keyword navigation 2016-06-08 06:11:21 -04:00
Nathan Marz
4cdc7a47a3 updated changelog 2016-06-08 06:09:24 -04:00
Nathan Marz
82321d7370 isolate desired operations to test in transient benchmarks and make the comparisons work on identical data, add transient namespace for doc generation 2016-06-08 06:02:41 -04:00
Nathan Marz
49957f2536 Merge pull request #107 from aengelberg/transient-navigators
Transient navigators
2016-06-08 05:48:51 -04:00
Alex Engelberg
2147584dca Change reduce to reduce-kv 2016-06-07 23:16:54 -07:00
Alex Engelberg
bafe10036f Add benchmarks to test transient navigators 2016-06-07 23:16:23 -07:00
Nathan Marz
577aa25e50 update changelog 2016-06-07 20:25:39 -04:00
Nathan Marz
2477d2d84c updated changelog 2016-06-07 18:18:44 -04:00
Nathan Marz
0f475ddba3 merge in optimizations to END 2016-06-07 16:11:21 -04:00
Nathan Marz
5161f6dfbf optimize END for vectors 2016-06-07 16:07:01 -04:00
Nathan Marz
c28245b420 add protocols ns to api docs 2016-06-07 14:40:31 -04:00
Nathan Marz
ec05c14225 document exact semantics of select*/transform* needed for Navigator 2016-06-07 14:34:32 -04:00
Nathan Marz
5087e500b7 finish select-any tests 2016-06-07 13:16:00 -04:00
Nathan Marz
e698f5f06b bump version, optimized multi-path select, lots of select-any tests 2016-06-07 12:35:59 -04:00
Nathan Marz
205b6a1319 fix MAP-VALS 2016-06-07 10:51:08 -04:00
Nathan Marz
6580f4df4b added selected-any? helper operation 2016-06-07 10:47:18 -04:00
Nathan Marz
81ec559e69 docstring for MAP-VALS 2016-06-07 10:31:07 -04:00
Nathan Marz
e76363c532 more docstrings and benchmarks 2016-06-07 10:18:20 -04:00
Nathan Marz
88a79e3d77 add docstrings for new API elements 2016-06-07 09:40:14 -04:00
Nathan Marz
a4c941b744 cljs compatible 2016-06-07 00:49:52 -04:00
Nathan Marz
68ac32ef56 optimize if-path and selected? for non basic fn case 2016-06-06 21:30:14 -04:00
Nathan Marz
f3bf935509 new semantics for select* for increased performance, new select-any operation with maximal query performance, needs more tests 2016-06-06 20:29:45 -04:00
Nathan Marz
4379a3dc9c update changelog 2016-06-06 16:10:47 -04:00
Nathan Marz
1efb3df8de improve readme 2016-06-06 16:04:45 -04:00
Nathan Marz
ae98aa48ba add specialized MAP-VALS navigator to circumvent the unavoidable overhead of [ALL LAST] 2016-06-06 16:03:08 -04:00
Nathan Marz
16063a6714 readme improvements 2016-06-06 15:52:31 -04:00
Nathan Marz
5d5ed2b8de improve readme 2016-06-06 12:55:10 -04:00
Nathan Marz
bc4d1d0051 updated readme 2016-06-06 12:34:21 -04:00
Nathan Marz
9a9f425b7f update changelog 2016-06-06 12:34:05 -04:00
Nathan Marz
12ce91c94b update changelog 2016-06-06 08:16:45 -04:00
Nathan Marz
5b949f9a52 update build to target clojure 1.6 during tests 2016-06-06 07:58:29 -04:00
Nathan Marz
c1a31bb2fe update changelog 2016-06-06 07:50:46 -04:00
Nathan Marz
571ba316da Fix #109 2016-06-06 07:48:30 -04:00
Nathan Marz
c81ba0196f eliminate warning about intern for bootstrap cljs 2016-06-06 07:38:21 -04:00
Alex Engelberg
399e5661f1 The (identical?) trick doesn't work in cljs, but select-keys does 2016-06-05 22:11:34 -07:00
Alex Engelberg
25ba21d9ee Remove no-longer-used transient-all-select|transform 2016-06-05 21:47:44 -07:00
Alex Engelberg
cb0dc261cf Add tests for transients, fix transient navigators based on test failures 2016-06-05 21:38:14 -07:00
Nathan Marz
2412d90f71 improve benchmarks script, add benchmark for large map values update 2016-06-05 15:25:00 -04:00
Alex Engelberg
067ce9edee Remove ALL! and filterer! 2016-06-05 11:11:14 -07:00
Alex Engelberg
b3e581f737 WIP, transient navigators 2016-06-05 10:08:30 -07:00
Nathan Marz
64afc6835a another benchmark case for map values transform 2016-06-05 12:02:24 -04:00
Nathan Marz
78a84959d2 added benchmarking script 2016-06-05 11:39:58 -04:00
Nathan Marz
b6cd3b227b update changelog 2016-06-05 01:18:40 -04:00
Nathan Marz
ff903cd236 optimize selected? and not-selected? 2016-06-04 22:57:56 -04:00
Nathan Marz
f82ab31b36 expand optimized if-path to encompass any sequence of static functions 2016-06-04 21:22:57 -04:00
Nathan Marz
18e736e5d8 update changelog 2016-06-04 21:01:29 -04:00
Nathan Marz
2d3902f478 huge speedup to if-path when condition is a single statically known function, big optimization for all transforms that don't use value collection by using identical? instead of empty? 2016-06-04 20:49:57 -04:00
Nathan Marz
94d5d2021a add nil case for all-transform 2016-06-04 17:22:27 -04:00
Nathan Marz
a4857a9d57 Fix #96 2016-06-04 17:19:39 -04:00
Nathan Marz
86f05b3cbe major performnace enhancements for ALL on vectors (2x) and maps (10% for arraymas, 25% for hashmaps) 2016-06-04 17:12:40 -04:00
Nathan Marz
0bc26c950e implement cond-path in terms of if-path to avoid all runtime sequence operations 2016-06-04 16:40:34 -04:00
Nathan Marz
97f7ba618c update changelog 2016-06-04 15:04:04 -04:00
Nathan Marz
ff2853381c huge speedup to if-path by having specialized implementation separate from cond-path 2016-06-04 15:01:28 -04:00
Nathan Marz
59423c358e huge speedup for cond-path 2016-06-04 14:51:26 -04:00
Nathan Marz
b6e1f5f20f defprotocolpath docstring 2016-06-04 14:30:38 -04:00
Nathan Marz
57a2e967d4 defpathedfn docstring 2016-06-04 14:23:09 -04:00
Nathan Marz
58865ee10d fill in missing docstrings 2016-06-04 09:40:41 -04:00
Nathan Marz
d9443232d8 add docstrings to the core select/transform/etc. macros 2016-06-04 09:29:45 -04:00
Nathan Marz
e1138747d9 fix #103 2016-06-03 16:25:27 -04:00
Nathan Marz
91599baf00 update README 2016-06-03 08:44:22 -04:00
Nathan Marz
00c234c967 update README 2016-06-03 07:44:40 -04:00
Nathan Marz
f2865615bd fix source links for api docs 2016-06-02 12:17:02 -04:00
Nathan Marz
42b751dd45 add API docs link 2016-06-02 12:05:09 -04:00
Nathan Marz
d85f21722b set up to generate html api docs 2016-06-02 11:59:19 -04:00
Nathan Marz
19eb5923c3 update changelog 2016-06-02 02:48:43 -04:00
Nathan Marz
fd01d355aa update changelog 2016-06-02 02:45:02 -04:00
Nathan Marz
52740d56ac add :notpath metadata for pathedfn args that should not be interpreted as paths during inline factoring/caching, fixed transformed to operate appropriately in inline factoring when transform-fn is anonymous or local, drop support for cljs below v1.7.10 2016-06-02 02:42:18 -04:00
Nathan Marz
8a0ba0b3b0 eliminate last cljs compiler warning 2016-06-02 01:15:35 -04:00
Nathan Marz
c1d9dff75e eliminate most of cljs warnings except for one 2016-06-01 20:54:04 -04:00
Nathan Marz
187299b3d9 make non 1 or 2 count case update-last faster for vectors, change updateextremes to be specific to persistentvector 2016-06-01 12:54:59 -04:00
Nathan Marz
2fe16769ff use nth instead of get for retrieving first element of vector 2016-06-01 12:42:41 -04:00
Nathan Marz
39a3755b49 implement optimized extremes protocols and fastempty for IPersistentVector rather than PersistentVector 2016-06-01 12:33:10 -04:00
Nathan Marz
d8473993c4 upate changelog 2016-06-01 12:27:22 -04:00
Nathan Marz
edfcc92a85 big optimization for LAST for small vectors (e.g. those used for mapentries) 2016-06-01 12:27:02 -04:00
Nathan Marz
a4dabfd7a9 update changelog 2016-06-01 12:03:01 -04:00
Nathan Marz
1c21be2262 minor performance optimizations to FIRST/LAST 2016-06-01 12:02:41 -04:00
Nathan Marz
6b7d18d874 use reduce-kv to optimize ALL transform on maps, significant performance boost 2016-06-01 11:08:49 -04:00
Nathan Marz
2aff955a99 update changelog 2016-05-31 23:05:53 -04:00
Nathan Marz
fbca7ab99c stop using ConcurrentHashMap for inline cache, instead intern a new var at macro-time. 17% performance improvement for [:a :b :c] benchmark 2016-05-31 23:04:24 -04:00
Nathan Marz
a583540f21 update readme 2016-05-31 11:11:03 -04:00
Nathan Marz
ad3baacf5f 0.11.0 2016-05-31 09:47:54 -04:00
Nathan Marz
cb3b33f63b another bootstrap compatibility change (from @mfikes) 2016-05-29 10:15:03 -04:00
Nathan Marz
d162c2b49f advise cljs compiler not to warn about undeclared var instead of the hack that was being used before 2016-05-29 09:06:51 -04:00
Nathan Marz
637f7fc819 code cleanup 2016-05-29 00:34:51 -04:00
Nathan Marz
f49f371eea eliminate undeclared var warning from cljs inline caching code 2016-05-28 21:06:43 -04:00
Nathan Marz
2c52355f8d remove todo 2016-05-28 20:01:35 -04:00
Nathan Marz
e5db7252c3 use local def for cljs inline cache. faster and enables bootstrap compatibility 2016-05-28 20:01:10 -04:00
Nathan Marz
bb5fcbe7b3 bootstrap compatibility changes 2016-05-28 19:00:58 -04:00
Nathan Marz
e2e8fa091e Added continuous-subseqs navigator 2016-05-28 13:57:30 -04:00
Nathan Marz
009a9c93f6 fix formatting 2016-05-26 16:54:55 -04:00
Nathan Marz
197dcb07ec fix testing instructions for cljs 2016-05-26 09:40:33 -04:00
Nathan Marz
22f0b7e5e1 minor inline caching failure string improvements 2016-05-25 00:13:46 -04:00
Nathan Marz
b927d8e241 major optimization for use case of passing an already compiled path to inline caching specter operations 2016-05-24 17:39:08 -04:00
Nathan Marz
73e460df80 optimization for using comp-paths on already compiled path 2016-05-24 17:29:34 -04:00
Nathan Marz
b8bcfd6054 change how macroexpansion is done during inline caching so that nested &env are correctly computed. Allows for inline caching to occur nested inside an inline caching expression 2016-05-24 17:01:17 -04:00
Nathan Marz
799c6578b8 update changelog 2016-05-24 16:08:36 -04:00
Nathan Marz
d7db4190dd fix nav constructors for cljs 2016-05-24 16:06:32 -04:00
Nathan Marz
e70cfb3623 added nav constructors + tests, fixed bug with clj platform eval'd params fn to bind to the namesapace where the path was defined 2016-05-24 16:00:22 -04:00
Nathan Marz
cd7b759c3a added nav constructors with integration into automatic inline factoring + caching 2016-05-24 13:49:41 -04:00
Nathan Marz
b2cb9f1940 add doc for must-cache-paths 2016-05-24 08:42:23 -04:00
Nathan Marz
33f8ed3e73 update changelog 2016-05-23 15:33:15 -04:00
Nathan Marz
29fc629899 update changelog 2016-05-23 15:30:26 -04:00
Nathan Marz
8dd0f7e168 finish test for inline caching 2016-05-23 15:17:47 -04:00
Nathan Marz
4f3990c239 added tests and cljs-specific fixes 2016-05-23 14:58:22 -04:00
Nathan Marz
218cbcb933 use mutable cell for must-cache-paths state for cljs compatibility 2016-05-23 10:36:52 -04:00
Nathan Marz
1e0a43c903 all tests passing for both clj and cljs 2016-05-23 10:21:43 -04:00
Nathan Marz
b156727f3c fix inline handle params for cljs, most tests passing 2016-05-23 10:01:44 -04:00
Nathan Marz
ac8119dbbe inline caching for cljs working in repl tests, tests not working for cljs yet 2016-05-23 09:23:14 -04:00
Nathan Marz
d30af6e972 cljs inline caching impl almost working 2016-05-23 08:18:49 -04:00
Nathan Marz
2071059695 revert back to using concurrenthashmap - the performance benefit is not worth the potential startup time problems with >5000 callsites 2016-05-22 09:52:37 -04:00
Nathan Marz
50b02c17f0 add case to automatically factor string for cached path if it is extended as a navigator 2016-05-22 09:25:33 -04:00
Nathan Marz
fcb5e013d4 fixed inline precompilation of inline sets in paths, fix bug leading to wrong params order during inline precompilation, change select/transform operations to be macros and move code around accordingly, rename previous versions of select/transform functions with * added 2016-05-22 08:57:53 -04:00
Nathan Marz
cdcdbbbaa4 clean up terminology – defpath -> defnav, path -> nav, fixed-pathed-path -> fixed-pathed-nav, variable-pathed-path -> variable-pathed-nav, StructurePath -> Navigator (breaking changes) 2016-05-21 15:54:07 -04:00
Nathan Marz
c567045fb5 better conditional checking of cached value 2016-05-21 10:08:50 -04:00
Nathan Marz
c2fa922717 automatically factor anonymous functions with pred, 20% more effient cache strategy, more efficient hot path, added ability with must-cache-paths to error when a path can't be cached and get detailed information why 2016-05-21 09:44:20 -04:00
Nathan Marz
d3a462aa06 initial impl working for basic use cases 2016-05-20 16:57:53 -04:00
Nathan Marz
ac5efb2eb9 add link to clojurians #specter channel 2016-05-17 11:27:52 -04:00
Nathan Marz
1d77d295fa minor refactoring of ATOM and update changelog 2016-05-08 15:55:45 -04:00
Nathan Marz
4a5f0b79b8 Merge pull request #87 from rakeshp/master
Added path to navigate to atom value
2016-05-08 15:52:21 -04:00
Rakesh
c280b40b12 renamed atompath to ATOM 2016-05-08 23:39:14 +05:30
Rakesh
7b33c93132 Added path to navigate to atom value 2016-05-07 22:40:21 +05:30
Nathan Marz
03f6a12baa fix must select 2016-05-05 14:59:25 -04:00
Nathan Marz
3602a08e5d docstring for view 2016-05-05 14:29:20 -04:00
Nathan Marz
b4b2200377 added must navigator 2016-05-05 14:24:57 -04:00
Nathan Marz
9d2a5ed46f 0.10.0 2016-04-26 10:21:54 -04:00
Nathan Marz
f5c5284ae1 add docstrings to zippers 2016-04-25 17:47:10 -04:00
Nathan Marz
48ad46d5e5 add zipper/PREV 2016-04-24 16:54:07 -04:00
Nathan Marz
9a3a19bb9b add NODE-SEQ, find-first, and NEXT-WALK 2016-04-24 16:49:45 -04:00
Nathan Marz
96d5e94a5b fix zipper navigation to stop navigating when right/left/up/down/next navigate nowhere 2016-04-24 13:03:29 -04:00
Nathan Marz
64700208f6 update changelog 2016-04-24 11:11:50 -04:00
Nathan Marz
a4e1f1267b zippers working in cljs 2016-04-24 11:10:55 -04:00
Nathan Marz
70523ac38f add zipper tests 2016-04-24 11:03:52 -04:00
Nathan Marz
747b080909 added INNER-RIGHT and INNER-LEFT 2016-04-24 10:42:19 -04:00
Nathan Marz
b68d59ca90 Merge branch 'zipper' 2016-04-23 13:30:15 -04:00
Nathan Marz
9c87a0ebab update changelog 2016-04-21 16:34:58 -04:00
Nathan Marz
db8451a47e fix travis ci build 2016-04-21 11:07:11 -04:00
Nathan Marz
4e38d0c3b3 fix travis ci build 2016-04-21 11:05:14 -04:00
Nathan Marz
a9aafc3eb8 clean up terminology usage by replacing selector with navigator or path as appropriate 2016-04-21 10:59:35 -04:00
Nathan Marz
0014e413b0 Merge pull request #25 from cgore/travis-ci
Adding config and a badge for travis-ci.org to do CI.
2016-04-21 10:49:39 -04:00
Nathan Marz
4df4603762 update changelog 2016-04-20 23:32:45 -04:00
Nathan Marz
2cbb49de48 verify that filterer maintains sequence types to the same degree that ALL does 2016-04-20 23:32:10 -04:00
Nathan Marz
6894578569 update README 2016-04-20 22:20:45 -04:00
Nathan Marz
82f591a5d3 redefine filterer in terms of subselect 2016-04-20 22:09:02 -04:00
Nathan Marz
5552c8fc0d Merge branch 'master' of github.com:nathanmarz/specter 2016-04-20 22:00:10 -04:00
Nathan Marz
d481f72c52 update changelog 2016-04-20 21:59:20 -04:00
Nathan Marz
7a29e3f056 Merge pull request #75 from aengelberg/add-select-view
Add new "subselect" path selector
2016-04-20 21:57:51 -04:00
Nathan Marz
643385d350 update changelog 2016-04-19 20:41:00 -04:00
Nathan Marz
0088239fe6 Merge pull request #76 from bfabry/beau/add-submap
add a submap path
2016-04-19 20:39:52 -04:00
Beau Fabry
eb20e86f9c Address PR comments
* Behave the same as select-keys for non-existent keys
 * Remove variadic input redirection
 * Use reduce to remove data instead of apply
2016-04-19 17:35:34 -07:00
Beau Fabry
b3c707092e add a submap path 2016-04-19 15:15:06 -07:00
Alex Engelberg
c7252a2b90 Changed name from 'select-view' to 'subselect' 2016-04-19 12:04:18 -07:00
Alex Engelberg
d797e1aec1 Add warning to docstring about input navigator requirement 2016-04-19 12:03:21 -07:00
Alex Engelberg
4c8aeebafc Switch from atom to mutable-cell 2016-04-19 11:55:32 -07:00
Alex Engelberg
fbacd49817 Fix (is (= x) y) caught by humane-test-output 2016-04-19 00:48:49 -07:00
Alex Engelberg
96f6bbc9f4 Add select-view path selector + test case 2016-04-19 00:47:56 -07:00
Nathan Marz
e1c63e51d3 update changelog 2016-04-18 14:26:11 -04:00
Nathan Marz
cd713f1439 test improvements 2016-04-18 14:25:33 -04:00
Nathan Marz
c861756836 Merge pull request #73 from thomasathorne/biview
Add `parser` path.
2016-04-18 14:20:19 -04:00
Thomas Athorne
1ddd8c22f6 Clearer names for arguments that are functions. 2016-04-18 16:21:33 +00:00
Nathan Marz
a74f03158a update changelog 2016-04-18 12:20:20 -04:00
Nathan Marz
0134656d0a minor code cleanup 2016-04-18 12:18:47 -04:00
Nathan Marz
f048d23cda Merge pull request #63 from StephenRudolph/master
Add idempotency for PersistentQueue collection type
2016-04-18 12:09:59 -04:00
Nathan Marz
c962971d7a fix for bootstrap cljs compatibility 2016-04-17 21:00:58 -04:00
Nathan Marz
7c510c1f9d new presentation link 2016-04-17 20:27:12 -04:00
Nathan Marz
703e25e1fe use->require in macros.clj for bootstrap cljs compatibility 2016-04-17 16:26:54 -07:00
Thomas Athorne
39b08bd9d5 Make the test a bit more thorough. 2016-04-17 16:13:56 +01:00
Thomas Athorne
60bf33ffab Change name; add a test spec. 2016-04-17 16:04:22 +01:00
Thomas Athorne
c34e900427 Add biview path. 2016-04-16 17:16:23 +01:00
Nathan Marz
585637b4fe update changelog 2016-04-15 17:21:28 -07:00
Nathan Marz
9576b882ef 0.9.3 2016-04-14 18:06:10 -07:00
Stephen Rudolph
ddea0a223d Moving CLJS code to use transformers instead of reducers 2016-02-26 17:10:09 -06:00
Stephen Rudolph
0f2118d939 Merge remote-tracking branch 'upstream/master' 2016-02-26 16:29:34 -06:00
Nathan Marz
dd906aad95 change clojure/cljs dependencies to provided 2016-02-26 09:17:40 -05:00
Nathan Marz
050384e0fd cleaner and more composable integration 2016-02-21 19:44:50 -05:00
Nathan Marz
70d916dd6a edit -> edited 2016-02-16 18:29:57 -05:00
Nathan Marz
2d868ec587 zipper/specter integration implementation 2016-02-16 15:21:26 -05:00
Nathan Marz
33e52d606a update changelog 2016-02-14 13:35:39 -05:00
Nathan Marz
6db23aa6c8 fix tests 2016-02-14 13:35:23 -05:00
Nathan Marz
ed6f1902b9 rename VOID to STOP 2016-02-13 18:56:35 -05:00
Stephen Rudolph
095fc00319 Added newline back 2016-02-12 20:29:40 -06:00
Stephen Rudolph
1b26aaff1b Fixed CLJS queue usage 2016-02-12 20:27:32 -06:00
Stephen Rudolph
a12222eac5 Persistent queues no longer treated as lists 2016-02-12 16:16:54 -06:00
Nathan Marz
e661df1aeb Merge branch 'master' of github.com:nathanmarz/specter 2016-02-11 16:54:25 -05:00
Nathan Marz
de15b1e23e handle paramsneededpath with 0 params (used for params-reset) correctly in higher order paths 2016-02-11 16:54:14 -05:00
Nathan Marz
d2c30efad9 Merge pull request #55 from cloojure/patch-1
Update README.md
2016-02-02 23:25:32 -05:00
Nathan Marz
7a48f47aed clarify README 2016-01-31 23:19:53 -08:00
Nathan Marz
9f0ba4d949 update changelog 2016-01-31 10:02:00 -08:00
Nathan Marz
8293f68696 allow defprotocolpath to be defined with no params argument for consistency with declarepath 2016-01-31 10:01:08 -08:00
Nathan Marz
b16dbdfdd2 change params-reset to backtrack in params-idx by number of needed params of its path, add test that verifies composability 2016-01-30 12:41:29 -08:00
Nathan Marz
cf0dae3699 update changelog 2016-01-30 12:13:28 -08:00
Nathan Marz
c259583e81 add test for recursive navigation with params 2016-01-30 12:12:26 -08:00
Nathan Marz
8dd2cb8939 allow declarepath to have parameters, implemented params-reset for enabling recursive parameterized paths 2016-01-30 12:03:46 -08:00
Nathan Marz
78b16ee5b7 improve example 2016-01-29 11:08:07 -08:00
Nathan Marz
7e54757659 auto-coerce map entries to vectors during ALL 2016-01-28 12:41:36 -08:00
Nathan Marz
3a38c844e6 0.9.2 2016-01-26 11:20:07 -08:00
Alan Thompson
49df05013a Update README.md
Added clojure `use` statement required to make examples work.
2016-01-25 08:26:00 -08:00
Nathan Marz
d56ea62e00 reimplement stay-then-continue and continue-then-stay in terms of multi-path 2016-01-22 19:45:20 -08:00
Nathan Marz
b98bc8e4ce update license year range 2016-01-21 15:40:26 -08:00
Nathan Marz
2be403784b update changelog 2016-01-15 10:29:35 -05:00
Nathan Marz
aa8755254f clean up notes 2016-01-14 17:48:39 -05:00
Nathan Marz
dd6dafc390 implemented declarepath/providepath 2016-01-14 17:36:49 -05:00
Nathan Marz
1905157c3c update changelog 2016-01-14 13:15:54 -05:00
Nathan Marz
1328551a19 added STAY, stay-then-continue, and continue-then-stay selectors 2016-01-14 13:15:19 -05:00
Nathan Marz
6528945797 update README 2016-01-13 11:35:22 -05:00
Nathan Marz
eaa05de4b4 clarify changelog 2016-01-13 01:46:26 -05:00
Nathan Marz
b3b6b99e7a fix replace-in to work with value collection 2016-01-12 16:25:02 -05:00
Nathan Marz
49c749b74e fix typo in comment 2016-01-12 15:40:37 -05:00
Nathan Marz
e243826173 update changelog 2016-01-11 14:45:31 -05:00
Nathan Marz
e37c605697 protocol path extensions now verify if correct number of parameters and error otherwise 2016-01-11 14:45:06 -05:00
Nathan Marz
06f0d6b656 add recursive navigation example 2016-01-11 14:03:03 -05:00
Nathan Marz
2ad873da11 fixed #48 2016-01-11 10:25:03 -05:00
Nathan Marz
f500a4bfa3 update changelog 2016-01-10 10:35:47 -05:00
Nathan Marz
af7d76be97 improve syntax checking for defpath 2016-01-10 10:35:38 -05:00
Nathan Marz
c9b78ce0d6 add VOID selector 2016-01-10 10:35:18 -05:00
Nathan Marz
732a6422e6 update changelog 2016-01-05 09:02:20 -05:00
Nathan Marz
bcd1392125 0.9.1 2016-01-05 08:58:41 -05:00
Nathan Marz
3b6c1ed972 update changelog 2016-01-05 08:57:55 -05:00
Nathan Marz
0f3ea6c79b throw error if function names incorrectly specified in defpath, improve formatting of error messages 2016-01-05 08:57:27 -05:00
Nathan Marz
b10c1f82ff Merge branch 'master' of github.com:nathanmarz/specter 2015-12-29 12:24:29 -05:00
Nathan Marz
019ff219e5 add note about asking questions 2015-12-29 12:24:22 -05:00
Nathan Marz
a18f458070 Merge pull request #44 from ahjones/patch-1
Fix link to Specter cljx file in README
2015-12-28 14:56:32 -05:00
Andrew Jones
0608276f7c Fix link to Specter cljx file
The link to specter.cljx is missing `clj` in the path.
2015-12-19 21:25:58 +00:00
Nathan Marz
66002f75a2 fix typo in changelog 2015-12-16 01:06:36 -05:00
Nathan Marz
e770880cb1 update changelog 2015-12-16 01:05:27 -05:00
Nathan Marz
3073f1256b fix array creation for cljs 2015-12-16 01:03:39 -05:00
Nathan Marz
41f42e20a1 fixed inadvertant reflection in protocol paths, replace object-array in jvm impl with faster impl 2015-12-16 00:53:50 -05:00
Nathan Marz
dd07769a42 added basic example of protocol paths to README 2015-12-13 13:38:16 -05:00
Nathan Marz
c602ca33bc 0.9.0 2015-12-12 12:52:27 -05:00
Nathan Marz
ad1fc525d5 update changelog 2015-12-12 12:38:28 -05:00
Nathan Marz
827726cf54 implement tests for protocol paths, make clear that it only works in clj 2015-12-12 12:37:21 -05:00
Nathan Marz
fc51d70b0f renamed defparamspath and defparamscollector to defpath and defcollector, defpath without params now produces compiledpath directly, implemented protocolpaths 2015-12-12 12:03:59 -05:00
Nathan Marz
322e9ff303 update changelog 2015-11-03 10:52:17 -05:00
Nathan Marz
3980e0f194 make comp-paths work on lazyseqs for cljs 2015-11-03 10:51:42 -05:00
Nathan Marz
2aa3cd85b8 improve README 2015-10-11 13:34:50 -04:00
Nathan Marz
5b9f3fa1d2 redo README 2015-10-11 13:33:58 -04:00
Nathan Marz
ea533c3df3 clarify changelog 2015-10-11 10:46:26 -04:00
Nathan Marz
22b29c05f3 update changelog 2015-10-11 10:45:01 -04:00
Nathan Marz
51ca7214b5 0.8.0 2015-10-10 12:32:38 -04:00
Nathan Marz
8d977296d7 make alias for deploying 2015-10-10 12:32:32 -04:00
Nathan Marz
dd5620cfec added nil->val test 2015-10-10 12:20:41 -04:00
Nathan Marz
d76142e448 add subset test 2015-10-10 12:14:20 -04:00
Nathan Marz
0859070b02 update changelog 2015-10-10 11:56:02 -04:00
Nathan Marz
3a13052145 add nil->val, NIL->SET, NIL->LIST, NIL->VECTOR, and subset selectors 2015-10-10 11:52:50 -04:00
Nathan Marz
25de0eca7f update changelog 2015-10-10 11:21:13 -04:00
Josh Tilles
61f5a0a09a Adapt the test instructions to cljx.
The alias is taken from [the cljx README](20ec61792b (installation)).
2015-10-09 15:08:08 -04:00
Josh Tilles
8df05c2597 Minor tweak: avoid top-level dos. 2015-10-09 14:58:59 -04:00
Josh Tilles
9e215b638e Support Clojure 1.6 by adopting cljx.
Fixes nathanmarz/specter#16.
Resolves nathanmarz/specter#18.
2015-10-09 14:55:10 -04:00
Christopher Mark Gore
157cc70407 Adding a Travis CI badge. 2015-09-26 11:39:48 -05:00
Christopher Mark Gore
a8db31754d Adding .travis.yml for Travis CI. 2015-09-26 10:38:04 -05:00
Nathan Marz
ad1c4fdd53 0.7.1 2015-09-24 12:02:58 -05:00
Nathan Marz
9fcd9e5ed4 walker and codewalker can now be late-bound parameterized 2015-09-24 11:55:24 -05:00
Nathan Marz
1f55a0e701 fix tests for cljs 2015-09-24 11:54:20 -05:00
Nathan Marz
4b9a415eff add example of keypath to README 2015-09-22 10:37:55 -04:00
Nathan Marz
df4c3bf974 added paramsfn helper macro for defining filter functions with later bound parameters 2015-09-20 22:24:11 -04:00
Nathan Marz
f12b6bc046 fix typo/formatting 2015-09-18 15:01:01 -04:00
Nathan Marz
3f22361fad update changelog 2015-09-12 14:16:35 -04:00
Nathan Marz
c6046aa874 parameterize view and make a parameterized version of using a function called pred 2015-09-12 14:15:35 -04:00
Nathan Marz
42befa556e removed dead code 2015-09-12 14:15:17 -04:00
Nathan Marz
f89d76dbe4 update README 2015-09-12 13:27:14 -04:00
Nathan Marz
7ea4e4ef9c improve docs 2015-09-12 13:17:06 -04:00
Nathan Marz
5e08fd199e cleanup capture-params-internally code 2015-09-12 13:16:58 -04:00
Nathan Marz
93210092e9 improve docs 2015-09-11 19:43:42 -04:00
Nathan Marz
0c31819eba add documentation on late-bound parameterization 2015-09-11 19:42:30 -04:00
Nathan Marz
8141da7be9 0.7.0 2015-09-11 18:09:01 -04:00
Nathan Marz
f7f2911350 precompile ALL, LAST, and FIRST 2015-09-11 17:36:16 -04:00
Nathan Marz
993015b128 disable many params test for cljs because of bug in cljs 2015-09-11 17:28:37 -04:00
Nathan Marz
c6522de07d clojurescript port complete, not working for greater than 20 params 2015-09-11 17:17:43 -04:00
Nathan Marz
ffaaf06f9f parameterized paths working for cljs 2015-09-11 16:51:21 -04:00
Nathan Marz
5cb0a8e4f0 complete cljs refactoring 2015-09-11 16:13:03 -04:00
Nathan Marz
7a4caa5b61 partial refactoring to prepare for cljs compatibility 2015-09-11 16:06:31 -04:00
Nathan Marz
5aa3bc5da2 parameterized multi-path test 2015-09-11 15:05:49 -04:00
Nathan Marz
9a4a0cb26c nested filterer/selected? test with params 2015-09-11 13:53:27 -04:00
Nathan Marz
a71654b21e test of filterer with parameterization 2015-09-11 13:18:00 -04:00
Nathan Marz
b1e8c54b5f basic parameterization tests 2015-09-11 11:58:50 -04:00
Nathan Marz
c633e3a80a fix comment for putval 2015-09-11 11:23:51 -04:00
Nathan Marz
224369f4eb add comments about what can be parameterized 2015-09-11 11:21:21 -04:00
Nathan Marz
2a30a6aba5 collect-val instead of collect* 2015-09-11 01:42:29 -04:00
Nathan Marz
ae679b6cc1 remove dead code from old collector code 2015-09-10 23:10:34 -04:00
Nathan Marz
fc6392cc9e implement pathed collectors and convert collect and collect-one 2015-09-10 23:09:19 -04:00
Nathan Marz
bcfcd02f80 implemented paramscollector and converted putval to use it 2015-09-10 22:55:43 -04:00
Nathan Marz
983bf84495 parameterize srange and srange-dynamic, ParamsNeededPaths can now be called as a function to convert to CompiledPath 2015-09-10 22:26:32 -04:00
Nathan Marz
ef40adbe3b better names for higher order parameterized path helpers 2015-09-10 21:47:52 -04:00
Nathan Marz
a983e6e028 all higher order selectors now converted to be parameterizable, helpers for making higher order selectors with fixed paths or variable paths complete 2015-09-10 20:47:46 -04:00
Nathan Marz
3187cdad34 have params-paramspath helper automatically compile the path, refactor filterer 2015-09-10 17:12:54 -04:00
Nathan Marz
efd4f2ee9a higher order parameterized builder working, implemented filterer 2015-09-10 17:09:16 -04:00
Nathan Marz
56ba5a5b8d removed unused KeyPath type 2015-09-10 14:08:57 -04:00
Nathan Marz
2dfc490438 remove debugging println 2015-09-10 13:57:27 -04:00
Nathan Marz
d8feed2ca1 paramspath working with composition, measured about 14% slower than compiled path without params and 15x faster than select with on the fly compilation 2015-09-10 13:56:33 -04:00
Nathan Marz
6e3f79dd53 restructured in terms of CompiledPath and implemented composition for ParamsNeededPath 2015-09-10 02:38:52 -04:00
Nathan Marz
d693ad29ae more name refactoring 2015-09-10 00:04:57 -04:00
Nathan Marz
d1a5b66c71 rename StructureValsPathComposer to PathComposer 2015-09-10 00:03:29 -04:00
Nathan Marz
f800a510dd remove unnecessary StructureValsPath protocol and move path composer protocol into impl 2015-09-09 15:20:58 -04:00
Nathan Marz
33e0055372 Merge pull request #15 from duelinmarkers/fix-test-missing-and
fix test missing (and ...)
2015-07-05 19:48:17 -04:00
John Hume
4317831ba8 fix test 2015-07-05 14:56:36 -05:00
Nathan Marz
192371a7ca 0.6.2 2015-07-03 17:46:23 -04:00
Nathan Marz
1d93961cd3 fix not-native tag for optimized cljs prot invocation 2015-07-03 17:40:13 -04:00
Nathan Marz
66f992efa7 do static-fns optimization for cljs 2015-07-03 16:38:19 -04:00
Nathan Marz
80e1e3dd30 optimized protocol invocations for cljs 2015-07-03 16:38:06 -04:00
Nathan Marz
a76f124579 get rid of field macro since .- syntax works in both clj and cljs 2015-07-03 15:46:35 -04:00
Nathan Marz
2383cc9aab replace obj-extends? with satisfies?, make default clojurescript impls for protocols by using default instead of object 2015-07-03 15:40:34 -04:00
Nathan Marz
6440e4e605 fix unsafe extensions of protocols to native javascript objects 2015-07-03 15:29:14 -04:00
Nathan Marz
052d541f8e fix test 2015-07-03 00:11:03 -04:00
Nathan Marz
4dcb6ee9a7 update changelog 2015-07-02 17:25:00 -04:00
Nathan Marz
051ba3f038 added not-selected? and transformed selectors 2015-07-02 17:23:18 -04:00
Nathan Marz
e54f18cb1f add a note on future work to README 2015-07-01 10:40:44 -04:00
Nathan Marz
40cc7f5b86 0.6.1 2015-07-01 10:36:21 -04:00
Nathan Marz
b626227a9e remove extra overhead in cljs protocol method invocations 2015-07-01 10:33:29 -04:00
Nathan Marz
859b31bce7 huge speedup to cljs version by optimizing field access 2015-07-01 10:30:51 -04:00
Nathan Marz
71b3f6e462 remove lein-cljsbuild since not used at all 2015-06-30 21:09:54 -04:00
Nathan Marz
f4c4ed7d56 0.6.0 2015-06-30 20:09:09 -04:00
Nathan Marz
2137159311 Merge branch 'cljs' 2015-06-30 20:04:20 -04:00
Nathan Marz
b7280318cd fix formatting 2015-06-30 20:00:14 -04:00
Nathan Marz
746f4ea859 added notes on running tests 2015-06-30 19:59:39 -04:00
Nathan Marz
9ebb8dfe3b add test runner file for cljs 2015-06-30 19:59:20 -04:00
Nathan Marz
784fb9fb8d add clojurescript dep and update clojure dep to 1.7.0, fix cljsbuild definition 2015-06-30 19:53:17 -04:00
Nathan Marz
38fed4a7d0 fixed cljs obj-extends? to have special case for nil because it cannot be reflected on like normal objects 2015-06-30 19:46:55 -04:00
Nathan Marz
553a835fd7 fix readme with update->transform change 2015-06-30 18:43:53 -04:00
Nathan Marz
72b16d1ab1 cleanup namespace for cljs test helpers file 2015-06-30 18:10:25 -04:00
Nathan Marz
0a0bab144b add cljs test helpers file 2015-06-30 18:09:03 -04:00
Nathan Marz
9bef1c6528 tests working in clojurescript but no runner yet 2015-06-30 18:08:49 -04:00
Nathan Marz
aeb86ab396 fix namespace definitions for cljs tests but tests still don't run in cljs 2015-06-30 17:38:57 -04:00
Nathan Marz
c20e4b3fd6 modified namespace of tests so that its clojurescript compatible, removed unncessary helpers 2015-06-30 17:26:19 -04:00
Nathan Marz
b85f761ff9 upgrade to latest version of test.check, for unkonwn reason couldn't use generator name max-size and had to rename to limit-size 2015-06-30 17:08:05 -04:00
Nathan Marz
905e6b9b07 fix namespace problem with LAST/FIRST 2015-06-30 17:07:44 -04:00
Nathan Marz
8900eac847 merge in latest changes to master 2015-06-30 14:31:07 -04:00
Nathan Marz
5f2f1087a5 0.5.7 2015-06-29 23:44:18 -04:00
Nathan Marz
1f2b4f03de fix bug in select-one! where nil result could not be returned 2015-06-29 23:43:15 -04:00
Nathan Marz
da554d8247 add test for select-one! returning nil result 2015-06-29 23:42:26 -04:00
Nathan Marz
ce837a39af update changelog 2015-06-29 18:31:03 -04:00
Nathan Marz
8053245c48 allow sets to be used directly as selector (acts as filter) 2015-06-29 18:30:30 -04:00
Nathan Marz
bcc15b1587 change FIRST/LAST to select nothing on empty sequences, closes #4 2015-06-29 18:17:52 -04:00
Nathan Marz
7d3c0ca7cb add test for first/last on empty structures 2015-06-29 18:16:33 -04:00
Nathan Marz
f6d23c777d Merge remote-tracking branch 'pietro/cljs-build-support' into cljs 2015-06-26 17:50:44 -04:00
Pietro F. Menna
6416826374 Changed directory structure for test directory to be the same as for src 2015-06-26 13:35:18 -04:00
Nathan Marz
94a451511f merge in new features from master 2015-06-25 16:34:02 -04:00
Nathan Marz
6a2afccbfd add multi-path implementation 2015-06-25 16:30:27 -04:00
Nathan Marz
dc1da8dfcd improve test 2015-06-25 16:27:27 -04:00
Nathan Marz
7520dd7e38 improve test 2015-06-25 13:35:01 -04:00
Pietro F. Menna
c1cb056f4e Fixed warning message during 'lein test' because of the hooks 2015-06-25 09:43:59 -04:00
Pietro F. Menna
2404445827 Added cljsbuild support 2015-06-24 18:37:58 -04:00
Nathan Marz
6b0a907fff fix mutable cell for clj 2015-06-24 18:27:26 -04:00
Nathan Marz
f3080d8e09 fix mutable cells (not sure what problem was 2015-06-24 18:15:07 -04:00
Nathan Marz
784378af34 allow coerce-path and comp-paths to work with more sequence types 2015-06-24 18:02:20 -04:00
Nathan Marz
c7e0fff2db fix readme with update->transform change 2015-06-24 18:02:05 -04:00
Nathan Marz
9ac06c10d8 Clojurescript version working 2015-06-24 17:44:40 -04:00
Nathan Marz
dcc061413f got obj-extends? working in both clj and cljs 2015-06-24 16:21:42 -04:00
Nathan Marz
ec9020b35b remove unwanted files 2015-06-24 16:21:24 -04:00
Nathan Marz
67139390ca 0.5.5 2015-06-24 14:29:47 -04:00
Pietro F. Menna
3b3bace828 Testing 2015-06-24 14:28:33 -04:00
Nathan Marz
b30d343290 Merge branch 'master' of github.com:nathanmarz/specter 2015-06-24 11:50:11 -04:00
Nathan Marz
09ef28c25f Merge pull request #10 from pietromenna/update-transform
Renamed update to transform because of Clojure 1.7
2015-06-24 11:49:45 -04:00
Pietro F. Menna
67d95ee00a Fixed merged cnflicts 2015-06-24 11:49:27 -04:00
Pietro F. Menna
1c01a1e808 Renamed update to transform because of Clojure 1.7 2015-06-24 11:42:50 -04:00
Nathan Marz
05968e9629 0.5.5 2015-06-22 14:38:34 -04:00
Nathan Marz
6d028cbe24 change filterer so it accepts a path that acts like selected? 2015-06-22 14:37:39 -04:00
Nathan Marz
eb11d3771a diassociate version from project.clj so can be used for other modules 2015-06-22 14:37:16 -04:00
Nathan Marz
bb1a70c339 fix api doc for cond-path 2015-06-19 14:27:22 -04:00
Nathan Marz
231fe9c95c improve example 2015-06-19 14:23:47 -04:00
Nathan Marz
298f031adb added example of if-path to README 2015-06-19 14:21:52 -04:00
Nathan Marz
c677486b7b update changelog 2015-06-19 14:19:16 -04:00
Nathan Marz
6590995bf6 0.5.4 2015-06-19 14:13:31 -04:00
Nathan Marz
e4a3275ff1 change cond-path/if-path to take a selector for the conditional (works like selected?) 2015-06-19 14:13:22 -04:00
Nathan Marz
2299bcc58b 0.5.3 2015-06-18 00:56:17 -04:00
Nathan Marz
da2019b07b added cond-path and if-path selectors 2015-06-18 00:56:03 -04:00
Nathan Marz
6ba23bc438 0.5.2 2015-06-01 14:22:44 -04:00
Nathan Marz
5a6d3fe630 fix composing together something defined with comp-paths with other selectors in a comp-unoptimal setting 2015-06-01 14:21:31 -04:00
Nathan Marz
4aaf058bef added changelog 2015-05-31 08:54:30 -04:00
Nathan Marz
86a3aee11d 0.5.1 2015-05-31 08:50:21 -04:00
Nathan Marz
50576e447b empty selectors and nil count as identity path now fixing #5, remove IDENTITY-PATH in favor of nil 2015-05-31 08:50:00 -04:00
Nathan Marz
228a949ac1 update README 2015-05-27 01:05:15 -04:00
Nathan Marz
6bae041970 added putval and IDENTITY-PATH selectors 2015-05-27 01:02:19 -04:00
Nathan Marz
96c5db7a35 show compiled-update in README 2015-05-13 09:50:38 -04:00
Nathan Marz
31fbb0b018 update README about extensibility 2015-05-11 19:44:46 -04:00
Nathan Marz
29ded336ee provide example of implementing StructurePath 2015-05-11 19:42:49 -04:00
Nathan Marz
a5e4244fc0 more examples in README 2015-05-11 19:36:52 -04:00
Nathan Marz
1f504e95b4 0.5.0 2015-05-11 15:19:47 -04:00
Nathan Marz
1a66dc511f update README to include information on high performance 2015-05-11 15:19:20 -04:00
Nathan Marz
6e440d235c notes 2015-05-11 15:08:51 -04:00
Nathan Marz
085dc1d333 optimize lookup of structurepath implementation for regular functions (about a 3x speedup in non-compiled execution when functions are involved 2015-05-11 15:00:21 -04:00
Nathan Marz
7f69a99861 speed up non-compiled execution path to where it was before 2015-05-11 14:21:02 -04:00
Nathan Marz
cd994b10ee improve execution of kws 2015-05-11 14:02:08 -04:00
Nathan Marz
e0bde53fef formatting 2015-05-11 13:31:12 -04:00
Nathan Marz
060e61218a optimize sequence of structurepaths to not add additional overhead by coercing to structurevalspath 2015-05-11 11:44:42 -04:00
Nathan Marz
0767afca61 prepare for optimized execution of pure structurepaths 2015-05-11 10:35:30 -04:00
Nathan Marz
a22df528f0 update readme 2015-05-11 10:35:10 -04:00
Nathan Marz
def5656e40 re-add ability to compose via vectors with a more efficient implementation 2015-05-10 20:00:13 -04:00
Nathan Marz
c47650993d improve performance of non-compiled code path by 3x, remove ability for a vector to be considered a structurepath 2015-05-10 19:56:30 -04:00
Nathan Marz
4e268629c3 complete no protocol execution, speed is generally now just as good or far better than equivalent clojure code 2015-05-10 18:47:57 -04:00
Nathan Marz
e052ee06a8 removed plugin 2015-05-10 18:39:09 -04:00
Nathan Marz
b20d035950 add update-fast 2015-05-10 08:09:48 -04:00
Nathan Marz
47aee4d000 broken implementation of skipping protocols 2015-05-10 02:12:06 -04:00
Nathan Marz
277735dd9b formatting 2015-04-27 14:49:39 -04:00
Nathan Marz
98295df9bc clarify example in README 2015-04-22 11:46:26 -04:00
Nathan Marz
5b62817bd5 add docstring to main api 2015-04-22 11:46:13 -04:00
Nathan Marz
ec097f8db2 make filterer eager on select 2015-04-22 11:45:35 -04:00
33 changed files with 6690 additions and 801 deletions

37
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,37 @@
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch_depth: 0
- name: Setup Babashka
uses: turtlequeue/setup-babashka@v1.3.0
with:
babashka-version: 0.7.8
- name: Prepare java
uses: actions/setup-java@v2
with:
distribution: 'zulu'
java-version: '8'
- name: Install clojure tools
uses: DeLaGuardo/setup-clojure@4.0
with:
lein: 2.9.1
- name: Run clj tests
run: bb test:clj
- name: Run cljs tests
run: bb test:cljs
- name: Run babashka tests
run: bb test:bb

4
.gitignore vendored
View file

@ -9,3 +9,7 @@ pom.xml.asc
.lein-repl-history
.lein-plugins/
.lein-failures
.cljs_node_repl
out/
.cpcache
.cache

312
CHANGES.md Normal file
View file

@ -0,0 +1,312 @@
## 1.1.4
* Add arglist metadata to navs (thanks @phronmophobic)
* Improve before-index performance by 150x on lists and 5x on vectors (thanks @jeff303)
* Bug fix: BEFORE-ELEM, AFTER-ELEM, FIRST, LAST, BEGINNING, and END on subvecs now produce vector type in cljs
* Add compatibility with [babashka](https://babashka.org/)
## 1.1.3 - 2019-10-13
* Better AOT behavior: path functions for inline caching and protpath extensions no longer write eval'd class files. You can force path functions to not override `*compile-files*` by binding `com.rpl.specter.impl/*path-compile-files*` to `true`.
* Bug fix: fix throw-illegal in cljs
## 1.1.2 - 2018-11-01
* Eliminate reflection warning
* Bug fix: Fix inline compiler symbol handling so class references can be used as constants within paths
* Bug fix: Fix handling of subvector paths in cljs
## 1.1.1 - 2018-04-23
* ClojureScript 1.10 introduced a change causing the `walker` navigator to fail to walk records. `ALL` has been updated to operate over `MapEntry` in ClojureScript, fixing the issue.
* Change ns form to comply with cljs.core.specs.alpha (thanks @gnl)
* Remove usage of pprint in cljs so advanced optimization doesn't include it
## 1.1.0 - 2018-01-02
* Add `vtransform` variant of `transform` that takes in collected values as a vector in the first argument rather than spliced into argument list.
* Add `vterminal` that takes in collected vals as vector in first argument rather than spliced into argument list.
* Add `compact` navigator. After each step of navigation of its subpath, `compact` removes the collection if it's empty.
* Change `terminal` to be a no-op on select codepath
* Bug fix: `subselect`/`filterer` removes first matched element instead of setting to nil when transformed to empty sequence
## 1.0.5 - 2017-11-16
* Add `regex-nav` navigator for regexes, which navigates to every match in a string and supports replacement with a new substring (thanks @mwfogleman)
* Regexes implicitly convert to `regex-nav` in paths
* Strings, numbers, booleans, characters, and symbols implicitly convert to `keypath` in paths
## 1.0.4 - 2017-10-17
* Add `indexed-vals` navigator, a variant of `INDEXED-VALS` that allows for a customized start index.
* Bug fix: Fix `INDEXED-VALS` invalidly overwriting elements in some transforms involving multiple index changes
## 1.0.3 - 2017-08-14
* Added `before-index` navigator for inserting a single element into a sequence.
* Added `index-nav` navigator for moving an element in a sequence to a new index, shifting other elements in the process.
* Added `INDEXED-VALS` navigator for navigating to every element of a sequence as [index elem] pair. Transform on index portion works the same as `index-nav`.
* Workaround for ClojureScript regression that causes warnings for record fields named "var" or other reserved names
## 1.0.2 - 2017-06-12
* Added `pred=`, `pred<`, `pred>`, `pred<=`, `pred>=` for filtering using common comparisons
* Add `map-key` navigator
* Add `set-elem` navigator
* Add `ALL-WITH-META` navigator
* `walker` and `codewalker` can now be used with `NONE` to remove elements
* Improve `walker` performance by 70% by replacing clojure.walk implementation with custom recursive path
* Extend `ALL` to work on records (navigate to key/value pairs)
* Add ability to declare a function for end index of `srange-dynamic` that takes in the result of the start index fn. Use `end-fn` macro to declare this function (takes in 2 args of [collection, start-index]). Functions defined with normal mechanisms (e.g. `fn`) will still only take in the collection as an argument.
* Workaround for ClojureScript bug that emits warnings for vars named the same as a private var in cljs.core (in this case `NONE`, added as private var to cljs.core with 1.9.562)
* For ALL transforms on maps, interpret transformed key/value pair of size < 2 as removal
* Bug fix: Fix incorrect inline compilation when a dynamic function invocation is nested in a data structure within a parameter to a navigator builder
## 1.0.1 - 2017-04-17
* `subselect`/`filterer` can remove entries in source by transforming to a smaller sequence
* Add `satisfies-protpath?`
* Inline cache vars are marked private so as not to interfere with tooling
* Improve performance of `ALL` transform on lists by 20%
* Bug fix: Using `pred` no longer inserts unnecessary `coerce-nav` call at callsite
* Bug fix: Dynamic navs in argument position to another nav now properly expanded and compiled
* Bug fix: Dynamic parameters nested inside data structures as arguments are now compiled correctly by inline compiler
## 1.0.0 - 2017-03-01
* Transform to `com.rpl.specter/NONE` to remove elements from data structures. Works with `keypath` (for both sequences and maps), `must`, `nthpath`, `ALL`, `MAP-VALS`, `FIRST`, and `LAST`
* Add `nthpath` navigator
* Add `with-fresh-collected` higher order navigator
* Added `traverse-all` which returns a transducer that traverses over all elements matching the given path.
* `select-first` and `select-any` now avoid traversal beyond the first value matched by the path (like when using `ALL`), so they are faster now for those use cases.
* Add `MAP-KEYS` navigator that's more efficient than `[ALL FIRST]`
* Add `NAME` and `NAMESPACE` navigators
* Extend `srange`, `BEGINNING`, `END` to work on strings. Navigates to a substring.
* Extend `FIRST` and `LAST` to work on strings. Navigates to a character.
* Add `BEFORE-ELEM` and `AFTER-ELEM` for prepending or appending a single element to a sequence
* Add `NONE-ELEM` to efficiently add a single element to a set
* Improved `ALL` performance for PersistentHashSet
* Dynamic navs automatically compile sequence returns if completely static
* Eliminate reflection warnings for clj (thanks @mpenet)
* Bug fix: Collected vals now properly passed to subpaths for `if-path`, `selected?`, and `not-selected?`
* Bug fix: `LAST`, `FIRST`, `BEGINNING`, and `END` properly transform subvector types to a vector type
## 0.13.2 - 2016-12-21
* Bug fix: Fix race condition relating to retrieving path from cache and AOT compilation
* Bug fix: LAST no longer converts lists to vectors
* Bug fix: Workaround issue with aot + uberjar
## 0.13.1 - 2016-11-07
* 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
* Added `traversed`
* 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
## 0.13.0 - 2016-09-06
* BREAKING CHANGE: `com.rpl.specter.macros` namespace removed and all macros moved into core `com.rpl.specter` namespace
* BREAKING CHANGE: Core protocol `Navigator` changed to `RichNavigator` and functions now have an extra argument.
* BREAKING CHANGE: All navigators must be defined with `defnav` and its variations from `com.rpl.specter`. The core protocols may no longer be extended. Existing types can be turned into navigators with the new `IndirectNav` protocol.
* BREAKING CHANGE: Removed `fixed-pathed-nav` and `variable-pathed-nav` and replaced with much more generic `late-bound-nav`. `late-bound-nav` can have normal values be late-bound parameterized (not just paths). Use `late-path` function to indicate which parameters are paths. If all bindings given to `late-bound-nav` are static, the navigator will be resolved and cached immediately. See `transformed` and `selected?` for examples.
* BREAKING CHANGE: Paths can no longer be compiled without their parameters. Instead, use the `path` macro to handle the parameterization.
* BREAKING CHANGE: Parameterized protocol paths now work differently since paths cannot be specified without their parameters. Instead, use the parameter names from the declaration in the extension to specify where the parameters should go. For example:
```clojure
(defprotocolpath MyProtPath [a])
(extend-protocolpath MyProtPath
clojure.lang.PersistentArrayMap
(must a))
```
* BREAKING CHANGE: Removed `defpathedfn` and replaced with much more generic `defdynamicnav`. `defdynamicnav` works similar to a macro and takes as input the parameters seen during inline caching. Use `dynamic-param?` to distinguish which parameters are statically specified and which are dynamic. `defdynamicnav` is typically used in conjunction with `late-bound-nav`  see implementation of `selected?` for an example.
* Inline caching now works with locals, dynamic vars, and special forms used in the nav position. When resolved at runtime, those values will be coerced to a navigator if a vector or implicit nav (e.g. keyword). Can hint with ^:direct-nav metadata to remove this coercion if know for sure those values will be implementations of `RichNavigator` interface.
* Redesigned internals so navigators use interface dispatch rather than storing transform/selection functions as separate fields.
* Added `local-declarepath` to assist in making local recursive or mutually recursive paths. Use with `providepath`.
* Added `recursive-path` to assist in making recursive paths, both parameterized and unparameterized. Example:
```clojure
(let [tree-walker (recursive-path [] p (if-path vector? [ALL p] STAY))]
(select tree-walker [1 [2 [3 4] 5] [[6]]]))
```
* Significantly improved performance of paths that use dynamic parameters.
* Inline factoring now parameterizes navigators immediately when all parameters are constants (rather than factoring it to use late-bound parameterization). This creates leaner, faster code.
* Added `IndirectNav` protocol for turning a value type into a navigator.
* Removed `must-cache-paths!`. No longer relevant since all paths can now be compiled and cached.
* Added `with-inline-debug` macro that prints information about the code being analyzed and produced by the inline compiler / cacher.
* Switched codebase from cljx to cljc
* Improved performance of ALL and MAP-VALS on PersistentArrayMap by about 2x
* `defnav` now generates helper functions for every method. For example, `keypath` now has helpers `keypath-select*` and `keypath-transform*`. These functions take parameters `[key structure next-fn]`
* Bug fix: ALL and MAP-VALS transforms on PersistentArrayMap above the threshold now output PersistentArrayMap instead of PersistentHashMap
## 0.12.0 - 2016-08-05
* BREAKING CHANGE: Changed semantics of `Navigator` protocol `select*` in order to enable very large performance improvements to `select`, `select-one`, `select-first`, and `select-one!`. Custom navigators will need to be updated to conform to the new required semantics. Codebases that do not use custom navigators do not require any changes. See the docstring on the protocol for the details.
* Added `select-any` operation which selects a single element navigated to by the path. Which element returned is undefined. If no elements are navigated to, returns `com.rpl.specter/NONE`. This is the fastest selection operation.
* Added `selected-any?` operation that returns true if any element is navigated to.
* Added `traverse` operation which returns a reducible object of all the elements navigated to by the path. Very efficient.
* Added `multi-transform` operation which can be used to perform multiple transformations in a single traversal. Much more efficient than doing the
transformations with `transform` one after another when the transformations share a lot of navigation. `multi-transform` is used in conjunction with `terminal` and `terminal-val` see the docstring for details.
* Huge performance improvements to `select`, `select-one`, `select-first`, and `select-one!`
* Huge performance improvement to `multi-path`
* Added META navigator (thanks @aengelberg)
* 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.
* Error now thrown if a pathedfn (like filterer) is used without being parameterized
* Performance improvement for ALL and MAP-VALS on small maps for Clojure by leveraging IMapIterable interface
* Added low-level `richnav` macro for creating navigators with full flexibility
* Bug fix: multi-path and if-path now work properly with value collection
* Bug fix: END, BEGINNING, FIRST, LAST, and MAP-VALS now work properly on nil
* Bug fix: ALL and MAP-VALS now maintain the comparator of sorted maps
* Bug fix: Using value collection along with `setval` no longer throws exception
* Bug fix: Fix error when trying to use Specter along with AOT compilation
## 0.11.2 - 2016-06-09
* Renamed com.rpl.specter.transient namespace to com.rpl.specter.transients to eliminate ClojureScript compiler warning about reserved keyword
* Eliminated compiler warnings for ClojureScript version
## 0.11.1 - 2016-06-08
* More efficient inline caching for Clojure version, benchmarks show inline caching within 5% of manually precompiled code for all cases
* Added navigators for transients in com.rpl.specter.transient namespace (thanks @aengelberg)
* Huge performance improvement for ALL transform on maps and vectors
* Significant performance improvements for FIRST/LAST for vectors
* Huge performance improvements for `if-path`, `cond-path`, `selected?`, and `not-selected?`, especially for condition path containing only static functions
* Huge performance improvement for `END` on vectors
* Added specialized MAP-VALS navigator that is twice as fast as using [ALL LAST]
* Dropped support for Clojurescript below v1.7.10
* Added :notpath metadata to signify pathedfn arguments that should be treated as regular arguments during inline factoring. If one of these arguments is not a static var reference or non-collection value, the path will not factor.
* Bug fix: `transformed` transform-fn no longer factors into `pred` when an anonymous function during inline factoring
* Bug fix: Fixed nil->val to not replace the val on `false`
* Bug fix: Eliminate reflection when using primitive parameters in an inline cached path
## 0.11.0 - 2016-05-31
* New `path` macro does intelligent inline caching of the provided path. The path is factored into a static portion and into params which may change on each usage of the path (e.g. local parameters). The static part is factored and compiled on the first run-through, and then re-used for all subsequent invocations. As an example, `[ALL (keypath k)]` is factored into `[ALL keypath]`, which is compiled and cached, and `[k]`, which is provided on each execution. If it is not possible to precompile the path (e.g. [ALL some-local-variable]), nothing is cached and the path will be compiled on each run-through.
* BREAKING CHANGE: all `select/transform/setval/replace-in` functions changed to macros and moved to com.rpl.specter.macros namespace. The new macros now automatically wrap the provided path in `path` to enable inline caching. Expect up to a 100x performance improvement without using explicit precompilation, and to be within 2% to 15% of the performance of explicitly precompiled usage.
* Added `select*/transform*/setval*/replace-in*/etc.` functions that have the same functionality as the old `select/transform/setval/replace-in` functions.
* Added `must-cache-paths!` function to throw an error if it is not possible to factor a path into a static portion and dynamic parameters.
* BREAKING CHANGE: `defpath` renamed to `defnav`
* BREAKING CHANGE: `path` renamed to `nav`
* BREAKING CHANGE: `fixed-pathed-path` and `variable-pathed-path` renamed to `fixed-pathed-nav` and `variabled-pathed-nav`
* Added `must` navigator to navigate to a key if and only if it exists in the structure
* Added `continuous-subseqs` navigator
* Added `ATOM` navigator (thanks @rakeshp)
* Added "navigator constructors" that can be defined via `defnavconstructor`. These allow defining a flexible function to parameterize a defnav, and the function integrates with inline caching for high performance.
## 0.10.0 - 2016-04-26
* Make codebase bootstrap cljs compatible
* Remove usage of reducers in cljs version in favor of transducers (thanks @StephenRudolph)
* ALL now maintains type of queues (thanks @StephenRudolph)
* Added `parser` path (thanks @thomasathorne)
* Added `submap` path (thanks @bfabry)
* Added `subselect` path (thanks @aengelberg)
* Fix filterer to maintain the type of the input sequence in transforms
* Integrated zipper navigation into com.rpl.specter.zipper namespace
## 0.9.3 - 2016-04-15
* Change clojure/clojurescript to provided dependencies
* ALL on maps auto-coerces MapEntry to vector, enabling smoother transformation of map keys
* declarepath can now be parameterized
* Added params-reset which calls its path with the params index walked back by the number of params needed by its path. This enables recursive parameterized paths
* Added convenience syntax for defprotocolpath with no params, e.g. (defprotocolpath foo)
* Rename VOID to STOP
## 0.9.2 - 2016-01-26
* Added VOID selector which navigates nowhere
* Better syntax checking for defpath
* Fixed bug in protocol paths (#48)
* Protocol paths now error when extension has invalid number of needed parameters
* Fix replace-in to work with value collection
* Added STAY selector
* Added stay-then-continue and continue-then-stay selectors which enable pre-order/post-order traversals
* Added declarepath and providepath, which enable arbitrary recursive or mutually recursive paths
* Renamed paramspath to path
## 0.9.1 - 2016-01-05
* Fixed reflection in protocol path code
* Optimized late-bound parameterization for JVM implementation by directly creating the object array rather than use object-array
* Incorrectly specified function names in defpath will now throw error
## 0.9.0 - 2015-12-12
* Fixed bug where comp-paths wouldn't work on lazy seqs in cljs
* Renamed defparamspath and defparamscollector to defpath and defcollector
* For Clojure version only, implemented protocol paths (see #38)
## 0.8.0 - 2015-10-10
* Now compatible with Clojure 1.6.0 and 1.5.1 by switching build to cljx (thanks @MerelyAPseudonym)
* Added subset selector (like srange but for sets)
* Added nil->val, NIL->SET, NIL->LIST, and NIL->VECTOR selectors to make it easier to manipulate maps (e.g. (setval [:akey NIL->VECTOR END] [:a :b] amap) to append that vector into that value for the map, even if nothing was at that value at the start)
## 0.7.1 - 2015-09-24
* view can now be late-bound parameterized
* Added a late-bound parameterized version of using a function as a selector called "pred"
* Added paramsfn helper macro for defining filter functions that take late-bound parameters
* walker and codewalker can now be late-bound parameterized
## 0.7.0 - 2015-09-11
* Added late-bound parameterization feature: allows selectors that require params to be precompiled without the parameters, and the parameters are supplied later in bulk. This effectively enables Specter to be used in any situation with very high performance.
* Converted Specter built-in selectors to use late-bound parameterization when appropriate
* ALL, FIRST, and LAST are now precompiled
## 0.6.2 - 2015-07-03
* Added not-selected? selector
* Added transformed selector
* Sped up CLJS implementation for comp-paths by replacing obj-extends? call with satisfies?
* Fixed CLJS implementation to extend core types appropriately
* Used not-native hint to enable direct method invocation to speed up CLJS implementation
## 0.6.1 - 2015-07-01
* Huge speedup to ClojureScript implementation by optimizing field access
## 0.6.0 - 2015-07-01
* Added ClojureScript compatibility
## 0.5.7 - 2015-06-30
* Fix bug in select-one! which wouldn't allow nil result
## 0.5.6 - 2015-06-29
* Add multi-path implementation
* change FIRST/LAST to select nothing on an empty sequence
* Allow sets to be used directly as selectors (acts as filter)
## 0.5.5 - 2015-06-22
* Change filterer to accept a selector (that acts like selected? to determine whether or not to select value)
## 0.5.4 - 2015-06-19
* Change cond-path and if-path to take in a selector for conditionals (same idea as selected?)
## 0.5.3 - 2015-06-18
* Added cond-path and if-path selectors for choosing paths depending on value of structure at that location
## 0.5.2 - 2015-06-01
* Fix error for selectors with one element defined using comp-paths, e.g. [:a (comp-paths :b)]
## 0.5.1 - 2015-05-31
* Added putval for adding external values to collected values list
* nil is now interpreted as identity selector
* empty selector is now interpreted as identity selector instead of producing error

9
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,9 @@
# Contributing
We welcome external contributions to this project. For ideas for new functionality, we recommend first opening an issue so we can discuss whether it makes sense for the project.
## Contribution process
1. Open a pull request. If possible, please include thorough tests of the new code.
2. If you have not already signed a contributor agreement, we will request that you sign one. We use Adobe Sign for this so it's very quick and easy. Note that we cannot look at your pull request until a contributor agreement is signed.
3. We will then review your pull request and possibly ask for changes.

25
DEVELOPER.md Normal file
View file

@ -0,0 +1,25 @@
# Running Clojure tests
```
lein do clean, test
```
# Running ClojureScript tests
```
lein javac
lein doo node test-build once
```
# Running benchmarks
## All benchmarks
```
scripts/run-benchmarks
```
## Individual benchmark(s)
Specify the benchmark names as command line args. They will likely each need quoted because they contain spaces.
Order is ignored.
```
scripts/run-benchmarks "prepend to a vector" "filter a sequence"
```

351
README.md
View file

@ -1,9 +1,76 @@
# Specter
Deep introspection and transformation of nested data
# About
Specter rejects Clojure's restrictive approach to immutable data structure manipulation, instead exposing an elegant API to allow any sort of manipulation imaginable. Specter especially excels at querying and transforming nested and recursive data, important use cases that are very complex to handle with vanilla Clojure.
Specter has an extremely simple core, just a single abstraction called "navigator". Queries and transforms are done by composing navigators into a "path" precisely targeting what you want to retrieve or change. Navigators can be composed with any other navigators, allowing sophisticated manipulations to be expressed very concisely.
In addition, Specter has performance rivaling hand-optimized code (see [this benchmark](https://gist.github.com/nathanmarz/b7c612b417647db80b9eaab618ff8d83)). Clojure's only comparable built-in operations are `get-in` and `update-in`, and the Specter equivalents are 30% and 85% faster respectively (while being just as concise). Under the hood, Specter uses [advanced dynamic techniques](https://github.com/redplanetlabs/specter/wiki/Specter's-inline-caching-implementation) to strip away the overhead of composition.
There are some key differences between the Clojure approach to data manipulation and the Specter approach. Unlike Clojure, Specter always uses the most efficient method possible to implement an operation for a datatype (e.g. `last` vs. `LAST`). Clojure intentionally leaves out many operations, such as prepending to a vector or inserting into the middle of a sequence. Specter has navigators that cover these use cases (`BEFORE-ELEM` and `before-index`) and many more. Finally, Specter transforms always target precise parts of a data structure, leaving everything else the same. For instance, `ALL` targets every value within a sequence, and the resulting transform is always the same type as the input (e.g. a vector stays a vector, a sorted map stays a sorted map).
Consider these examples:
**Example 1: Increment every even number nested within map of vector of maps**
```clojure
(def data {:a [{:aa 1 :bb 2}
{:cc 3}]
:b [{:dd 4}]})
;; Manual Clojure
(defn map-vals [m afn]
(->> m (map (fn [[k v]] [k (afn v)])) (into (empty m))))
(map-vals data
(fn [v]
(mapv
(fn [m]
(map-vals
m
(fn [v] (if (even? v) (inc v) v))))
v)))
;; Specter
(transform [MAP-VALS ALL MAP-VALS even?] inc data)
```
**Example 2: Append a sequence of elements to a nested vector**
```clojure
(def data {:a [1 2 3]})
;; Manual Clojure
(update data :a (fn [v] (into (if v v []) [4 5])))
;; Specter
(setval [:a END] [4 5] data)
```
**Example 3: Increment the last odd number in a sequence**
```clojure
(def data [1 2 3 4 5 6 7 8])
;; Manual Clojure
(let [idx (reduce-kv (fn [res i v] (if (odd? v) i res)) nil data)]
(if idx (update data idx inc) data))
;; Specter
(transform [(filterer odd?) LAST] inc data)
```
**Example 4: Map a function over a sequence without changing the type or order of the sequence**
```clojure
;; Manual Clojure
(map inc data) ;; doesn't work, becomes a lazy sequence
(into (empty data) (map inc data)) ;; doesn't work, reverses the order of lists
;; Specter
(transform ALL inc data) ;; works for all Clojure datatypes with near-optimal efficiency
```
Specter is a library for concisely querying and updating nested data structures. One way to think of it is "get-in" and "assoc-in" on steroids, though Specter works on any data structure, not just maps. It is similar to the concept of a "lens" in functional programming, though it has some important extensions.
# Latest Version
@ -11,70 +78,111 @@ The latest release version of Specter is hosted on [Clojars](https://clojars.org
[![Current Version](https://clojars.org/com.rpl/specter/latest-version.svg)](https://clojars.org/com.rpl/specter)
# How to use
# Learn Specter
The usage of Specter will be explained via example. Suppose you have a sequence of maps, and you want to extract all the even values for :a keys. Here's how you do it:
- Introductory blog post: [Clojure's missing piece](http://nathanmarz.com/blog/clojures-missing-piece.html)
- Presentation about Specter: [Specter: Powerful and Simple Data Structure Manipulation](https://www.youtube.com/watch?v=VTCy_DkAJGk)
- Note that this presentation was given before Specter's inline compilation/caching system was developed. You no longer need to do anything special to get near-optimal performance.
- Screencast on Specter: [Understanding Specter](https://www.youtube.com/watch?v=rh5J4vacG98)
- List of navigators with examples: [This wiki page](https://github.com/redplanetlabs/specter/wiki/List-of-Navigators) provides a more comprehensive overview than the API docs about the behavior of specific navigators and includes many examples.
- Core operations and defining new navigators: [This wiki page](https://github.com/redplanetlabs/specter/wiki/List-of-Macros) provides a more comprehensive overview than the API docs of the core select/transform/etc. operations and the operations for defining new navigators.
- [This wiki page](https://github.com/redplanetlabs/specter/wiki/Using-Specter-Recursively) explains how to do precise and efficient recursive navigation with Specter.
- [This wiki page](https://github.com/redplanetlabs/specter/wiki/Using-Specter-With-Zippers) provides a comprehensive overview of how to use Specter's zipper navigators. Zippers are a much slower navigation method but can perform certain tasks that are not possible with Specter's regular navigators. Note that zippers are rarely needed.
- [Cheat Sheet](https://github.com/redplanetlabs/specter/wiki/Cheat-Sheet)
- [API docs](http://redplanetlabs.github.io/specter/)
- Performance guide: [This post](https://github.com/redplanetlabs/specter/wiki/Specter's-inline-caching-implementation) provides an overview of how Specter achieves its performance.
Specter's API is contained in these files:
- [specter.cljc](https://github.com/redplanetlabs/specter/blob/master/src/clj/com/rpl/specter.cljc): This contains the built-in navigators and the definition of the core operations.
- [transients.cljc](https://github.com/redplanetlabs/specter/blob/master/src/clj/com/rpl/specter/transients.cljc): This contains navigators for transient collections.
- [zipper.cljc](https://github.com/redplanetlabs/specter/blob/master/src/clj/com/rpl/specter/zipper.cljc): This integrates zipper-based navigation into Specter.
# Questions?
You can ask questions about Specter by [opening an issue](https://github.com/redplanetlabs/specter/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3Aquestion+) on Github.
You can also find help in the #specter channel on [Clojurians](http://clojurians.net/).
# Examples
Increment all the values in maps of maps:
```clojure
user> (use 'com.rpl.specter)
nil
user> (select [ALL :a even?]
[{:a 1} {:a 2} {:a 4} {:a 3}])
[2 4]
user> (transform [MAP-VALS MAP-VALS]
inc
{:a {:aa 1} :b {:ba -1 :bb 2}})
{:a {:aa 2}, :b {:ba 0, :bb 3}}
```
`select` extracts a sequence of results from a data structure. It takes in a "selector", which is a sequence of steps on how to navigate into that data structure. In this case, `ALL` looks at every element in the sequence, `:a` looks at the :a key for each element currently navigated to, and `even?` filters out any elements that aren't an even value.
Another function called `update` is used to perform a transformation on a data structure. In addition to a selector, it takes in an "update function" which specifies what to do with each element navigated to. For example, here's how to increment all the even values for :a keys in a sequence of maps:
Increment all the even values for :a keys in a sequence of maps:
```clojure
user> (update [ALL :a even?]
user> (transform [ALL :a even?]
inc
[{:a 1} {:a 2} {:a 4} {:a 3}])
[{:a 1} {:a 3} {:a 5} {:a 3}]
```
Specter comes with all sorts of built-in ways of navigating data structures. For example, here's how to increment the last odd number in a sequence:
Retrieve every number divisible by 3 out of a sequence of sequences:
```clojure
user> (select [ALL ALL #(= 0 (mod % 3))]
[[1 2 3 4] [] [5 3 2 18] [2 4 6] [12]])
[3 3 18 6 12]
```
Increment the last odd number in a sequence:
```clojure
user> (update [(filterer odd?) LAST]
user> (transform [(filterer odd?) LAST]
inc
[2 1 3 6 9 4 8])
[2 1 3 6 10 4 8]
```
`filterer` navigates you to a view of the sequence currently being looked at. `LAST` navigates you to the last element of whatever sequence you're looking at. But of course during updates, the updates are performed on the original data structure.
`srange` is a selector for looking at or replacing a subsequence of a sequence. For example, here's how to increment all the odd numbers between indexes 1 and 4:
Remove nils from a nested sequence:
```clojure
user> (update [(srange 1 4) ALL odd?] inc [0 1 2 3 4 5 6 7])
user> (setval [:a ALL nil?] NONE {:a [1 2 nil 3 nil]})
{:a [1 2 3]}
```
Remove key/value pair from nested map:
```clojure
user> (setval [:a :b :c] NONE {:a {:b {:c 1}}})
{:a {:b {}}}
```
Remove key/value pair from nested map, removing maps that become empty along the way:
```clojure
user> (setval [:a (compact :b :c)] NONE {:a {:b {:c 1}}})
{}
```
Increment all the odd numbers between indices 1 (inclusive) and 4 (exclusive):
```clojure
user> (transform [(srange 1 4) ALL odd?] inc [0 1 2 3 4 5 6 7])
[0 2 2 4 4 5 6 7]
```
`srange` can also be used to replace that subsequence entirely with a new sequence. For example, here's how to replace the subsequence from index 2 to 4 with [-1 -1 -1]:
Replace the subsequence from indices 2 to 4 with [:a :b :c :d :e]:
```clojure
user> (update (srange 2 4) (fn [_] [-1 -1 -1]) [0 1 2 3 4 5 6 7 8 9])
[0 1 -1 -1 -1 4 5 6 7 8 9]
user> (setval (srange 2 4) [:a :b :c :d :e] [0 1 2 3 4 5 6 7 8 9])
[0 1 :a :b :c :d :e 4 5 6 7 8 9]
```
The above can be written more concisely using the `setval` function, which is a wrapper around `update`:
```clojure
user> (setval (srange 2 4) [-1 -1 -1] [0 1 2 3 4 5 6 7 8 9])
[0 1 -1 -1 -1 4 5 6 7 8 9]
```
Here's how to concatenate the sequence [:a :b] to every nested sequence of a sequence:
Concatenate the sequence [:a :b] to every nested sequence of a sequence:
```clojure
user> (setval [ALL END] [:a :b] [[1] '(1 2) [:c]])
[[1 :a :b] (1 2 :a :b) [:c :a :b]]
```
`END` is a wrapper around `srange-dynamic`, which takes in functions that return the start index and end index given the structure.
`walker` is another useful selector that walks the data structure until a predicate is matched. Here's how to get all the numbers out of a map:
Get all the numbers out of a data structure, no matter how they're nested:
```clojure
user> (select (walker number?)
@ -82,71 +190,154 @@ user> (select (walker number?)
[2 1 2 1 2 6 7 4]
```
When doing more involved transformations, you often find you lose context when navigating deep within a data structure and need information "up" the data structure to perform the transformation. Specter solves this problem by allowing you to collect values during navigation to use in the update function. Here's an example which transforms a sequence of maps by adding the value of the :b key to the value of the :a key, but only if the :a key is even:
Navigate with string keys:
```clojure
user> (update [ALL (collect-one :b) :a even?]
+
[{:a 1 :b 3} {:a 2 :b -10} {:a 4 :b 10} {:a 3}])
[{:b 3, :a 1} {:b -10, :a -8} {:b 10, :a 14} {:a 3}]
user> (select ["a" "b"]
{"a" {"b" 10}})
[10]
```
The update function receives as arguments all the collected values followed by the navigated to value. So in this case `+` receives the value of the :b key followed by the value of the :a key, and the update is performed to :a's value.
The three built-in ways for collecting values are `VAL`, `collect`, and `collect-one`. `VAL` just adds whatever element it's currently on to the value list, while `collect` and `collect-one` take in a selector to navigate to the desired value. `collect` works just like `select` by finding a sequence of values, while `collect-one` expects to only navigate to a single value.
To make your own selector, implement the `StructurePath` protocol which looks like:
Reverse the positions of all even numbers between indices 4 and 11:
```clojure
(defprotocol StructurePath
(select* [this structure next-fn])
(update* [this structure next-fn])
)
```
Looking at the implementations of the built-in selectors should provide you with the guidance you need to make your own selectors.
Finally, you can make `select` and `update` work much faster by precompiling your selectors using the `comp-paths` function. There's about a 5x speed difference between the following two invocations of update:
```clojure
(def precompiled (comp-paths ALL :a even?))
(update [ALL :a even?] structure)
(update precompiled structure)
```
Some more examples:
Decrement every value in a map:
```clojure
user> (update [ALL LAST]
dec
{:a 1 :b 3})
{:b 2 :a 0}
```
Get every number divisible by 3 out of a sequence of sequences:
```clojure
user> (select [ALL ALL #(= 0 (mod % 3))]
[[1 2 3 4] [] [5 3 2 18] [2 4 6] [12]])
[3 3 18 6 12]
user> (transform [(srange 4 11) (filterer even?)]
reverse
[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15])
[0 1 2 3 10 5 8 7 6 9 4 11 12 13 14 15]
```
Append [:c :d] to every subsequence that has at least two even numbers:
```clojure
user> (setval [ALL
(selected? (filterer even?) (view count) #(>= % 2))
(selected? (filterer even?) (view count) (pred>= 2))
END]
[:c :d]
[[1 2 3 4 5 6] [7 0 -1] [8 8] []])
[[1 2 3 4 5 6 :c :d] [7 0 -1] [8 8 :c :d] []]
```
When doing more involved transformations, you often find you lose context when navigating deep within a data structure and need information "up" the data structure to perform the transformation. Specter solves this problem by allowing you to collect values during navigation to use in the transform function. Here's an example which transforms a sequence of maps by adding the value of the :b key to the value of the :a key, but only if the :a key is even:
```clojure
user> (transform [ALL (collect-one :b) :a even?]
+
[{:a 1 :b 3} {:a 2 :b -10} {:a 4 :b 10} {:a 3}])
[{:b 3, :a 1} {:b -10, :a -8} {:b 10, :a 14} {:a 3}]
```
The transform function receives as arguments all the collected values followed by the navigated to value. So in this case `+` receives the value of the :b key followed by the value of the :a key, and the transform is performed to :a's value.
The four built-in ways for collecting values are `VAL`, `collect`, `collect-one`, and `putval`. `VAL` just adds whatever element it's currently on to the value list, while `collect` and `collect-one` take in a selector to navigate to the desired value. `collect` works just like `select` by finding a sequence of values, while `collect-one` expects to only navigate to a single value. Finally, `putval` adds an external value into the collected values list.
Increment the value for :a key by 10:
```clojure
user> (transform [:a (putval 10)]
+
{:a 1 :b 3})
{:b 3 :a 11}
```
For every map in a sequence, increment every number in :c's value if :a is even or increment :d if :a is odd:
```clojure
user> (transform [ALL (if-path [:a even?] [:c ALL] :d)]
inc
[{:a 2 :c [1 2] :d 4} {:a 4 :c [0 10 -1]} {:a -1 :c [1 1 1] :d 1}])
[{:c [2 3], :d 4, :a 2} {:c [1 11 0], :a 4} {:c [1 1 1], :d 2, :a -1}]
```
"Protocol paths" can be used to navigate on polymorphic data. For example, if you have two ways of storing "account" information:
```clojure
(defrecord Account [funds])
(defrecord User [account])
(defrecord Family [accounts-list])
```
You can make an "AccountPath" that dynamically chooses its path based on the type of element it is currently navigated to:
```clojure
(defprotocolpath AccountPath [])
(extend-protocolpath AccountPath
User :account
Family [:accounts-list ALL])
```
Then, here is how to select all the funds out of a list of `User` and `Family`:
```clojure
user> (select [ALL AccountPath :funds]
[(->User (->Account 50))
(->User (->Account 51))
(->Family [(->Account 1)
(->Account 2)])
])
[50 51 1 2]
```
The next examples demonstrate recursive navigation. Here's one way to double all the even numbers in a tree:
```clojure
(defprotocolpath TreeWalker [])
(extend-protocolpath TreeWalker
Object nil
clojure.lang.PersistentVector [ALL TreeWalker])
(transform [TreeWalker number? even?] #(* 2 %) [:a 1 [2 [[[3]]] :e] [4 5 [6 7]]])
;; => [:a 1 [4 [[[3]]] :e] [8 5 [12 7]]]
```
Here's how to reverse the positions of all even numbers in a tree (with order based on a depth first search). This example uses conditional navigation instead of protocol paths to do the walk:
```clojure
(def TreeValues
(recursive-path [] p
(if-path vector?
[ALL p]
STAY
)))
(transform (subselect TreeValues even?)
reverse
[1 2 [3 [[4]] 5] [6 [7 8] 9 [[10]]]]
)
;; => [1 10 [3 [[8]] 5] [6 [7 4] 9 [[2]]]]
```
# ClojureScript
Specter supports ClojureScript! However, some of the differences between Clojure and ClojureScript affect how you use Specter in ClojureScript, in particular with the namespace declarations. In Clojure, you might `(use 'com.rpl.specter)` or say `(:require [com.rpl.specter :refer :all])` in your namespace declaration. But in ClojureScript, these options [aren't allowed](https://groups.google.com/d/msg/clojurescript/SzYK08Oduxo/MxLUjg50gQwJ). Instead, consider using one of these options:
```clojure
(:require [com.rpl.specter :as s])
(:require [com.rpl.specter :as s :refer-macros [select transform]]) ;; add in the Specter macros that you need
```
# Future work
- Make it possible to parallelize selects/updates
- Any connection to transducers?
- Add Clojurescript compatibility
- Integrate Specter with other kinds of data structures, such as graphs. Desired navigations include: reduction in topological order, navigate to outgoing/incoming nodes, to a subgraph (with metadata indicating how to attach external edges on transformation), to node attributes, to node values, to specific nodes.
# clj-kondo
When using Specter in a project with [clj-kondo](https://github.com/clj-kondo/clj-kondo), a lot of the vars will be considered unresolved because internally Specter defines them with macros. The following configuration snippet will resolve these issues if you include it in your `.clj-kondo/config.edn` file.
```clojure
{:lint-as {com.rpl.specter/defcollector clojure.core/defn
com.rpl.specter/defdynamicnav clojure.core/defn
com.rpl.specter/defmacroalias clojure.core/def
com.rpl.specter/defnav clojure.core/defn
com.rpl.specter/defrichnav clojure.core/defn}}
```
# Babashka
This library is compatible with [babashka](https://babashka.org/) as of specter 1.1.4 and babashka 0.7.8.
# License
Copyright 2015 Red Planet Labs, Inc. Specter is licensed under Apache License v2.0.
Copyright 2015-2020 Red Planet Labs, Inc. Specter is licensed under Apache License v2.0.

1
VERSION Normal file
View file

@ -0,0 +1 @@
1.1.5-SNAPSHOT

19
bb.edn Normal file
View file

@ -0,0 +1,19 @@
{:paths ["src/clj"]
:tasks
{test:clj {:doc "Run clj tests with leiningen"
:task (shell "lein test")}
test:cljs {:doc "Run cljs tests with leiningen"
:task (shell "lein doo node test-build once")}
test:bb {:doc "Run bb tests"
:extra-paths ["test"]
:extra-deps {org.clojure/test.check {:mvn/version "0.9.0"}
io.github.cognitect-labs/test-runner
{:git/tag "v0.5.0" :git/sha "b3fd0d2"}
org.clojure/tools.namespace {:git/url "https://github.com/babashka/tools.namespace"
:git/sha "3625153ee66dfcec2ba600851b5b2cbdab8fae6c"}}
:requires ([cognitect.test-runner :as tr])
:task (apply tr/-main
"-d" "test"
*command-line-args*)}}}

View file

@ -1,9 +1,43 @@
(defproject com.rpl/specter "0.1.0"
:dependencies [[org.clojure/clojure "1.6.0"]
]
(def VERSION (.trim (slurp "VERSION")))
(defproject com.rpl/specter VERSION
:jvm-opts ["-XX:-OmitStackTraceInFastThrow"] ; this prevents JVM from doing optimizations which can remove stack traces from NPE and other exceptions
;"-agentpath:/Applications/YourKit_Java_Profiler_2015_build_15056.app/Contents/Resources/bin/mac/libyjpagent.jnilib"]
:source-paths ["src/clj"]
:test-paths ["test/clj"]
:java-source-paths ["src/java"]
:test-paths ["test", "target/test-classes"]
:auto-clean false
:dependencies [[riddley "0.1.12"]]
:plugins [[lein-codox "0.10.7"]
[lein-doo "0.1.7"]]
:codox {:source-paths ["target/classes" "src/clj"]
:namespaces [com.rpl.specter
com.rpl.specter.zipper
com.rpl.specter.protocols
com.rpl.specter.transients]
:source-uri
{#"target/classes" "https://github.com/nathanmarz/specter/tree/{version}/src/clj/{classpath}x#L{line}"
#".*" "https://github.com/nathanmarz/specter/tree/{version}/src/clj/{classpath}#L{line}"}}
:cljsbuild {:builds [{:id "test-build"
:source-paths ["src/clj" "target/classes" "test"]
:compiler {:output-to "out/testable.js"
:main 'com.rpl.specter.cljs-test-runner
:target :nodejs
:optimizations :none}}]}
:profiles {:dev {:dependencies
[[org.clojure/test.check "0.5.9"]]}
})
[[org.clojure/test.check "0.9.0"]
[org.clojure/clojure "1.9.0"]
[org.clojure/clojurescript "1.10.439"]]}
:bench {:dependencies [[org.clojure/clojure "1.9.0"]
[criterium "0.4.4"]]}
:test {:dependencies [[org.clojure/clojure "1.7.0"]]}}
:deploy-repositories
[["clojars" {:url "https://repo.clojars.org"
:sign-releases false}]]
:aliases {"deploy" ["do" "clean," "deploy" "clojars"]})

357
scripts/benchmarks.clj Normal file
View file

@ -0,0 +1,357 @@
(ns com.rpl.specter.benchmarks
(:use [com.rpl.specter]
[com.rpl.specter.transients])
(:require [clojure.walk :as walk]
[com.rpl.specter.impl :as i]
[criterium.core :as bench]))
(defn pretty-float3 [anum]
(format "%.3g" anum))
(defn mean [a-fn]
(-> a-fn (bench/benchmark* {}) :mean first (* 1000000)))
(defn compare-benchmark [afn-map]
(let [results (transform MAP-VALS mean afn-map)
[[_ best-time] & _ :as sorted] (sort-by last results)]
(println "\nMean(us)\tvs best\t\tCode")
(doseq [[k t] sorted]
(println (pretty-float3 t) "\t\t" (pretty-float3 (/ t best-time 1.0)) "\t\t" k))))
(defmacro run-benchmark [name & exprs]
(let [only-benchmarks (set (filter some? *command-line-args*))
all-benchmarks? (empty? only-benchmarks)]
(if (or all-benchmarks? (contains? only-benchmarks name))
(let [afn-map (->> exprs shuffle (map (fn [e] [`(quote ~e) `(fn [] ~e)])) (into {}))]
`(do
(println "Benchmark:" ~name)
(compare-benchmark ~afn-map)
(println "\n********************************\n"))))))
(defn specter-dynamic-nested-get [data a b c]
(select-any (keypath a b c) data))
(defn get-k [k] (fn [m next] (next (get m k))))
(def get-a-b-c
(reduce
(fn [curr afn]
(fn [structure]
(afn structure curr)))
[identity (get-k :c) (get-k :b) (get-k :a)]))
(let [data {:a {:b {:c 1}}}
p (comp-paths :a :b :c)]
(run-benchmark "get value in nested map"
(select-any [:a :b :c] data)
(select-any (keypath :a :b :c) data)
(select-one [:a :b :c] data)
(select-first [:a :b :c] data)
(select-one! [:a :b :c] data)
(compiled-select-any p data)
(specter-dynamic-nested-get data :a :b :c)
(get-in data [:a :b :c])
(get-a-b-c data)
(-> data :a :b :c identity)
(-> data (get :a) (get :b) (get :c))
(-> data :a :b :c)
(select-any [(keypath :a) (keypath :b) (keypath :c)] data)))
(let [data {:a {:b {:c 1}}}]
(run-benchmark "set value in nested map"
(assoc-in data [:a :b :c] 1)
(setval [:a :b :c] 1 data)))
;; because below 1.7 there is no update function
(defn- my-update [m k afn]
(assoc m k (afn (get m k))))
(defn manual-transform [m afn]
(my-update m :a
(fn [m2]
(my-update m2 :b
(fn [m3]
(my-update m3 :c afn))))))
(let [data {:a {:b {:c 1}}}]
(run-benchmark "update value in nested map"
(update-in data [:a :b :c] inc)
(transform [:a :b :c] inc data)
(manual-transform data inc)))
(defn map-vals-map-iterable [^clojure.lang.IMapIterable m afn]
(let [k-it (.keyIterator m)
v-it (.valIterator m)]
(loop [ret {}]
(if (.hasNext k-it)
(let [k (.next k-it)
v (.next v-it)]
(recur (assoc ret k (afn v))))
ret))))
(defn map-vals-map-iterable-transient [^clojure.lang.IMapIterable m afn]
(persistent!
(let [k-it (.keyIterator m)
v-it (.valIterator m)]
(loop [ret (transient {})]
(if (.hasNext k-it)
(let [k (.next k-it)
v (.next v-it)]
(recur (assoc! ret k (afn v))))
ret)))))
(let [data '(1 2 3 4 5)]
(run-benchmark "transform values of a list"
(transform ALL inc data)
(doall (sequence (map inc) data))
(reverse (into '() (map inc) data))
))
(let [data {:a 1 :b 2 :c 3 :d 4}]
(run-benchmark "transform values of a small map"
(into {} (for [[k v] data] [k (inc v)]))
(reduce-kv (fn [m k v] (assoc m k (inc v))) {} data)
(persistent! (reduce-kv (fn [m k v] (assoc! m k (inc v))) (transient {}) data))
(reduce-kv (fn [m k v] (assoc m k (inc v))) (empty data) data)
(transform [ALL LAST] inc data)
(transform MAP-VALS inc data)
(zipmap (keys data) (map inc (vals data)))
(into {} (map (fn [e] [(key e) (inc (val e))]) data))
(into {} (map (fn [e] [(key e) (inc (val e))])) data)
(map-vals-map-iterable data inc)
(map-vals-map-iterable-transient data inc)
))
(let [data (->> (for [i (range 1000)] [i i]) (into {}))]
(run-benchmark "transform values of large map"
(into {} (for [[k v] data] [k (inc v)]))
(reduce-kv (fn [m k v] (assoc m k (inc v))) {} data)
(persistent! (reduce-kv (fn [m k v] (assoc! m k (inc v))) (transient {}) data))
(persistent! (reduce-kv (fn [m k v] (assoc! m k (inc v))) (transient clojure.lang.PersistentHashMap/EMPTY) data))
(reduce-kv (fn [m k v] (assoc m k (inc v))) (empty data) data)
(transform [ALL LAST] inc data)
(transform MAP-VALS inc data)
(zipmap (keys data) (map inc (vals data)))
(into {} (map (fn [e] [(key e) (inc (val e))]) data))
(into {} (map (fn [e] [(key e) (inc (val e))])) data)
(map-vals-map-iterable data inc)
(map-vals-map-iterable-transient data inc)))
(let [data [1 2 3 4 5 6 7 8 9 10]]
(run-benchmark "first value of a size 10 vector"
(first data)
(select-any ALL data)
(select-any FIRST data)
(select-first ALL data)
))
(let [data [1 2 3 4 5]]
(run-benchmark "map a function over a vector"
(vec (map inc data))
(mapv inc data)
(transform ALL inc data)
(into [] (map inc) data)))
(let [data [1 2 3 4 5 6 7 8 9 10]]
(run-benchmark "filter a sequence"
(doall (filter even? data))
(filterv even? data)
(select [ALL even?] data)
(select-any (filterer even?) data)
(into [] (filter even?) data)))
(let [data [{:a 2 :b 2} {:a 1} {:a 4} {:a 6}]
xf (comp (map :a) (filter even?))]
(run-benchmark "even :a values from sequence of maps"
(select [ALL :a even?] data)
(->> data (mapv :a) (filter even?) doall)
(into [] (comp (map :a) (filter even?)) data)
(into [] xf data)))
(let [v (vec (range 1000))]
(run-benchmark "Append to a large vector"
(setval END [1] v)
(setval AFTER-ELEM 1 v)
(reduce conj v [1])
(conj v 1)))
(let [data [1 2 3 4 5 6 7 8 9 10]]
(run-benchmark "prepend to a vector"
(vec (cons 0 data))
(setval BEFORE-ELEM 0 data)
(into [0] data)
))
(declarepath TreeValues)
(providepath TreeValues
(if-path vector?
[ALL TreeValues]
STAY))
(defprotocolpath TreeValuesProt)
(extend-protocolpath TreeValuesProt
clojure.lang.PersistentVector [ALL TreeValuesProt]
Object STAY)
(defn tree-value-transform [afn atree]
(if (vector? atree)
(mapv #(tree-value-transform afn %) atree)
(afn atree)))
(let [data [1 2 [[3]] [4 6 [7 [8]] 10]]]
(run-benchmark "update every value in a tree (represented with vectors)"
(walk/postwalk (fn [e] (if (and (number? e) (even? e)) (inc e) e)) data)
(transform [(walker number?) even?] inc data)
(transform [TreeValues even?] inc data)
(transform [TreeValuesProt even?] inc data)
(tree-value-transform (fn [e] (if (even? e) (inc e) e)) data)))
(let [toappend (range 1000)]
(run-benchmark "transient comparison: building up vectors"
(reduce (fn [v i] (conj v i)) [] toappend)
(reduce (fn [v i] (conj! v i)) (transient []) toappend)
(setval END toappend [])
(setval END! toappend (transient []))))
(let [toappend (range 1000)]
(run-benchmark "transient comparison: building up vectors one at a time"
(reduce (fn [v i] (conj v i)) [] toappend)
(reduce (fn [v i] (conj! v i)) (transient []) toappend)
(reduce (fn [v i] (setval END [i] v)) [] toappend)
(reduce (fn [v i] (setval END! [i] v)) (transient []) toappend)))
(let [data (vec (range 1000))
tdata (transient data)
tdata2 (transient data)]
(run-benchmark "transient comparison: assoc'ing in vectors"
(assoc data 600 0)
(assoc! tdata 600 0)
(setval (keypath 600) 0 data)
(setval (keypath! 600) 0 tdata2)))
(let [data (into {} (for [k (range 1000)]
[k (rand)]))
tdata (transient data)
tdata2 (transient data)]
(run-benchmark "transient comparison: assoc'ing in maps"
(assoc data 600 0)
(assoc! tdata 600 0)
(setval (keypath 600) 0 data)
(setval (keypath! 600) 0 tdata2)))
(defn modify-submap
[m]
(assoc m 0 1 458 89))
(let [data (into {} (for [k (range 1000)]
[k (rand)]))
tdata (transient data)]
(run-benchmark "transient comparison: submap"
(transform (submap [600 700]) modify-submap data)
(transform (submap! [600 700]) modify-submap tdata)))
(let [data {:x 1}
meta-map {:my :metadata}]
(run-benchmark "set metadata"
(with-meta data meta-map)
(setval META meta-map data)))
(let [data (with-meta {:x 1} {:my :metadata})]
(run-benchmark "get metadata"
(meta data)
(select-any META data)))
(let [data (with-meta {:x 1} {:my :metadata})]
(run-benchmark "vary metadata"
(vary-meta data assoc :y 2)
(setval [META :y] 2 data)))
(let [data (range 1000)]
(run-benchmark "Traverse into a set"
(set data)
(set (select ALL data))
(into #{} (traverse ALL data))
(persistent!
(reduce conj! (transient #{}) (traverse ALL data)))
(reduce conj #{} (traverse ALL data))))
(defn mult-10 [v] (* 10 v))
(let [data [1 2 3 4 5 6 7 8 9]]
(run-benchmark "multi-transform vs. consecutive transforms, one shared nav"
(->> data (transform [ALL even?] mult-10) (transform [ALL odd?] dec))
(multi-transform [ALL (multi-path [even? (terminal mult-10)] [odd? (terminal dec)])] data)))
(let [data [[1 2 3 4 :a] [5] [6 7 :b 8 9] [10 11 12 13]]]
(run-benchmark "multi-transform vs. consecutive transforms, three shared navs"
(->> data (transform [ALL ALL number? even?] mult-10) (transform [ALL ALL number? odd?] dec))
(multi-transform [ALL ALL number? (multi-path [even? (terminal mult-10)] [odd? (terminal dec)])] data)))
(let [data {:a 1 :b 2 :c 3 :d 4}]
(run-benchmark "namespace qualify keys of a small map"
(into {}
(map (fn [[k v]] [(keyword (str *ns*) (name k)) v]))
data)
(reduce-kv (fn [m k v] (assoc m (keyword (str *ns*) (name k)) v)) {} data)
(setval [MAP-KEYS NAMESPACE] (str *ns*) data)
))
(let [data (->> (for [i (range 1000)] [(keyword (str i)) i]) (into {}))]
(run-benchmark "namespace qualify keys of a large map"
(into {}
(map (fn [[k v]] [(keyword (str *ns*) (name k)) v]))
data)
(reduce-kv (fn [m k v] (assoc m (keyword (str *ns*) (name k)) v)) {} data)
(setval [MAP-KEYS NAMESPACE] (str *ns*) data)
))
(defnav walker-old [afn]
(select* [this structure next-fn]
(i/walk-select afn next-fn structure))
(transform* [this structure next-fn]
(i/walk-until afn next-fn structure)))
(let [data {:a [1 2 {:c '(3 4) :d {:e [1 2 3] 7 8 9 10}}]}]
(run-benchmark "walker vs. clojure.walk version"
(transform (walker number?) inc data)
(transform (walker-old number?) inc data)
))
(let [size 1000
middle-idx (/ size 2)
v -1
rng (range size)
data-vec (vec rng)
data-lst (apply list rng)]
(run-benchmark "before-index vs. srange in middle (vector)"
(setval (before-index middle-idx) v data-vec)
(setval (srange middle-idx middle-idx) [v] data-vec))
(run-benchmark "before-index vs. srange in middle (list)"
(setval (before-index middle-idx) v data-lst)
(setval (srange middle-idx middle-idx) [v] data-lst))
(run-benchmark "before-index at 0 vs. srange vs. cons (list)"
(setval (before-index 0) v data-lst)
(setval (srange 0 0) [v] data-lst)
(cons v data-lst)))

4
scripts/cljs-repl.sh Executable file
View file

@ -0,0 +1,4 @@
#!/bin/bash
rlwrap java -cp `lein classpath` clojure.main scripts/repl.clj

5
scripts/repl.clj Normal file
View file

@ -0,0 +1,5 @@
(require
'[cljs.repl :as repl]
'[cljs.repl.node :as node])
(repl/repl (node/repl-env))

8
scripts/run-benchmarks Executable file
View file

@ -0,0 +1,8 @@
#!/bin/bash
lein javac
lein version
echo
lein show-profiles bench
echo
java -server -XX:MaxInlineSize=100 -cp "$(lein with-profile bench classpath)" clojure.main scripts/benchmarks.clj "$@"

View file

@ -1,147 +0,0 @@
(ns com.rpl.specter
(:use [com.rpl.specter impl protocols])
)
;;TODO: can make usage of vals much more efficient by determining during composition how many vals
;;there are going to be. this should make it much easier to allocate space for vals without doing concats
;;all over the place. The apply to the vals + structure can also be avoided since the number of vals is known
;;beforehand
(defn comp-paths [& paths]
(comp-paths* (vec paths)))
;; Selector functions
(defn select [selector structure]
(let [sp (comp-paths* selector)]
(select-full* sp
[]
structure
(fn [vals structure]
(if-not (empty? vals) [(conj vals structure)] [structure])))
))
(defn select-one
"Like select, but returns either one element or nil. Throws exception if multiple elements returned"
[selector structure]
(let [res (select selector structure)]
(when (> (count res) 1)
(throw-illegal "More than one element found for params: " selector structure))
(first res)
))
(defn select-one!
"Returns exactly one element, throws exception if zero or multiple elements returned"
[selector structure]
(let [res (select-one selector structure)]
(when (nil? res) (throw-illegal "No elements found for params: " selector structure))
res
))
(defn select-first
"Returns first element returned. Not any more efficient than select, just a convenience"
[selector structure]
(first (select selector structure)))
;; Update functions
(defn update [selector update-fn structure]
(let [selector (comp-paths* selector)]
(update-full* selector
[]
structure
(fn [vals structure]
(if (empty? vals)
(update-fn structure)
(apply update-fn (conj vals structure)))
))))
(defn setval [selector val structure]
(update selector (fn [_] val) structure))
(defn replace-in [selector update-fn structure & {:keys [merge-fn] :or {merge-fn concat}}]
"Returns [new structure [<user-ret> <user-ret>...]"
(let [state (mutable-cell nil)]
[(update selector
(fn [e]
(let [res (update-fn e)]
(if res
(let [[ret user-ret] res]
(->> user-ret
(merge-fn (get-cell state))
(set-cell! state))
ret)
e
)))
structure)
(get-cell state)]
))
;; Built-in pathing and context operations
(def ALL (->AllStructurePath))
(def VAL (->ValCollect))
(def LAST (->LastStructurePath))
(def FIRST (->FirstStructurePath))
(defn srange-dynamic [start-fn end-fn] (->SRangePath start-fn end-fn))
(defn srange [start end] (srange-dynamic (fn [_] start) (fn [_] end)))
(def START (srange 0 0))
(def END (srange-dynamic count count))
(defn walker [afn] (->WalkerStructurePath afn))
(defn codewalker [afn] (->CodeWalkerStructurePath afn))
(defn filterer [afn] (->FilterStructurePath afn))
(defn keypath [akey] (->KeyPath akey))
(defn view [afn] (->ViewPath afn))
(defmacro viewfn [& args]
`(view (fn ~@args)))
(defn split [& selectors]
(->SplitPath (->> selectors (map comp-paths*) doall)))
(defn selected?
"Filters the current value based on whether a selector finds anything.
e.g. (selected? :vals ALL even?) keeps the current element only if an
even number exists for the :vals key"
[& selectors]
(let [s (comp-paths selectors)]
(fn [structure]
(->> structure
(select s)
empty?
not))))
(extend-type clojure.lang.Keyword
StructurePath
(select* [kw structure next-fn]
(key-select kw structure next-fn))
(update* [kw structure next-fn]
(key-update kw structure next-fn)
))
(extend-type clojure.lang.AFn
StructurePath
(select* [afn structure next-fn]
(if (afn structure)
(next-fn structure)))
(update* [afn structure next-fn]
(if (afn structure)
(next-fn structure)
structure)))
(defn collect [& selector]
(->SelectCollector select (comp-paths* selector)))
(defn collect-one [& selector]
(->SelectCollector select-one (comp-paths* selector)))

1506
src/clj/com/rpl/specter.cljc Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,279 +0,0 @@
(ns com.rpl.specter.impl
(:use [com.rpl.specter protocols])
(:require [clojure.walk :as walk]
[clojure.core.reducers :as r])
)
(defn benchmark [iters afn]
(time
(dotimes [_ iters]
(afn))))
(defprotocol CoerceStructureValsPath
(coerce-path [this]))
(extend-protocol CoerceStructureValsPath
com.rpl.specter.protocols.StructureValsPath
(coerce-path [this] this)
com.rpl.specter.protocols.Collector
(coerce-path [collector]
(reify StructureValsPath
(select-full* [this vals structure next-fn]
(next-fn (conj vals (collect-val collector structure)) structure))
(update-full* [this vals structure next-fn]
(next-fn (conj vals (collect-val collector structure)) structure))))
;; need to say Object instead of StructurePath so that things like Keyword are properly coerced
Object
(coerce-path [spath]
(reify StructureValsPath
(select-full* [this vals structure next-fn]
(select* spath structure (fn [structure] (next-fn vals structure))))
(update-full* [this vals structure next-fn]
(update* spath structure (fn [structure] (next-fn vals structure)))
)))
)
(extend-protocol StructureValsPathComposer
Object
(comp-paths* [sp]
(coerce-path sp))
java.util.List
(comp-paths* [structure-paths]
(reduce (fn [sp-curr sp]
(reify StructureValsPath
(select-full* [this vals structure next-fn]
(select-full* sp vals structure
(fn [vals-next structure-next]
(select-full* sp-curr vals-next structure-next next-fn)))
)
(update-full* [this vals structure next-fn]
(update-full* sp vals structure
(fn [vals-next structure-next]
(update-full* sp-curr vals-next structure-next next-fn))))
))
(->> structure-paths flatten (map coerce-path) reverse))
))
;; cell implementation idea taken from prismatic schema library
(definterface PMutableCell
(get_cell ^Object [])
(set_cell [^Object x]))
(deftype MutableCell [^:volatile-mutable ^Object q]
PMutableCell
(get_cell [this] q)
(set_cell [this x] (set! q x)))
(defn mutable-cell ^PMutableCell
([] (mutable-cell nil))
([init] (MutableCell. init)))
(defn set-cell! [^PMutableCell cell val]
(.set_cell cell val))
(defn get-cell [^PMutableCell cell]
(.get_cell cell))
(defmacro throw* [etype & args]
`(throw (new ~etype (pr-str ~@args))))
(defmacro throw-illegal [& args]
`(throw* IllegalArgumentException ~@args))
(defn update-cell! [cell afn]
(let [ret (afn (get-cell cell))]
(set-cell! cell ret)
ret))
(defn- append [coll elem]
(-> coll vec (conj elem)))
(defprotocol SetExtremes
(set-first [s val])
(set-last [s val]))
(defn- set-first-list [l v]
(cons v (rest l)))
(defn- set-last-list [l v]
(append (butlast l) v))
(extend-protocol SetExtremes
clojure.lang.PersistentVector
(set-first [v val]
(assoc v 0 val))
(set-last [v val]
(assoc v (-> v count dec) val))
Object
(set-first [l val]
(set-first-list l val))
(set-last [l val]
(set-last-list l val)
))
(defn- walk-until [pred on-match-fn structure]
(if (pred structure)
(on-match-fn structure)
(walk/walk (partial walk-until pred on-match-fn) identity structure)
))
(defn- fn-invocation? [f]
(or (instance? clojure.lang.Cons f)
(instance? clojure.lang.LazySeq f)
(list? f)))
(defn- codewalk-until [pred on-match-fn structure]
(if (pred structure)
(on-match-fn structure)
(let [ret (walk/walk (partial codewalk-until pred on-match-fn) identity structure)]
(if (and (fn-invocation? structure) (fn-invocation? ret))
(with-meta ret (meta structure))
ret
))))
(defn- conj-all! [cell elems]
(set-cell! cell (concat (get-cell cell) elems)))
;; returns vector of all results
(defn- walk-select [pred continue-fn structure]
(let [ret (mutable-cell [])
walker (fn this [structure]
(if (pred structure)
(conj-all! ret (continue-fn structure))
(walk/walk this identity structure))
)]
(walker structure)
(get-cell ret)
))
(defn- filter+ancestry [afn aseq]
(let [aseq (vec aseq)]
(reduce (fn [[s m :as orig] i]
(let [e (get aseq i)
pos (count s)]
(if (afn e)
[(conj s e) (assoc m pos i)]
orig
)))
[[] {}]
(range (count aseq))
)))
(defn key-select [akey structure next-fn]
(next-fn (get structure akey)))
(defn key-update [akey structure next-fn]
(assoc structure akey (next-fn (get structure akey))
))
(deftype AllStructurePath []
StructurePath
(select* [this structure next-fn]
(into [] (r/mapcat next-fn structure)))
(update* [this structure next-fn]
(let [empty-structure (empty structure)]
(if (list? empty-structure)
;; this is done to maintain order, otherwise lists get reversed
(doall (map next-fn structure))
(->> structure (r/map next-fn) (into empty-structure))
))))
(deftype ValCollect []
Collector
(collect-val [this structure]
structure))
(deftype LastStructurePath []
StructurePath
(select* [this structure next-fn]
(next-fn (last structure)))
(update* [this structure next-fn]
(set-last structure (next-fn (last structure)))))
(deftype FirstStructurePath []
StructurePath
(select* [this structure next-fn]
(next-fn (first structure)))
(update* [this structure next-fn]
(set-first structure (next-fn (first structure)))))
(deftype WalkerStructurePath [afn]
StructurePath
(select* [this structure next-fn]
(walk-select afn next-fn structure))
(update* [this structure next-fn]
(walk-until afn next-fn structure)))
(deftype CodeWalkerStructurePath [afn]
StructurePath
(select* [this structure next-fn]
(walk-select afn next-fn structure))
(update* [this structure next-fn]
(codewalk-until afn next-fn structure)))
(deftype FilterStructurePath [afn]
StructurePath
(select* [this structure next-fn]
(next-fn (filter afn structure)))
(update* [this structure next-fn]
(let [[filtered ancestry] (filter+ancestry afn structure)
;; the vec is necessary so that we can get by index later
;; (can't get by index for cons'd lists)
next (vec (next-fn filtered))]
(reduce (fn [curr [newi oldi]]
(assoc curr oldi (get next newi)))
(vec structure)
ancestry))))
(deftype KeyPath [akey]
StructurePath
(select* [this structure next-fn]
(key-select akey structure next-fn))
(update* [this structure next-fn]
(key-update akey structure next-fn)
))
(deftype SelectCollector [sel-fn selector]
Collector
(collect-val [this structure]
(sel-fn selector structure)))
(deftype SRangePath [start-fn end-fn]
StructurePath
(select* [this structure next-fn]
(let [start (start-fn structure)
end (end-fn structure)]
(next-fn (-> structure vec (subvec start end)))
))
(update* [this structure next-fn]
(let [start (start-fn structure)
end (end-fn structure)
structurev (vec structure)
newpart (next-fn (-> structurev (subvec start end)))
res (concat (subvec structurev 0 start)
newpart
(subvec structurev end (count structure)))]
(if (vector? structure)
(vec res)
res
))))
(deftype ViewPath [view-fn]
StructurePath
(select* [this structure next-fn]
(-> structure view-fn next-fn))
(update* [this structure next-fn]
(-> structure view-fn next-fn)
))
(deftype SplitPath [selectors]
StructureValsPath
(select-full* [this vals structure next-fn]
(into [] (r/mapcat #(select-full* % vals structure next-fn) selectors)))
(update-full* [this vals structure next-fn]
(reduce (fn [structure s] (update-full* s vals structure next-fn)) structure selectors)
))

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,54 @@
(ns com.rpl.specter.macros
(:use [com.rpl.specter.protocols :only [RichNavigator]])
(:require [com.rpl.specter.impl :as i]))
(defn- determine-params-impls [impls]
(let [grouped (->> impls (map (fn [[n & body]] [n body])) (into {}))]
(if-not (= #{'select* 'transform*} (-> grouped keys set))
(throw (ex-info "defnav must implement select* and transform*"
{:methods (keys grouped)})))
grouped))
(defmacro richnav [params & impls]
(if (empty? params)
`(reify RichNavigator ~@impls)
`(i/direct-nav-obj
(fn ~params
(reify RichNavigator
~@impls)))))
(defmacro nav [params & impls]
(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)]
`(richnav ~params
(~'select* [this# vals# ~s-structure-sym next-fn#]
(let [~s-next-fn-sym (fn [s#] (next-fn# vals# s#))]
~@s-body))
(~'transform* [this# vals# ~t-structure-sym next-fn#]
(let [~t-next-fn-sym (fn [s#] (next-fn# vals# s#))]
~@t-body)))))
(defn- helper-name [name method-name]
(with-meta (symbol (str name "-" method-name)) {:no-doc true}))
(defmacro defnav [name params & impls]
;; remove the "this" param for the helper
(let [helpers (for [[mname [_ & mparams] & mbody] impls]
`(defn ~(helper-name name mname) [~@params ~@mparams] ~@mbody))
decls (for [[mname & _] impls]
`(declare ~(helper-name name mname)))
name-with-meta (vary-meta name
assoc :arglists (list 'quote (list params)))]
`(do
~@decls
~@helpers
(def ~name-with-meta (nav ~params ~@impls)))))
(defmacro defrichnav [name params & impls]
(let [name-with-meta (vary-meta name
assoc :arglists (list 'quote (list params)))]
`(def ~name-with-meta
(richnav ~params ~@impls))))

View file

@ -0,0 +1,786 @@
(ns com.rpl.specter.navs
#?(:cljs (:require-macros
[com.rpl.specter
:refer
[defnav defrichnav]]
[com.rpl.specter.util-macros :refer
[doseqres]]))
#?(:clj (:use [com.rpl.specter.macros :only [defnav defrichnav]]
[com.rpl.specter.util-macros :only [doseqres]]))
(:require [com.rpl.specter.impl :as i]
#?@(:bb []
:clj [[clojure.core.reducers :as r]])))
(defn not-selected?*
[compiled-path vals structure]
(->> structure
(i/compiled-select-any* compiled-path vals)
(identical? i/NONE)))
(defn selected?*
[compiled-path vals structure]
(not (not-selected?* compiled-path vals structure)))
(defn all-select [structure next-fn]
(doseqres i/NONE [e structure]
(next-fn e)))
#?(
:clj
(defn queue? [coll]
(instance? clojure.lang.PersistentQueue coll))
:cljs
(defn queue? [coll]
(= (type coll) (type #queue []))))
(defprotocol AllTransformProtocol
(all-transform [structure next-fn]))
(defn void-transformed-kv-pair? [newkv]
(or (identical? newkv i/NONE) (< (count newkv) 2)))
(defn- non-transient-map-all-transform [structure next-fn empty-map]
(reduce-kv
(fn [m k v]
(let [newkv (next-fn [k v])]
(if (void-transformed-kv-pair? newkv)
m
(assoc m (nth newkv 0) (nth newkv 1)))))
empty-map
structure))
(defn not-NONE? [v]
(-> v (identical? i/NONE) not))
(defn- all-transform-list [structure next-fn]
(doall (sequence (comp (map next-fn) (filter not-NONE?)) structure)))
(defn- all-transform-record [structure next-fn]
(reduce
(fn [res kv] (conj res (next-fn kv)))
structure
structure
))
(extend-protocol AllTransformProtocol
nil
(all-transform [structure next-fn]
nil)
#?(:clj clojure.lang.MapEntry)
#?(:clj
(all-transform [structure next-fn]
(let [newk (next-fn (key structure))
newv (next-fn (val structure))]
(clojure.lang.MapEntry. newk newv))))
#?(:cljs cljs.core/MapEntry)
#?(:cljs
(all-transform [structure next-fn]
(let [newk (next-fn (key structure))
newv (next-fn (val structure))]
(cljs.core/->MapEntry newk newv nil))))
#?(:clj clojure.lang.IPersistentVector :cljs cljs.core/PersistentVector)
(all-transform [structure next-fn]
(into []
(comp (map next-fn)
(filter not-NONE?))
structure))
#?(:clj String :cljs string)
(all-transform [structure next-fn]
(apply str (into []
(comp (map next-fn)
(filter not-NONE?))
structure)))
#?(:clj clojure.lang.PersistentHashSet :cljs cljs.core/PersistentHashSet)
(all-transform [structure next-fn]
(into #{}
(comp (map next-fn)
(filter not-NONE?))
structure))
#?(:clj clojure.lang.PersistentArrayMap)
#?(:bb
(all-transform [structure next-fn]
(non-transient-map-all-transform structure next-fn {}))
:clj
(all-transform [structure next-fn]
(let [k-it (.keyIterator structure)
v-it (.valIterator structure)
none-cell (i/mutable-cell 0)
len (.count structure)
array (i/fast-object-array (* 2 len))]
(loop [i 0
j 0]
(if (.hasNext k-it)
(let [k (.next k-it)
v (.next v-it)
newkv (next-fn [k v])]
(if (void-transformed-kv-pair? newkv)
(do
(i/update-cell! none-cell inc)
(recur (+ i 2) j))
(do
(aset array j (nth newkv 0))
(aset array (inc j) (nth newkv 1))
(recur (+ i 2) (+ j 2)))))))
(let [none-count (i/get-cell none-cell)
array (if (not= 0 none-count)
(java.util.Arrays/copyOf array (int (* 2 (- len none-count))))
array
)]
(clojure.lang.PersistentArrayMap/createAsIfByAssoc array)))))
#?(:cljs cljs.core/PersistentArrayMap)
#?(:cljs
(all-transform [structure next-fn]
(non-transient-map-all-transform structure next-fn {})))
#?(:clj clojure.lang.PersistentTreeMap :cljs cljs.core/PersistentTreeMap)
(all-transform [structure next-fn]
(non-transient-map-all-transform structure next-fn (empty structure)))
#?(:clj clojure.lang.IRecord)
#?(:clj
(all-transform [structure next-fn]
(all-transform-record structure next-fn)))
#?(:clj clojure.lang.PersistentHashMap :cljs cljs.core/PersistentHashMap)
(all-transform [structure next-fn]
(persistent!
(reduce-kv
(fn [m k v]
(let [newkv (next-fn [k v])]
(if (void-transformed-kv-pair? newkv)
m
(assoc! m (nth newkv 0) (nth newkv 1)))))
(transient
#?(:clj clojure.lang.PersistentHashMap/EMPTY :cljs cljs.core.PersistentHashMap.EMPTY))
structure)))
#?(:clj Object)
#?(:clj
(all-transform [structure next-fn]
(let [empty-structure (empty structure)]
(cond (and (list? empty-structure) (not (queue? empty-structure)))
(all-transform-list structure next-fn)
(map? structure)
;; reduce-kv is much faster than doing r/map through call to (into ...)
(reduce-kv
(fn [m k v]
(let [newkv (next-fn [k v])]
(if (void-transformed-kv-pair? newkv)
m
(assoc m (nth newkv 0) (nth newkv 1)))))
empty-structure
structure)
:else
#?(:bb (into empty-structure
(comp (map next-fn) (filter not-NONE?))
structure)
:clj (->> structure
(r/map next-fn)
(r/filter not-NONE?)
(into empty-structure)))))))
#?(:cljs default)
#?(:cljs
(all-transform [structure next-fn]
(if (record? structure)
;; this case is solely for cljs since extending to IRecord doesn't work for cljs
(all-transform-record structure next-fn)
(let [empty-structure (empty structure)]
(cond
(and (list? empty-structure) (not (queue? empty-structure)))
(all-transform-list structure next-fn)
(map? structure)
(reduce-kv
(fn [m k v]
(let [newkv (next-fn [k v])]
(if (void-transformed-kv-pair? newkv)
m
(assoc m (nth newkv 0) (nth newkv 1)))))
empty-structure
structure)
:else
(into empty-structure
(comp (map next-fn) (filter not-NONE?))
structure)))))))
(defprotocol MapTransformProtocol
(map-vals-transform [structure next-fn])
(map-keys-transform [structure next-fn])
)
(defn map-vals-non-transient-transform [structure empty-map next-fn]
(reduce-kv
(fn [m k v]
(let [newv (next-fn v)]
(if (identical? newv i/NONE)
m
(assoc m k newv))))
empty-map
structure))
(defn map-keys-non-transient-transform [structure empty-map next-fn]
(reduce-kv
(fn [m k v]
(let [newk (next-fn k)]
(if (identical? newk i/NONE)
m
(assoc m newk v))))
empty-map
structure))
(extend-protocol MapTransformProtocol
nil
(map-vals-transform [structure next-fn]
nil)
(map-keys-transform [structure next-fn]
nil)
#?(:clj clojure.lang.PersistentArrayMap)
#?(:bb
(map-vals-transform [structure next-fn]
(map-vals-non-transient-transform structure {} next-fn))
:clj
(map-vals-transform [structure next-fn]
(let [k-it (.keyIterator structure)
v-it (.valIterator structure)
none-cell (i/mutable-cell 0)
len (.count structure)
array (i/fast-object-array (* 2 len))]
(loop [i 0
j 0]
(if (.hasNext k-it)
(let [k (.next k-it)
v (.next v-it)
newv (next-fn v)]
(if (identical? newv i/NONE)
(do
(i/update-cell! none-cell inc)
(recur (+ i 2) j))
(do
(aset array j k)
(aset array (inc j) newv)
(recur (+ i 2) (+ j 2)))))))
(let [none-count (i/get-cell none-cell)
array (if (not= 0 none-count)
(java.util.Arrays/copyOf array (int (* 2 (- len none-count))))
array
)]
(clojure.lang.PersistentArrayMap. array)))))
#?(:bb
(map-keys-transform [structure next-fn]
(map-keys-non-transient-transform structure {} next-fn))
:clj
(map-keys-transform [structure next-fn]
(let [k-it (.keyIterator structure)
v-it (.valIterator structure)
none-cell (i/mutable-cell 0)
len (.count structure)
array (i/fast-object-array (* 2 len))]
(loop [i 0
j 0]
(if (.hasNext k-it)
(let [k (.next k-it)
v (.next v-it)
newk (next-fn k)]
(if (identical? newk i/NONE)
(do
(i/update-cell! none-cell inc)
(recur (+ i 2) j))
(do
(aset array j newk)
(aset array (inc j) v)
(recur (+ i 2) (+ j 2)))))))
(let [none-count (i/get-cell none-cell)
array (if (not= 0 none-count)
(java.util.Arrays/copyOf array (int (* 2 (- len none-count))))
array
)]
(clojure.lang.PersistentArrayMap/createAsIfByAssoc array)))))
#?(:cljs cljs.core/PersistentArrayMap)
#?(:cljs
(map-vals-transform [structure next-fn]
(map-vals-non-transient-transform structure {} next-fn)))
#?(:cljs
(map-keys-transform [structure next-fn]
(map-keys-non-transient-transform structure {} next-fn)))
#?(:clj clojure.lang.PersistentTreeMap :cljs cljs.core/PersistentTreeMap)
(map-vals-transform [structure next-fn]
(map-vals-non-transient-transform structure (empty structure) next-fn))
(map-keys-transform [structure next-fn]
(map-keys-non-transient-transform structure (empty structure) next-fn))
#?(:clj clojure.lang.PersistentHashMap :cljs cljs.core/PersistentHashMap)
(map-vals-transform [structure next-fn]
(persistent!
(reduce-kv
(fn [m k v]
(let [newv (next-fn v)]
(if (identical? newv i/NONE)
m
(assoc! m k newv))))
(transient
#?(:clj clojure.lang.PersistentHashMap/EMPTY :cljs cljs.core.PersistentHashMap.EMPTY))
structure)))
(map-keys-transform [structure next-fn]
(persistent!
(reduce-kv
(fn [m k v]
(let [newk (next-fn k)]
(if (identical? newk i/NONE)
m
(assoc! m newk v))))
(transient
#?(:clj clojure.lang.PersistentHashMap/EMPTY :cljs cljs.core.PersistentHashMap.EMPTY))
structure)))
#?(:clj Object :cljs default)
(map-vals-transform [structure next-fn]
(reduce-kv
(fn [m k v]
(let [newv (next-fn v)]
(if (identical? newv i/NONE)
m
(assoc m k newv))))
(empty structure)
structure))
(map-keys-transform [structure next-fn]
(reduce-kv
(fn [m k v]
(let [newk (next-fn k)]
(if (identical? newk i/NONE)
m
(assoc m newk v))))
(empty structure)
structure)))
(defn srange-select [structure start end next-fn]
(next-fn
(if (string? structure)
(subs structure start end)
(-> structure vec (subvec start end))
)))
(def srange-transform i/srange-transform*)
(defn extract-basic-filter-fn [path]
(cond (fn? path)
path
(and (coll? path)
(every? fn? path))
(reduce
(fn [combined afn]
(fn [structure]
(and (combined structure) (afn structure))))
path)))
(defn if-select [vals structure next-fn then-tester then-nav else-nav]
(i/exec-select*
(if (then-tester structure) then-nav else-nav)
vals
structure
next-fn))
(defn if-transform [vals structure next-fn then-tester then-nav else-nav]
(i/exec-transform*
(if (then-tester structure) then-nav else-nav)
vals
structure
next-fn))
(defprotocol AddExtremes
(append-all [structure elements])
(prepend-all [structure elements])
(append-one [structure elem])
(prepend-one [structure elem])
)
(extend-protocol AddExtremes
nil
(append-all [_ elements]
elements)
(prepend-all [_ elements]
elements)
(append-one [_ elem]
(list elem))
(prepend-one [_ elem]
(list elem))
#?(:clj clojure.lang.IPersistentVector :cljs cljs.core/PersistentVector)
(append-all [structure elements]
(reduce conj structure elements))
(prepend-all [structure elements]
(let [ret (transient [])]
(as-> ret <>
(reduce conj! <> elements)
(reduce conj! <> structure)
(persistent! <>))))
(append-one [structure elem]
(conj structure elem))
(prepend-one [structure elem]
(into [elem] structure))
#?(:cljs cljs.core/Subvec)
#?(:cljs
(append-all [structure elements]
(reduce conj structure elements)))
#?(:cljs
(prepend-all [structure elements]
(let [ret (transient [])]
(as-> ret <>
(reduce conj! <> elements)
(reduce conj! <> structure)
(persistent! <>)))))
#?(:cljs
(append-one [structure elem]
(conj structure elem)))
#?(:cljs
(prepend-one [structure elem]
(into [elem] structure)))
#?(:clj Object :cljs default)
(append-all [structure elements]
(concat structure elements))
(prepend-all [structure elements]
(concat elements structure))
(append-one [structure elem]
(concat structure [elem]))
(prepend-one [structure elem]
(cons elem structure))
)
(defprotocol UpdateExtremes
(update-first [s afn])
(update-last [s afn]))
(defprotocol GetExtremes
(get-first [s])
(get-last [s]))
(defprotocol FastEmpty
(fast-empty? [s]))
(defprotocol InsertBeforeIndex
(insert-before-idx [aseq idx val]))
(defnav PosNavigator [getter updater]
(select* [this structure next-fn]
(if-not (fast-empty? structure)
(next-fn (getter structure))
i/NONE))
(transform* [this structure next-fn]
(if (fast-empty? structure)
structure
(updater structure next-fn))))
#?(:bb
(defn vec-count [v]
(count v))
:clj
(defn vec-count [^clojure.lang.IPersistentVector v]
(.length v))
:cljs
(defn vec-count [v]
(count v)))
(defn- update-first-list [l afn]
(let [newf (afn (first l))
restl (rest l)]
(if (identical? i/NONE newf)
restl
(cons newf restl))))
(defn- update-last-list [l afn]
(let [lastl (afn (last l))
bl (butlast l)]
(if (identical? i/NONE lastl)
(if (nil? bl) '() bl)
(concat bl [lastl]))))
(defn- update-first-vector [v afn]
(let [val (nth v 0)
newv (afn val)]
(if (identical? i/NONE newv)
(subvec v 1)
(assoc v 0 newv)
)))
(defn- update-last-vector [v afn]
;; type-hinting vec-count to ^int caused weird errors with case
(let [c (int (vec-count v))]
(case c
1 (let [[e] v
newe (afn e)]
(if (identical? i/NONE newe)
[]
[newe]))
2 (let [[e1 e2] v
newe (afn e2)]
(if (identical? i/NONE newe)
[e1]
[e1 newe]))
(let [i (dec c)
newe (afn (nth v i))]
(if (identical? i/NONE newe)
(pop v)
(assoc v i newe))))))
#?(:bb
(defn transient-vec-count [v]
(count v))
:clj
(defn transient-vec-count [^clojure.lang.ITransientVector v]
(.count v))
:cljs
(defn transient-vec-count [v]
(count v)))
(extend-protocol UpdateExtremes
#?(:clj clojure.lang.IPersistentVector :cljs cljs.core/PersistentVector)
(update-first [v afn]
(update-first-vector v afn))
(update-last [v afn]
(update-last-vector v afn))
#?(:cljs cljs.core/Subvec)
#?(:cljs
(update-first [v afn]
(update-first-vector v afn)))
#?(:cljs
(update-last [v afn]
(update-last-vector v afn)))
#?(:clj String :cljs string)
(update-first [s afn]
(let [rests (subs s 1 (count s))
newb (afn (nth s 0))]
(if (identical? i/NONE newb)
rests
(str newb rests))))
(update-last [s afn]
(let [last-idx (-> s count dec)
newl (afn (nth s last-idx))
begins (subs s 0 last-idx)]
(if (identical? i/NONE newl)
begins
(str begins newl)
)))
#?(:cljs cljs.core/MapEntry)
#?(:cljs
(update-first [e afn]
(cljs.core/->MapEntry (-> e key afn) (val e) nil)))
#?(:cljs
(update-last [e afn]
(cljs.core/->MapEntry (key e) (-> e val afn) nil)))
#?(:clj Object :cljs default)
(update-first [l val]
(update-first-list l val))
(update-last [l val]
(update-last-list l val)))
(extend-protocol GetExtremes
#?(:clj clojure.lang.IPersistentVector :cljs cljs.core/PersistentVector)
(get-first [v]
(nth v 0))
(get-last [v]
(peek v))
#?(:clj Object :cljs default)
(get-first [s]
(first s))
(get-last [s]
(last s))
#?(:cljs cljs.core/MapEntry)
#?(:cljs
(get-first [e]
(key e)))
#?(:cljs
(get-last [e]
(val e)))
#?(:clj String :cljs string)
(get-first [s]
(nth s 0))
(get-last [s]
(nth s (-> s count dec))
))
(extend-protocol FastEmpty
nil
(fast-empty? [_] true)
#?(:clj clojure.lang.IPersistentVector :cljs cljs.core/PersistentVector)
(fast-empty? [v]
(= 0 (vec-count v)))
#?(:clj clojure.lang.ITransientVector :cljs cljs.core/TransientVector)
(fast-empty? [v]
(= 0 (transient-vec-count v)))
#?(:clj Object :cljs default)
(fast-empty? [s]
(empty? s)))
(defn- do-keypath-transform [vals structure key next-fn]
(let [newv (next-fn vals (get structure key))]
(if (identical? newv i/NONE)
(if (sequential? structure)
(i/srange-transform* structure key (inc key) (fn [_] []))
(dissoc structure key))
(assoc structure key newv))))
(defrichnav
^{:doc "Navigates to the specified key, navigating to nil if it does not exist.
Setting the value to NONE will remove it from the collection."}
keypath*
[key]
(select* [this vals structure next-fn]
(next-fn vals (get structure key)))
(transform* [this vals structure next-fn]
(do-keypath-transform vals structure key next-fn)
))
(defrichnav
^{:doc "Navigates to the key only if it exists in the map. Setting the value to NONE
will remove it from the collection."}
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)
(do-keypath-transform vals structure k next-fn)
structure)))
(defrichnav nthpath*
^{:doc "Navigates to the given position in the sequence. Setting the value to NONE
will remove it from the sequence. Works for all sequence types."}
[i]
(select* [this vals structure next-fn]
(next-fn vals (nth structure i)))
(transform* [this vals structure next-fn]
(if (vector? structure)
(let [newv (next-fn vals (nth structure i))]
(if (identical? newv i/NONE)
(i/srange-transform* structure i (inc i) (fn [_] []))
(assoc structure i newv)))
(i/srange-transform* ; can make this much more efficient with alternate impl
structure
i
(inc i)
(fn [[e]]
(let [v (next-fn vals e)]
(if (identical? v i/NONE)
[]
[v])
))))))
(defrecord SrangeEndFunction [end-fn])
;; done this way to maintain backwards compatibility
(defn invoke-end-fn [end-fn structure start]
(if (instance? SrangeEndFunction end-fn)
((:end-fn end-fn) structure start)
(end-fn structure)
))
(defn- insert-before-index-list [lst idx val]
;; an implementation that is most efficient for list style structures
(let [[front back] (split-at idx lst)]
(concat front (cons val back))))
(extend-protocol InsertBeforeIndex
nil
(insert-before-idx [_ idx val]
(if (= 0 idx)
(list val)
(throw (ex-info "For a nil structure, can only insert before index 0"
{:insertion-index idx}))))
#?(:clj java.lang.String :cljs string)
(insert-before-idx [aseq idx val]
(apply str (insert-before-index-list aseq idx val)))
#?(:clj clojure.lang.LazySeq :cljs cljs.core/LazySeq)
(insert-before-idx [aseq idx val]
(insert-before-index-list aseq idx val))
#?(:clj clojure.lang.IPersistentVector :cljs cljs.core/PersistentVector)
(insert-before-idx [aseq idx val]
(let [front (subvec aseq 0 idx)
back (subvec aseq idx)]
(into (conj front val) back)))
#?(:clj clojure.lang.IPersistentList :cljs cljs.core/List)
(insert-before-idx [aseq idx val]
(cond (= idx 0)
(cons val aseq)
:else (insert-before-index-list aseq idx val))))

View file

@ -1,16 +0,0 @@
(ns com.rpl.specter.protocols)
(defprotocol StructureValsPath
(select-full* [this vals structure next-fn])
(update-full* [this vals structure next-fn]))
(defprotocol StructurePath
(select* [this structure next-fn])
(update* [this structure next-fn]))
(defprotocol Collector
(collect-val [this structure]))
(defprotocol StructureValsPathComposer
(comp-paths* [paths]))

View file

@ -0,0 +1,27 @@
(ns com.rpl.specter.protocols)
(defprotocol RichNavigator
"Do not use this protocol directly. All navigators must be created using macros
in com.rpl.specter namespace."
(select* [this vals structure next-fn]
"An implementation of `select*` must call `next-fn` on each
subvalue of `structure`. The result of `select*` is specified
as follows:
1. `NONE` if `next-fn` never called
2. `NONE` if all calls to `next-fn` return `NONE`
3. Otherwise, any non-`NONE` return value from calling `next-fn`
")
(transform* [this vals structure next-fn]
"An implementation of `transform*` must use `next-fn` to transform
any subvalues of `structure` and then merge those transformed values
back into `structure`. Everything else in `structure` must be unchanged."))
(defprotocol Collector
"Do not use this protocol directly. All navigators must be created using
macros in com.rpl.specter namespace."
(collect-val [this structure]))
(defprotocol ImplicitNav
(implicit-nav [obj]))

View file

@ -0,0 +1,100 @@
(ns com.rpl.specter.transients
#?(:cljs
(:require-macros [com.rpl.specter
:refer
[defnav]]))
(:use #?(:clj
[com.rpl.specter :only
[defnav]]))
(:require [com.rpl.specter.navs :as n]
[com.rpl.specter :refer [subselect selected?]]))
(defnav
^{:doc "Navigates to the specified key of a transient collection,
navigating to nil if it doesn't exist."}
keypath!
[key]
(select* [this structure next-fn]
(next-fn (get structure key)))
(transform* [this structure next-fn]
(assoc! structure key (next-fn (get structure key)))))
(defnav
^{:doc "Navigates to an empty (persistent) vector at the end of a transient vector."}
END!
[]
(select* [this structure next-fn]
(next-fn []))
(transform* [this structure next-fn]
(let [res (next-fn [])]
(reduce conj! structure res))))
(defn- t-get-first
[tv]
(nth tv 0))
(defn- t-get-last
[tv]
(nth tv (dec (n/transient-vec-count tv))))
(defn- t-update-first
[tv next-fn]
(assoc! tv 0 (next-fn (nth tv 0))))
(defn- t-update-last
[tv next-fn]
(let [i (dec (n/transient-vec-count tv))]
(assoc! tv i (next-fn (nth tv i)))))
(def FIRST!
"Navigates to the first element of a transient vector."
(n/PosNavigator t-get-first t-update-first))
(def LAST!
"Navigates to the last element of a transient vector."
(n/PosNavigator t-get-last t-update-last))
#?(
:clj
(defn- select-keys-from-transient-map
"Selects keys from transient map, because built-in select-keys uses
`find` which is unsupported."
[m m-keys]
(loop [result {}
m-keys m-keys]
(if-not (seq m-keys)
result
(let [k (first m-keys)
;; support Clojure 1.6 where contains? is broken on transients
item (get m k ::not-found)]
(recur (if-not (identical? item ::not-found)
(assoc result k item)
result)
(rest m-keys))))))
:cljs
(defn- select-keys-from-transient-map
"Uses select-keys on a transient map."
[m m-keys]
(select-keys m m-keys)))
(defnav
^{:doc "Navigates to the specified persistent submap of a transient map."}
submap!
[m-keys]
(select* [this structure next-fn]
(next-fn (select-keys-from-transient-map structure m-keys)))
(transform* [this structure next-fn]
(let [selected (select-keys-from-transient-map structure m-keys)
res (next-fn selected)]
(as-> structure %
(reduce (fn [m k]
(dissoc! m k))
% m-keys)
(reduce-kv (fn [m k v]
(assoc! m k v))
% res)))))

View file

@ -0,0 +1,68 @@
(ns com.rpl.specter.util-macros)
(defmacro doseqres [backup-res [n aseq] & body]
`(reduce
(fn [curr# ~n]
(let [ret# (do ~@body)]
(if (identical? ret# ~backup-res)
curr#
(if (reduced? ret#) (reduced ret#) ret#))))
~backup-res
~aseq))
(defn- gensyms [amt]
(vec (repeatedly amt gensym)))
(defmacro mk-comp-navs []
(let [impls (for [i (range 3 20)]
(let [[fsym & rsyms :as syms] (gensyms i)]
`([~@syms] (~'comp-navs ~fsym (~'comp-navs ~@rsyms)))))
last-syms (gensyms 19)]
`(defn ~'comp-navs
([] ~'com.rpl.specter.impl/STAY*)
([nav1#] nav1#)
([nav1# nav2#] (~'com.rpl.specter.impl/combine-two-navs nav1# nav2#))
~@impls
([~@last-syms ~'& rest#]
(~'comp-navs
(~'comp-navs ~@last-syms)
(reduce ~'comp-navs rest#))))))
;;TODO: move these definitions somewhere else
(defn late-fn-record-name [i]
(symbol (str "LateFn" i)))
(defn late-fn-record-constructor-name [i]
(symbol (str "->LateFn" i)))
(defn- mk-late-fn-record [i]
(let [fields (concat ['fn] (for [j (range i)] (symbol (str "arg" j))))
dparams (gensym "dynamic-params")
resolvers (for [f fields]
`(~'late-resolve ~f ~dparams))]
`(defrecord ~(late-fn-record-name i) [~@fields]
~'LateResolve
(~'late-resolve [this# ~dparams]
(~@resolvers)))))
(defmacro mk-late-fn-records []
(let [impls (for [i (range 20)] (mk-late-fn-record i))]
`(do ~@impls)))
(defmacro mk-late-fn []
(let [f (gensym "afn")
args (gensym "args")
cases (for [i (range 19)]
[i
(let [gets (for [j (range i)] `(nth ~args ~j))]
`(~(late-fn-record-constructor-name i)
~f
~@gets))])]
`(defn ~'late-fn [~f ~args]
(case (count ~args)
~@(apply concat cases)
(throw (ex-info "Cannot have late function with more than 18 args" {}))))))

View file

@ -0,0 +1,138 @@
(ns com.rpl.specter.zipper
#?(:cljs (:require-macros
[com.rpl.specter
:refer [defnav nav declarepath providepath recursive-path]]))
#?(:clj
(:use
[com.rpl.specter :only [defnav nav declarepath providepath
recursive-path]]))
(:require [com.rpl.specter :as s]
[clojure.zip :as zip]))
(defnav zipper [constructor]
(select* [this structure next-fn]
(next-fn (constructor structure)))
(transform* [this structure next-fn]
(zip/root (next-fn (constructor structure)))))
(def VECTOR-ZIP (zipper zip/vector-zip))
(def SEQ-ZIP (zipper zip/seq-zip))
(def XML-ZIP (zipper zip/xml-zip))
(def ^{:doc "Navigate to the next element in the structure.
If no next element, works like STOP."}
NEXT
(s/comp-paths
(s/view zip/next)
(s/if-path zip/end?
s/STOP
s/STAY)))
(defn- mk-zip-nav [znav]
(nav []
(select* [this structure next-fn]
(let [ret (znav structure)]
(if ret (next-fn ret))))
(transform* [this structure next-fn]
(let [ret (znav structure)]
(if ret (next-fn ret) structure)))))
;; (multi-path RIGHT LEFT) will not navigate to the right and left
;; of the currently navigated element because locations aren't stable
;; like they are for maps/graphs. The path following RIGHT could
;; insert lots of elements all over the sequence, and there's no
;; way to determine how to get "back".
(def ^{:doc "Navigate to the element to the right.
If no element there, works like STOP."}
RIGHT (mk-zip-nav zip/right))
(def ^{:doc "Navigate to the element to the left.
If no element there, works like STOP."}
LEFT (mk-zip-nav zip/left))
(def DOWN (mk-zip-nav zip/down))
(def UP (mk-zip-nav zip/up))
(def ^{:doc "Navigate to the previous element.
If this is the first element, works like STOP."}
PREV (mk-zip-nav zip/prev))
(def RIGHTMOST (s/view zip/rightmost))
(def LEFTMOST (s/view zip/leftmost))
(defn- inner-insert [structure next-fn inserter mover backer]
(let [to-insert (next-fn [])
inserts (reduce
(fn [z e] (-> z (inserter e) mover))
structure
to-insert)]
(if backer
(reduce (fn [z _] (backer z)) inserts to-insert)
inserts)))
(defnav ^{:doc "Navigate to the empty subsequence directly to the
right of this element."}
INNER-RIGHT []
(select* [this structure next-fn]
(next-fn []))
(transform* [this structure next-fn]
(inner-insert structure next-fn zip/insert-right zip/right zip/left)))
(defnav ^{:doc "Navigate to the empty subsequence directly to the
left of this element."}
INNER-LEFT []
(select* [this structure next-fn]
(next-fn []))
(transform* [this structure next-fn]
(inner-insert structure next-fn zip/insert-left identity nil)))
(defnav NODE []
(select* [this structure next-fn]
(next-fn (zip/node structure)))
(transform* [this structure next-fn]
(zip/edit structure next-fn)))
(defnav ^{:doc "Navigate to the subsequence containing only
the node currently pointed to. This works just
like srange and can be used to remove elements
from the structure"}
NODE-SEQ []
(select* [this structure next-fn]
(next-fn [(zip/node structure)]))
(transform* [this structure next-fn]
(let [to-insert (next-fn [(zip/node structure)])
inserted (reduce zip/insert-left structure to-insert)]
(zip/remove inserted))))
(def ^{:doc "Navigate the zipper to the first element
in the structure matching predfn. A linear scan
is done using NEXT to find the element."}
find-first
(recursive-path [predfn] p
(s/if-path [NODE (s/pred predfn)]
s/STAY
[NEXT p])))
(declarepath ^{:doc "Navigate to every element reachable using calls
to NEXT"}
NEXT-WALK)
(providepath NEXT-WALK
(s/stay-then-continue
NEXT
NEXT-WALK))

View file

@ -0,0 +1,17 @@
package com.rpl.specter;
public class MutableCell {
private Object o;
public MutableCell(Object o) {
this.o = o;
}
public Object get() {
return o;
}
public void set(Object o) {
this.o = o;
}
}

View file

@ -0,0 +1,7 @@
package com.rpl.specter;
public class Util {
public static Object[] makeObjectArray(int size) {
return new Object[size];
}
}

View file

@ -1,241 +0,0 @@
(ns com.rpl.specter.core-test
(:use [clojure.test]
[clojure.test.check.clojure-test]
[com.rpl specter]
[com.rpl.specter test-helpers])
(:require [clojure.test.check
[generators :as gen]
[properties :as prop]]
[clojure.test.check :as qc]))
;;TODO:
;; test walk, codewalk
;; test keypath
;; test comp-structure-paths
(defn gen-map-with-keys [key-gen val-gen & keys]
(gen/bind (gen/map key-gen val-gen)
(fn [m]
(gen/bind
(apply gen/hash-map (mapcat (fn [k] [k val-gen]) keys))
(fn [m2]
(gen/return (merge m m2)))))))
(defspec select-all-keyword-filter
(for-all+
[kw gen/keyword
v (gen/vector (max-size 5
(gen-map-with-keys gen/keyword gen/int kw)))
pred (gen/elements [odd? even?])]
(= (select [ALL kw pred] v)
(->> v (map kw) (filter pred))
)))
(defspec select-pos-extreme-pred
(for-all+
[v (gen/vector gen/int)
pred (gen/elements [odd? even?])
pos (gen/elements [[FIRST first] [LAST last]])]
(= (select-one [(filterer pred) (first pos)] v)
(->> v (filter pred) ((last pos)))
)))
(defspec select-all-on-map
(for-all+
[m (max-size 5 (gen/map gen/keyword gen/int))]
(= (select [ALL LAST] m)
(for [[k v] m] v))
))
(deftest select-one-test
(is (thrown? Exception (select-one [ALL even?] [1 2 3 4])))
(is (= 1 (select-one [ALL odd?] [2 4 1 6])))
)
(deftest select-first-test
(is (= 7 (select-first [(filterer odd?) ALL #(> % 4)] [3 4 2 3 7 5 9 8])))
(is (nil? (select-first [ALL even?] [1 3 5 9])))
)
(defspec update-all-on-map
(for-all+
[m (max-size 5 (gen/map gen/keyword gen/int))]
(= (update [ALL LAST] inc m)
(into {} (for [[k v] m] [k (inc v)]))
)))
(defspec update-all
(for-all+
[v (gen/vector gen/int)]
(let [v2 (update [ALL] inc v)]
(and (vector? v2) (= v2 (map inc v)))
)))
(defspec update-all-list
(for-all+
[v (gen/list gen/int)]
(let [v2 (update [ALL] inc v)]
(and (seq? v2) (= v2 (map inc v)))
)))
(defspec update-all-filter
(for-all+
[v (gen/vector gen/int)
pred (gen/elements [odd? even?])
action (gen/elements [inc dec])]
(let [v2 (update [ALL pred] action v)]
(= v2 (map (fn [v] (if (pred v) (action v) v)) v))
)))
(defspec update-last
(for-all+
[v (gen/not-empty (gen/vector gen/int))
pred (gen/elements [inc dec])]
(let [v2 (update [LAST] pred v)]
(= v2 (concat (butlast v) [(pred (last v))]))
)))
(defspec update-first
(for-all+
[v (gen/not-empty (gen/vector gen/int))
pred (gen/elements [inc dec])]
(let [v2 (update [FIRST] pred v)]
(= v2 (concat [(pred (first v))] (rest v) ))
)))
(defspec update-filterer-all-equivalency
(prop/for-all
[v (gen/vector gen/int)]
(let [v2 (update [(filterer odd?) ALL] inc v)
v3 (update [ALL odd?] inc v)]
(= v2 v3))
))
(defspec update-with-context
(for-all+
[kw1 gen/keyword
kw2 gen/keyword
m (max-size 10 (gen-map-with-keys gen/keyword gen/int kw1 kw2))
pred (gen/elements [odd? even?])]
(= (update [(collect-one kw2) kw1 pred] + m)
(if (pred (kw1 m))
(assoc m kw1 (+ (kw1 m) (kw2 m)))
m
))))
(defn differing-elements [v1 v2]
(->> (map vector v1 v2)
(map-indexed (fn [i [e1 e2]]
(if (not= e1 e2)
i)))
(filter identity)))
(defspec update-last-compound
(for-all+
[v (gen/such-that #(some odd? %) (gen/vector gen/int))]
(let [v2 (update [(filterer odd?) LAST] inc v)
differing-elems (differing-elements v v2)]
(and (= (count v2) (count v))
(= (count differing-elems) 1)
(every? even? (drop (first differing-elems) v2))
))))
;; max sizes prevent too much data from being generated and keeps test from taking forever
(defspec update-keyword
(for-all+
[k1 (max-size 3 gen/keyword)
k2 (max-size 3 gen/keyword)
m1 (max-size 5
(gen-map-with-keys
gen/keyword
(gen-map-with-keys gen/keyword gen/int k2)
k1))
pred (gen/elements [inc dec])]
(let [m2 (update [k1 k2] pred m1)]
(= (assoc-in m1 [k1 k2] nil) (assoc-in m2 [k1 k2] nil))
(= (pred (get-in m1 [k1 k2])) (get-in m2 [k1 k2]))
)))
(defspec replace-in-test
(for-all+
[v (gen/vector gen/int)]
(let [res (->> v (map (fn [v] (if (even? v) (inc v) v))))
user-ret (->> v
(filter even?)
(map (fn [v] [v v]))
(apply concat))
user-ret (if (empty? user-ret) nil user-ret)]
(= (replace-in [ALL even?] (fn [v] [(inc v) [v v]]) v)
[res user-ret]
))))
(defspec replace-in-custom-merge
(for-all+
[v (gen/vector gen/int)]
(let [res (->> v (map (fn [v] (if (even? v) (inc v) v))))
last-even (->> v (filter even?) last)
user-ret (if last-even {:a last-even})]
(= (replace-in [ALL even?] (fn [v] [(inc v) v]) v :merge-fn (fn [curr new]
(assoc curr :a new)))
[res user-ret]
))))
(defspec srange-extremes-test
(for-all+
[v (gen/vector gen/int)
v2 (gen/vector gen/int)]
(let [b (setval START v2 v)
e (setval END v2 v)]
(and (= b (concat v2 v))
(= e (concat v v2)))
)))
(defspec srange-test
(for-all+
[v (gen/vector gen/int)
b (gen/elements (-> v count inc range))
e (gen/elements (range b (-> v count inc)))
]
(let [sv (subvec v b e)
predcount (fn [pred v] (->> v (filter pred) count))
even-count (partial predcount even?)
odd-count (partial predcount odd?)
b (update (srange b e) (fn [r] (filter odd? r)) v)]
(and (= (odd-count v) (odd-count b))
(= (+ (even-count b) (even-count sv))
(even-count v)))
)))
(deftest structure-path-directly-test
(is (= 3 (select-one :b {:a 1 :b 3})))
(is (= 5 (select-one (comp-paths :a :b) {:a {:b 5}})))
)
(defspec view-test
(for-all+
[i gen/int
afn (gen/elements [inc dec])]
(= (first (select (view afn) i))
(first (select (viewfn [i] (afn i)) i))
(afn i)
(update (view afn) identity i)
)))
(deftest selected?-test
(is (= [[1 3 5] [2 :a] [7 11 4 2 :a] [10 1 :a] []]
(setval [ALL (selected? ALL even?) END]
[:a]
[[1 3 5] [2] [7 11 4 2] [10 1] []]
))))
(deftest split-test
(let [data {:a [1 2 3 4] :b {:c :d}}]
(is (= (select (split [:a ALL even?] [:b :c]) data)
[2 4 :d]))
;;TODO: this behavior is highly confusing... any way to have split selectors go first and THEN updates applied? ... only affects updates... what if it matches both selectors?
;; maybe shouldn't allow splitting for ALL
(is (= (update [:a ALL (split [even? (view -)] odd?)]
inc
data)
{:a [2 0 4 -2] :b {:c :d}}
))))

View file

@ -1,32 +0,0 @@
(ns com.rpl.specter.test-helpers
(:use [clojure.test]
[clojure.test.check.clojure-test])
(:require [clojure.test.check
[generators :as gen]
[properties :as prop]]
[clojure.test.check :as qc]
[clojure.pprint :as pp]))
;; it seems like gen/bind and gen/return are a monad (hence the names)
(defmacro for-all+ [bindings & body]
(let [parts (partition 2 bindings)
vars (vec (map first parts))
genned (reduce
(fn [curr [v code]]
`(gen/bind ~code (fn [~v] ~curr)))
`(gen/return ~vars)
(reverse parts))]
`(prop/for-all [~vars ~genned]
~@body )))
(defn max-size [n {gen :gen}]
(gen/make-gen
(fn [rnd _size]
(gen rnd (if (< _size n) _size n)))))
(defn gen-tuple+ [& inputs]
(->> inputs
(map #(if (gen/generator? %) % (gen/return %)))
(apply gen/tuple)
))

View file

@ -0,0 +1,13 @@
(ns com.rpl.specter.cljs-test-helpers)
;; it seems like gen/bind and gen/return are a monad (hence the names)
(defmacro for-all+ [bindings & body]
(let [parts (partition 2 bindings)
vars (vec (map first parts))
genned (reduce
(fn [curr [v code]]
`(clojure.test.check.generators/bind ~code (fn [~v] ~curr)))
`(clojure.test.check.generators/return ~vars)
(reverse parts))]
`(clojure.test.check.properties/for-all [~vars ~genned]
~@body)))

View file

@ -0,0 +1,7 @@
(ns com.rpl.specter.cljs-test-runner
(:require [doo.runner :refer-macros [doo-tests]]
[com.rpl.specter.core-test]
[com.rpl.specter.zipper-test]))
(doo-tests 'com.rpl.specter.core-test
'com.rpl.specter.zipper-test)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
(ns com.rpl.specter.test-helpers
(:require [clojure.test.check
[generators :as gen]
[properties :as prop]]
[clojure.test])
(:use [com.rpl.specter :only [select transform]]
[com.rpl.specter :only [select* transform*]]))
;; it seems like gen/bind and gen/return are a monad (hence the names)
;; this is only for clj (cljs version in different file)
(defmacro for-all+ [bindings & body]
(let [parts (partition 2 bindings)
vars (vec (map first parts))
genned (reduce
(fn [curr [v code]]
`(gen/bind ~code (fn [~v] ~curr)))
`(gen/return ~vars)
(reverse parts))]
`(prop/for-all [~vars ~genned]
~@body)))
(defmacro ic-test [params-decl apath transform-fn data params]
(let [platform (if (contains? &env :locals) :cljs :clj)
is-sym (if (= platform :clj) 'clojure.test/is 'cljs.test/is)]
`(let [icfnsel# (fn [~@params-decl] (select ~apath ~data))
icfntran# (fn [~@params-decl] (transform ~apath ~transform-fn ~data))
regfnsel# (fn [~@params-decl] (select* ~apath ~data))
regfntran# (fn [~@params-decl] (transform* ~apath ~transform-fn ~data))
params# (if (empty? ~params) [[]] ~params)]
(dotimes [_# 3]
(doseq [ps# params#]
(~is-sym (= (apply icfnsel# ps#) (apply regfnsel# ps#)))
(~is-sym (= (apply icfntran# ps#) (apply regfntran# ps#))))))))

View file

@ -0,0 +1,122 @@
(ns com.rpl.specter.zipper-test
#?(:cljs (:require-macros
[cljs.test :refer [is deftest]]
[clojure.test.check.clojure-test :refer [defspec]]
[com.rpl.specter.cljs-test-helpers :refer [for-all+]]
[com.rpl.specter
:refer [declarepath providepath select select-one select-one!
select-first transform setval replace-in]]))
(:use
#?(:clj [clojure.test :only [deftest is]])
#?(:clj [clojure.test.check.clojure-test :only [defspec]])
#?(:clj [com.rpl.specter.test-helpers :only [for-all+]])
#?(:clj [com.rpl.specter
:only [declarepath providepath select select-one select-one!
select-first transform setval replace-in]]))
(:require #?(:clj [clojure.test.check.generators :as gen])
#?(:clj [clojure.test.check.properties :as prop])
#?(:cljs [clojure.test.check :as tc])
#?(:cljs [clojure.test.check.generators :as gen])
#?(:cljs [clojure.test.check.properties :as prop :include-macros true])
[com.rpl.specter :as s]
[com.rpl.specter.zipper :as z]))
(defspec zipper-end-equivalency-test
(for-all+
[v (gen/not-empty (gen/vector gen/int))
i (gen/vector gen/int)]
(= (setval s/END i v)
(setval [z/VECTOR-ZIP z/DOWN z/RIGHTMOST z/INNER-RIGHT] i v))))
(deftest zipper-multi-insert-test
(is (= [1 2 :a :b 3 :a :b 4]
(setval [z/VECTOR-ZIP
z/DOWN
z/RIGHT
z/RIGHT
(s/multi-path z/INNER-RIGHT z/INNER-LEFT)]
[:a :b]
[1 2 3 4])
(setval [z/VECTOR-ZIP
z/DOWN
z/RIGHT
z/RIGHT
(s/multi-path z/INNER-LEFT z/INNER-RIGHT)]
[:a :b]
[1 2 3 4]))))
(deftest zipper-down-up-test
(is (= [1 [2 3 5] 6]
(transform [z/VECTOR-ZIP
z/DOWN
z/RIGHT
z/DOWN
z/RIGHT
z/RIGHT
(s/multi-path
s/STAY
[z/UP z/RIGHT])
z/NODE]
inc
[1 [2 3 4] 5]))))
(deftest next-terminate-test
(is (= [2 [3 4 [5]] 6]
(transform [z/VECTOR-ZIP z/NEXT-WALK z/NODE number?]
inc
[1 [2 3 [4]] 5])))
(is (= [1 [3 [[]] 5]]
(setval [z/VECTOR-ZIP
z/NEXT-WALK
(s/selected? z/NODE number? even?)
z/NODE-SEQ]
[]
[1 2 [3 [[4]] 5] 6]))))
(deftest zipper-nav-stop-test
(is (= [1]
(transform [z/VECTOR-ZIP z/UP z/NODE] inc [1])))
(is (= [1]
(transform [z/VECTOR-ZIP z/DOWN z/LEFT z/NODE] inc [1])))
(is (= [1]
(transform [z/VECTOR-ZIP z/DOWN z/RIGHT z/NODE] inc [1])))
(is (= []
(transform [z/VECTOR-ZIP z/DOWN z/NODE] inc []))))
(deftest find-first-test
(is (= [1 [3 [[4]] 5] 6]
(setval [z/VECTOR-ZIP
(z/find-first #(and (number? %) (even? %)))
z/NODE-SEQ]
[]
[1 2 [3 [[4]] 5] 6]))))
(deftest nodeseq-expand-test
(is (= [2 [2] [[4 4 4]] 4 4 4 6]
(transform [z/VECTOR-ZIP
z/NEXT-WALK
(s/selected? z/NODE number? odd?)
(s/collect-one z/NODE)
z/NODE-SEQ]
(fn [v _]
(repeat v (inc v)))
[1 [2] [[3]] 3 6]))))