metosin / malli

High-performance data-driven data specification library for Clojure/Script.
Eclipse Public License 2.0
1.43k stars 204 forks source link

Add :gen/resolve property #1043

Open jgdavey opened 2 months ago

jgdavey commented 2 months ago

This works much like :gen/gen, but will first attempt to resolve the fully-qualified symbol.

This separation means that malli schemas are free to carry around this extra data about where generators live, without having to have them loaded at runtime. Instead, generators declared in this way are dynamically loaded when used.

ikitommi commented 2 months ago

Thanks for the PR. Quick comments:

  1. CLJS tests fail currently (resolve is a macro)
  2. this enables running arbitrary code, so there should be a mechanism to disable this (if one reads serialised schemas from untrusted sources) - like there is with SCI.
[:int {:gen/resolve 'mount.core/stop}]
jgdavey commented 2 months ago

Thanks for the feedback!

CLJS tests fail currently (resolve is a macro)

I'm curious if there's a pattern that makes more sense for lazy-loading that would be regardless of platform.

this enables running arbitrary code, so there should be a mechanism to disable this (if one reads serialised schemas from untrusted sources) - like there is with SCI.

That's a good idea. I might look into sci or dynaload or somesuch for this kind of dynamic loading.

Taking a step back, is this idea useful? And if so, is this the way that it ought to be implemented? My desire is for a serializable schema (dependency-free) that can still point at more sophisticated generators that may or may not be available at runtime. My use-case is generators that do exist on the classpath in production, but are available to tests.

frenchy64 commented 1 month ago

IMO this is useful. Perhaps a simpler cross-platform implementation would be m/eval :gen/gen if it is not a generator, and then have users of the schema manually load the generator namespaces when needed.

(ns spec-ns
(def schema [:map {:gen/gen `(gen-ns/my-generator)} ...])

(ns gen-ns)
(defn my-generator [] ...)

(ns test-ns
  (:require spec-ns gen-ns))
(m/generate spec-ns/schema)

It would also be nice if :gen/gen could be a fn? that gets auto-called to retrieve the generator.