PUBLISHER ========= PUBLISHER is a tclOO-class providing a general facility for implementing the publisher-subscribers pattern. A publisher is an object usually attached to a master-object (e.g. a complex data structure such a list, a graph, a table, a database ...) that can raise events of public interest. These events usually occur when something in the master-object changes. The master-object knows nothing about its potentials subscribers; it simply tells the publisher to notify some events to all the (dynamically) registered subscribers. Subscribers (also referred as observers) are clients interested about changes occurring on that particular master-object. They inform the publisher they are interested on some events and give it a callback, i.e. a command that publisher should call every time events are generated. Events may have parameters that will be passed to subscribers' callbacks. RECOMMENDED NAMING CONVENTIONS ------------------------------ These naming conventions are not mandatory. You are encouraged to follow them - or any other coherent set of conventions - just because they help to write a more readable code (without the need of extra "comments"). * Each *event-name* has a leading "!" (e.g !ev1 !alert ...) * Each event may carry details (*event's data*) as a list of key-values; Keys have a leading "-" (e.g. -color blue -temperature 100.0 .... ) * Each different *subscriber* must be identified by a different "id". If subscriber is a widget, "id" could be the widget-name If subscriber is a (snit) object, "id" could be its name Otherwise, you should choose an "id", provided it is unique among all possible subscribers (of the same publisher) * Each *callback* name should be "On!event" (e.g. On!alert ) How to interact with a publisher (subscribers-side) -------------------------------------------------- Let's suppose we have a publisher ($pub) attached to a master-object and some observers wishing to be informed whenever a change in the master-object occur. The folllowing command lists all the possible events $pub events ;# --> { !configure !alert !full !evXYZ !destroyed } Then an observer (whose "id" is "$obsA") interested on event "!evXYZ" should 1) setup a callback proc On!evXYZ {args} { array set param $args .. do something with param(-color), param(-x) param(-y) .... } 2) tell the publisher to call its callback for all the next notifications. $pub register !evXYZ $obsA On!evXYZ Of course each different event may provide different details (event-data), and it is the subscriber's responsibility to setup a conformant callback. When an observer ($obsA) is no more interested in publisher's notifications it must revoke the subscription $pub unregister !evXYZ $obsA ;# revoke subscription for event !evXYZ or $pub unregister * $obsA ;# revoke ALL its-own subscriptions Note that an observer MUST revoke ALL its subscriptions before being destroyed, or the publisher will send all next events to a no-more-existing client. ** Universal callback ** For testing purpose with just few lines of code, you can setup an universal callback, able to print every detail, catching all possible events: # Universal-callback; note the first two "fixed" parameters ... proc On!EveryEvent {ev pub args} { puts "event ($ev) from ($pub)" foreach {key val} $args { puts "\tkey: ($key) -- ($val)" } } # register for all events foreach ev [$pub events] { $pub register $ev $obsA [list On!EveryEvent $ev $pub] } Note that the first two parameters of the callback are fixed at "register-time"; the publisher only "appends" events-data (as the usual key-value list) to a command with two "pre-fixed" parameters. How to interact with subscribers (publisher--side) -------------------------------------------------- When a master-object needs to interact with several observers, it must create its own publisher for handling such interactions. First, master-object creates a publisher: set pub [publisher new] or set pub [publisher create _name_] Then it declares the names of events it will provide $pub declare !evA !evB !evC or $pub declare !evA ; $pub declare !evB !evC Other than the explicitelly declared event-names, all publishers always provide a standard event named "!destroyed" informing the subscribers that it has been .. destroyed (usually by the master-object). This standard event doesn't need to be declared. The !destroyed event carries no event-data. Note that (currently) when declaring events there is no way to declare the parameters (event-data). It's just a matter of good documentation practice: each publishers should document all its events, their meanings, and their parameters. *** It is strongly recommended that parameters always be transmitted as an unordered list of key-value pairs. This key-value convention allows to upgrade the publisher-part ( e.g. adding a parameter "-speed" for a given event), without the need to rewrite the previous subscribers callbacks. (Of course non-upgraded subscribers will simply ignore the new parameter). *** When a change in the master-object occur, the master-object must tell to its publisher to notify the event. $pub notify !itemRemoved -id 42342 $pub notify !itemAdded -id 12312 -parent 1239866 $pub notify !itemConfigured -id 23452 -color blue -rank "A" Note that master-object knows nothing about its currently registered subscribers; that's the publisher's job! When a master-object deletes its publisher, the publisher implicitely trasmits a last event $pub notify !destroyed Subscribers should simply 'forget' the publisher, without the need to unregister their callback (in fact, they can't unregister, because there is no publisher to contact!) === Publisher-Subscribers vs. Tk-events ======================================= The main difference to the event system built into the Tcl/Tk core is that the latter can generate only virtual events, and only for Tk-widgets. It is not possible to use the builtin facilities to bind to events on arbitrary (non-Tk-)objects, nor is it able to generate events for such. Moreover, even for widgets, the bind-event system is rather clumsy when multiple callbacks should be independently attached (bind) to an event, and indipendently detached. The publisher-subscribers system can be used in a coherent way both for Tk-widgets and for arbitrary objects.