Last active
April 8, 2018 20:06
-
-
Save alexanderkiel/a1b583741008aa346733a85bda074725 to your computer and use it in GitHub Desktop.
An attempt to write a ring spec in clojure.spec.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns ring.spec | |
(:require [clojure.java.io :as io] | |
[clojure.spec :as s] | |
[clojure.spec.gen :as gen] | |
[clojure.string :as str]) | |
(:import [java.io File InputStream])) | |
;; ---- Internal Primitives --------------------------------------------------- | |
(def ^:private token-pattern #"[\!#$%&'*+-.^_`|0-9A-Za-z]+") | |
(defn special [] | |
(s/gen #{\\ \! \# \$ \% \& \' \* \+ \- \. \^ \_ \`})) | |
(defn token [] | |
(->> (gen/one-of [(gen/char-alphanumeric) (special)]) | |
(gen/vector) | |
(gen/fmap str/join))) | |
(s/def ::token (s/with-gen (s/and string? #(re-matches token-pattern %)) | |
token)) | |
(def ^:private lc-token-pattern #"[\!#$%&'*+-.^_`|0-9a-z]+") | |
(defn char-lc-alphanumeric [] | |
(gen/fmap char (gen/one-of [(gen/choose 48 57) | |
(gen/choose 97 122)]))) | |
(defn lc-token [] | |
(->> (gen/one-of [(char-lc-alphanumeric) (special)]) | |
(gen/vector) | |
(gen/fmap str/join))) | |
(s/def ::lc-token (s/with-gen (s/and string? #(re-matches lc-token-pattern %)) | |
lc-token)) | |
;; ---- Foreign Primitives ---------------------------------------------------- | |
(defn- input-stream [n] | |
(io/input-stream (byte-array n))) | |
(s/def ::io/input-stream (s/spec #(instance? InputStream %) | |
:gen #(gen/fmap input-stream (gen/choose 0 100)))) | |
;; ---- Primitives ------------------------------------------------------------ | |
(s/def :ring/server-port (s/with-gen (s/and nat-int? #(<= % 65535)) | |
#(gen/choose 0x41 0x5a))) | |
(s/def :ring/server-name string?) | |
(s/def :ring/remote-addr string?) | |
;; The request URI, excluding the query string and the "?" separator. | |
;; Must start with "/". | |
(s/def :ring/uri (s/with-gen (s/and string? #(str/starts-with? % "/")) | |
#(gen/fmap (fn [s] (str "/" s)) (s/gen string?)))) | |
(s/def :ring/query-string string?) | |
(s/def :ring/scheme #{:http :https}) | |
(s/def :ring/request-method (s/spec keyword? :gen #(s/gen #{:get :head :options :put :post :delete}))) | |
(s/def :ring/protocol (s/spec string? :gen #(s/gen #{"HTTP/1.1"}))) | |
(s/def :ring/header-name ::token) | |
(s/def :ring/lower-case-header-name ::lc-token) | |
(s/def :ring.request/headers (s/map-of :ring/lower-case-header-name string?)) | |
(s/def :ring.response/header-value | |
(s/with-gen (s/or :str string? | |
:seq (s/coll-of string? :gen-max 2)) | |
#(s/gen string?))) | |
(s/def :ring.response/headers (s/map-of :ring/header-name :ring.response/header-value)) | |
(s/def :ring.request/body ::io/input-stream) | |
(s/def :ring.response/body (s/with-gen | |
(s/or :str string? | |
:seq seq? | |
:file #(instance? File %) | |
:in ::io/input-stream) | |
#(s/gen string?))) | |
(s/def :ring/status (s/with-gen (s/and nat-int? #(<= 100 % 599)) | |
#(gen/choose 100 599))) | |
;; ---- Request Map ----------------------------------------------------------- | |
;; A request map is a Clojure map containing at least the following keys and | |
;; corresponding values: | |
(s/def :ring/request (s/keys :req-un [:ring/server-port | |
:ring/server-name | |
:ring/remote-addr | |
:ring/uri | |
:ring/scheme | |
:ring/request-method | |
:ring/protocol | |
:ring.request/headers] | |
:opt-un [:ring/query-string | |
:ring.request/body])) | |
;; ---- Response Map ---------------------------------------------------------- | |
;; A response map is a Clojure map containing at least the following keys and | |
;; corresponding values: | |
(s/def :ring/response (s/keys :req-un [:ring/status | |
:ring.response/headers] | |
:opt-un [:ring.response/body])) | |
;; ---- Handlers -------------------------------------------------------------- | |
;; Ring handlers constitute the core logic of the web application. Handlers are | |
;; implemented as Clojure functions that process a given request map to generate | |
;; and return a response map. | |
(s/def :ring/handler (s/fspec :args (s/cat :req :ring/request) | |
:ret :ring/response)) |
nat-int?
is 0 or pos?
- added in 1.9.0-alpha7
@ddossot Thanks, I changed the :ring/request-method
definition.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A few comments:
request-method
to a well-known set is problematic, IMO. What if people use:patch
or:purge
? Or whatever they may come up with.nat-int
a new addition in1.9
? Is it likepos?