Module mediator
Mediator pattern implementation for Lua.
mediator2 allows you to subscribe and publish to a central object so you can decouple function calls in your application. It's as simple as:
mediator:addSubscriber({"channel"}, function)
Supports namespacing, predicates, wildcards, and more.
Some basics:
Priorities
Subscribers can have priorities. The lower the number, the higher the priority. The default priority is after all existing handlers. The priorities are implemented as array-indices, so they are 1-based (highest). This also means that changing priority of a subscriber might impact the absolute value of the priority of other subscribers.
Channels
Channels have a tree structure, where each channel can have multiple sub-channels.
When publishing to a channel, the parent channel will be published to as well.
Channels are automatically created when subscribing or publishing to them.
Technically the channel is implemented as an array of namespaces, for example:
{"car", "engine", "rpm"}
Context
Subscribers can have a context. The context is a value that will be passed to the subscriber
on each call. The context will be omitted from the callback if not provided (nil
). It can be
any valid Lua value, and usually is a table.
The context doubles as a self
parameter for object-based handlers.
Predicates
Subscribers can have predicates. A predicate is a function that returns a boolean.
If the predicate returns true
, the subscriber will be called.
The predicate function will be passed the ctx (if present) + the arguments that were
passed to the publish function.
Callback results
Subscriber callback functions can return 2 values:
- A signal to the mediator to stop or continue calling the next subscriber. Should be mediator.CONTINUE (default) or mediator.STOP.
- Any value to be stored in the result table and passed back to the publisher.
Info:
- Copyright: Copyright (c) 2012-2020 Olivine Labs, 2024-2025 Thijs Schreijer
- Release: 2.0.0
- License: MIT
Class Subscriber
Subscriber:remove () | Removes the subscriber. |
Subscriber:setPriority (priority) | Changes the priority of the subscriber. |
Subscriber:update (updates) | Updates the subscriber with new options. |
Class Channel
Channel:addChannel (namespace) | Adds a single namespace/sub-channel to the current channel. |
Channel:addSubscriber (fn, options) | Creates a subscriber and adds it to the channel. |
Channel:getChannel (namespace) | Gets a single namespace/sub-channel from the current channel, or creates it if it doesn't exist. |
Channel:getNamespaces () | Gets the full namespace array for the current channel. |
Channel:hasChannel (namespace) | Checks if a single namespace/sub-channel exists within the current channel. |
Class Mediator
Mediator.CONTINUE | Lets the mediator continue calling the next subscriber. |
Mediator.STOP | Stops the mediator from calling the next subscriber. |
Mediator.WILDCARD | A wildcard value to be used in channel namespaces to match any namespace. |
Mediator:addSubscriber (channelNamespaces, fn, options) | Subscribes to a channel. |
Mediator:getChannel (channelNamespaces) | Gets a channel by its namespaces, or creates them if they don't exist. |
Mediator:publish (channelNamespaces, ...) | Publishes to a channel (and its parents). |
Class Subscriber
Usage:
local m = require("mediator")() local sub1 = m:addSubscriber({"car", "engine", "rpm"}, function(value, unit) print("Sub1 ", value, unit) end) local sub2 = m:addSubscriber({"car", "engine", "rpm"}, function(value, unit) print("Sub2 ", value, unit) end) m:publish({"car", "engine", "rpm"}, 1000, "rpm") -- Output: -- Sub1 1000 rpm -- Sub2 1000 rpm sub2:setPriority(1) m:publish({"car", "engine", "rpm"}, 2000, "rpm") -- Output: -- Sub2 2000 rpm -- Sub1 2000 rpm sub1:remove() m:publish({"car", "engine", "rpm"}, 3000, "rpm") -- Output: -- Sub2 3000 rpm local options = { ctx = { count = 0 }, -- if provided, will be passed on each call predicate = nil, priority = 1, -- make this one the top-priority } local sub3 = m:addSubscriber({"car", "engine", "rpm"}, function(ctx, value, unit) ctx.count = ctx.count + 1 print("Sub3 ", ctx.count, value, unit) return m.STOP, count -- stop the mediator from calling the next subscriber end) local results = m:publish({"car", "engine", "rpm"}, 1000, "rpm") -- Output: -- Sub3 1 1000 rpm print(results[1]) -- 1 -- the result, count, returned from subscriber sub3
- Subscriber:remove ()
-
Removes the subscriber.
Returns:
-
the removed Subscriber
- Subscriber:setPriority (priority)
-
Changes the priority of the subscriber.
Parameters:
- priority number The new priority of the subscriber.
Returns:
-
the priority as set
- Subscriber:update (updates)
-
Updates the subscriber with new options.
Parameters:
- updates A table of updates options for the subscriber, with fields:
- fn function The new callback function to be called when the channel is published to. (optional)
- options table The new options for the subscriber, see mediator:addSubscriber for fields. (optional)
Returns:
-
nothing
- updates A table of updates options for the subscriber, with fields:
Class Channel
- Channel:addChannel (namespace)
-
Adds a single namespace/sub-channel to the current channel.
If the channel already exists, the existing one will be returned.
Parameters:
- namespace string The namespace of the channel to add.
Returns:
-
Channel
the newly created channel
- Channel:addSubscriber (fn, options)
-
Creates a subscriber and adds it to the channel.
Parameters:
- fn function The callback function to be called when the channel is published to.
- options
table
A table of options for the subscriber. See
mediator:subscribe
for fields.
Returns:
-
Subscriber
the newly created subscriber
- Channel:getChannel (namespace)
-
Gets a single namespace/sub-channel from the current channel, or creates it if it doesn't exist.
Parameters:
- namespace string The namespace of the channel to get.
Returns:
-
Channel
the existing, or newly created channel
- Channel:getNamespaces ()
-
Gets the full namespace array for the current channel.
Returns:
-
Array
the full namespace array
- Channel:hasChannel (namespace)
-
Checks if a single namespace/sub-channel exists within the current channel.
Parameters:
- namespace string The namespace of the channel to check.
Returns:
-
boolean
true
if the channel exists,false
otherwise
Class Mediator
- Mediator.CONTINUE
-
Lets the mediator continue calling the next subscriber.
This is the default value if nothing is returned from a subscriber callback.
- CONTINUE
Usage:
local sub = mediator:addSubscriber({"channel"}, function() result_data = {} return mediator.CONTINUE, result_data end)
- Mediator.STOP
-
Stops the mediator from calling the next subscriber.
- STOP
Usage:
local sub = mediator:addSubscriber({"channel"}, function() result_data = {} return mediator.STOP, result_data end)
- Mediator.WILDCARD
-
A wildcard value to be used in channel namespaces to match any namespace.
Note: when using wildcards, the priority will be to always call the named channel first,
and then the wildcard channel. So changing the priority of a subscriber has an effect
within the named or wildcard channel, but not between them.
- WILDCARD
Usage:
local sub = mediator:addSubscriber({"part1", mediator.WILDCARD, "part2"}, function() print('This will be called for {"part1", "anything", "part2"}') print('but also for: {"part1", "otherthing", "part2"}') end)
- Mediator:addSubscriber (channelNamespaces, fn, options)
-
Subscribes to a channel.
Parameters:
- channelNamespaces array The namespace-array of the channel to subscribe to (created if it doesn't exist).
- fn
function
The callback function to be called when the channel is published to.
signature:
continueSignal, result = fn([ctx,] ...)
whereresult
is any value to be stored in the result table and passed back to the publisher.continueSignal
is a signal to the mediator to stop or continue calling the next subscriber, should be mediator.STOP or mediator.CONTINUE (default). - options A table of options for the subscriber, with fields:
- ctx
any
The context to call the subscriber with, will be omitted from the callback if
nil
. (optional) - predicate
function
A function that returns a boolean. If
true
, the subscriber will be called. The predicate function will be passed the ctx + the arguments that were passed to the publish function. (optional) - priority integer The priority of the subscriber. The lower the number, the higher the priority. Defaults to after all existing handlers. (optional)
- skipChildren
boolean
If
true
, the subscriber will only be invoked on direct publishes to this channel, but not for any child channels. (optional)
- ctx
any
The context to call the subscriber with, will be omitted from the callback if
Returns:
-
Subscriber
the newly created subscriber
- Mediator:getChannel (channelNamespaces)
-
Gets a channel by its namespaces, or creates them if they don't exist.
Parameters:
- channelNamespaces array The namespace-array of the channel to get.
Returns:
-
Channel
the existing, or newly created channel
Usage:
local m = require("mediator")() local channel = m:getChannel({"car", "engine", "rpm"})
- Mediator:publish (channelNamespaces, ...)
-
Publishes to a channel (and its parents).
Parameters:
- channelNamespaces array The namespace-array of the channel to publish to (created if it doesn't exist).
- ... The arguments to pass to the subscribers.
Returns:
-
table
The result table after all subscribers have been called.
Usage:
local m = require("mediator")() m:publish({"car", "engine", "rpm"}, 1000, "rpm")