/
resource.clj
120 lines (103 loc) · 3.47 KB
/
resource.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
;; Copyright © 2014-2017, JUXT LTD.
(ns yada.resource
(:require
[hiccup.core :refer [html]]
[schema.core :as s]
[schema.utils :as su]
[manifold.stream]
[yada.charset :as charset]
[yada.context :refer [content-type]]
[yada.schema :as ys]
[yada.util :refer [arity]]
[yada.media-type]
[manifold.stream.core])
(:import java.util.Date
yada.charset.CharsetMap
yada.media_type.MediaTypeMap
[manifold.stream.core IEventSource]))
(defprotocol ResourceCoercion
(as-resource [_] "Coerce to a resource. Often, resources need to be
coerced rather than extending types directly with the Resource
protocol. We can exploit the time of coercion to know the time of
birth for the resource, which supports time-based conditional
requests. For example, a simple StringResource is immutable, so by
knowing the time of construction, we can precisely state its
Last-Modified-Date."))
;; Deprecated
(s/defschema MediaTypeSchema
(s/either String MediaTypeMap))
(s/defschema CharsetSchema
(s/either String CharsetMap))
(s/defschema QualifiedKeyword
(s/both s/Keyword (s/pred namespace)))
(s/defschema MediaTypeSchemaSet
#{MediaTypeSchema})
(s/defschema CharsetSchemaSet
#{CharsetSchema})
(s/defschema StringSet
#{String})
(defn as-set [x] (if (coll? x) x (set [x])))
(def +properties-coercions+
{Date #(condp instance? %
java.lang.Long (Date. ^Long %)
%)
MediaTypeSchemaSet as-set
CharsetSchemaSet as-set
StringSet as-set})
(defrecord Resource []
ResourceCoercion
(as-resource [this] this))
(defn resource [model]
(let [r (ys/resource-coercer model)]
(when (su/error? r) (throw (ex-info "Cannot turn resource-model into resource, because it doesn't conform to a resource-model schema" {:resource-model model :error (:error r)})))
(map->Resource r)))
(extend-protocol ResourceCoercion
nil
(as-resource [_]
(resource
{:summary "Nil resource"
:methods {}
:interceptor-chain [(fn [_]
(throw (ex-info "" {:status 404})))]}))
clojure.lang.Fn
(as-resource [f]
(let [ar (arity f)]
(resource
{:produces #{"text/html"
"text/plain"
"application/json"
"application/edn"}
:methods
{:* {:response (fn [ctx]
(case (int ar)
0 (f)
1 (f ctx)
(apply f ctx (repeat (dec arity) nil)))
)}}})))
clojure.lang.Var
(as-resource [v]
(as-resource (deref v)))
IEventSource
(as-resource [source]
(resource
{:produces [{:media-type "text/event-stream"
:charset charset/platform-charsets}]
:methods {:get {:response (fn [ctx] source)}}}))
Exception
(as-resource [e]
(resource
{:produces #{"text/html" "text/plain;q=0.9"}
:methods
{:* {:response
(fn [ctx]
(case (content-type ctx)
"text/html"
(html
[:body
(interpose [:p "Caused by"]
(for [e (take-while some? (iterate (fn [^Throwable x] (.getCause x)) e))]
[:div
[:h2 "Error: " (.getMessage ^Throwable e)]
[:div
(for [stl (.getStackTrace ^Throwable e)]
[:p [:tt stl]])]]))])))}}})))