Skip to content

Commit d4404d0

Browse files
committed
Add Actor model
1 parent 8c5d357 commit d4404d0

File tree

2 files changed

+162
-0
lines changed

2 files changed

+162
-0
lines changed

actor.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package fpgo
2+
3+
import (
4+
"time"
5+
)
6+
7+
// ActorDef Actor model inspired by Erlang/Akka
8+
type ActorDef struct {
9+
isClosed bool
10+
ch *chan interface{}
11+
effect func(*ActorDef, interface{})
12+
13+
context map[string]interface{}
14+
15+
children map[time.Time]*ActorDef
16+
parent *ActorDef
17+
}
18+
19+
var defaultActor *ActorDef
20+
21+
// GetDefault Get Default Actor
22+
func (actorSelf *ActorDef) GetDefault() *ActorDef {
23+
return defaultActor
24+
}
25+
26+
// New New Actor instance
27+
func (actorSelf *ActorDef) New(effect func(*ActorDef, interface{})) *ActorDef {
28+
ch := make(chan interface{})
29+
return actorSelf.NewByOptions(effect, &ch, map[string]interface{}{})
30+
}
31+
32+
// NewByOptions New Actor by its options
33+
func (actorSelf *ActorDef) NewByOptions(effect func(*ActorDef, interface{}), ioCh *chan interface{}, context map[string]interface{}) *ActorDef {
34+
newOne := ActorDef{ch: ioCh, effect: effect, context: context, children: map[time.Time]*ActorDef{}}
35+
go newOne.run()
36+
37+
return &newOne
38+
}
39+
40+
// Send Send a message to the Actor
41+
func (actorSelf *ActorDef) Send(message interface{}) {
42+
if actorSelf.isClosed {
43+
return
44+
}
45+
46+
*(actorSelf.ch) <- message
47+
}
48+
49+
// Spawn Spawn a new Actor with parent(this actor)
50+
func (actorSelf *ActorDef) Spawn(effect func(*ActorDef, interface{})) *ActorDef {
51+
newOne := Actor.New(effect)
52+
if actorSelf.isClosed {
53+
return newOne
54+
}
55+
56+
newOne.parent = actorSelf
57+
actorSelf.children[time.Now()] = newOne
58+
59+
return newOne
60+
}
61+
62+
// GetChild Get a child Actor by ID
63+
func (actorSelf *ActorDef) GetChild(id time.Time) *ActorDef {
64+
return actorSelf.children[id]
65+
}
66+
67+
// GetParent Get its parent Actor
68+
func (actorSelf *ActorDef) GetParent() *ActorDef {
69+
return actorSelf.parent
70+
}
71+
72+
// Close Close the Actor
73+
func (actorSelf *ActorDef) Close() {
74+
actorSelf.isClosed = true
75+
76+
close(*actorSelf.ch)
77+
}
78+
79+
// IsClosed Check is Closed
80+
func (actorSelf *ActorDef) IsClosed() bool {
81+
return actorSelf.isClosed
82+
}
83+
84+
func (actorSelf *ActorDef) run() {
85+
for message := range *actorSelf.ch {
86+
actorSelf.effect(actorSelf, message)
87+
}
88+
}
89+
90+
// Actor Actor utils instance
91+
var Actor ActorDef
92+
93+
func init() {
94+
Actor = *Actor.New(func(_ *ActorDef, _ interface{}) {})
95+
Actor.Close()
96+
defaultActor = &Actor
97+
}

actor_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package fpgo
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestActorCommon(t *testing.T) {
10+
var expectedInt int
11+
var actual int
12+
var resultChannel chan interface{}
13+
14+
actual = 0
15+
expectedInt = 1400
16+
// Testee
17+
resultChannel = make(chan interface{}, 5)
18+
cmdSpawn := "spawn"
19+
cmdShutdown := "shutdown"
20+
actorRoot := Actor.New(func(self *ActorDef, input interface{}) {
21+
if input == cmdSpawn {
22+
self.Spawn(func(self *ActorDef, input interface{}) {
23+
if input == cmdShutdown {
24+
self.Close()
25+
return
26+
}
27+
28+
val, _ := Maybe.Just(input).ToInt()
29+
resultChannel <- val * 10
30+
})
31+
return
32+
}
33+
if input == cmdShutdown {
34+
for _, child := range self.children {
35+
child.Send(cmdShutdown)
36+
}
37+
self.Close()
38+
39+
close(resultChannel)
40+
return
41+
}
42+
43+
intVal, _ := Maybe.Just(input).ToInt()
44+
if intVal > 0 {
45+
for _, child := range self.children {
46+
child.Send(intVal)
47+
}
48+
}
49+
})
50+
actorRoot.Send(cmdSpawn)
51+
actorRoot.Send(10)
52+
actorRoot.Send(cmdSpawn)
53+
actorRoot.Send(20)
54+
actorRoot.Send(cmdSpawn)
55+
actorRoot.Send(30)
56+
57+
actorRoot.Send(cmdShutdown)
58+
59+
for val := range resultChannel {
60+
intVal, _ := Maybe.Just(val).ToInt()
61+
actual += intVal
62+
}
63+
64+
assert.Equal(t, expectedInt, actual)
65+
}

0 commit comments

Comments
 (0)