Property Changes

Using a Signal for Property Updates

Because Wyrd resources can (in principle) be updated outside of your current process, it may make sense for your code to be notified of such events. This is mapped to standard signals.

You can set up any GObject as the receiver of such a signal. The following code sets a receiver object up to receive a signal for updates for a named wyrd property.

receiver = GObject.Object() # Use a more sensible receiver

signal = handle.setup_property_signal(receiver, ".foo.bar")

Because of the way GObject mappings to other languages work, it’s difficult to already connect the signal here. Instead, the function returns a signal name that you can connect a callback to:

def func(handle, propert_path, value):
  # do something with the new value
  pass

handle.connect(signal, func)

If the property now gets updates — also from within the same process — the callback above will be invoked via the signalling mechanism.

handle.set_property(".baz", 42) # does not trigger the callback
handle.set_property(".foo.bar", "hello") # triggers the callback

Wyrd Property to GObject Property Mapping

Of course when you think about signals and properties, you’re likely going to think of GObject properties and their very own notify:: signal. Would it not be nice if you could somehow make updates to such GObject properties be persisted in Wyrd properties, and vice versa have updates to Wyrd properties propagate to GObject properties?

Why yes, it would. One could argue that that is the main purpose of this SDK, to offer easy persistence of application state.

For this, we’ll define a GObject with a property first:

class ObjectWithProperty(GObject.Object):
    @GObject.Property(type=int)
    def prop_gint(self):
        """Read-write integer property."""
        if not hasattr(self, 'value'):
            return None
        return self.value

    @prop_gint.setter
    def prop_gint(self, value):
        self.value = value

Of course, this is a rather boring example, but nothing prevents you from using more interesting code you may already have.

Next, we’ll use the WyrdHandle to connect a Wyrd property to a GObject property like so:

obj = ObjectWithProperty()

res = handle.property_connect(obj, "prop-gint", ".foo.bar")

obj.set_property("prop-gint", 321)
val = handle.get_property(".foo.bar")
assert 321 == val

ret = handle.set_property(".foo.bar", 123)
assert 123 == obj.get_property("prop-gint")

And there you have it. Easy state persistence for your code.

Further Reading

It’s worth pointing out that Wyrd documents are actually vessel resources. Vessel is a container file format for arbitrary data, not just Wyrd properties. But it has features that make it particularly suitable for networked data, where nodes may synchronize updates to a resource periodically.

Wyrd on the other hand makes use of those features, and adds conflict-free replicated data type functionality. Each wyrd property is, in fact, such a CRDT. That means that using this method for storing application state prepares your application for networked, multi-user operations.

Future updates to this SDK will allow you to configure such networking code as well. In the meantime, this library “just” offers easy state persistence with a pathway to full offline-first, eventually synchronized operations.