Files
fpc-events/docs/API.md
Ken Johnson 128ddcb4df v0.1.0: thread-safe pub/sub event bus
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.
2026-05-05 18:13:10 -07:00

5.0 KiB

fpc-events — API reference

Every callable the library exposes, with parameters, return values, and runnable examples. See architecture.md for the big picture and DEVELOPER_GUIDE.md for the consumer-oriented walkthrough.

Contents


Quick start

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

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:

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

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; pass '' if your consumer doesn't need bulk removal.

Subscribe does NOT deduplicate — registering the same callback twice produces two entries.

TEventBus.Unsubscribe

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

procedure UnsubscribeCallback(ACallback: TEventCallback);

Remove every subscription whose method pointer (Code+Data) matches ACallback. Use this for per-handler removal.

TEventBus.Fire

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

function GetSubscriptionCount: Integer;

Number of registered subscriptions. Acquires the lock; safe to call from any thread.

TEventBus.OnBroadcast

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

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:

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.