Fork me on GitHub
#clojure-spec
<
2016-09-22
>
stuartmitchell00:09:46

so you are saying :thing1/id (non aliased namespace) used everywhere we were thinking that, but it doesn't seem to look like the examples (which I assume are best practice)

stuartmitchell00:09:04

I guess you loose the uniqueness of the keywords though.

hiredman00:09:18

what uniqueness?

hiredman00:09:20

in five years on a large clojure project we had several shakeups where namespaces were changed or renamed in mass, a long with regular day to day code churn

stuartmitchell00:09:46

for instance if i define :spec/thing1 in library code it could conflict with someone else defining :spec/thing1

hiredman00:09:00

use a better namespace then

hiredman00:09:18

my.orgs.spec/thing1

stuartmitchell00:09:33

yeah good point, I was just pondering what the differences would be

hiredman00:09:53

data is something that can be exchanged with other systems, stored long term, etc, so using names that reflect the organization of code in one codebase isn't the best

jmglov12:09:55

Just noticed you here. 😉

jmglov12:09:50

I think I'm running into an infinite loop with a custom generator. I need an atom holding a map with specific keys, which seems pretty straightforward:

(s/def ::foo (s/map-of ::foo/id ::foo/event))
(s/def ::bar (s/map-of ::bar/country ::bar/event))
(s/def ::db (s/keys :req [::foo
                          ::bar]))

;; Give these predicates names so spec's error messages are clear
(defn- atom? [x] (instance? clojure.lang.Atom x))
(defn- deref-db? [x] (s/valid? ::db @x))

(s/def ::db-atom (s/and atom? deref-db?))
So far, so good:
user> (s/explain ::db-atom {::foo {}, ::bar {}})
val: #:user{:foo {}, :bar {}} fails spec: :user/db-atom predicate: atom?
nil
user> (s/explain ::db-atom (atom 42))
val: #atom[42 0x702dec31] fails spec: :user/db-atom predicate: deref-db?
nil
user> (s/explain ::db-atom (atom {::foo {}, ::bar {}}))
Success!
nil

jmglov13:09:51

However, using a custom generator is getting me into a world of trouble:

(s/def ::db-atom (s/with-gen
                   (s/and atom? deref-db?)
                   (fn [] (gen/fmap atom (s/gen ::db)))))
When I try to generate a value, my REPL becomes unresponsive for awhile and then eventually crashes with a really odd stacktrace:
user> (gen/generate (s/gen ::db-atom))
...

jmglov13:09:43

Can anyone spot my error?

manderson13:09:29

Thanks @seancorfield, just saw your reply. That does make sense. I think I need to rethink the way I organize my specs. Right now, I have everything in a single namespace and now I'm wondering if I should break these into sub-namespaces. I know that I can still collect my specs in 1 namespace and just add different namespaces to the keys. Not sure which is the best approach. How do you group your specs? I'm still trying to get a feel for the proper balance of the right amount of spec without becoming cumbersome.

manderson13:09:54

Ha, funny. I just actually read back through the rest of the messages and see @hiredman making some good suggestions to my very question... sounds like best practice is to de-couple top level namespace of a file from namespace of a keyword. So, all could be grouped in a file, but with explicit namespaces for keys instead of ::.

patrkris13:09:00

@manderson: @hiredman talked about specs for data that might be exchanged between systems. If you want to spec data used only inside your system, creating namespaces for that may be feasible. For example, if you have a component in a com.example.payments-processor with a factory function that takes an options map, you could potentially spec it inside com.example.payments-processor.options, and alias that namespace wherever you need to create the options map. Hope I make sense.

manderson13:09:55

Yep, that makes sense. I suppose there is no one right answer. Like I said, I'm still getting the feel for what works and what doesn't. We're building our first real app with this (as I imagine most are!) and running into pain points with name conflicts and a large spec file, so I've been thinking of how to best maintain it.

patrkris13:09:29

Yes. I have been thinking a lot about this too. The problem is that you can't use aliased namespaces in specs if there isn't actually a file containing that namespace. In those cases you need to spell out the whole namespace of the spec, which can be verbose.

