Getting Started

Build your first command in 5 minutes.

Install the framework

shell

go get github.com/gloo-foo/framework

Zero transitive dependencies beyond rill for concurrent streams and afero for testable filesystem access.

Choose a pattern

Every command maps to one of 7 patterns. For your first command, use Map — the simplest. One in, one out.

I want to…Pattern
Transform each itemMap
Filter itemsFilter
Collect and reorderAccumulate
Reduce to summaryAggregate
Split into manyExpand

See all 7 patterns →

Implement a command

Build uppercase — converts each line to upper case:

go

func Uppercase(opts ...any) gloo.Command[[]byte, []byte] {
    return patterns.Map(func(line []byte) ([]byte, error) {
        return bytes.ToUpper(line), nil
    })
}

One function, one pattern, zero boilerplate.

Write tests

Test without touching the filesystem:

go

func TestUppercase(t *testing.T) {
    source := gloo.SliceSource([][]byte{
        []byte("hello"),
        []byte("world"),
    })

    results, _ := gloo.Chain(source).
        To(Uppercase()).
        Collect()

    // results.([][]byte) == {[]byte("HELLO"), []byte("WORLD")}
}

No files, no I/O, no mocks. Pure in-memory streams.

Compose into a pipeline

Import command aliases and compose with Run or Chain:

go

// Run: source, sink, then commands
gloo.Run(source, gloo.ByteWriteTo(os.Stdout),
    Grep("error", IgnoreCase),
    Sort(Reverse),
    Uniq(Count),
)

// Chain: fluent builder
gloo.Chain(source).
    To(Grep("error", IgnoreCase)).
    To(Sort(Reverse)).
    To(Uniq(Count)).
    Sink(gloo.ByteWriteTo(os.Stdout))

Commands are values. Pipelines are composition.

Next steps

You’ve built a command, tested it without I/O, and composed it into a pipeline. Now explore:

Explore all 7 patterns →