/
components.cljc
173 lines (143 loc) · 5.33 KB
/
components.cljc
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
(ns speclj.components)
(defprotocol SpecComponent
(install [this description]))
#?(:clj
(extend-protocol SpecComponent
java.lang.Object
(install [this description] (comment "This prohibits multimethod defs, and other stuff. Don't be so stingy! Let it pass."))
nil
(install [this description] (throw (java.lang.Exception. (str "Oops! It looks like you tried to add 'nil' to a spec. That's probably not what you wanted."))))
clojure.lang.Var
(install [this description] (comment "Vars are cool. Let them pass."))
clojure.lang.Seqable
(install [this description] (doseq [component (seq this)] (install component description))))
:cljs
(extend-protocol SpecComponent
LazySeq
(install [this description] (doseq [component (seq this)] (install component description)))
List
(install [this description] (doseq [component (seq this)] (install component description)))
EmptyList
(install [this description] (doseq [component (seq this)] (install component description)))
PersistentVector
(install [this description] (doseq [component (seq this)] (install component description)))
nil
(install [this description] (throw (ex-info (str "Oops! It looks like you tried to add 'nil' to a spec. That's probably not what you wanted.") {})))
object
(install [this description] (comment "Whatever... Let them pass."))))
(deftype Description [name is-focused? has-focus? ns parent children characteristics tags befores before-alls afters after-alls withs with-alls arounds around-alls]
SpecComponent
(install [this description]
(reset! (.-parent this) description)
(swap! (.-children description) conj this))
Object
(toString [this] (str "Description: " \" name \")))
(defn new-description [name is-focused? ns]
(Description. name (atom is-focused?) (atom false) ns (atom nil) (atom []) (atom []) (atom #{}) (atom []) (atom []) (atom []) (atom []) (atom []) (atom []) (atom []) (atom [])))
(defn is-description? [component]
(instance? Description component))
(deftype Characteristic [name parent body is-focused?]
SpecComponent
(install [this description]
(reset! (.-parent this) description)
(swap! (.-characteristics description) conj this))
Object
(toString [this] (str \" name \")))
(defn new-characteristic
([name body is-focused?] (Characteristic. name (atom nil) body (atom is-focused?)))
([name description body is-focused?] (Characteristic. name (atom description) body (atom is-focused?))))
(defn is-characteristic? [component]
(instance? Characteristic component))
(deftype Before [body]
SpecComponent
(install [this description]
(swap! (.-befores description) conj this)))
(defn new-before [body]
(Before. body))
(deftype After [body]
SpecComponent
(install [this description]
(swap! (.-afters description) conj this)))
(defn new-after [body]
(After. body))
(deftype Around [body]
SpecComponent
(install [this description]
(swap! (.-arounds description) conj this)))
(defn new-around [body]
(Around. body))
(deftype BeforeAll [body]
SpecComponent
(install [this description]
(swap! (.-before-alls description) conj this)))
(defn new-before-all [body]
(BeforeAll. body))
(deftype AfterAll [body]
SpecComponent
(install [this description]
(swap! (.-after-alls description) conj this)))
(defn new-after-all [body]
(AfterAll. body))
(deftype AroundAll [body]
SpecComponent
(install [this description]
(swap! (.-around-alls description) conj this)))
(defn new-around-all [body]
(AroundAll. body))
#?(:clj
(deftype With [name unique-name body value bang]
SpecComponent
(install [this description]
(swap! (.-withs description) conj this))
clojure.lang.IDeref
(deref [this]
(when (= ::none @value)
(reset! value (body)))
@value))
:cljs
(deftype With [name unique-name body value bang]
SpecComponent
(install [this description]
(swap! (.-withs description) conj this))
cljs.core/IDeref
(-deref [this]
(when (= ::none @value)
(reset! value (body)))
@value)))
(defn reset-with [with]
(reset! (.-value with) ::none)
(if (.-bang with) (deref with)))
(defn new-with [name unique-name body bang]
(let [with (With. name unique-name body (atom ::none) bang)]
(when bang (deref with)) ; TODO - MDM: This is the wrong place to deref. Should do it in body right after arounds.
with))
#?(:clj
(deftype WithAll [name unique-name body value bang]
SpecComponent
(install [this description]
(swap! (.-with-alls description) conj this))
clojure.lang.IDeref
(deref [this]
(when (= ::none @value)
(reset! value (body)))
@value))
:cljs
(deftype WithAll [name unique-name body value bang]
SpecComponent
(install [this description]
(swap! (.-with-alls description) conj this))
cljs.core/IDeref
(-deref [this]
(when (= ::none @value)
(reset! value (body)))
@value)))
(defn new-with-all [name unique-name body bang]
(let [with-all (WithAll. name unique-name body (atom ::none) bang)]
(when bang (deref with-all))
with-all))
(deftype Tag [name]
SpecComponent
(install [this description]
(swap! (.-tags description) conj name)))
(defn new-tag [name]
(Tag. name))