manderson13:09:37

Hm. that's a good point.

manderson13:09:47

It's kind of similar to the question of lots of fn's in a single namespace so only 1 to require, or fn's spread over ns's, which is more modular, but adds complexity. With spec, you would have to keep up with more ns's, but then you could alias them for simpler inline usage... It seems to encourage simplifying your model domain as much as possible in terms of defining it, which is probably a good thing.

seancorfield16:09:49

There are workarounds for the alias issue that let you introduce aliases for non-existent namespaces.

seancorfield16:09:34

@manderson: we group our data specs into files organized for what they spec, primarily, with almost no code in those files. Then we require them into namespaces that use those specs (i.e., where the code / functions are).

seancorfield16:09:41

That's probably not a very helpful answer without you knowing a lot more about our codebase 😸

manderson17:09:59

Thanks @seancorfield, that's similar to what we're doing now, but it's a single spec file for a new app with a changing domain so it's been a bit of a pain. I think it's just going to take some trial and error to work out the kinks.

seancorfield17:09:22

Yeah, we’re still evolving our approach. One of the issues for us, particularly, is that our domain model spec is the most extensive part and we’re working to automate the validation and conformance from our API spec to our domain model spec, so we’re moving toward dynamic derivation of API specs from domain model specs, which I hadn’t considered at first… so that’s changing the dependencies between our spec namespaces as well as how we use the specs in the first place. And we haven’t even gotten very far into spec’ing functions at this point (spec’ing the domain model and doing data validation and transformation has proved much more valuable for our initial focus).

kkruit17:09:36

I've noticed that, for specs that are specific to your application, clojure src is using them in a sub-namespace e.g. all the specs for clojure.core are in clojure.core.specs. This has seemed to work okay for me.

kkruit17:09:30

@patrkris I've been using create-ns to make namespaces for that issue.

seancorfield17:09:25

That’s nicer than the workaround I was using, from Alex. I think I’ll switch to that @kkruit — thanks!

kkruit17:09:42

no problem

mpenet19:09:33

it's just an util fn, but maybe that's what you're refering to @seancorfield

seancorfield19:09:33

That’s a big file @mpenet — are you referring to a particular part of it?

mpenet19:09:22

it's just a wrapper for create-ns+alias

seancorfield19:09:12

Ah, from another project...

mpenet19:09:04

it's a 3 line fn, as i said, create-ns + alias

seancorfield19:09:42

Since your alias is so long, it took me a while to realize it even was an alias… and the args are the opposite way round to alias which made it look even stranger:

(x/ns-as 'qbits.alia.cluster-options.ssl-options
       'cluster-options.ssl-options)
as opposed to
(alias 'cluster-options.ssl-options (create-ns 'qbits.alia.cluster-options.ssl-options))

seancorfield19:09:24

Given that alias will probably be modified to auto-create the ns, I’m preferring alias directly since I can probably just remove the create-ns call at some point.

mpenet19:09:52

you have a point

mpenet19:09:35

I just wanted to get rid of "qbits.alia" if I recall, since I used this single file for the whole project I still like to keep some ns'ing

nicola19:09:07

We had discussion on clojure-russia hangout about clojure.spec & namespaced keys and there is an idea by @prepor: we have reader macro to do ::key => :my.ns/key , we have sugar to do (require ... :as a) ::a/key => :my.required.ns/key. But there are a lot of cases where we need something like ::.subns/key => my.ns.subns/key. For real world deeply nested data-structures create a lot of namespaces just for fully qualified keys for spec looks like overhead...

patrkris21:09:33

@seancorfield: what was that workaround you mentioned earlier? > That’s nicer than the workaround I was using, from Alex. I think I’ll switch to that @kkruit — thanks!

patrkris21:09:48

@seancorfield: I mean the one from Alex

seancorfield21:09:29

Oh, that was to use in-ns twice which is what something in core does if I recall.

seancorfield21:09:45

I doubt it's still available in the scroll back and I don't remember what code he linked to.

noprompt23:09:36

has anyone been able to bend spec to parse and conform strings?