Verbatim port of Fastway-Server's TFWEventBus from fw_plugin_host.pas
per feedback_copy_dont_reinterpret.md. Adjustments limited to:
- Type renames (TFW* -> T*).
- uses clause: drop fw_log; add log.types from fpc-log so the
optional Logger property uses the canonical ecosystem-wide
TLogProc shape, matching every other fpc-* library.
- Per-handler exception logging now calls Logger with
Level=llError, Category='events', and includes the source
plugin (ASourcePlugin parameter) in the message text so the
canonical signature stays meaningful.
Behaviours preserved verbatim: APluginName bulk-Unsubscribe key,
wildcard '*' subscriber, OnBroadcast external-listener tap,
snapshot-iterate-outside-lock pattern, per-handler exception
isolation, TCriticalSection.
docs/DEVELOPER_GUIDE.md added covering threading, payload
ownership, recursive Fire, OnBroadcast, logger plumbing, and
the relationship between fpc-events (ecosystem-wide pub/sub)
and per-library typed observer callbacks (bp.events / cm.events
pattern).
Tests: 44 assertions across 14 scenarios pass on x86_64-linux.
Pre-tag -vh audit on src/ev.bus.pas reports zero hints/warnings.
198 lines
5.0 KiB
Markdown
198 lines
5.0 KiB
Markdown
# fpc-events — API reference
|
|
|
|
Every callable the library exposes, with parameters, return values,
|
|
and runnable examples. See [`architecture.md`](architecture.md)
|
|
for the big picture and [`DEVELOPER_GUIDE.md`](DEVELOPER_GUIDE.md)
|
|
for the consumer-oriented walkthrough.
|
|
|
|
## Contents
|
|
|
|
- [Quick start](#quick-start)
|
|
- [Types (`events.bus`)](#types)
|
|
- [TEventBus](#teventbus)
|
|
- [Subscribe](#teventbussubscribe)
|
|
- [Unsubscribe](#teventbusunsubscribe)
|
|
- [UnsubscribeCallback](#teventbusunsubscribecallback)
|
|
- [Fire](#teventbusfire)
|
|
- [GetSubscriptionCount](#teventbusgetsubscriptioncount)
|
|
- [OnBroadcast (property)](#teventbusonbroadcast)
|
|
- [Logger (property)](#teventbuslogger)
|
|
- [Version constants](#version-constants)
|
|
|
|
---
|
|
|
|
## Quick start
|
|
|
|
```pascal
|
|
uses
|
|
Classes, SysUtils, fpjson,
|
|
log.types,
|
|
events.bus;
|
|
|
|
var
|
|
Bus: TEventBus;
|
|
Data: TJSONObject;
|
|
begin
|
|
Bus := TEventBus.Create;
|
|
try
|
|
Bus.Subscribe('demo', 'user.login', @MyHandler);
|
|
Data := TJSONObject.Create;
|
|
try
|
|
Data.Add('username', 'alice');
|
|
Bus.Fire('auth', 'user.login', Data);
|
|
finally
|
|
Data.Free;
|
|
end;
|
|
finally
|
|
Bus.Free;
|
|
end;
|
|
end.
|
|
```
|
|
|
|
## Types
|
|
|
|
```pascal
|
|
type
|
|
TEventCallback = procedure(const AEventType: string;
|
|
AData: TJSONObject) of object;
|
|
|
|
TEventBroadcast = procedure(const AEventType: string;
|
|
AData: TJSONObject) of object;
|
|
|
|
TEventSubscription = record
|
|
PluginName: string;
|
|
EventType: string;
|
|
Callback: TEventCallback;
|
|
end;
|
|
```
|
|
|
|
The optional `Logger` property is typed as `log.types.TLogProc`
|
|
(from fpc-log) — the canonical ecosystem-wide logger shape.
|
|
|
|
## TEventBus
|
|
|
|
Constructed with the parameterless `Create` constructor:
|
|
|
|
```pascal
|
|
Bus := TEventBus.Create;
|
|
try
|
|
...
|
|
finally
|
|
Bus.Free;
|
|
end;
|
|
```
|
|
|
|
Thread-safe via a single `TCriticalSection`. The `Free` call
|
|
releases the subscription array and the lock; outstanding
|
|
subscribers' method pointers are released but their *targets*
|
|
(the consumer's class instances) are never freed by the bus.
|
|
|
|
### `TEventBus.Subscribe`
|
|
|
|
```pascal
|
|
procedure Subscribe(const APluginName, AEventType: string;
|
|
ACallback: TEventCallback);
|
|
```
|
|
|
|
Register `ACallback` for events whose type equals `AEventType`,
|
|
or for *every* event when `AEventType = '*'`. `APluginName` is
|
|
a free-form group key used by [`Unsubscribe`](#teventbusunsubscribe);
|
|
pass `''` if your consumer doesn't need bulk removal.
|
|
|
|
`Subscribe` does NOT deduplicate — registering the same callback
|
|
twice produces two entries.
|
|
|
|
### `TEventBus.Unsubscribe`
|
|
|
|
```pascal
|
|
procedure Unsubscribe(const APluginName: string);
|
|
```
|
|
|
|
Remove every subscription whose `PluginName` matches
|
|
`APluginName` (case-insensitive). Use this when a logical
|
|
subsystem owns N subscriptions and is shutting down.
|
|
|
|
### `TEventBus.UnsubscribeCallback`
|
|
|
|
```pascal
|
|
procedure UnsubscribeCallback(ACallback: TEventCallback);
|
|
```
|
|
|
|
Remove every subscription whose method pointer (Code+Data)
|
|
matches `ACallback`. Use this for per-handler removal.
|
|
|
|
### `TEventBus.Fire`
|
|
|
|
```pascal
|
|
procedure Fire(const ASourcePlugin, AEventType: string;
|
|
AData: TJSONObject);
|
|
```
|
|
|
|
Deliver `(AEventType, AData)` to every subscriber whose
|
|
`EventType` equals `AEventType` *or* `'*'`, then invoke
|
|
`OnBroadcast` once if assigned. `ASourcePlugin` is metadata —
|
|
included in handler-error log messages but not used for
|
|
delivery routing.
|
|
|
|
`AData` ownership stays with the caller. Subscribers may read
|
|
fields and copy values out, but must not call `AData.Free`.
|
|
|
|
If a subscriber callback raises, the exception is caught,
|
|
forwarded to the `Logger` (with `Level=llError`,
|
|
`Category='events'`), and delivery continues to the next
|
|
subscriber. Same for `OnBroadcast`.
|
|
|
|
### `TEventBus.GetSubscriptionCount`
|
|
|
|
```pascal
|
|
function GetSubscriptionCount: Integer;
|
|
```
|
|
|
|
Number of registered subscriptions. Acquires the lock; safe to
|
|
call from any thread.
|
|
|
|
### `TEventBus.OnBroadcast`
|
|
|
|
```pascal
|
|
property OnBroadcast: TEventBroadcast read FOnBroadcast write FOnBroadcast;
|
|
```
|
|
|
|
Single nullable callback fired *after* subscriber delivery on
|
|
every `Fire` call. Originally used by Fastway-Server to
|
|
forward every event to web-admin WebSocket clients.
|
|
|
|
A `nil` `OnBroadcast` (the default) means no broadcast.
|
|
|
|
### `TEventBus.Logger`
|
|
|
|
```pascal
|
|
property Logger: TLogProc read FLogger write FLogger;
|
|
```
|
|
|
|
Optional logger for handler-exception messages. `TLogProc` is
|
|
defined in `log.types` (fpc-log) — the same logger shape used
|
|
by every other fpc-* library.
|
|
|
|
When a subscriber callback or the `OnBroadcast` tap raises:
|
|
|
|
- `Logger(llError, 'events', 'Handler error in <plugin> for <type> (from <src>): <msg>')`
|
|
- (`OnBroadcast` errors omit the plugin field.)
|
|
|
|
If `Logger` is `nil` (the default), exceptions are silently
|
|
swallowed.
|
|
|
|
## Version constants
|
|
|
|
In `events.version`:
|
|
|
|
```pascal
|
|
const
|
|
EVENTS_VERSION_MAJOR = 0;
|
|
EVENTS_VERSION_MINOR = 1;
|
|
EVENTS_VERSION_PATCH = 0;
|
|
EVENTS_VERSION_STRING = '0.1.0';
|
|
```
|
|
|
|
Bumped together with the git tag. Pin downstream consumers by
|
|
tag, not commit hash.
|