Could someone shed a light on the following behavior please?
Let's assume I have this namespace with a spec:
(ns user.specs
(:require [clojure.alpha.spec :as s]
[clojure.alpha.spec.gen :as gen]
[clojure.string :as str]))
# Non-blank string of 20 to 50 ascii chars.
(s/def ::text (s/with-gen
(s/and string? #(not (str/blank? %)))
#(gen/such-that
(complement str/blank?)
(gen/fmap
clojure.string/join
(gen/vector
(gen/char)
20 50)))))
Now I want to reuse this spec.
(in-ns 'user)
(require '[user.specs :as su])
=> nil
(def kws [::dir
::ns])
=> #'user/kws
(s/def ::dir ::su/text)
=> :user/dir
(s/def ::ns string?)
=> :user/ns
(s/register ::spec (s/schema* kws))
=> :user/spec
When exercising the last spec, I get an error:
(s/exercise ::spec)
Error printing return value (IllegalArgumentException) at clojure.core/-cache-protocol-fn (core_deftype.clj:583).
No implementation of method: :conform* of protocol: #'clojure.alpha.spec.protocols/Spec found for class: clojure.lang.Keyword
However, if I redef the ::dir
spec usinig s/register
and s/get-spec
instead of s/def
, no problem:
(s/register ::dir (s/get-spec ::su/text))
=> :user/dir
(s/exercise ::spec)
=>
([#:user{:dir "teôÆ>EüáéNj¬u}zþs²DÍ$", :ns ""}
#:user{:dir "teôÆ>EüáéNj¬u}zþs²DÍ$", :ns ""}]
[#:user{:dir ":éû,@Î|)Q«óCS\t´ÿ4ÚÝܺ»Ân5Zq", :ns ""}
#:user{:dir ":éû,@Î|)Q«óCS\t´ÿ4ÚÝܺ»Ân5Zq", :ns ""}]
... elided
I'm assuming, from the error message, that with s/def
, spec resolves ::dir
as the literal ::su/text
keyword instead of the associated spec.
1) Why?
2) Is s/register
+ s/get-spec
an appropriate solution?
I'm trying to reuse a "utility" spec in a few places under domain specific names.
FWIW, I'm using spec-alpha2 in order to build specs dynamically and benefit from schema
+ select
.
Aliasing specs like (s/def ::dir ::su/text)
is not currently working in spec 2, which is still a work in progress.