Module: gears.reactive

Utility module to convert functions to Excel like objects.

When converting a function into a gears.reactive object, all properties accessed by the function are monitored for changes.

When such a change is detected, the property::value signal is emitted. When used within a widget declarative construct, the property it is attached to will also be automatically updated.

Theory

Client geometry

To use this module and, more importantly, to understand how to write something that actually works based on it, some background is required. Most AwesomeWM objects are based on the gears.object module or it's C equivalent. Those objects have "properties". Behind the scene, they have getters, setters and a signal to notify the value changed.

gears.reactive adds a firewall like sandbox around the function. It intercept any gears.object instance trying to cross the sandbox boundary and replace them with proxy objects. Those proxies have built-in introspection code to detect how they are used. This is then converted into a list of objects and signals to monitor. Once one of the monitored object emits one of the monitored signal, then the whole function is re-evaluated. Each time the function is evaluated, the "target" properties are updated. The reactive function result or any call to external function from within goes through the firewall again and any proxies are removed.

That design has one big limitation. It cannot detect any changes which are not directly part of gears.object instance. You cannot use random tables and expect the function to be called when it's content change. To work around this, it is recommanded to make "hub" objects to store the data used within the reactive function.

Recommanded usage

The best way to use gears.reactive is when the value used within the expressions are part of other objects. It can be a gears.watcher, but it can also be a custom object:

Usage example

-- It's important to set 'enable_auto_signals' to true or it wont work.
--
-- Note that most AwesomeWM objects (and most modules) objects can be
-- used directly as long as they implement the signal property:: spec.
--
-- So you don't *need* a hub object, but it's safer to use one.
local my_hub = gears.object {
    enable_properties   = true,
    enable_auto_signals = true
}

-- Better set a default value to avoid weirdness.
my_hub.some_property = 42

-- This is an example, in practice do this in your
-- wibar widget declaration tree.
local w = wibox.widget {
    markup = gears.reactive(function()
        -- Each time my_hub.some_property changes, this will be
        -- re-interpreted.
        return '<i>' .. (my_hub.some_property / 100) .. '</i>'
    end),
    widget = wibox.widget.textbox
}

-- This will update the widget text to '<i>13.37</i>'
my_hub.some_property = 1337

Limitations

gears.reactive is pushing Lua way beyond what it has been designed for. Because of this, there is some limitations.

  • This module will NOT try to track the change of other functions and methods called by the expression. It is NOT recursive and only the top level properties are tracked. This is a feature, not a bug. If it was recursive, this list of limitations or gotchas would be endless.
  • This only works with gears.object and Core API objects which implement the property::* signals correctly. If it is a regular Lua table or the property signals are incorrectly used, the value changes cannot be detected. If you find something that should work, but doesn't in one of the AwesomeWM API, please report a bug.
  • More generally, when making a custom gears.object with custom setters, it is the programmer responsibility to emit the signal. It is also required to only emit those signals when the property actually changes to avoid an unecessary re-evaluations.
  • Changing the type of the variables accessed by the reactive function (its "upvalues") after the reactive expression has been created wont be detected. It will cause missed updates and, potentially, hard to debug Lua errors within the proxy code itself.
  • Internally, the engine tries its best to prevent the internal proxy objects to leak out the sandbox. However this cannot be perfect, at least not without adding limitation elsewhere. It is probably worth reporting a bug if you encounter such an issue. But set your expectations, not all corner case can be fixed.
  • Don't use rawset in the expression.
  • If the return value is a table, only the first 3 nesting levels are sanitized. Avoid using nested tables in the returned value if you can. gears.object instances should be fine.
  • There is currently no way to disable a reactive expression once it's been defined. This may change eventually.
  • Rio Lua 5.1 (not LuaJIT 2.1) is currently not support. If you need it, please report a bug.

Info:

  • Copyright: 2017-2020 Emmanuel Lepage-Vallee
  • Originally authored by: Emmanuel Lepage-Vallee <elv1313@gmail.com>
    (Full contributors list available on our github project)

Constructors

gears.reactive {[args]} Create a new gears.reactive object.

Object properties

expression function A function which will be evaluated each time its content changes.
value N/A The current value of the expression.
delayed boolean Only evaluate once per event loop iteration.

Object methods

:disconnect () Disconnect all expression signals.
:refresh () Recompute the expression.
:emit_signal (name, ...) Emit a signal. Inherited from gears.object
:connect_signal (name, func) Connect to a signal. Inherited from gears.object
:weak_connect_signal (name, func) Connect to a signal weakly. Inherited from gears.object

Signals

target_added Emitted when a new property is attached to this reactive expression.


Constructors

gears.reactive {[args]}
Create a new gears.reactive object.

Parameters:

  • args
    • expression function A function which accesses other gears.object.
    • object gears.object Any AwesomeWM object.
    • property string The name of the property to track.

Object properties

expression function · 1 signal
A function which will be evaluated each time its content changes.
Click to display more

Emit signals:

  • property::gears.reactive.expression When the gears.reactive.expression value changes.
    • self gears.reactive The object which changed (useful when connecting many object to the same callback).
    • new_value function The new value affected to the property.
value N/A · 1 signal
The current value of the expression.
Click to display more

Emit signals:

  • property::gears.reactive.value When the gears.reactive.value value changes.
    • self gears.reactive The object which changed (useful when connecting many object to the same callback).
delayed boolean · 1 signal
Only evaluate once per event loop iteration.

In most case this is a simple performance win, but there is some case where you might want the expression to be evaluated each time one of the upvalue "really" change rather than batch them. This option is enabled by default.

Click to display more

Emit signals:

  • property::gears.reactive.delayed When the gears.reactive.delayed value changes.
    • self gears.reactive The object which changed (useful when connecting many object to the same callback).
    • new_value delayed The new value affected to the property.

Object methods

:disconnect ()
Disconnect all expression signals.
:refresh ()
Recompute the expression.

When the expression uses a non-object upvalue, the changes cannot be auto-retected. Calling :refresh() will immediatly recompute the expression.

:emit_signal (name, ...) · Inherited from gears.object
Emit a signal.

Parameters:

  • name string The name of the signal.
  • ... Extra arguments for the callback functions. Each connected function receives the object as first argument and then any extra arguments that are given to emit_signal().
:connect_signal (name, func) · Inherited from gears.object
Connect to a signal.

Parameters:

  • name string The name of the signal.
  • func function The callback to call when the signal is emitted.
:weak_connect_signal (name, func) · Inherited from gears.object
Connect to a signal weakly.

This allows the callback function to be garbage collected and automatically disconnects the signal when that happens.

Warning: Only use this function if you really, really, really know what you are doing.

Parameters:

  • name string The name of the signal.
  • func function The callback to call when the signal is emitted.

Signals

target_added
Emitted when a new property is attached to this reactive expression.

Arguments:

  • object gears.object The object (often the widget).
  • property string The property name.
generated by LDoc 1.4.6 Last updated 2021-11-13 00:35:50