Rebouncer is a generic library that takes a noisy source of events, and produces a cleaner source.
It employes a plugin architecture that can allow it to be used flexibly whenever the fan-out/fan-in concurrency pattern is needed.
The canonical case is a file-watcher that discards events involving temp files and other artefacts, providing its consumer with a clean, sane, and curated source of events. It is the engine behind Fasthak.
For the canonical case, rebouncer is also available as a binary. It takes a directory as an argument, producing SSE events to stdout.
This simplest case is accomplished like so:
$ go install github.com/sean9999/rebouncer/cmd
$ rebouncer -dir ~/projects/myapp/build
Which might stream to stdout something that looks like this:
event: rebouncer/fs/output data: {"file": "index.html", "operation": "modify"} event: rebouncer/fs/output data: {"file": "css/debug.css", "operation": "delete"} event: rebouncer/fs/output data: {"file": "css/mobile", "operation": "create"} event: rebouncer/fs/output data: {"file": "css/mobile", "operation": "modify"}
You may want more flexibility than that. Rebouncer can be invoked as a library, allowing you to embed it in your application and giving you fine-grained control.
Rebouncer needs a few basic to be passed in. Continuing the example a file-watcher, let's go over the basic architecture of these plugin lifecycle functions:
An injestor is defined as runs in a go routine, and sends events of interest to Rebouncer, pushing them onto the Queue. It looks like this:
Reducer operates on the entire Queue, each time Injestor runs, modifiying, removing, or even adding events as needed.
Runs in a go routine, keeping tabs on the Queue and telling Rebouncer when it's ready for a Flush().
The simplest case for use in a library is, again, the canonical case of a file-watcher, for which there is a convenience function:
package main
import (
"fmt"
"github.com/sean9999/rebouncer"
)
// watch ./build and emit every 1000 milliseconds
stateMachine := rebouncer.NewInotify("./build", 1000)
for niceEvent := range stateMachine.Subscribe() {
fmt.Println(niceEvent.Dump())
}
Calling rebouncer.NewInotify()
in this way is the equivilant of:
// rebecca is our singleton instance
stateMachine := rebouncer.New(rebouncer.Config{
BufferSize: rebouncer.DefaultBufferSize,
Quantizer: rebouncer.DefaultInotifyQuantizer(1000),
Reducer: rebouncer.DefaultInotifyReduce,
Injestor: rebouncer.DefaultInotifyInjestor("./build", rebouncer.DefaultBufferSize),
})
DefaultInotifyQuantizer()
, DefaultInotifyReduce()
, and DefaultInotifyInjestor()
are all themselves convenience functions that alleviate you from having to write your own respective Quantizer, Reducer, and Injestor.