Skip to content

sean9999/rebouncer

Repository files navigation

Rebouncer

Conventional Commits

Maintenance

Go Reference

Go Report Card

Go version

A Powerful Debouncer for your Conjuring Needs

Hand of Fish

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.

Using it as a binary

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"}

Using it as a library

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:

Injestor

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

Reducer operates on the entire Queue, each time Injestor runs, modifiying, removing, or even adding events as needed.

Quantizer

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.