Skip to content

Commit 41192b6

Browse files
committed
added testable example, updated README
1 parent 008ed3b commit 41192b6

File tree

2 files changed

+101
-89
lines changed

2 files changed

+101
-89
lines changed

README.md

Lines changed: 11 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -4,95 +4,6 @@ A way to structure your application modules.
44

55
<img width="573" height="435" alt="Screenshot 2025-09-26 at 10 14 57" src="https://github.com/user-attachments/assets/3e67e0f7-dd48-4ca0-98e5-80d216e86749" />
66

7-
^ Output of internal [tool](https://github.com/roboslone/go-framework/blob/main/.tools/main.go#L13) built on `go-framework`.
8-
Linting and testing are done in parallel.
9-
10-
## Example
11-
12-
```go
13-
// State describes your application, both configuration and runtime.
14-
// In this example state configures `Interval` for "incrementer" and "printer" modules,
15-
// and also stores `Counter`, which is modified by "incrementer" and read by "printer".
16-
type State {
17-
// configuration
18-
Interval time.Duration
19-
20-
// runtime
21-
Counter int
22-
}
23-
24-
// CounterIncrementer is a simple module, that increments given `Counter` each `Interval`.
25-
type CounterIncrementer struct {}
26-
27-
func (*CounterIncrementer) Start(ctx context.Context, s *State) error {
28-
go func() {
29-
timedLoop(ctx, s.Interval, func() { s.Counter++ })
30-
}()
31-
return nil
32-
}
33-
34-
// CounterPrinter is a simple module, that prints given `Counter` each `Interval`.
35-
// It depends on CounterIncrementer.
36-
type CounterPrinter struct {}
37-
38-
func (*CounterPrinter) Start(ctx context.Context, s *State) error {
39-
go func() {
40-
timedLoop(ctx, s.Interval, func() { log.Println(s.Counter) })
41-
}()
42-
return nil
43-
}
44-
45-
func (*CounterPrinter) Dependencies(_ context.Context) []string {
46-
return []string{
47-
"incrementer",
48-
}
49-
}
50-
51-
func timedLoop(ctx context.Context, d time.Duration, fn func()) {
52-
ticker := time.NewTicker(d)
53-
defer ticker.Stop()
54-
55-
for {
56-
select {
57-
case <-ticker.C:
58-
fn()
59-
case <-ctx.Done():
60-
return
61-
}
62-
}
63-
}
64-
65-
// App contains all available modules and their dependencies.
66-
var App = framework.NewApplication[State](
67-
"counter",
68-
framework.Modules{
69-
"incrementer": &CounterIncrementer{},
70-
"printer": &CounterPrinter{},
71-
},
72-
)
73-
74-
// Prepares and starts both `incrementer` and `printer`.
75-
App.Run(context.Background(), &State{}, "printer")
76-
77-
// To ensure all your application modules are valid (satisfy at least one module interface):
78-
func TestApp(t *testing.T) {
79-
if err := App.Check(); err != nil {
80-
t.Error(err)
81-
}
82-
}
83-
```
84-
85-
## Module interfaces
86-
Available interfaces can be found in `module.go`:
87-
88-
```go
89-
Dependent
90-
Preparable[State any]
91-
Startable[State any]
92-
Awaitable[State any]
93-
Cleanable[State any]
94-
```
95-
967
## Command line tool
978
There's a command line tool for running simple command modules (`framework.CommandModule`).
989

@@ -137,3 +48,14 @@ fexec --help
13748
# pre-commit
13849
# depends on lint, test
13950
```
51+
52+
## Module interfaces
53+
Available interfaces can be found in `module.go`:
54+
55+
```go
56+
Dependent
57+
Preparable[State any]
58+
Startable[State any]
59+
Awaitable[State any]
60+
Cleanable[State any]
61+
```

example_test.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package framework_test
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
framework "github.com/roboslone/go-framework"
9+
)
10+
11+
// ExampleState describes example application, both configuration and runtime.
12+
type ExampleState struct {
13+
// configuration
14+
Interval time.Duration
15+
MaxIterations int
16+
17+
// runtime
18+
Channel chan int
19+
}
20+
21+
// Sender is a simple module, that sends increasing integers to `Channel` each `Interval`.
22+
type Sender struct{}
23+
24+
func (*Sender) Start(ctx context.Context, s *ExampleState) error {
25+
go func() {
26+
defer close(s.Channel)
27+
28+
ticker := time.NewTicker(s.Interval)
29+
var i int
30+
for {
31+
select {
32+
case <-ticker.C:
33+
s.Channel <- i
34+
i++
35+
36+
if i >= s.MaxIterations {
37+
return
38+
}
39+
40+
case <-ctx.Done():
41+
return
42+
}
43+
}
44+
}()
45+
return nil
46+
}
47+
48+
// Receiver is a simple module, that prints values from `Channel`.
49+
// It depends on Sender.
50+
type Receiver struct{}
51+
52+
func (*Receiver) Start(ctx context.Context, s *ExampleState) error {
53+
for i := range s.Channel {
54+
fmt.Println(i)
55+
}
56+
return nil
57+
}
58+
59+
func (*Receiver) Dependencies(_ context.Context) []string {
60+
return []string{
61+
"sender",
62+
}
63+
}
64+
65+
func ExampleApplication() {
66+
// app contains all available modules and their dependencies.
67+
app := framework.NewApplication[ExampleState](
68+
"counter",
69+
framework.Modules{
70+
"sender": &Sender{},
71+
"receiver": &Receiver{},
72+
},
73+
)
74+
75+
state := &ExampleState{
76+
Interval: 75 * time.Millisecond,
77+
MaxIterations: 3,
78+
Channel: make(chan int),
79+
}
80+
81+
// Ensure each module satisfies at least one module interface.
82+
fmt.Println("check error:", app.Check())
83+
84+
fmt.Println("run error:", app.Run(context.Background(), state, "receiver"))
85+
// Output: check error: <nil>
86+
// 0
87+
// 1
88+
// 2
89+
// run error: <nil>
90+
}

0 commit comments

Comments
 (0)