Passing runtime info to clojure.spec/conform

154 views
Skip to first unread message

Tommi Reiman

unread,
Sep 14, 2016, 10:33:05 AM9/14/16
to Clojure Dev
Hi,

I'm creating a library for runtime boundary/api conformation with clojure.spec to for ring-style apps. I would like to allow same spec to be used with different wire formats / parameter sets. Different formats have different requirements for the runtime conforming.

E.g. a user-id:

(s/def ::user-id integer?)

could be used as a path/query-parameter (needs string->integer conform) or in a JSON/EDN/Transit body (no conform should be done).

Problem is that I can't pass any runtime information to the current clojure.spec/conform. I got this working by using a dynamic Var (binding the needed information before calling conform and reading the info in the customer conformers), but this feels hacky and using dynamic var adds an extra runtime cost.

Question:

Would it be possible to add an options map as a third parameter to clojure.spec/conform*? clojure.spec/confom would have an extra 3-arity version with the options map. This would allow runtime information to be passed elegantly to the conformers.

Or is there another way to achieve this kind of functionality?

I'm new here and thought it would be better to get comments before writing an issue (or thinking of writing a patch) for this.

Here's an example I'm doing:

https://github.com/metosin/spec-tools#examples

regards,

Tommi

Stuart Halloway

unread,
Sep 14, 2016, 10:45:52 AM9/14/16
to cloju...@googlegroups.com
Hi Tommi,

I think you could accomplish what you want by composing specs, rather than by needing options.  This would have the benefit that all the other features to spec (generation, testing, etc.) would already work.

E.g. why isn't there a :my.ns.json/age and a :my.ns/age?  Specs are data, so you could invest in tools that e.g. make json specs out of the raw specs.

Stu


--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure-dev+unsubscribe@googlegroups.com.
To post to this group, send email to cloju...@googlegroups.com.
Visit this group at https://groups.google.com/group/clojure-dev.
For more options, visit https://groups.google.com/d/optout.

Tommi Reiman

unread,
Sep 15, 2016, 3:28:25 AM9/15/16
to Clojure Dev
Hi,

and thanks for the quick response. Having a tool to create differently conforming (json/string/no-op) specs from arbitrary raw specs seems like a valid and performant option for the runtime.

About reading the existing specs: if I have understood correctly, specs are actually reified protocols. To make a spec JSON conforming I think I need be doing the following:

1) read the spec data via `s/form`
2) walk the form (with multimethod to allow extension)
3) inject conformers to the leaf specs via s/and
4) store new new spec somewhere

Am I on the right path?

Tested with few combinations of composing:

I) integer? => (s/and string->integer integer?)
  * conform works, not the gen

II) integer? => (s/and integer? string->integer)
  * gen works, conform doesn't

Is there a way to get them both?

Another thing that we want to solve is the spec => JSON Schema conversion, for the api-docs like Swagger/OpenAPI. The initial transformers we did uses the s/form walking, but wanted to ask would it be possible for the Specs to be Records/Types instead of reified Protocols? Would it have a performance penalty? With this, we could just extend the JSONSchema Protocol for all core Records. This would also allow things like external generic spec-walkers (similar to https://github.com/metosin/schema-tools/blob/master/src/schema_tools/walk.cljc).

Tommi

keskiviikko 14. syyskuuta 2016 17.45.52 UTC+3 Stuart Halloway kirjoitti:
Hi Tommi,

I think you could accomplish what you want by composing specs, rather than by needing options.  This would have the benefit that all the other features to spec (generation, testing, etc.) would already work.

E.g. why isn't there a :my.ns.json/age and a :my.ns/age?  Specs are data, so you could invest in tools that e.g. make json specs out of the raw specs.

Stu
On Wed, Sep 14, 2016 at 8:14 AM, Tommi Reiman <tommi....@gmail.com> wrote:
Hi,

I'm creating a library for runtime boundary/api conformation with clojure.spec to for ring-style apps. I would like to allow same spec to be used with different wire formats / parameter sets. Different formats have different requirements for the runtime conforming.

E.g. a user-id:

(s/def ::user-id integer?)

could be used as a path/query-parameter (needs string->integer conform) or in a JSON/EDN/Transit body (no conform should be done).

Problem is that I can't pass any runtime information to the current clojure.spec/conform. I got this working by using a dynamic Var (binding the needed information before calling conform and reading the info in the customer conformers), but this feels hacky and using dynamic var adds an extra runtime cost.

Question:

Would it be possible to add an options map as a third parameter to clojure.spec/conform*? clojure.spec/confom would have an extra 3-arity version with the options map. This would allow runtime information to be passed elegantly to the conformers.

Or is there another way to achieve this kind of functionality?

I'm new here and thought it would be better to get comments before writing an issue (or thinking of writing a patch) for this.

Here's an example I'm doing:

https://github.com/metosin/spec-tools#examples

regards,

Tommi

--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure-dev...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages