Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@
[com.stuartsierra/component "0.3.1"]
[reloaded.repl "0.2.1"]
[http-kit "2.1.19"]
[criterium "0.4.3"]
; Required when using with Java 1.6
[org.codehaus.jsr166-mirror/jsr166y "1.7.0"]]
:ring {:handler examples.thingie/app
:reload-paths ["src" "examples/src"]}
:source-paths ["examples/src" "examples/dev-src"]
:main examples.server}
:perf {:jvm-opts ^:replace []}
:logging {:dependencies [[org.clojure/tools.logging "0.3.1"]]}
:1.8 {:dependencies [[org.clojure/clojure "1.8.0-RC2"]]}}
:eastwood {:namespaces [:source-paths]
Expand All @@ -55,5 +57,6 @@
"start-thingie" ["run"]
"aot-uberjar" ["with-profile" "uberjar" "do" "clean," "ring" "uberjar"]
"test-ancient" ["midje"]
"perf" ["with-profile" "default,dev,perf"]
"deploy!" ^{:doc "Recompile sources, then deploy if tests succeed."}
["do" ["clean"] ["midje"] ["deploy" "clojars"]]})
17 changes: 11 additions & 6 deletions src/compojure/api/meta.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
[plumbing.core :refer :all]
[plumbing.fnk.impl :as fnk-impl]
[ring.swagger.common :refer :all]
[ring.swagger.schema :as schema]
[ring.swagger.json-schema :as js]
[ring.util.http-response :refer [internal-server-error]]
[slingshot.slingshot :refer [throw+]]
[schema.core :as s]
[schema.coerce :as sc]
[schema.utils :as su]
[schema-tools.core :as st]))

;;
Expand All @@ -23,7 +24,7 @@
'+compojure-api-request+)

(def +compojure-api-meta+
"lexically bound meta-data for handlers. EXPERIMENTAL."
"lexically bound meta-data for handlers."
'+compojure-api-meta+)

(defmacro meta-container [meta & form]
Expand All @@ -46,6 +47,8 @@
;; Schema
;;

(def memoized-coercer (memoize sc/coercer))

(defn strict [schema]
(dissoc schema 'schema.core/Keyword))

Expand All @@ -59,8 +62,9 @@
(if-let [{:keys [status] :as response} (handler request)]
(if-let [schema (:schema (responses status))]
(if-let [matcher (:response (mw/get-coercion-matcher-provider request))]
(let [body (schema/coerce schema (:body response) matcher)]
(if (schema/error? body)
(let [coerce (memoized-coercer (value-of schema) matcher)
body (coerce (:body response))]
(if (su/error? body)
(throw+ (assoc body :type ::ex/response-validation))
(assoc response
::serializable? true
Expand All @@ -75,8 +79,9 @@
(assert (not (#{:query :json} type)) (str type " is DEPRECATED since 0.22.0. Use :body or :string instead."))
`(let [value# (keywordize-keys (~key ~+compojure-api-request+))]
(if-let [matcher# (~type (mw/get-coercion-matcher-provider ~+compojure-api-request+))]
(let [result# (schema/coerce ~schema value# matcher#)]
(if (schema/error? result#)
(let [coerce# (memoized-coercer ~schema matcher#)
result# (coerce# value#)]
(if (su/error? result#)
(throw+ (assoc result# :type ::ex/request-validation))
result#))
value#)))
Expand Down
116 changes: 116 additions & 0 deletions test/compojure/api/perf_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
(ns compojure.api.perf-test
(:require [compojure.api.sweet :refer :all]
[compojure.api.test-utils :refer :all]
[criterium.core :as cc]
[ring.util.http-response :refer :all]
[schema.core :as s]))

;;
;; start repl with `lein perf repl`
;; perf measured with the following setup:
;;
;; Model Name: MacBook Pro
;; Model Identifier: MacBookPro11,3
;; Processor Name: Intel Core i7
;; Processor Speed: 2,5 GHz
;; Number of Processors: 1
;; Total Number of Cores: 4
;; L2 Cache (per Core): 256 KB
;; L3 Cache: 6 MB
;; Memory: 16 GB
;;

(defn title [s]
(println
(str "\n\u001B[35m"
(apply str (repeat (+ 6 (count s)) "#"))
"\n## " s " ##\n"
(apply str (repeat (+ 6 (count s)) "#"))
"\u001B[0m\n")))

(s/defschema Order {:id s/Str
:name s/Str
(s/optional-key :description) s/Str
:address (s/maybe {:street s/Str
:country (s/enum "FI" "PO")})
:orders [{:name #"^k"
:price s/Any
:shipping s/Bool}]})

(defn bench []


(let [app (api
(GET* "/30" []
(ok {:result 30})))
call #(get* app "/30")]

(title "GET JSON")

(assert (= {:result 30} (second (call))))
(cc/bench (call)))

; 26µs => 26µs (-0%)

(let [app (api
(POST* "/plus" []
:return {:result s/Int}
:body-params [x :- s/Int, y :- s/Int]
(ok {:result (+ x y)})))
data (json {:x 10, :y 20})
call #(post* app "/plus" data)]

(title "JSON POST with 2-way coercion")

(assert (= {:result 30} (second (call))))
(cc/bench (call)))

;; 87µs => 65µs (-25%)

(let [app (api
(context* "/a" []
(context* "/b" []
(context* "/c" []
(POST* "/plus" []
:return {:result s/Int}
:body-params [x :- s/Int, y :- s/Int]
(ok {:result (+ x y)}))))))
data (json {:x 10, :y 20})
call #(post* app "/a/b/c/plus" data)]

(title "JSON POST with 2-way coercion + contexts")

(assert (= {:result 30} (second (call))))
(cc/bench (call)))

;; 102µs => 78µs (-24%)

(let [app (api
(POST* "/echo" []
:return Order
:body [order Order]
(ok order)))
data (json {:id "123"
:name "Tommi's order"
:description "Totally great order"
:address {:street "Randomstreet 123"
:country "FI"}
:orders [{:name "k1"
:price 123.0
:shipping true}
{:name "k2"
:price 42.0
:shipping false}]})
call #(post* app "/echo" data)]

(title "JSON POST with nested data")

(s/check Order (second (call)))
(cc/bench (call)))

;; 311µs => 194µs (-38%)

)

(comment
(bench))