Installation
Copy
Ask AI
go get github.com/gorules/zen-go
Basic usage
Copy
Ask AI
package main
import (
"fmt"
"os"
zen "github.com/gorules/zen-go"
)
func main() {
content, _ := os.ReadFile("./pricing-rules.json")
engine := zen.NewEngine(zen.EngineConfig{})
defer engine.Dispose()
decision, _ := engine.CreateDecision(content)
defer decision.Dispose()
response, _ := decision.Evaluate(map[string]any{
"customer": map[string]any{"tier": "gold", "yearsActive": 3},
"order": map[string]any{"subtotal": 150, "items": 5},
})
fmt.Println(string(response.Result))
// => {"discount":0.15,"freeShipping":true}
}
Loader
The loader pattern enables dynamic decision loading from any storage backend. Usesync.Map to cache decisions for optimal performance.
File system
Copy
Ask AI
package main
import (
"fmt"
"os"
"path/filepath"
"sync"
zen "github.com/gorules/zen-go"
)
var cache sync.Map
func loader(key string) ([]byte, error) {
if data, ok := cache.Load(key); ok {
return data.([]byte), nil
}
content, err := os.ReadFile(filepath.Join("./rules", key))
if err != nil {
return nil, err
}
cache.Store(key, content)
return content, nil
}
func main() {
engine := zen.NewEngine(zen.EngineConfig{Loader: loader})
defer engine.Dispose()
response, _ := engine.Evaluate("pricing.json", map[string]any{"amount": 100})
fmt.Println(string(response.Result))
}
AWS S3
Copy
Ask AI
package main
import (
"archive/zip"
"bytes"
"context"
"errors"
"fmt"
"io"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
zen "github.com/gorules/zen-go"
)
var rules = make(map[string][]byte)
func main() {
// Download and extract all decisions at startup
cfg, _ := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-east-1"))
s3Client := s3.NewFromConfig(cfg)
result, _ := s3Client.GetObject(context.TODO(), &s3.GetObjectInput{
Bucket: aws.String("my-rules-bucket"),
Key: aws.String("decisions.zip"),
})
defer result.Body.Close()
zipBytes, _ := io.ReadAll(result.Body)
zipReader, _ := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))
for _, file := range zipReader.File {
if !file.FileInfo().IsDir() {
rc, _ := file.Open()
content, _ := io.ReadAll(rc)
rc.Close()
rules[file.Name] = content
}
}
engine := zen.NewEngine(zen.EngineConfig{
Loader: func(key string) ([]byte, error) {
if data, ok := rules[key]; ok {
return data, nil
}
return nil, errors.New("decision not found: " + key)
},
})
defer engine.Dispose()
response, _ := engine.Evaluate("pricing.json", map[string]any{"amount": 100})
fmt.Println(string(response.Result))
}
Azure Blob Storage
Copy
Ask AI
package main
import (
"archive/zip"
"bytes"
"context"
"errors"
"fmt"
"io"
"os"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
zen "github.com/gorules/zen-go"
)
var rules = make(map[string][]byte)
func main() {
// Download and extract all decisions at startup
client, _ := azblob.NewClientFromConnectionString(os.Getenv("AZURE_STORAGE_CONNECTION"), nil)
containerClient := client.ServiceClient().NewContainerClient("rules")
blobClient := containerClient.NewBlobClient("decisions.zip")
resp, _ := blobClient.DownloadStream(context.TODO(), nil)
defer resp.Body.Close()
zipBytes, _ := io.ReadAll(resp.Body)
zipReader, _ := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))
for _, file := range zipReader.File {
if !file.FileInfo().IsDir() {
rc, _ := file.Open()
content, _ := io.ReadAll(rc)
rc.Close()
rules[file.Name] = content
}
}
engine := zen.NewEngine(zen.EngineConfig{
Loader: func(key string) ([]byte, error) {
if data, ok := rules[key]; ok {
return data, nil
}
return nil, errors.New("decision not found: " + key)
},
})
defer engine.Dispose()
response, _ := engine.Evaluate("pricing.json", map[string]any{"amount": 100})
fmt.Println(string(response.Result))
}
Google Cloud Storage
Copy
Ask AI
package main
import (
"archive/zip"
"bytes"
"context"
"errors"
"fmt"
"io"
"cloud.google.com/go/storage"
zen "github.com/gorules/zen-go"
)
var rules = make(map[string][]byte)
func main() {
// Download and extract all decisions at startup
client, _ := storage.NewClient(context.TODO())
bucket := client.Bucket("my-rules-bucket")
reader, _ := bucket.Object("decisions.zip").NewReader(context.TODO())
defer reader.Close()
zipBytes, _ := io.ReadAll(reader)
zipReader, _ := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))
for _, file := range zipReader.File {
if !file.FileInfo().IsDir() {
rc, _ := file.Open()
content, _ := io.ReadAll(rc)
rc.Close()
rules[file.Name] = content
}
}
engine := zen.NewEngine(zen.EngineConfig{
Loader: func(key string) ([]byte, error) {
if data, ok := rules[key]; ok {
return data, nil
}
return nil, errors.New("decision not found: " + key)
},
})
defer engine.Dispose()
response, _ := engine.Evaluate("pricing.json", map[string]any{"amount": 100})
fmt.Println(string(response.Result))
}
Error handling
Copy
Ask AI
response, err := decision.Evaluate(input)
if err != nil {
log.Printf("Evaluation failed: %v", err)
return err
}
fmt.Println(string(response.Result))
Tracing
Enable tracing to inspect decision execution:Copy
Ask AI
response, err := decision.EvaluateWithOpts(input, zen.EvaluationOptions{
Trace: true,
})
if err != nil {
return err
}
fmt.Println(response.Trace)
// Each node's input, output, and performance timing
fmt.Println(response.Performance)
// Total evaluation time
Expression utilities
Evaluate ZEN expressions outside of a decision context:Copy
Ask AI
import zen "github.com/gorules/zen-go"
// Standard expressions (with generics)
result, _ := zen.EvaluateExpression[int]("a + b", map[string]any{"a": 5, "b": 3})
// => 8
total, _ := zen.EvaluateExpression[int]("sum(items)", map[string]any{"items": []int{1, 2, 3, 4}})
// => 10
// Unary expressions (comparison against $)
isValid, _ := zen.EvaluateUnaryExpression(">= 5", map[string]any{"$": 10})
// => true
inList, _ := zen.EvaluateUnaryExpression("'US', 'CA', 'MX'", map[string]any{"$": "US"})
// => true
Best practices
Initialize the engine once. Create a singleZenEngine instance at application startup and reuse it for all evaluations.
Implement a loader for dynamic decisions. The loader pattern centralizes decision loading logic and enables caching at the source.
Call Dispose() on cleanup. Release engine and decision resources when your application terminates.
Copy
Ask AI
engine := zen.NewEngine(zen.EngineConfig{Loader: loader})
defer engine.Dispose()
Copy
Ask AI
var wg sync.WaitGroup
for _, input := range inputs {
wg.Add(1)
go func(in map[string]any) {
defer wg.Done()
response, _ := engine.Evaluate("pricing.json", in)
// process response
}(input)
}
wg.Wait()