diff --git a/apis/realtime.go b/apis/realtime.go index 418b2da5..790c620a 100644 --- a/apis/realtime.go +++ b/apis/realtime.go @@ -166,7 +166,7 @@ func (api *realtimeApi) bindEvents() { adminTable := (&models.Admin{}).TableName() // update user/admin auth state - api.app.OnModelAfterUpdate().Add(func(e *core.ModelEvent) error { + api.app.OnModelAfterUpdate().PreAdd(func(e *core.ModelEvent) error { modelTable := e.Model.TableName() var contextKey string @@ -190,7 +190,7 @@ func (api *realtimeApi) bindEvents() { }) // remove user/admin client(s) - api.app.OnModelAfterDelete().Add(func(e *core.ModelEvent) error { + api.app.OnModelAfterDelete().PreAdd(func(e *core.ModelEvent) error { modelTable := e.Model.TableName() var contextKey string @@ -213,14 +213,14 @@ func (api *realtimeApi) bindEvents() { return nil }) - api.app.OnModelAfterCreate().Add(func(e *core.ModelEvent) error { + api.app.OnModelAfterCreate().PreAdd(func(e *core.ModelEvent) error { if record, ok := e.Model.(*models.Record); ok { api.broadcastRecord("create", record) } return nil }) - api.app.OnModelAfterUpdate().Add(func(e *core.ModelEvent) error { + api.app.OnModelAfterUpdate().PreAdd(func(e *core.ModelEvent) error { if record, ok := e.Model.(*models.Record); ok { api.broadcastRecord("update", record) } diff --git a/core/base.go b/core/base.go index 7c79db31..b5574977 100644 --- a/core/base.go +++ b/core/base.go @@ -716,7 +716,7 @@ func (app *BaseApp) initLogsDB() error { return connectErr } - app.logsDao = app.createDao(app.logsDB) + app.logsDao = daos.New(app.logsDB) return nil } @@ -740,12 +740,12 @@ func (app *BaseApp) initDataDB() error { } } - app.dao = app.createDao(app.db) + app.dao = app.createDaoWithHooks(app.db) return nil } -func (app *BaseApp) createDao(db dbx.Builder) *daos.Dao { +func (app *BaseApp) createDaoWithHooks(db dbx.Builder) *daos.Dao { dao := daos.New(db) dao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model) error { diff --git a/tools/hook/hook.go b/tools/hook/hook.go index 05402802..e3d94147 100644 --- a/tools/hook/hook.go +++ b/tools/hook/hook.go @@ -17,7 +17,18 @@ type Hook[T any] struct { handlers []Handler[T] } -// Add registers a new handler to the hook. +// PreAdd registers a new handler to the hook by prepending it to the existing queue. +func (h *Hook[T]) PreAdd(fn Handler[T]) { + h.mux.Lock() + defer h.mux.Unlock() + + // minimize allocations by shifting the slice + h.handlers = append(h.handlers, nil) + copy(h.handlers[1:], h.handlers) + h.handlers[0] = fn +} + +// Add registers a new handler to the hook by appending it to the existing queue. func (h *Hook[T]) Add(fn Handler[T]) { h.mux.Lock() defer h.mux.Unlock() diff --git a/tools/hook/hook_test.go b/tools/hook/hook_test.go index 24e46e88..11fd3a02 100644 --- a/tools/hook/hook_test.go +++ b/tools/hook/hook_test.go @@ -5,18 +5,34 @@ import ( "testing" ) -func TestAdd(t *testing.T) { +func TestAddAndPreAdd(t *testing.T) { h := Hook[int]{} if total := len(h.handlers); total != 0 { t.Fatalf("Expected no handlers, found %d", total) } - h.Add(func(data int) error { return nil }) - h.Add(func(data int) error { return nil }) + triggerSequence := "" - if total := len(h.handlers); total != 2 { - t.Fatalf("Expected 2 handlers, found %d", total) + f1 := func(data int) error { triggerSequence += "f1"; return nil } + f2 := func(data int) error { triggerSequence += "f2"; return nil } + f3 := func(data int) error { triggerSequence += "f3"; return nil } + f4 := func(data int) error { triggerSequence += "f4"; return nil } + + h.Add(f1) + h.Add(f2) + h.PreAdd(f3) + h.PreAdd(f4) + h.Trigger(1) + + if total := len(h.handlers); total != 4 { + t.Fatalf("Expected %d handlers, found %d", 4, total) + } + + expectedTriggerSequence := "f4f3f1f2" + + if triggerSequence != expectedTriggerSequence { + t.Fatalf("Expected trigger sequence %s, got %s", expectedTriggerSequence, triggerSequence) } }