Fork me on GitHub

I’m working with instants and ended up with this code:

(defn years-ago
  "Given a number of years, return an Instant that long ago."
  (-> (LocalDate/now) (.minusYears y) .atStartOfDay (.atOffset ZoneOffset/UTC) .toInstant))

(defn age-18-120?
  "Given an Instant, return true if it represents a date of birth such
  that someone would be between 18 and 120 years old."
  (s/inst-in-range? (years-ago 120) (years-ago 18) i))

(s/def ::date-of-birth (s/with-gen age-18-120?
                         (fn [] (s/gen (s/inst-in (years-ago 120) (years-ago 18))))))
Could this be done more cleanly? (without the duplication of the range of years)



(s/def ::age-18-120? (s/inst-in (years-ago 120) (years-ago 18)))
=> :kafka-google-connector.runner/age-18-120?
(defn age-18-120? [x] (s/valid? ::age-18-120? x))
=> #'kafka-google-connector.runner/age-18-120?
(s/def ::date-of-birth ::age-18-120?)
=> :kafka-google-connector.runner/date-of-birth
(s/exercise ::date-of-birth 1)
=> ([#inst"1970-01-01T00:00:00.000-00:00" #inst"1970-01-01T00:00:00.000-00:00"])


though the age-18-120? fn seems totally redundant at that point, but if you still wanted it


No, because the years-ago calls must happen when the predicate is applied, not just once at compile time.


riiiiight, makes sense


And I'm assuming the with-gen generator-returning fn is called each whenever the spec is exercised / test-gen'd but that's not really as important since tests are short-lived, whereas the spec has to be long-lived and check the correct range of dob each time it's conformed / used for validation.

Oliver George09:07:38

@seancorfield: could "now" be part of the data structure you spec/validate? that makes it a simple input rather than implied context.

Oliver George11:07:19

Will it make sense to update defn to allow a spec to be provided as metadata? seems like it could overlap with pre/post...

(defn option-match
  "Default search for local datasource: case-insensitive substring match"
  [simple? option query]
  {:args (s/cat :simple? boolean? :option ::option :query string?)}

Alex Miller (Clojure team)11:07:56

No, we won't be doing that


So, specs can also be used to convert invalid data into valid data? I’m reading this…

Usage: (conformer f)
       (conformer f unf)
takes a predicate function with the semantics of conform i.e. it should return either a
(possibly converted) value or :clojure.spec/invalid, and returns a
spec that uses it as a predicate/conformer. Optionally takes a
second fn that does unform of result of first
So how do you use the feature to get the response (possibly converted)?


@seantempesta: you have to provide a function that returns :invalid or a result that is the converted value


(conformer (fn [a] (if (= a "one") 1 :clojure.spec/invalid)))


holy crap, this is great!


@ghadi: one simple option that I see is to move the cached data to the map meta-data, this way the map validation can stay simple


But I’d like to validate the cached data as well. For purposes of the code, it’s not valid if those keys aren’t there.


(That was clumsily worded, but you know what I mean … the code expects those keys.)


@glv: I had a different idea, what you think on this:


here, I used a conformer to remove the namespaced keys before doing the map validation, but checking the keys before doing it


this way all namespaced keys will be validated, then removed just to check the rest of the map


Hmm … that’s better than my current horrific solution. 🙂


@olivergeorge: Interesting idea… The data structure comes from the database and we already decorate it with some computed fields that are transient (short-lived) so adding a current timestamp to the data wouldn’t be a hardship.


I’m trying to use spec-test/test in my clojure.test tests (that was a lot of test :)). When I try to realize the result of test I get the following exception

java.lang.ClassCastException: clojure.lang.AFunction$1 cannot be cast to clojure.lang.MultiFn, compiling:(clojure/test/check/clojure_test.cljc:95:1)


If I run the test outside of a deftest it works no problem


Does anyone have a strategy they are using to use spec-test/test within a deftest?


@alexmiller no worries … whenever things calm down is fine.


What is the idiomatic way to spec an empty :args seq? I currently use (s/cat).