Fork me on GitHub
#clojure-spec
<
2017-08-15
>
bbrinck01:08:49

For anyone that uses Orchestra to instrument :fn and :ret specs, Expound 0.2.0 now supports pretty error messages for Orchestra errors. https://github.com/bhb/expound#using-orchestra

hmaurer10:08:45

Hi. I am having an issue with clojure.spec. This code throws an error: https://gist.github.com/hmaurer/d030f9ff9250482c6dea3e3738737863

hmaurer10:08:11

(I’ve added the error as a comment on the gist)

hmaurer10:08:53

I think this is because s/keys is a macro, but I am not sure how to get it to do what I want

hmaurer10:08:59

(dynamically build that spec from the map)

moxaj10:08:33

@hmaurer left a comment on the gist

hmaurer11:08:00

@moxaj thank you! So I assume I cannot / should not define specs at runtime? e.g. I can’t define specs based on my database schema after opening a connection?

moxaj11:08:27

eval is another option

hmaurer11:08:08

ah, I’ll try that. Is it an outright terrible idea to define specs at runtime though?

urbank11:08:26

Does anyone here define specs in the same file as the functions? Above the functions like haskell type annotations

souenzzo11:08:47

I just started to write my specs like that. Not sure if is the best practice or not.

urbank11:08:13

The second answer here gives a good arguments

misha13:08:28

I lean towards: data specs in a separate file(s), function specs – next to actual implementation. (Yet to see how it'll turn out in longer run)

urbank13:08:26

@U051HUZLD Right, that makes sense to. Because functions might share data specs, right?

misha13:08:39

@U489U338R yes, and, functions are more "implementation detail"-y than data, I think. But I'd start with everything in one file, and see what's up later in project.

didibus18:08:16

Ya, data spec in their own namespace gives you a clean domain model. And fn specs I put below the functions.

seancorfield19:08:22

Most of our specs are for data structures and they go in whatever is the appropriate namespace for those data structures -- which is often a separate ns but sometimes is a ns with some "utility" functions specific to the data structure. Where we spec functions we generally put the spec right above the function (like a type annotation).

seancorfield19:08:54

But, as noted, if you want your code -- a library -- to be usable by Clojure < 1.9 then you have to put specs in a separate and completely optional namespace.

seancorfield19:08:18

See, for example, how clojure.java.jdbc handles this (specs in a separate namespace on their own -- even for functions).

stathissideris12:08:59

Make clojure.spec behave more like schema in terms of keys strictness (no unknown keys allowed)

stathissideris12:08:09

disclaimer: untested, most likely buggy

stathissideris14:08:46

sure, but I wanted to be able to be strict conditionally

gfredericks14:08:23

oh sorry, I didn't look closely

hmaurer12:08:42

Re-iterating my earlier question, does anyone have a big objection to defining specs at runtime (with eval), based, .e.g, on the app’s database schema? I am a beginner so I am not sure if it is “good taste/practice” in the clojure world

misha13:08:12

it possible, but keep in mind, you cannot yet retract specs from registry as of now @hmaurer

misha13:08:17

so if your specs will change during application run time - make sure you migrate those appropriately

Alex Miller (Clojure team)13:08:18

well, you can if you reach in and munge the registry atom :) but that’s coming soon.

misha13:08:59

I meant in official way opieop

Alex Miller (Clojure team)13:08:07

@hmaurer I think dynamic generation of specs is fine

scaturr16:08:41

I am having a hard time understanding s/keys. I keep my specs organized in separate namespaces - i.e something like entity.clj and entity/spec.clj. I have the following used to spec a generic entity map

scaturr16:08:40

I define ::id to make importing it elsewhere easier, and then use it in ::entity/id because its my understanding that is how the :req field in ::entity is able to conform that specific key?

scaturr16:08:59

is that correct? am I doing this in a weird way?

scaturr16:08:05

mainly so some other “entity spec” can merge in ::entity

scaturr16:08:36

which implies todos would need an ::entity/id key

didibus19:08:33

I'm not sure I follow your logic. It seems to me your overcomplicating it.

didibus19:08:11

