Small utility/extension library for clojure.spec
.
Subject to changes/breakage. Use at own risk.
At the moment it does only 3 things:
assuming
(require '[qbits.spex :as spex])
-
add
def-derived
which creates a keyword hierarchy behind the scenes (the hierarchy is internal/scoped to spex/ so no risk of cluttering the global one)(s/def ::foo string?) (spex/def-derived ::bar ::foo)
equivalent to:
(s/def ::foo string?) (s/def ::bar ::foo) (spex/derive ::bar ::foo)
but that also works with maps, here foo will derive from ::baz and ::bar
(spex/def-merged ::foo [::bar ::baz])
equivalent to:
(s/def ::foo (s/merge ::bar ::baz)) (spex/derive ::foo ::bar) (spex/derive ::foo ::baz)
So why do that? well you can then inspect the hierarchy, which can be handy:
(s/def-merged ::foo [::bar ::baz]) (spex/ancestors ::foo) => #{::bar ::baz} (spex/isa? ::foo ::bar) => true (spex/isa? ::foo ::baz) => true
-
adds a metadata registry for registered specs, it currently supports variants of
vary-meta!
,with-meta!
,meta
, addsunregister-meta!
andwith-doc
.you can then write code like:
(-> (s/def ::foo string?) (spex/vary-meta! assoc :something :you-need)) ;; and retrieve the values with spex/meta (spex/meta ::foo) => {:something :you-need}
Since we have this hierarchy in place we can integrate this into metadata retrieval. Told you it could be useful :)
(spex/def-derived ::bar ::foo) (spex/meta ::bar) => nil ;; remember we have meta data on :foo already, such that: (spex/meta ::foo) => {:something :you-need} ;; register meta at ::bar level (spex/vary-meta! ::bar assoc :another :key) ;; just the meta of ::bar (spex/meta ::bar) => {:another :key} ;; retrieve the meta for ::bar but also all its ancestors if you pass true to spex/meta (spex/meta ::bar true) => {:something :you-need, :another :key}
and
spex/with-doc
is just sugar on top of all this to add docstrings to specs(spex/with-doc ::foo "bla bla bla") (s/doc ::foo) => "bla bla bla"
All the functions that mutate the metadata of a spec return the spec key, that makes chaining easier, same goes for
spex/def-derived
:(-> (s/def ::foo string?) (spex/vary-meta! assoc :something :you-need) (cond-> something? (spex/vary-meta! assoc :something-else :you-might-need)))
The internal hierarchy is queriable just like the global keyword hierarchy, you can use
spex/isa?
spex/descendants
spex/ancestors
spex/parents
spex/derive
spex/underive
, which are just partially applied functions over the same functions in core with our own internal hierarchyspex/spec-hierarchy
.(s/def ::port (int-in-range? 1 65535)) (spex/def-derived ::redis-port ::port) (spex/isa? ::redis-port ::port) => true (spex/def-derived ::cassandra-port ::port) ;; list all things ::port (spex/descendants ::port) => #{::redis-port ::cassandra-port}))
This only works for aliases obviously.
-
adds a sugar to create namespaces within a ns. Ex: if you are in the user namespace
(spex/rel-ns 'foo.bar)
would create user.foo.bar
spex is available on Clojars.
Copyright © 2016 Max Penet
Distributed under the Eclipse Public License, the same as Clojure.