Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Spec by
As of 1.9.0-alpha13, specs in the registry lack :file metadata despite having :line, :column


user=> (require '[clojure.spec :as s])
user=> (-> (s/registry) (get :clojure.core.specs/arg-list) (meta))
{:line 1118, :column 5, :clojure.spec/name :clojure.core.specs/arg-list}
user=> (-> (s/registry) (get 'clojure.core/let) (meta))
{:line 1675, :column 5, :clojure.spec/name clojure.core/let}


This would be useful because:
* we could list all the specs defined in a project, by filtering the registry.
* we could read the source of a spec, like clojure.repl/source, for pretty formatting.

(specifically, for use in Codox https://github.com/weavejester/codox/pull/134 )

I had a quick look but couldn't see where the metadata is set.
Cheers

5 Answers

0 votes
by

Comment made by: alexmiller

You can use s/describe or s/form to grab the source of a spec now, btw.

0 votes
by
_Comment made by: floybix_

The following works in my tests. (For testing I used {{in-ns}}, {{@#'registry-ref}}, {{#'ns-qualify}})).

The approach is to set the registry item metadata after a def. It is not enough to set metadata on the {{def}}'d value because it is subsequently altered inside {{def}}.


(ns clojure.spec)
(alias 'c 'clojure.core)

(defmacro def
  [k spec-form]
  (let [k (if (symbol? k) (ns-qualify k) k)
        m (assoc (meta &form) :file *file*)]
    `(do
       (def-impl '~k '~(res spec-form) ~spec-form)
       (swap! registry-ref update '~k vary-meta c/merge ~m)
       '~k)))

(defmacro fdef
  [fn-sym & specs]
  (let [k (ns-qualify fn-sym)
        m (assoc (meta &form) :file *file*)]
    `(do
       (clojure.spec/def ~fn-sym (clojure.spec/fspec ~@specs))
       (swap! registry-ref update '~k vary-meta c/merge ~m)
       '~k)))




You can use s/describe or s/form to grab the source of a spec now, btw.


Yes, that's nice except for longer specs when line wrapping and indentation would help.
0 votes
by

Comment made by: wagjo

Note that current :line and :column meta are not pointing to the place where the spec was defined but to the clojure/spec.clj file, e.g. second example (c.c/let) points to (link: https://github.com/clojure/clojure/blob/c0326d2386dd1227f35f46f1c75a8f87e2e93076/src/clj/clojure/spec.clj#L1675 text: fspec-impl)

0 votes
by
_Comment made by: martinklepsch_

I've been looking into fixing this and for specs that aren't {{ident?}} we can use metadata or the map describing {{regex?}} specs.

For {{ident?}} specs I'm not sure how to handle the additional data — it could be stored in some other location of the registry which might be a viable path.

With any of these approaches it's unclear to me how to integrate this information into the return value of {{get-spec}} in a consistent way since it's return value may not be maps (therefore not extensible.)

Storing them in a different location in the registry would work as mentioned before but then we would need another function or protocol method to get information about source file/line/column.

0 votes
by
Reference: https://clojure.atlassian.net/browse/CLJ-2037 (reported by alex+import)
...