Skip to content

Commit 056c4b8

Browse files
committed
chore: update scope.go to support context-based disposal
1 parent 142ea31 commit 056c4b8

2 files changed

Lines changed: 116 additions & 30 deletions

File tree

scope.go

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package submodule
22

33
import (
4+
"context"
45
"reflect"
56
"sync"
67
)
@@ -34,7 +35,8 @@ type Scope interface {
3435
InitError(g Retrievable, e error)
3536

3637
Dispose() error
37-
AppendMiddleware(Middleware)
38+
DisposeWithContext(ctx context.Context) error
39+
AppendMiddleware(...Middleware)
3840
Apply(Submodule[Middleware])
3941
}
4042

@@ -140,44 +142,94 @@ func (s *scope) Apply(submodule Submodule[Middleware]) {
140142
s.AppendMiddleware(m)
141143
}
142144

143-
// Dispose scope to free up all resolved values and trigger scope end middlewares
144-
func (s *scope) Dispose() error {
145+
// remove all values in the scope
146+
func (s *scope) release() {
147+
s.mu.Lock()
148+
defer s.mu.Unlock()
149+
150+
for k := range s.values {
151+
delete(s.values, k)
152+
}
153+
}
154+
155+
type middlewareCaller func(Middleware) error
156+
157+
func (s *scope) dispose(cond middlewareCaller) error {
145158
for _, m := range s.middleware {
146159
if m.hasOnScopeEnd {
147-
e := m.onScopeEnd()
148-
if e != nil {
149-
return e
160+
if err := cond(m); err != nil {
161+
return err
150162
}
151163
}
152164
}
165+
return nil
166+
}
153167

154-
s.mu.Lock()
155-
defer s.mu.Unlock()
168+
var disposeCond = func(m Middleware) error {
169+
if m.onScopeEnd != nil {
170+
return m.onScopeEnd()
171+
}
172+
return nil
173+
}
156174

157-
for k := range s.values {
158-
delete(s.values, k)
175+
var disposeWithContextCond = func(ctx context.Context) func(m Middleware) error {
176+
return func(m Middleware) error {
177+
if m.onScopeEndWithContext != nil {
178+
return m.onScopeEndWithContext(ctx)
179+
}
180+
return nil
159181
}
182+
}
160183

184+
// DisposeWithContext dispose scope with context
185+
func (s *scope) DisposeWithContext(ctx context.Context) error {
186+
if err := s.dispose(disposeWithContextCond(ctx)); err != nil {
187+
return err
188+
}
189+
if err := s.dispose(disposeCond); err != nil {
190+
return err
191+
}
192+
s.release()
193+
return nil
194+
}
195+
196+
// Dispose scope to free up all resolved values and trigger scope end middlewares
197+
func (s *scope) Dispose() error {
198+
if err := s.dispose(disposeWithContextCond(context.TODO())); err != nil {
199+
return err
200+
}
201+
if err := s.dispose(disposeCond); err != nil {
202+
return err
203+
}
204+
s.release()
161205
return nil
162206
}
163207

164208
// Append middleware to the scope
165-
func (s *scope) AppendMiddleware(m Middleware) {
166-
s.middleware = append(s.middleware, m)
209+
func (s *scope) AppendMiddleware(m ...Middleware) {
210+
if len(m) == 0 {
211+
return
212+
}
213+
s.middleware = append(s.middleware, m...)
167214
}
168215

169216
// Append global middleware to the global scope
170217
func AppendGlobalMiddleware(ms ...Middleware) {
171-
for _, m := range ms {
172-
globalScope.AppendMiddleware(m)
218+
if len(ms) == 0 {
219+
return
173220
}
221+
globalScope.AppendMiddleware(ms...)
174222
}
175223

176224
// Dispose global scope to free up all resolved values and trigger scope end middlewares
177225
func DisposeGlobalScope() error {
178226
return globalScope.Dispose()
179227
}
180228

229+
func DisposeGlobalScopeWithContext(ctx context.Context) error {
230+
return globalScope.DisposeWithContext(ctx)
231+
}
232+
181233
// Apply middleware to the global scope
182234
func Apply(s Middleware) {
183235
globalScope.AppendMiddleware(s)
@@ -194,7 +246,8 @@ type Middleware struct {
194246
onScopeResolveType reflect.Type
195247
onScopeResolve reflect.Value
196248

197-
onScopeEnd func() error
249+
onScopeEnd func() error
250+
onScopeEndWithContext func(context.Context) error
198251
}
199252

200253
type MiddlewareFn func(Middleware) Middleware
@@ -214,6 +267,13 @@ func WithScopeEnd(fn func() error) Middleware {
214267
}
215268
}
216269

270+
func WithContextScopeEnd(fn func(context.Context) error) Middleware {
271+
return Middleware{
272+
hasOnScopeEnd: true,
273+
onScopeEndWithContext: fn,
274+
}
275+
}
276+
217277
type ScopeOpts struct {
218278
inherit bool
219279
parent Scope

scope_test.go

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
11
package submodule_test
22

33
import (
4+
"context"
45
"fmt"
56
"testing"
67

78
"github.com/stretchr/testify/assert"
89
"github.com/submodule-org/submodule.go/v2"
910
)
1011

11-
type k struct {
12-
v int
13-
}
14-
1512
func TestScope(t *testing.T) {
1613
var seed int
14+
var isDisposeGlobal bool
15+
var isDisposeLocal bool
1716
intValue := submodule.Make[int](func(self submodule.Self) int {
18-
self.Scope.AppendMiddleware(submodule.WithScopeEnd(func() error {
19-
fmt.Println("add 2 at the end of the day")
20-
seed = seed + 2
21-
return nil
22-
}))
23-
24-
self.Scope.AppendMiddleware(submodule.WithScopeResolve(func(i any) any {
25-
fmt.Println("catch everything")
26-
return i
27-
}))
28-
17+
self.Scope.AppendMiddleware(
18+
submodule.WithScopeEnd(func() error {
19+
fmt.Println("add 2 at the end of the day")
20+
seed = seed + 2
21+
isDisposeLocal = true
22+
return nil
23+
}),
24+
submodule.WithScopeResolve(func(i any) any {
25+
fmt.Println("catch everything")
26+
return i
27+
}),
28+
)
2929
return seed
3030
})
3131

@@ -36,6 +36,7 @@ func TestScope(t *testing.T) {
3636

3737
onEnd := submodule.WithScopeEnd(func() error {
3838
fmt.Println("scope is ended")
39+
isDisposeGlobal = true
3940
return nil
4041
})
4142

@@ -50,4 +51,29 @@ func TestScope(t *testing.T) {
5051
assert.Nil(t, e)
5152

5253
assert.Equal(t, 2, seed)
54+
assert.True(t, isDisposeGlobal)
55+
assert.True(t, isDisposeLocal)
56+
}
57+
58+
func Test_Scope_With_Context(t *testing.T) {
59+
ctx, cancel := context.WithCancel(context.Background())
60+
var isDispose bool
61+
sub := submodule.Make[int](func(self submodule.Self) int {
62+
self.Scope.AppendMiddleware(
63+
submodule.WithContextScopeEnd(func(ctx context.Context) error {
64+
<-ctx.Done()
65+
isDispose = true
66+
return nil
67+
}),
68+
)
69+
return 0
70+
})
71+
scope := submodule.CreateScope()
72+
_ = sub.ResolveWith(scope)
73+
go func() {
74+
cancel()
75+
}()
76+
err := scope.DisposeWithContext(ctx)
77+
assert.Nil(t, err)
78+
assert.True(t, isDispose)
5379
}

0 commit comments

Comments
 (0)