go-ruby-observer

Ruby's Observable mixin โ€” the observer registry and changed flag โ€” in pure Go, MRI-compatible, no cgo.

pure Go ยท zero cgo Observable mixin observer registry changed flag insertion order add / delete / count notify decision & reset update dispatch in rbgo MRI-faithful 100% coverage 6 arches
Documentation GitHub
Documentation (MkDocs Material + mike) License: BSD-3-Clause Go 1.26.4+ Coverage 100%

go-ruby-observer is a pure-Go (no cgo) reimplementation of the core state behind Ruby's Observable mixin โ€” the observer registry and the changed flag โ€” faithful to MRI's lib/observer.rb and verified against the reference interpreter (Ruby 4.0.5) with ruby -robserver. A Registry owns the ordered set of observers (insertion order, as MRI's Hash-keyed store), the changed-flag lifecycle, and the notify decision/reset. It deliberately does not invoke observers: NotifyObservers returns the ordered (observer, method) tuples to call, and the actual update dispatch stays in the embedding interpreter (rbgo). Responsiveness (respond_to?) is delegated to a caller-supplied callback, so the package has no dependency on any interpreter. It is bound into go-embedded-ruby by rbgo just like go-ruby-regexp and go-ruby-erb โ€” differential-tested against MRI, 100% coverage, CI green across 6 arches and 3 OSes.

Observer registry ready

A Registry backs one object that includes Observable: it owns the ordered set of observers (MRI’s insertion-ordered Hash-keyed store) and the changed flag. A zero Registry is ready to use.

Add / delete / count ready

AddObserver, DeleteObserver, DeleteObservers and CountObservers mirror add_observer / delete_observer / delete_observers / count_observers. Re-adding an observer updates its method and keeps its position.

Changed-flag lifecycle ready

Changed(state) and ChangedQ() map to changed(state=true) and changed?: the flag starts false, Changed(true) sets it, Changed(false) clears it.

Notify decision & reset ready

NotifyObservers is a no-op returning ok == false when not changed; when changed it returns the observers in insertion order, each paired with its method, then resets changed? to false. The update dispatch stays in rbgo.

respond_to? & raise semantics ready

Responsiveness is delegated to a caller-supplied RespondTo callback. AddObserver of a non-responding method returns *NotRespondingError with MRI’s verbatim message observer does not respond to `update'.

MRI verification & coverage ready

Behaviour pinned against the system ruby -robserver (Ruby 4.0.5); 100% coverage, gofmt + go vet clean, green across all six 64-bit Go arches and three OSes.

A faithful port of the registry behind Ruby's Observable mixin in pure Go, cgo disabled, so it cross-compiles and embeds anywhere. It owns the observer set, insertion ordering, the changed-flag lifecycle and the notify decision; the update dispatch onto each observer stays in the consumer. Verified against the system ruby -robserver โ€” insertion-order notification, the verbatim observer does not respond to `update' message, and the no-op-when-unchanged / notify-then-reset lifecycle. It is a standalone, reusable module bound into the sibling org github.com/go-embedded-ruby.