If conceptually the id of entity is supposed to be the same as the id of todo, the you s/def :some-ns/id. And in your spec for entity you say keys req [:some-ns/id]. And do tje same in your todo spec.

didibus19:08:28

Basically s/keys describes an associative structure which when used with :req says that the structure must have as keys the given list of namespaced keywords. If the keyword has a spec defined, it will be used to validate and conform the value of the key.

scaturr15:08:50

The todo example does use the :some-ns/id approach

scaturr15:08:22

But if I were to define (s/def ::entity/id ...) how can I then import that elsewhere?

scaturr15:08:31

This might just be a misunderstanding of clojure O_o

scaturr15:08:56

(:require [todos.entity.spec :refer [?])

scaturr15:08:19

or even if you use :as - since its already namespaced in the spec - is there a way to get at it?

scaturr15:08:28

thank you for the reply by the way 🙂

didibus16:08:42

You don't need to refer anything, just require it. (:require [todos.entity.spec]) Specs are always defined globally. All that needs to happen is your app must eval the s/def form before you use it. Require will do that.

didibus16:08:34

Also, ::entity/id doesn't make sense. It would be :entity/id

didibus16:08:47

So specs are namespaced. They're globally accessible from anywhere after they've been deffed, but they need to be prefixed with a namespace. This helps reduce name clash.

didibus16:08:45

So if you are in namespace todos.entity.spec and you s/def a spec, and you use the double colon ::, then you created :todos.entity.spec/your-spec

didibus16:08:11

Now if you are in say todos.app namespace, you can require todos.entity.spec and refer to the spec as :todos.entity.spec/your-spec

didibus16:08:10

If what you want it :entity/id instead of :your-ns/is you have two options

didibus16:08:47

Either define the spec like that, so don't use the double colon, you can (s/def :entity/id int?) for example

didibus16:08:22

This will be make it less portable though, since the chance of name clash are higher.

didibus17:08:05

Or you can define it prefixed with the full namespace using ::, and then when you use it elsewhere, you can alias the namespace as entity.

didibus17:08:36

`(ns ns1) (s/def ::id int?)` Then in ns2 `(ns ns2 (:require [ns1 :as entity]) (s/def ::user :entity/id)`

didibus17:08:06

As far as I know, you can't refer a spec yet. But you can kinda manually do it if you want

didibus17:08:12

(s/def ::id :some-other-ns/id)

csm18:08:32

So I have a cat spec that contains a UUID for one field, and another field is a coll-of maps, and each map has a :guid->UUID; the requirement is that one of the maps contain the UUID. I can express this in a spec, but I’m not sure how to make a generator for such a scenario

didibus18:08:16

Ya, data spec in their own namespace gives you a clean domain model. And fn specs I put below the functions.

csm19:08:14

clojure.spec.gen.alpha/uuid is sufficient for generating UUIDs, the problem is ensuring the first UUID appears in the second collection

gfredericks19:08:17

(gen/let [recs (gen/not-empty recs) special-uuid (gen/elements (map :guid recs))] [recs special-uuid])

hmaurer21:08:51

Hi. Is there a straightforward way to serialize the result of explain-data to JSON? I am getting an error on trying to serialize spec predicate functions

didibus22:08:30

Can you serialize to edn instead? That generally works better for Clojure.

souenzzo22:08:57

cheshire do it without extra settings

hmaurer10:08:39

@U2J4FRT2T it looks like it cannot encode functions (`:pred` key in the spec problems)

hmaurer10:08:13

@U0K064KQV I am consuming it from a JS frontend so EDN does not seem ideal

misha11:08:30

depends on what you plan to do with predicates.

hmaurer15:08:28

@U051HUZLD not much, I just want a string so I can know why validation failed for a specific field

misha15:08:29

then (map #(update % :../pred pr-str)) or something, prior to json-serialization

dergutemoritz16:08:32

Or just use s/explain-str instead of s/explain-data

dergutemoritz16:08:04

Or would that be too opaque? 🙂

didibus16:08:01

I always wished cheshire offered custom encoders.

hmaurer16:08:39

@U06GVE6NR too opaque; I need to know which fields contain errors 🙂

hmaurer16:08:23

@U051HUZLD I didn’t know the function pr-str; thank you!