OSDN Git Service

feat: init cross_tx keepers (#146)
[bytom/vapor.git] / vendor / github.com / jinzhu / gorm / callback.go
1 package gorm
2
3 import "log"
4
5 // DefaultCallback default callbacks defined by gorm
6 var DefaultCallback = &Callback{}
7
8 // Callback is a struct that contains all CRUD callbacks
9 //   Field `creates` contains callbacks will be call when creating object
10 //   Field `updates` contains callbacks will be call when updating object
11 //   Field `deletes` contains callbacks will be call when deleting object
12 //   Field `queries` contains callbacks will be call when querying object with query methods like Find, First, Related, Association...
13 //   Field `rowQueries` contains callbacks will be call when querying object with Row, Rows...
14 //   Field `processors` contains all callback processors, will be used to generate above callbacks in order
15 type Callback struct {
16         creates    []*func(scope *Scope)
17         updates    []*func(scope *Scope)
18         deletes    []*func(scope *Scope)
19         queries    []*func(scope *Scope)
20         rowQueries []*func(scope *Scope)
21         processors []*CallbackProcessor
22 }
23
24 // CallbackProcessor contains callback informations
25 type CallbackProcessor struct {
26         name      string              // current callback's name
27         before    string              // register current callback before a callback
28         after     string              // register current callback after a callback
29         replace   bool                // replace callbacks with same name
30         remove    bool                // delete callbacks with same name
31         kind      string              // callback type: create, update, delete, query, row_query
32         processor *func(scope *Scope) // callback handler
33         parent    *Callback
34 }
35
36 func (c *Callback) clone() *Callback {
37         return &Callback{
38                 creates:    c.creates,
39                 updates:    c.updates,
40                 deletes:    c.deletes,
41                 queries:    c.queries,
42                 rowQueries: c.rowQueries,
43                 processors: c.processors,
44         }
45 }
46
47 // Create could be used to register callbacks for creating object
48 //     db.Callback().Create().After("gorm:create").Register("plugin:run_after_create", func(*Scope) {
49 //       // business logic
50 //       ...
51 //
52 //       // set error if some thing wrong happened, will rollback the creating
53 //       scope.Err(errors.New("error"))
54 //     })
55 func (c *Callback) Create() *CallbackProcessor {
56         return &CallbackProcessor{kind: "create", parent: c}
57 }
58
59 // Update could be used to register callbacks for updating object, refer `Create` for usage
60 func (c *Callback) Update() *CallbackProcessor {
61         return &CallbackProcessor{kind: "update", parent: c}
62 }
63
64 // Delete could be used to register callbacks for deleting object, refer `Create` for usage
65 func (c *Callback) Delete() *CallbackProcessor {
66         return &CallbackProcessor{kind: "delete", parent: c}
67 }
68
69 // Query could be used to register callbacks for querying objects with query methods like `Find`, `First`, `Related`, `Association`...
70 // Refer `Create` for usage
71 func (c *Callback) Query() *CallbackProcessor {
72         return &CallbackProcessor{kind: "query", parent: c}
73 }
74
75 // RowQuery could be used to register callbacks for querying objects with `Row`, `Rows`, refer `Create` for usage
76 func (c *Callback) RowQuery() *CallbackProcessor {
77         return &CallbackProcessor{kind: "row_query", parent: c}
78 }
79
80 // After insert a new callback after callback `callbackName`, refer `Callbacks.Create`
81 func (cp *CallbackProcessor) After(callbackName string) *CallbackProcessor {
82         cp.after = callbackName
83         return cp
84 }
85
86 // Before insert a new callback before callback `callbackName`, refer `Callbacks.Create`
87 func (cp *CallbackProcessor) Before(callbackName string) *CallbackProcessor {
88         cp.before = callbackName
89         return cp
90 }
91
92 // Register a new callback, refer `Callbacks.Create`
93 func (cp *CallbackProcessor) Register(callbackName string, callback func(scope *Scope)) {
94         if cp.kind == "row_query" {
95                 if cp.before == "" && cp.after == "" && callbackName != "gorm:row_query" {
96                         log.Printf("Registing RowQuery callback %v without specify order with Before(), After(), applying Before('gorm:row_query') by default for compatibility...\n", callbackName)
97                         cp.before = "gorm:row_query"
98                 }
99         }
100
101         cp.name = callbackName
102         cp.processor = &callback
103         cp.parent.processors = append(cp.parent.processors, cp)
104         cp.parent.reorder()
105 }
106
107 // Remove a registered callback
108 //     db.Callback().Create().Remove("gorm:update_time_stamp_when_create")
109 func (cp *CallbackProcessor) Remove(callbackName string) {
110         log.Printf("[info] removing callback `%v` from %v\n", callbackName, fileWithLineNum())
111         cp.name = callbackName
112         cp.remove = true
113         cp.parent.processors = append(cp.parent.processors, cp)
114         cp.parent.reorder()
115 }
116
117 // Replace a registered callback with new callback
118 //     db.Callback().Create().Replace("gorm:update_time_stamp_when_create", func(*Scope) {
119 //                 scope.SetColumn("Created", now)
120 //                 scope.SetColumn("Updated", now)
121 //     })
122 func (cp *CallbackProcessor) Replace(callbackName string, callback func(scope *Scope)) {
123         log.Printf("[info] replacing callback `%v` from %v\n", callbackName, fileWithLineNum())
124         cp.name = callbackName
125         cp.processor = &callback
126         cp.replace = true
127         cp.parent.processors = append(cp.parent.processors, cp)
128         cp.parent.reorder()
129 }
130
131 // Get registered callback
132 //    db.Callback().Create().Get("gorm:create")
133 func (cp *CallbackProcessor) Get(callbackName string) (callback func(scope *Scope)) {
134         for _, p := range cp.parent.processors {
135                 if p.name == callbackName && p.kind == cp.kind && !cp.remove {
136                         return *p.processor
137                 }
138         }
139         return nil
140 }
141
142 // getRIndex get right index from string slice
143 func getRIndex(strs []string, str string) int {
144         for i := len(strs) - 1; i >= 0; i-- {
145                 if strs[i] == str {
146                         return i
147                 }
148         }
149         return -1
150 }
151
152 // sortProcessors sort callback processors based on its before, after, remove, replace
153 func sortProcessors(cps []*CallbackProcessor) []*func(scope *Scope) {
154         var (
155                 allNames, sortedNames []string
156                 sortCallbackProcessor func(c *CallbackProcessor)
157         )
158
159         for _, cp := range cps {
160                 // show warning message the callback name already exists
161                 if index := getRIndex(allNames, cp.name); index > -1 && !cp.replace && !cp.remove {
162                         log.Printf("[warning] duplicated callback `%v` from %v\n", cp.name, fileWithLineNum())
163                 }
164                 allNames = append(allNames, cp.name)
165         }
166
167         sortCallbackProcessor = func(c *CallbackProcessor) {
168                 if getRIndex(sortedNames, c.name) == -1 { // if not sorted
169                         if c.before != "" { // if defined before callback
170                                 if index := getRIndex(sortedNames, c.before); index != -1 {
171                                         // if before callback already sorted, append current callback just after it
172                                         sortedNames = append(sortedNames[:index], append([]string{c.name}, sortedNames[index:]...)...)
173                                 } else if index := getRIndex(allNames, c.before); index != -1 {
174                                         // if before callback exists but haven't sorted, append current callback to last
175                                         sortedNames = append(sortedNames, c.name)
176                                         sortCallbackProcessor(cps[index])
177                                 }
178                         }
179
180                         if c.after != "" { // if defined after callback
181                                 if index := getRIndex(sortedNames, c.after); index != -1 {
182                                         // if after callback already sorted, append current callback just before it
183                                         sortedNames = append(sortedNames[:index+1], append([]string{c.name}, sortedNames[index+1:]...)...)
184                                 } else if index := getRIndex(allNames, c.after); index != -1 {
185                                         // if after callback exists but haven't sorted
186                                         cp := cps[index]
187                                         // set after callback's before callback to current callback
188                                         if cp.before == "" {
189                                                 cp.before = c.name
190                                         }
191                                         sortCallbackProcessor(cp)
192                                 }
193                         }
194
195                         // if current callback haven't been sorted, append it to last
196                         if getRIndex(sortedNames, c.name) == -1 {
197                                 sortedNames = append(sortedNames, c.name)
198                         }
199                 }
200         }
201
202         for _, cp := range cps {
203                 sortCallbackProcessor(cp)
204         }
205
206         var sortedFuncs []*func(scope *Scope)
207         for _, name := range sortedNames {
208                 if index := getRIndex(allNames, name); !cps[index].remove {
209                         sortedFuncs = append(sortedFuncs, cps[index].processor)
210                 }
211         }
212
213         return sortedFuncs
214 }
215
216 // reorder all registered processors, and reset CRUD callbacks
217 func (c *Callback) reorder() {
218         var creates, updates, deletes, queries, rowQueries []*CallbackProcessor
219
220         for _, processor := range c.processors {
221                 if processor.name != "" {
222                         switch processor.kind {
223                         case "create":
224                                 creates = append(creates, processor)
225                         case "update":
226                                 updates = append(updates, processor)
227                         case "delete":
228                                 deletes = append(deletes, processor)
229                         case "query":
230                                 queries = append(queries, processor)
231                         case "row_query":
232                                 rowQueries = append(rowQueries, processor)
233                         }
234                 }
235         }
236
237         c.creates = sortProcessors(creates)
238         c.updates = sortProcessors(updates)
239         c.deletes = sortProcessors(deletes)
240         c.queries = sortProcessors(queries)
241         c.rowQueries = sortProcessors(rowQueries)
242 }