美文网首页
clojure spec

clojure spec

作者: onedam | 来源:发表于2020-04-09 12:01 被阅读0次
  • clojure是一门动态类型的语言,在类型检查方面并没有c++/java 这种静态类型语言好用,
    所以多个模块之间进行接口参数传递时,由于接口文档设计不严谨等原因,
    总会发生接口参数类型错误,参数个数不正确等问题,给代码调试带来很大的挑战,
    因此在clojure 中,对接口参数的进行类型和范围的检查是非常必要的。
    为此,我们找到了clojure.spec这个库(https://clojure.github.io/clojure/branch-master/clojure.spec-api.html),
    正好可以解决以上问题。

  • 原来spec 只要在任何地方 s/def 任何地方都可以用. 因为都注册在了 (s/registry)
    体会到了类型的好处(以前主要是体会感觉到不灵活,不足). 但如果没类型,每个函数都要去手动写代码校验. 这个也很痛苦和低效.
    现在用spec 规定好每个参数的具体类型.虽然僵化了点. 但对团队规范/程序正确性来说,提升了一大步.值得.

  • spec 的另一个核心作用在于 配合 test.check 生成测试数据. 并且spec本身依赖 test.check
    (s/exercise-fn `slarp) ;非常强大的功能. 能把输入参数和结果 用结构化数据输出来. (注意:自动! good)
    (stest/check 'slarp)

  • 要用好工具,首先必须要学习使用工具,并且不断重复锻炼提升水平. 工具才能发挥作用.

实例 ring-spec


(ns ring.core.spec

(:require [clojure.spec.alpha:as s]

[clojure.spec.gen.alpha:as gen]

[clojure.string:as str]

[ring.core.protocols:as p]))

(defn- lower-case? [s]

(= s (str/lower-case s)))

(defn- trimmed? [s]

(= s (str/trim s)))

(defn- char-range [a b]

(map char (range (int a) (inc (int b)))))

(def ^:private lower-case-chars

(set (char-range \a \z)))

(def ^:private alphanumeric-chars

(set (concat (char-range \A \Z) (char-range \a \z) (char-range \0 \9))))

(def ^:private uri-chars

(into alphanumeric-chars #{\- \. \_ \~ \/ \+ \,}))

(def ^:private field-name-chars

(into alphanumeric-chars #{\! \# \$ \% \& \' \* \+ \- \. \^ \_ \` \| \~}))

(def ^:private whitespace-chars

#{(char 0x09) (char 0x20)})

(def ^:private visible-chars

(set (map char (range 0x21 (inc 0x7e)))))

(def ^:private obs-text-chars

(set (map char (range 0x80 (inc 0xff)))))

(def ^:private field-value-chars*

(into whitespace-chars visible-chars))

(def ^:private field-value-chars

(into field-value-chars* obs-text-chars))

(defn- field-name-chars? [s]

(every? field-name-chars s))

(defn- field-value-chars? [s]

(every? field-value-chars s))

(defn- gen-string [chars]

(gen/fmap str/join (gen/vector (gen/elements chars))))

(defn- gen-query-string []

(->> (gen/tuple (gen/not-empty (gen/string-alphanumeric)) (gen-string uri-chars))

(gen/fmap (fn [[k v]] (str k"=" v)))

(gen/vector)

(gen/fmap #(str/join "&" %))))

(defn- gen-method []

(gen/fmap keyword (gen/not-empty (gen-string lower-case-chars))))

(defn- gen-input-stream []

(gen/fmap #(java.io.ByteArrayInputStream. %) (gen/bytes)))

(defn- gen-exception []

(gen/fmap (fn [s] (Exception. s)) (gen/string-alphanumeric)))

;; Internal

(s/def :ring.core/error

  (-> #(instance? Throwable %) (s/with-gen gen-exception)))

(s/def :ring.http/field-name

  (-> (s/and string? not-empty field-name-chars?)

(s/with-gen #(gen/not-empty (gen-string field-name-chars)))))

(s/def :ring.http/field-value

  (-> (s/and string? field-value-chars? trimmed?)

(s/with-gen #(gen/fmap str/trim (gen-string field-value-chars*)))))

;; Request

(s/def :ring.request/server-port (s/int-in 1 65535))

(s/def :ring.request/server-name string?)

(s/def :ring.request/remote-addr string?)

(s/def :ring.request/uri

  (-> (s/and string? #(str/starts-with? %"/"))

(s/with-gen (fn [] (gen/fmap #(str "/" %) (gen-string uri-chars))))))

(s/def :ring.request/query-string

  (s/with-gen string? gen-query-string))

(s/def :ring.request/scheme #{:http :https})

(s/def :ring.request/request-method

  (-> (s/and keyword? (comp lower-case? name))

(s/with-gen gen-method)))

(s/def :ring.request/protocol

  (s/with-gen string? #(gen/return "HTTP/1.1")))

(s/def :ring.request/header-name

  (-> (s/and :ring.http/field-name lower-case?)

(s/with-gen #(gen/fmap str/lower-case (s/gen :ring.http/field-name)))))

(s/def :ring.request/header-value :ring.http/field-value)

(s/def :ring.request/headers

  (s/map-of :ring.request/header-name :ring.request/header-value))

(s/def :ring.request/body

  (s/with-gen #(instance? java.io.InputStream %) gen-input-stream))

(s/def :ring/request

  (s/keys :req-un [:ring.request/server-port

:ring.request/server-name

:ring.request/remote-addr

:ring.request/uri

:ring.request/scheme

:ring.request/protocol

:ring.request/headers

                  :ring.request/request-method]

:opt-un [:ring.request/query-string

                  :ring.request/body]))

;; Response

(s/def :ring.response/status (s/int-in 100 600))

(s/def :ring.response/header-name :ring.http/field-name)

(s/def :ring.response/header-value

  (s/or :one :ring.http/field-value :many (s/coll-of :ring.http/field-value)))

(s/def :ring.response/headers

  (s/map-of :ring.response/header-name :ring.response/header-value))

(s/def :ring.response/body

  (-> #(satisfies? p/StreamableResponseBody %)

(s/with-gen #(gen/one-of [(gen/return nil)

(gen/string-ascii)

(gen/list (gen/string-ascii))

(gen-input-stream)]))))

(s/def :ring/response

  (s/keys :req-un [:ring.response/status

                  :ring.response/headers]

:opt-un [:ring.response/body]))

;; Handler

(s/def :ring.sync.handler/args

  (s/cat :request :ring/request))

(s/def :ring.async.handler/args

  (s/cat :request :ring/request

        :respond (s/fspec :args (s/cat :response :ring/response):ret any?)

:raise  (s/fspec :args (s/cat :error :ring.core/error):ret any?)))

(s/def :ring.sync.handler/ret  :ring/response)

(s/def :ring.async.handler/ret any?)

(s/fdef :ring.sync/handler

:args :ring.sync.handler/args

  :ret  :ring.sync.handler/ret)

(s/fdef :ring.async/handler

:args :ring.async.handler/args

  :ret  :ring.async.handler/ret)

(s/def :ring.sync+async/handler

  (s/and :ring.sync/handler

        :ring.async/handler))

(s/def :ring/handler

  (s/or :sync :ring.sync/handler

:async :ring.async/handler

        :sync+async :ring.sync+async/handler))

(gen/generate (s/gen :ring.sync.handler/args))

(gen/generate (s/gen :ring.sync.handler/ret))

(gen/generate (s/gen :ring.async.handler/args))

(gen/generate (s/gen :ring.async.handler/ret))

(gen/generate (s/gen :ring/handler))

(gen/generate (s/gen :ring.http/field-name))

(gen/generate (s/gen :ring.http/field-value))

(gen/generate (s/gen :ring.request/server-port))

(gen/generate (s/gen :ring.request/server-name))

(gen/generate (s/gen :ring.request/uri))

(gen/generate (s/gen :ring.request/query-string));比较综合的一个生成例子

(gen/generate (s/gen :ring.request/request-method))

(gen/generate (s/gen :ring.request/protocol))

(gen/generate (s/gen :ring.request/header-name))

(gen/generate (s/gen :ring.request/header-value))

(gen/generate (s/gen :ring.request/headers))

(slurp (gen/generate (s/gen :ring.request/body)))

(gen/generate (s/gen :ring/request))

;上面是request 下面是response

(gen/generate (s/gen :ring.response/status))

(gen/generate (s/gen :ring.response/header-name))

(gen/generate (s/gen :ring.response/header-value))

(gen/generate (s/gen :ring.response/headers))

(gen/generate (s/gen :ring.response/body))

(gen/generate (s/gen :ring/response))

相关文章

  • clojure spec

    clojure是一门动态类型的语言,在类型检查方面并没有c++/java 这种静态类型语言好用,所以多个模块之间进...

  • clojure.spec库入门学习

    此文已由作者张佃鹏授权网易云社区发布。 欢迎访问网易云社区,了解更多网易技术产品运营经验。 clojure是一门动...

  • Clojure 脚本

    Clojure 语法 https://www.w3cschool.cn/clojure/clojure_basic...

  • clojure 内部

    The Life of a Clojure Expression: A Quick Tour of Clojure...

  • Docker下安装 Clojure

    Docker环境下Clojure 搜索 Clojure 镜像,下载,并在命令行下启动镜像 使用 Clojure 的...

  • [job]Clojure Jobs | ClojureDocs

    Clojure Jobs | ClojureDocs - Community-Powered Clojure Do...

  • Clojure 编译器实现

    准备Clojure源代码阅读环境 获取clojure源代码clojure项目托管在github,运行以下命令获取源...

  • 如何高效地学习编程语言

    这是Clojure好书《Clojure for the Brave and True》作者 Daniel Higg...

  • Clojure学习资料汇总

    注:转自《Clojure学习资料汇总》 官方文档 中文资料(强烈推荐):clojure入门教程clojure文档翻...

  • Clojure基础操作

    在macOS上安装Clojure的方法 升级Clojure的方法 进入REPL的方法 查看Clojure当前版本的方法

网友评论

      本文标题:clojure spec

      本文链接:https://www.haomeiwen.com/subject/tmoqmhtx.html