Fork me on GitHub
#clojure-spec
<
2018-06-12
>
kurt-o-sys07:06:55

I'm using cljs-spec but I have issues with #objects...:

Call to #'... did not conform to spec:
In: [0 :data :from] val: #object[b 2018-06-11T00:00:00.000+02:00] fails spec: :week.data/from at: [:args :arg-0 :data :from] predicate: vector?
:cljs.spec.alpha/spec  #object[cljs.spec.alpha.t_cljs$spec$alpha40486]
:cljs.spec.alpha/value  ({:data {:from #object[b 2018-06-11T00:00:00.000+02:00]}})
:cljs.spec.alpha/args  ({:data {:from #object[b 2018-06-11T00:00:00.000+02:00]}})
:cljs.spec.alpha/failure  :instrument
so, what I try to spec is #object[b 2018-06-11T00:00:00.000+02:00].

kurt-o-sys07:06:48

how to spec that from-field?

djtango08:06:20

how robust do you want the spec? Does that date object contain a "type" field? If you want to check if the from field only is valid, you could pass it back into the date library to parse the string to check it's a valid time stamp?

kurt-o-sys08:06:05

there is no type-field (apparently) - I'm using a js-library for datetime handling (need timezones, and there are not many options). so, checking if the from field is a valid datetime would do...

djtango08:06:49

what's consuming the date object further downstream?

djtango08:06:59

If it's internal to your project, Is it good enough to spec it to be valid only for the code you know is going to use it?

kurt-o-sys08:06:07

other functions that do operations on it, like .plus etc...

kurt-o-sys08:06:25

yeah, that's good enough...

kurt-o-sys08:06:12

for dev, I just want to be sure I get an kind of datetime object, on which I can perform .plus and .minus operations (or something like that).

kurt-o-sys08:06:58

Now, I'm using any?, but that too generic 🙂.

djtango08:06:10

ah, fair. You could store your own representation of the date object as a map, then use the library only for doing manipulations (then translate it back to your date representation)

kurt-o-sys08:06:58

right... could do. that will be quite a lot of translations - not sure about performance impact here. But I may just try to do a .plus-operation on it and check if it works, or something similar. That may do as well... will try. Thx!

kurt-o-sys08:06:39

works fine, like it:

(s/def :luxon/plus #(try (.plus % #js {:days 1}) true (catch :default e false)))
(s/def :luxon/setZone #(try (.setZone % "Europe/Hamburg") true (catch :default e false)))
(s/def :luxon/startOf #(try (.startOf % "day") true (catch :default e false)))

kurt-o-sys08:06:11

(just testing if one can perform some operations, which should give a good enough indication that it's working)

roklenarcic08:06:10

is there a way to feed existing explain data to expound?

bbrinck11:06:47

You can call the expound printer directly:

(let [d (s/explain-data (s/coll-of int?) [1 2 ""])]
  (expound/printer d))
or
(let [d (s/explain-data (s/coll-of int?) [1 2 ""])
       printer (expound/custom-printer {:show-valid-values? true})]
   (printer d))

mpenet13:06:04

cross posting here since it got no answer in #clojure

Alex Miller (Clojure team)13:06:08

If you use s/def with a resolvable symbol, the symbol itself is the spec registry key (this is his function specs are stored). So, not a bug, and matches the docs afaict

mpenet13:06:27

not sure I follow, do you have an example?

Alex Miller (Clojure team)13:06:05

maybe I misunderstand what you are asking about, it’s not clear to me what you think the bug is

Alex Miller (Clojure team)13:06:40

the purpose of resolvable symbol k in that docstring is to support registering function specs named by their resolvable symbol. the example given is doing something other than that, which is not what it is for

mpenet13:06:17

(def x string?) (s/valid? x "sdf")

mpenet13:06:20

something like this then

mpenet13:06:31

yeah that makes more sense

mpenet13:06:00

I still don't get it then 😛

mpenet13:06:24

since we're talking about the first arg to s/def it's a bit odd

Alex Miller (Clojure team)13:06:26

x here should be a symbol referring to a var that is a function, to declare a function spec for x

Alex Miller (Clojure team)13:06:59

(s/fdef clojure.core/symbol? :args (s/cat :x any?) :ret any?) => (s/def clojure.core/symbol? (s/fspec :args (s/cat :x any?) :ret any?))

Alex Miller (Clojure team)13:06:14

the first is a shorthand for the second

Alex Miller (Clojure team)13:06:49

like, literally s/fdef is a macro that does that

Alex Miller (Clojure team)13:06:23

the registry has keys that are either keywords (pointing to data specs) or symbols (for function specs on the function on that symbol)

PB16:06:38

I have written a couple of fdef specs for my functions. What is the best way to test that in a test file? I'm looking at defspec and prop/for-all but the path forward doesn't seem obvious

taylor16:06:25

You can instrument the function to assert arguments conform to the :args spec on each call. You can also check functions to assert that it's returning valid values for randomly generated inputs.

taylor16:06:27

there's also exercise-fn

PB16:06:08

@U3DAE8HMG: Thanks for the response. Are you suggesting something along the lines of:

(deftest some-test
  (every? true? (map #(get-in % [:clojure.spec.test.check/ret :result])
                     (stest/check `sut/my-test {::stc/opts {:num-tests 1}}))))

taylor16:06:29

that should work, although you probably want more than 1 test, and I believe there's a helper function to make the stest/check result easier to examine

PB16:06:55

The problem with this is that it won't give me any information about why it failed

bbrinck18:06:08

@U08UTJ5PB maybe something like the following (untested)?:

(deftest some-test
  (let [results (stest/check `sut/my-test {::stc/opts {:num-tests 1}})]
  (is (every? true? (map #(get-in % [:clojure.spec.test.check/ret :result] results)
       (stest/summarize-results results))))

bbrinck18:06:52

FWIW, expound can also format the check results. https://github.com/bhb/expound#printing-results-for-check . You could call explain-results-str for the 2nd arg to is

cjsauer14:06:46

@U08UTJ5PB here is how I've done some generative testing in the past. Works very well: https://github.com/seesawlabs/digraph/blob/master/test/digraph/core_test.clj

PB16:06:50

I've been looking at that, it's still not clear to me how I run these in an automated test environment

bortexz17:06:48

I have a message coming from an API in a format like '(a b c d e f), and I’d like to propagate that message through a channel to my program with a proper format in a map, with keys for each of the properties that come in the list. I am using spec to conform the message, so I have something like (s/def msg (s/cat :a number? :b number?…)) and then when I call conform I do have a map like {:a a, :b b, :c c, …}, which is the format I want already (a map). Okay now I have a format I like to pass it on to the rest of the program, except I’d prefer to have namespaced keys, but s/conform does not seem to put namespaced keys even if they are specified as keys or values in the s/cat. Is there any other way to make this happen? Is this a valid approach to “transform” messages from the outside world to the inside language? It is really helpful as it is, but I am not sure it is the preferred way to do it. What other way then? How do you usually do it? Thanks!

taylor18:06:58

s/conform + s/cat does seem to preserve qualified keyword tags for me:

(s/conform (s/cat :foo/bar int? :what/ever string?) [1 "!"])
=> {:foo/bar 1, :what/ever "!"}

bortexz18:06:18

Oh, you’re right, it does for me too. I got confused because proto-repl prints it out like #:bitfinex-clj.spec.data{:ts 1, :open 2, :close 3, :high 4, :low 5, :volume 6}, but I have checked and they are namespaced.

taylor18:06:16

ya that's just the shorthand for when all the keys in the map share the same namespace

bortexz18:06:00

Another question: imagine I have all the namespaced keys already as predicates previously defined, so I can reuse them. Anyway to make this to not repeat each key? (s/def a (s/cat ::a ::a ::b ::b …))

taylor18:06:02

the only solution that comes to mind is writing a macro that outputs a s/cat like you want it, but before I did that I'd probably reconsider doing the transformations outside spec, maybe before/after conforming

taylor18:06:37

this seems like it might lend itself more naturally to a s/keys spec, and you could just handle the transformation before you conform (using zipmap or something)

bortexz18:06:28

hm…So the thing is the messages can come in different shapes and I use spec and s/or to tell me which type of message it is. But I probably can, instead of conform, to just check which message comes in without keeping the transformation, and then do the transformation outside. On the other hand, it seems so much related that it feels everything should be together (the same keys used by s/cat and zipmap, etc…), also because conform already does the transformation I want

bortexz18:06:36

Anyway, I’ll have a rethink about the current structure I am using. Thanks @U3DAE8HMG 🙂

taylor18:06:50

np, here's a macro that I think does what you want:

(defmacro kat [& ks]
  (let [ks' (mapcat #(repeat 2 %) ks)]
    `(s/cat ~@ks')))

👍 4