2017-08-22 3 views
2

clojure.spec.alpha ermöglicht eine nicht auflösbare Spezifikation zu verwenden, wenn ein neuen definieren: HierWie kann die Auflösbarkeit von Clojure-Spezifikationen überprüft werden?

(s/def :foo/bar (s/or :nope :foo/foo)) 

, :foo/foo nicht :foo/bar aufgelöst und so verwenden wird, eine Ausnahme von der Nutzung erhöhen:

(s/valid? :foo/bar 42) 
;; Exception Unable to resolve spec: :foo/foo clojure.spec.alpha/reg-resolve! (alpha.clj:69) 

Das passiert in meinem Code, wenn ich Tippfehler wie :my-ns/my-spec anstelle von ::my-ns/my-spec mache. Ich würde gerne diejenigen mit Unit-Tests fangen.

Tauchen im clojure.spec.alpha source code Ich fand ich alle Spezifikationen mit (keys (s/registry)) bekommen kann so mein Test wie folgt aussehen:

(ns my-ns.spec-test 
    (:require [clojure.test :refer :all] 
      [clojure.spec.alpha :as s] 
      ;; :require all the relevant namespaces to populate the 
      ;; global spec registry. 
      [my-ns.spec1] 
      [my-ns.spec2])) 

(deftest resolvable-specs 
    (doseq [spec (keys (s/registry))] 
    (is (resolvable? spec)))) 
    ;; ^^^^^^^^^^^ placeholder; that’s the function I want 

Leider gibt es nicht so etwas wie s/resolvable? in clojure.spec.alpha. Die einzige Lösung, die ich bisher gefunden habe ist (s/valid? spec 42) zu nennen und annehmen, dass es nicht eine Ausnahme ausgelöst bedeutet es auflösbar ist, aber es wird nicht geprüft Alle Branchen:

(s/def :int/int int?) 
(s/def :bool/bool bool?) 

(s/def :my/spec (s/or :int :int/int 
         :other (s/or :bool bool/bool 
            :nope :idont/exist))) 

(s/valid? :my/spec 1) ; <- matches the :int branch 
;; => true 

(s/valid? :my/spec :foo) 
;; Exception Unable to resolve spec: :idont/exist clojure.spec.alpha/reg-resolve! (alpha.clj:69) 

ich die Ausnahme Stacktrace sowie den Quellcode überprüft um zu sehen, ob ich irgendeine Funktion finden konnte, um eine Spezifikation vollständig zu lösen, ohne einen Testwert wie 42 oder :foo oben zu verwenden, aber konnte keine finden.

Gibt es einen Weg zu überprüfen, dass für eine bestimmte Spezifikation, alle Spezifikationen, auf die es sich bezieht, in allen seinen Filialen existieren?

Antwort

1

konnte ich folgendes tun:

(ns my-ns.utils 
    (:require [clojure.spec.alpha :as s])) 

(defn- unresolvable-spec 
    [spec] 
    (try 
    (do (s/describe spec) nil) 
    (catch Exception e 
     (if-let [[_ ns* name*] (re-matches #"Unable to resolve spec: :([^/]+)/(.+)$" (.getMessage e))] 
     (keyword ns* name*) 
     (throw e))))) 

(defn unresolvable? 
    "Test if a spec is unresolvable, and if so return a sequence 
    of the unresolvable specs it refers to." 
    [spec] 
    (cond 
    (symbol? spec) 
     nil 

    (keyword? spec) 
     (if-let [unresolvable (unresolvable-spec spec)] 
     [unresolvable] 
     (not-empty (distinct (unresolvable? (s/describe spec))))) 

    (seq? spec) 
     (case (first spec) 
     or (->> spec (take-nth 2) rest (mapcat unresolvable?)) 
     and (->> spec rest (mapcat unresolvable?)) 

     ;; undecidable 
     nil) 

    :default (unresolvable-spec spec))) 

(def resolvable? (complement unresolvable?)) 

Es funktioniert auf s/and 's und s/or' s, die meine minimale Anwendungsfall war:

(u/resolvable? :int/int) ;; => true 
(u/resolvable? :my/spec) ;; => false 
(u/unresolvable? :my/spec) ;; => (:idont/exist) 

Aber es hat einige Mängel :

  • Es erfindet das Rad neu; Ich denke, dass diese Spec-Walking-Funktionen existieren bereits irgendwo in clojure.spec.alpha
  • Es beruht auf Abfangen einer Ausnahme dann Parsen seiner Nachricht, weil (1) clojure.spec.alpha hat keine Funktion, die keine Ausnahme und (2) die Funktionen auslösen das raise man nicht etwas spezifischeres als Exception

Ich würde glücklich sein, jede andere Antwort zu akzeptieren, wenn jemand etwas robusteres hat.

Verwandte Themen