-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
protocols.clj
122 lines (101 loc) · 3.86 KB
/
protocols.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
121
122
(ns toucan2.protocols
(:require
[potemkin :as p]))
(set! *warn-on-reflection* true)
(p/defprotocol+ IModel
:extend-via-metadata true
"Protocol for something that is-a or has-a model."
(model [this]
"Get the Toucan model associated with `this`."))
(extend-protocol IModel
nil
(model [_this]
nil)
Object
(model [_this]
nil))
(p/defprotocol+ IWithModel
:extend-via-metadata true
"Protocol for something that has-a model that supports creating a copy with a different model."
(^{:style/indent nil} with-model [this new-model]
"Return a copy of `instance` with its model set to `new-model.`"))
;;; there are some default impls of [[with-model]] in [[toucan2.instance]]
(p/defprotocol+ IRecordChanges
"Protocol for something that records the changes made to it, e.g. a Toucan instance."
(original [instance]
"Get the original version of `instance` as it appeared when it first came out of the DB.")
(^{:style/indent nil} with-original [instance new-original]
"Return a copy of `instance` with its `original` map set to `new-original`.")
(current [instance]
"Return the underlying map representing the current state of an `instance`.")
(^{:style/indent nil} with-current [instance new-current]
"Return a copy of `instance` with its underlying `current` map set to `new-current`.")
(changes [instance]
"Get a map with any changes made to `instance` since it came out of the DB. Only includes keys that have been
added or given different values; keys that were removed are not counted. Returns `nil` if there are no changes."))
;;; `nil` and `IPersistentMap` can implement the methods that make sense for them: `nil` or a plain map doesn't have any
;;; changes, so [[changes]] can return `nil`. I don't know what sort of implementation for stuff like
;;; [[with-original]] or [[with-current]] makes sense so I'm not implementing those for now.
(extend-protocol IRecordChanges
nil
(original [_this]
nil)
;; (with-original [this])
(current [_this]
nil)
;; (with-current [this])
(changes [_this]
nil)
;; generally just treat a plain map like an instance with nil model/and original = nil,
;; and no-op for anything that would require "upgrading" the map to an actual instance in such a way that if
;;
;; (= plain-map instance)
;;
;; then
;;
;; (= (f plain-map) (f instance))
clojure.lang.IPersistentMap
(original [_this]
nil)
(with-original [this _m]
this)
(current [this]
this)
(with-current [_this new-current]
new-current)
;; treat the entire map as `changes` -- that way if you accidentally do something like
;;
;; (merge plain-map instance)
;;
;; in a `before-update` method, you don't accidentally shoot yourself in the foot and break `update!` or the like.
(changes [this]
this))
(p/defprotocol+ IDispatchValue
:extend-via-metadata true
"Protocol to get the value to use for multimethod dispatch in Toucan from something."
(dispatch-value
[this]
"Get the value that we should dispatch off of in multimethods for `this`. By default, the dispatch of a keyword is
itself while the dispatch value of everything else is its [[type]]."))
(extend-protocol IDispatchValue
Object
(dispatch-value [x]
(type x))
nil
(dispatch-value [_nil]
nil)
clojure.lang.Keyword
(dispatch-value [k]
k))
(p/defprotocol+ IDeferrableUpdate
(deferrable-update [this k f]
"Like [[clojure.core/update]], but this update can be deferred until later. For things like transient rows where you
might want to apply transforms to values that ultimately get realized, but not *cause* them to be realized. Unlike
[[clojure.core/update]], does not support additional args to pass to `f`."))
(extend-protocol IDeferrableUpdate
nil
(deferrable-update [_this k f]
(update nil k f))
clojure.lang.IPersistentMap
(deferrable-update [m k f]
(update m k f)))