Skip to content

Commit bbd9fce

Browse files
committed
implement fluent builder pattern
1 parent 5baad80 commit bbd9fce

File tree

4 files changed

+544
-32
lines changed

4 files changed

+544
-32
lines changed

README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,43 @@ func main() {
100100
}
101101
```
102102

103+
### Builder Pattern (New)
104+
105+
Flyt now supports a fluent builder pattern for creating nodes:
106+
107+
```go
108+
node := flyt.NewNode().
109+
WithMaxRetries(3).
110+
WithWait(time.Second).
111+
WithExecFunc(func(ctx context.Context, prepResult flyt.Result) (flyt.Result, error) {
112+
fmt.Println("Hello from builder pattern!")
113+
return flyt.R("done"), nil
114+
})
115+
116+
// The Build() method is optional since NodeBuilder implements Node
117+
// node := flyt.NewNode().WithExecFunc(fn).Build()
118+
```
119+
120+
You can mix traditional and builder patterns:
121+
122+
```go
123+
// Start with traditional options
124+
node := flyt.NewNode(
125+
flyt.WithMaxRetries(3),
126+
flyt.WithWait(time.Second),
127+
)
128+
129+
// Continue with builder pattern
130+
node = node.
131+
WithExecFunc(func(ctx context.Context, prepResult flyt.Result) (flyt.Result, error) {
132+
return flyt.R("processed"), nil
133+
}).
134+
WithPostFunc(func(ctx context.Context, shared *flyt.SharedStore, prepResult, execResult flyt.Result) (flyt.Action, error) {
135+
shared.Set("result", execResult.Value())
136+
return flyt.DefaultAction, nil
137+
})
138+
```
139+
103140
## Core Concepts
104141

105142
### Nodes
@@ -282,6 +319,31 @@ The type-safe getters handle numeric conversions automatically:
282319

283320
## Intermediate Patterns
284321

322+
### Creating Nodes with Builder Pattern
323+
324+
The builder pattern provides a fluent interface for creating nodes:
325+
326+
```go
327+
node := flyt.NewNode().
328+
WithMaxRetries(3).
329+
WithWait(time.Second).
330+
WithPrepFunc(func(ctx context.Context, shared *flyt.SharedStore) (flyt.Result, error) {
331+
// Read input data
332+
data := shared.Get("input")
333+
return flyt.NewResult(data), nil
334+
}).
335+
WithExecFunc(func(ctx context.Context, prepResult flyt.Result) (flyt.Result, error) {
336+
// Process data
337+
result := processData(prepResult.Value())
338+
return flyt.NewResult(result), nil
339+
}).
340+
WithPostFunc(func(ctx context.Context, shared *flyt.SharedStore, prepResult, execResult flyt.Result) (flyt.Action, error) {
341+
// Store result
342+
shared.Set("output", execResult.Value())
343+
return flyt.DefaultAction, nil
344+
})
345+
```
346+
285347
### Configuration via Closures
286348

287349
Pass configuration to nodes using closures:

builder.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package flyt
2+
3+
import (
4+
"context"
5+
"time"
6+
)
7+
8+
// NodeBuilder provides a fluent interface for creating and configuring nodes.
9+
// It implements the Node interface while also providing chainable methods
10+
// for configuration. This allows both styles:
11+
// - Traditional: flyt.NewNode(WithExecFunc(...), WithMaxRetries(3))
12+
// - Builder: flyt.NewNode().WithExecFunc(...).WithMaxRetries(3)
13+
type NodeBuilder struct {
14+
*CustomNode
15+
}
16+
17+
// Prep implements Node.Prep by delegating to the embedded CustomNode
18+
func (b *NodeBuilder) Prep(ctx context.Context, shared *SharedStore) (any, error) {
19+
return b.CustomNode.Prep(ctx, shared)
20+
}
21+
22+
// Exec implements Node.Exec by delegating to the embedded CustomNode
23+
func (b *NodeBuilder) Exec(ctx context.Context, prepResult any) (any, error) {
24+
return b.CustomNode.Exec(ctx, prepResult)
25+
}
26+
27+
// Post implements Node.Post by delegating to the embedded CustomNode
28+
func (b *NodeBuilder) Post(ctx context.Context, shared *SharedStore, prepResult, execResult any) (Action, error) {
29+
return b.CustomNode.Post(ctx, shared, prepResult, execResult)
30+
}
31+
32+
// ExecFallback implements FallbackNode.ExecFallback by delegating to the embedded CustomNode
33+
func (b *NodeBuilder) ExecFallback(prepResult any, err error) (any, error) {
34+
return b.CustomNode.ExecFallback(prepResult, err)
35+
}
36+
37+
// GetMaxRetries implements RetryableNode.GetMaxRetries by delegating to the embedded BaseNode
38+
func (b *NodeBuilder) GetMaxRetries() int {
39+
return b.CustomNode.BaseNode.GetMaxRetries()
40+
}
41+
42+
// GetWait implements RetryableNode.GetWait by delegating to the embedded BaseNode
43+
func (b *NodeBuilder) GetWait() time.Duration {
44+
return b.CustomNode.BaseNode.GetWait()
45+
}
46+
47+
// WithMaxRetries sets the maximum number of retries for the node's Exec phase.
48+
// Returns the builder for method chaining.
49+
func (b *NodeBuilder) WithMaxRetries(retries int) *NodeBuilder {
50+
WithMaxRetries(retries)(b.CustomNode.BaseNode)
51+
return b
52+
}
53+
54+
// WithWait sets the wait duration between retries.
55+
// Returns the builder for method chaining.
56+
func (b *NodeBuilder) WithWait(wait time.Duration) *NodeBuilder {
57+
WithWait(wait)(b.CustomNode.BaseNode)
58+
return b
59+
}
60+
61+
// WithPrepFunc sets a custom Prep implementation using Result types.
62+
// Returns the builder for method chaining.
63+
func (b *NodeBuilder) WithPrepFunc(fn func(context.Context, *SharedStore) (Result, error)) *NodeBuilder {
64+
b.CustomNode.prepFunc = fn
65+
return b
66+
}
67+
68+
// WithExecFunc sets a custom Exec implementation using Result types.
69+
// Returns the builder for method chaining.
70+
func (b *NodeBuilder) WithExecFunc(fn func(context.Context, Result) (Result, error)) *NodeBuilder {
71+
b.CustomNode.execFunc = fn
72+
return b
73+
}
74+
75+
// WithPostFunc sets a custom Post implementation using Result types.
76+
// Returns the builder for method chaining.
77+
func (b *NodeBuilder) WithPostFunc(fn func(context.Context, *SharedStore, Result, Result) (Action, error)) *NodeBuilder {
78+
b.CustomNode.postFunc = fn
79+
return b
80+
}
81+
82+
// WithExecFallbackFunc sets a custom ExecFallback implementation.
83+
// Returns the builder for method chaining.
84+
func (b *NodeBuilder) WithExecFallbackFunc(fn func(any, error) (any, error)) *NodeBuilder {
85+
b.CustomNode.execFallbackFunc = fn
86+
return b
87+
}
88+
89+
// WithPrepFuncAny sets a custom Prep implementation using any types.
90+
// Returns the builder for method chaining.
91+
func (b *NodeBuilder) WithPrepFuncAny(fn func(context.Context, *SharedStore) (any, error)) *NodeBuilder {
92+
b.CustomNode.prepFunc = func(ctx context.Context, shared *SharedStore) (Result, error) {
93+
val, err := fn(ctx, shared)
94+
if err != nil {
95+
return Result{}, err
96+
}
97+
return NewResult(val), nil
98+
}
99+
return b
100+
}
101+
102+
// WithExecFuncAny sets a custom Exec implementation using any types.
103+
// Returns the builder for method chaining.
104+
func (b *NodeBuilder) WithExecFuncAny(fn func(context.Context, any) (any, error)) *NodeBuilder {
105+
b.CustomNode.execFunc = func(ctx context.Context, prepResult Result) (Result, error) {
106+
val, err := fn(ctx, prepResult.Value())
107+
if err != nil {
108+
return Result{}, err
109+
}
110+
return NewResult(val), nil
111+
}
112+
return b
113+
}
114+
115+
// WithPostFuncAny sets a custom Post implementation using any types.
116+
// Returns the builder for method chaining.
117+
func (b *NodeBuilder) WithPostFuncAny(fn func(context.Context, *SharedStore, any, any) (Action, error)) *NodeBuilder {
118+
b.CustomNode.postFunc = func(ctx context.Context, shared *SharedStore, prepResult, execResult Result) (Action, error) {
119+
return fn(ctx, shared, prepResult.Value(), execResult.Value())
120+
}
121+
return b
122+
}
123+
124+
// Build returns the configured Node.
125+
// This method is optional as NodeBuilder already implements Node,
126+
// but it provides explicit intent when ending the builder chain.
127+
func (b *NodeBuilder) Build() Node {
128+
return b
129+
}

0 commit comments

Comments
 (0)