Fork me on GitHub
#clojure-spec
<
2016-12-27
>
curlyfry09:12:08

Hi, I have the following scenario:

(s/def ::name string?)
(s/def ::person (s/keys :req-un [::name]))

(s/def ::name (s/and string? #(str/starts-with? % "Jean")))
(s/def ::french-person (s/keys :req-un [::name]))
I want to keep these specs in the same file, but I also want to disambiguate the different ::name specs, without giving them different names. Is there any way to do this? Something like ::french/name vs just ::name, but that isn't working.

mpenet10:12:27

@curlyfry you ca use create-ns and alias for that

curlyfry10:12:07

@mpenet Cool, thanks! Can I create a concatenation of the "current" ns with a symbol using create-ns?

roelof13:12:35

I hope this is not a beginner question.

roelof13:12:48

I want to test a function that has as input something like this : [{ :name "Roelof", :place "internet"} {:name "Jane Doe" :place "unknown" }] and as output {"Roelof", "Jane Doe"} . If I want to test this , can I use the same idea as this :

(def email-regex #"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$")(s/def ::email-type (s/and string? #(re-matches email-regex %)))(s/def ::acctid int?)(s/def ::first-name string?)(s/def ::last-name string?)(s/def ::email ::email-type)(s/def ::person (s/keys :req [::first-name ::last-name ::email]                        :opt [::phone]))

roelof13:12:22

or what must I fill in if a use a fdef with :args and :ret and :fn

joshjones16:12:00

Perhaps your question is constructed in a way that makes answering it a bit difficult. For example, “input something like this” is ambiguous — are there always two maps in the input vector, or might there be more? And, do you really mean to have a single k/v pair map as the output, where both the key and value are strings? Or did you mean to make that a vector of the :names from the input? It’s clearing up this confusion that results in the things the admins don’t want in the channel, I think. But here’s some info that might be valuable to anyone new to spec, just a sample of how to construct what you’re asking for. What you want to do is ask, “what am I trying to spec”? In your case, it’s a function which takes a vector of maps, and returns a vector of strings. Those are your :args and :ret, and a decent sanity check for :fn is that the number of names you return should equal the number of maps you were given. So, here’s a spec based on that:

joshjones16:12:20

(ns clj.core
  (:require [clojure.spec :as s]
            [clojure.spec.gen :as gen]
            [clojure.spec.test :as stest]))

(defn get-names [v]
  (reduce #(conj % (:name %2)) [] v))

(s/def ::name string?)
(s/def ::place string?)

(s/def ::get-names-args (s/coll-of
                          (s/keys :req-un [::name ::place])
                          :kind vector?
                          :gen-max 5))

(s/fdef get-names
        :args (s/cat :v ::get-names-args)
        :ret (s/coll-of string? :kind vector?)
        :fn (fn [{{input-vector :v} :args, return-vector :ret}]
              (= (count input-vector) (count return-vector))))

joshjones16:12:08

You can now exercise and check the function with:

(s/exercise-fn `get-names)
(stest/check `get-names {:clojure.spec.test.check/opts {:num-tests 100}})