Current section

Files

Jump to
macula_neuroevolution src evolve neuroevolution_events_local.erl
Raw

src/evolve/neuroevolution_events_local.erl

%% @doc Local event backend using Erlang's pg (process groups).
%%
%% This is the default backend for single-node or development use.
%% For distributed operation, use `neuroevolution_events_macula'.
%%
%% == Message Format ==
%%
%% Subscribers receive messages in the format:
%% `{neuro_event, Topic, Event}'
%%
%% == Example ==
%%
%% Subscribe to events:
%% neuroevolution_events:subscribe(Topic)
%%
%% In handle_info:
%% handle_info({neuro_event, Topic, Event}, State) ->
%% io:format("Received ~p on ~p~n", [Event, Topic]),
%% {noreply, State}.
%%
%% @author Macula.io
%% @copyright 2025 Macula.io
-module(neuroevolution_events_local).
-behaviour(neuroevolution_events).
-export([
publish/2,
subscribe/2,
unsubscribe/2,
start/0
]).
%% Process group scope
-define(SCOPE, neuro_events).
%%% ============================================================================
%%% API Functions
%%% ============================================================================
%% @doc Start the local event system.
%%
%% This must be called once at application startup to initialize
%% the pg scope. It's safe to call multiple times.
-spec start() -> ok.
start() ->
case pg:start(?SCOPE) of
{ok, _Pid} -> ok;
{error, {already_started, _Pid}} -> ok
end.
%% @doc Publish an event to all subscribers of a topic.
-spec publish(Topic, Event) -> ok when
Topic :: binary(),
Event :: term().
publish(Topic, Event) ->
%% Ensure pg scope exists
_ = start(),
%% Get all subscribers for this topic
Members = pg:get_members(?SCOPE, Topic),
%% Send event to all subscribers
Message = {neuro_event, Topic, Event},
lists:foreach(
fun(Pid) ->
Pid ! Message
end,
Members
),
ok.
%% @doc Subscribe a process to a topic.
-spec subscribe(Topic, Pid) -> ok when
Topic :: binary(),
Pid :: pid().
subscribe(Topic, Pid) ->
%% Ensure pg scope exists
_ = start(),
%% Join the process group for this topic
ok = pg:join(?SCOPE, Topic, Pid),
ok.
%% @doc Unsubscribe a process from a topic.
-spec unsubscribe(Topic, Pid) -> ok when
Topic :: binary(),
Pid :: pid().
unsubscribe(Topic, Pid) ->
%% Leave the process group
ok = pg:leave(?SCOPE, Topic, Pid),
ok.