Documentation
¶
Index ¶
- func Cached[T any](original Refreshable[T]) (Refreshable[T], UnsubscribeFunc)
- func Collect[T any](list ...Refreshable[T]) (Refreshable[[]T], UnsubscribeFunc)
- func Map[T any, M any](original Refreshable[T], mapFn func(T) M) (Refreshable[M], UnsubscribeFunc)
- func MapWithError[T any, M any](original Refreshable[T], mapFn func(T) (M, error)) (Validated[M], UnsubscribeFunc, error)
- func Merge[T1 any, T2 any, R any](original1 Refreshable[T1], original2 Refreshable[T2], mergeFn func(T1, T2) R) (Refreshable[R], UnsubscribeFunc)
- func NewFromTickerFunc[T any](ctx context.Context, interval time.Duration, ...) (Ready[T], UnsubscribeFunc)
- func Validate[T any](original Refreshable[T], validatingFn func(T) error) (Validated[T], UnsubscribeFunc, error)
- func Wait[T any](ctx context.Context, ready Ready[T]) (T, bool)
- type Ready
- type Refreshable
- type UnsubscribeFunc
- type Updatable
- type Validated
- func MapValues[K comparable, V, R any](ctx context.Context, refreshableMap Refreshable[map[K]V], ...) Validated[map[K]R]
- func NewFileRefreshable(ctx context.Context, filePath string) Validated[[]byte]
- func NewFileRefreshableWithTicker(ctx context.Context, filePath string, updateTicker <-chan time.Time) Validated[[]byte]
- func NewMultiFileRefreshable(ctx context.Context, paths Refreshable[map[string]struct{}]) Validated[map[string][]byte]
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Cached ¶ added in v2.1.0
func Cached[T any](original Refreshable[T]) (Refreshable[T], UnsubscribeFunc)
Cached returns a new Refreshable that subscribes to the original Refreshable and caches its value. This is useful in combination with View to avoid recomputing an expensive mapped value each time it is retrieved. The returned refreshable is read-only (does not implement Update).
Example ¶
r := refreshable.New(42)
cached, stop := refreshable.Cached(r)
cached.Subscribe(func(val int) {
fmt.Println("Update:", val)
})
fmt.Println(cached.Current())
r.Update(100)
fmt.Println(cached.Current())
r.Update(100) // No update
stop()
r.Update(200)
fmt.Println(cached.Current())
Output: Update: 42 42 Update: 100 100 100
func Collect ¶ added in v2.1.0
func Collect[T any](list ...Refreshable[T]) (Refreshable[[]T], UnsubscribeFunc)
Collect returns a new Refreshable that combines the latest values of multiple Refreshables into a slice. The returned Refreshable is updated whenever any of the original Refreshables updates. The unsubscribe function removes subscriptions from all original Refreshables.
Example ¶
r1 := refreshable.New(10)
r2 := refreshable.New(20)
r3 := refreshable.New(30)
collected, stop := refreshable.Collect(r1, r2, r3)
printCollected := func() {
values := collected.Current()
fmt.Printf("Collected values: %v\n", values)
}
printCollected() // Initial values
r1.Update(15)
printCollected() // After updating r1
r2.Update(25)
printCollected() // After updating r2
r3.Update(35)
printCollected() // After updating r3
stop() // Stop collecting updates
r1.Update(40)
printCollected() // No change after stopping
Output: Collected values: [10 20 30] Collected values: [15 20 30] Collected values: [15 25 30] Collected values: [15 25 35] Collected values: [15 25 35]
func Map ¶
func Map[T any, M any](original Refreshable[T], mapFn func(T) M) (Refreshable[M], UnsubscribeFunc)
Map returns a new Refreshable based on the current one that handles updates based on the current Refreshable. See Cached and View for more information.
Example ¶
r := refreshable.New(42)
mapped, stop := refreshable.Map(r, func(val int) string {
return fmt.Sprintf("Mapped: %d", val)
})
fmt.Println(mapped.Current())
r.Update(100)
fmt.Println(mapped.Current())
stop()
r.Update(200)
fmt.Println(mapped.Current())
Output: Mapped: 42 Mapped: 100 Mapped: 100
func MapWithError ¶
func MapWithError[T any, M any](original Refreshable[T], mapFn func(T) (M, error)) (Validated[M], UnsubscribeFunc, error)
MapWithError is similar to Validate but allows for the function to return a mapping/mutation of the input object in addition to returning an error. The returned validRefreshable will contain the mapped value. An error is returned if the current original value fails to map.
Example ¶
r := refreshable.New(42)
validated, stop, err := refreshable.MapWithError(r, func(val int) (string, error) {
if val < 50 {
return "", fmt.Errorf("invalid: %d", val)
}
return fmt.Sprintf("Valid: %d", val), nil
})
if err != nil {
fmt.Println("Initial error:", err)
}
fmt.Println(validated.Validation())
r.Update(100)
fmt.Println(validated.Validation())
r.Update(24)
fmt.Println(validated.Validation())
stop()
r.Update(200)
fmt.Println(validated.Validation())
Output: Initial error: invalid: 42 invalid: 42 Valid: 100 <nil> invalid: 24 invalid: 24
func Merge ¶ added in v2.1.0
func Merge[T1 any, T2 any, R any](original1 Refreshable[T1], original2 Refreshable[T2], mergeFn func(T1, T2) R) (Refreshable[R], UnsubscribeFunc)
Merge returns a new Refreshable that combines the latest values of two Refreshables of different types using the mergeFn. The returned Refreshable is updated whenever either of the original Refreshables updates. The unsubscribe function removes subscriptions from both original Refreshables.
Example ¶
r1 := refreshable.New(42)
r2 := refreshable.New(100)
merged, stop := refreshable.Merge(r1, r2, func(v1, v2 int) string {
return fmt.Sprintf("Sum: %d", v1+v2)
})
fmt.Println(merged.Current())
r1.Update(50)
fmt.Println(merged.Current())
r2.Update(150)
fmt.Println(merged.Current())
stop()
r1.Update(60)
fmt.Println(merged.Current())
Output: Sum: 142 Sum: 150 Sum: 200 Sum: 200
func NewFromTickerFunc ¶
func NewFromTickerFunc[T any](ctx context.Context, interval time.Duration, provider func(ctx context.Context) (T, bool)) (Ready[T], UnsubscribeFunc)
NewFromTickerFunc returns a Ready Refreshable populated by the result of the provider called each interval. If the providers bool return is false, the value is ignored. The result's ReadyC channel is closed when a new value is populated. The refreshable will stop updating when the provided context is cancelled or the returned UnsubscribeFunc func is called.
func Validate ¶
func Validate[T any](original Refreshable[T], validatingFn func(T) error) (Validated[T], UnsubscribeFunc, error)
Validate returns a new Refreshable that returns the latest original value accepted by the validatingFn. If the upstream value results in an error, it is reported by Validation(). An error is returned if the current original value is invalid.
Example ¶
r := refreshable.New(42)
validated, stop, err := refreshable.Validate(r, func(val int) error {
if val < 50 {
return errors.New("value too low")
}
return nil
})
if err != nil {
fmt.Println("Initial error:", err)
}
fmt.Println(validated.Validation())
r.Update(100)
fmt.Println(validated.Validation())
stop()
r.Update(200)
fmt.Println(validated.Validation())
Output: Initial error: value too low 42 value too low 100 <nil> 100 <nil>
Types ¶
type Ready ¶
type Ready[T any] interface { Refreshable[T] // ReadyC returns a channel which is closed after a value is successfully populated. ReadyC() <-chan struct{} }
Ready extends Refreshable for asynchronous implementations which may not have a value when they are constructed. Callers should check that the Ready channel is closed before using the Current value.
func NewFromChannel ¶
NewFromChannel populates an Updatable with the values channel. If an element is already available, the returned Value is guaranteed to be populated. The channel should be closed when no longer used to avoid leaking resources.
type Refreshable ¶
type Refreshable[T any] interface { // Current returns the most recent value of this Refreshable. // If the value has not been initialized, returns T's zero value. Current() T // Subscribe calls the consumer function when Value updates until stop is closed. // The consumer must be relatively fast: Updatable.Set blocks until all subscribers have returned. // Expensive or error-prone responses to refreshed values should be asynchronous. // Updates considered no-ops by reflect.DeepEqual may be skipped. // When called, consumer is executed with the Current value. Subscribe(consumer func(T)) UnsubscribeFunc }
A Refreshable is a generic container type for a volatile underlying value. It supports atomic access and user-provided callback "subscriptions" on updates.
func MapContext ¶
func MapContext[T any, M any](ctx context.Context, original Refreshable[T], mapFn func(T) M) Refreshable[M]
MapContext is like Map but unsubscribes when the context is cancelled.
Example ¶
ctx, cancel := context.WithCancel(context.Background())
r := refreshable.New(42)
mapped := refreshable.MapContext(ctx, r, func(val int) string {
return fmt.Sprintf("Mapped: %d", val)
})
fmt.Println(mapped.Current())
r.Update(100)
fmt.Println(mapped.Current())
cancel()
time.Sleep(time.Millisecond)
runtime.Gosched()
r.Update(200)
fmt.Println(mapped.Current())
Output: Mapped: 42 Mapped: 100 Mapped: 100
func View ¶ added in v2.1.0
func View[T any, M any](original Refreshable[T], mapFn func(T) M) Refreshable[M]
View returns a Refreshable implementation that converts the original Refreshable value to a new value using mapFn. Current() and Subscribe() invoke mapFn as needed on the current value of the original Refreshable. Subscription callbacks are invoked with the mapped value each time the original value changes and the result is not cached nor compared for equality with the previous value, so functions subscribing to View refreshables are more likely to receive duplicate updates.
Example ¶
r := refreshable.New(42)
view := refreshable.View(r, func(val int) string {
return fmt.Sprintf("Value: %d", val)
})
fmt.Println(view.Current())
r.Update(100)
fmt.Println(view.Current())
r.Update(100) // Duplicate update
fmt.Println(view.Current())
Output: Value: 42 Value: 100 Value: 100
type UnsubscribeFunc ¶
type UnsubscribeFunc func()
UnsubscribeFunc removes a subscription from a refreshable's internal tracking and/or stops its update routine. It is safe to call multiple times.
type Updatable ¶
type Updatable[T any] interface { Refreshable[T] // Update updates the Refreshable with a new T. // It blocks until all subscribers have completed. Update(T) }
A Updatable is a Refreshable which supports setting the value with a user-provided value. When a utility returns a (non-Updatable) Refreshable, it implies that value updates are handled internally.
type Validated ¶
type Validated[T any] interface { Refreshable[T] // Validation returns the result of the most recent validation. // If the last value was valid, Validation returns the same value as Current and a nil error. // If the last value was invalid, it and the error are returned. Current returns the most recent valid value. Validation() (T, error) }
A Validated is a Refreshable capable of rejecting updates according to validation logic. Its Current method returns the most recent value to pass validation.
func MapValues ¶ added in v2.4.0
func MapValues[K comparable, V, R any]( ctx context.Context, refreshableMap Refreshable[map[K]V], mapperFn func(context.Context, K, V) Validated[R], ) Validated[map[K]R]
MapValues creates a Validated Refreshable by applying a mapper function to each entry in a map. For each key-value pair in the input map, the mapper function creates a Validated[R] refreshable. The output is a Validated[map[K]R] that aggregates all mapped values.
When keys are added to the input map, new refreshables are created via the mapper function. When keys are removed, their corresponding refreshables are unsubscribed. When any individual mapped refreshable updates, the output map is rebuilt.
Current() returns a map containing only keys whose mapped refreshables are valid. Validation() returns the map and a joined error of all validation failures.
This should be used instead of just calling Map on a map[K]V when you need to interject an additional refreshable that can be updated independently
func NewFileRefreshable ¶ added in v2.3.0
NewFileRefreshable creates a Validated refreshable that reads from a file every second. It is equivalent to calling NewFileRefreshableWithTicker with time.Tick(time.Second).
func NewFileRefreshableWithTicker ¶ added in v2.3.0
func NewFileRefreshableWithTicker(ctx context.Context, filePath string, updateTicker <-chan time.Time) Validated[[]byte]
NewFileRefreshableWithTicker returns a Validated refreshable whose current value is the bytes of the file at the provided path. This function reads the file once then starts a goroutine which re-reads the file on each tick until the provided context is cancelled. If reading the file fails, the Current() value will be unchanged. The error is present in v.Validation().
func NewMultiFileRefreshable ¶ added in v2.4.0
func NewMultiFileRefreshable(ctx context.Context, paths Refreshable[map[string]struct{}]) Validated[map[string][]byte]
NewMultiFileRefreshable creates a Validated Refreshable that tracks the contents of multiple files. The input is a Refreshable of a set of file paths (map keys). The output is a Validated Refreshable of a map from file path to file contents. When files are added to or removed from the input set, the corresponding file watchers are created or destroyed. Each file is read periodically using NewFileRefreshable.
Current() returns a map containing only successfully read files. Validation() returns the map and a joined error of all file read failures.