Class: Teek::CallbackRegistry
- Inherits:
-
Object
- Object
- Teek::CallbackRegistry
- Defined in:
- lib/teek/callback_registry.rb
Overview
Tracks Ruby callback ids scoped to something narrower than a whole
widget - keyed by (container, key) pairs - so they can be released
again without waiting for a
There is exactly one way to keep the registry in sync: #reconcile. Its block is handed the => id hash tracked last time and must return the => id hash that should be tracked now; whatever id drops out between the two gets released. What the block does with the hash it's handed is entirely up to the caller - reuse its values as a starting point for a cheap in-memory update (nothing external can silently change an event binding), or ignore it and recompute the truth from scratch by asking Tk (Tk silently renumbers menu entries, so nothing short of asking can be trusted there). The registry itself never knows or cares which one a caller chose.
The one hard rule either way: the returned hash must be a DIFFERENT
object from the one the block was handed - e.g. before.merge(...),
never before.merge!(...) returned as-is. Released ids are computed
as before.values - after.values; if the block mutates before in
place and returns that same object, before and after are
identical by the time that subtraction runs, so any id that was
dropped or replaced is silently never released - a leak, the exact
thing this class exists to prevent.
#forget_all_for_path is the only thing a
Instance Method Summary collapse
-
#counts_by_tag ⇒ Hash{Object => Integer}
Diagnostic aggregate, not itself load-bearing for cleanup - a live snapshot of how many tracked callback ids currently exist, grouped by the tag every #reconcile container is already keyed on (+:bind+,
:menu,:canvas_bind,:tag_bind,:widget_option,:wm_protocol, ...). -
#forget_all_for_path(path) ⇒ void
Release every callback tracked under any container registered for
path, regardless of which feature created it, and forget them. -
#initialize(app) ⇒ CallbackRegistry
constructor
A new instance of CallbackRegistry.
- #reconcile(container) {|before| ... } ⇒ void
Constructor Details
#initialize(app) ⇒ CallbackRegistry
Returns a new instance of CallbackRegistry.
40 41 42 43 44 |
# File 'lib/teek/callback_registry.rb', line 40 def initialize(app) @app = app @entries = Hash.new { |h, k| h[k] = {} } @containers_by_path = Hash.new { |h, k| h[k] = Set.new } end |
Instance Method Details
#counts_by_tag ⇒ Hash{Object => Integer}
Diagnostic aggregate, not itself load-bearing for cleanup - a live
snapshot of how many tracked callback ids currently exist, grouped
by the tag every #reconcile container is already keyed on
(+:bind+, :menu, :canvas_bind, :tag_bind, :widget_option,
:wm_protocol, ...). Counts individual ids, not containers - a
single container can hold several (e.g. one widget bound to
several events). A tag with nothing currently tracked under it is
simply absent from the result, not present with a zero count.
82 83 84 85 86 87 88 |
# File 'lib/teek/callback_registry.rb', line 82 def counts_by_tag counts = Hash.new(0) @entries.each do |(tag, _path), ids| counts[tag] += ids.size unless ids.empty? end counts end |
#forget_all_for_path(path) ⇒ void
This method returns an undefined value.
Release every callback tracked under any container registered for
path, regardless of which feature created it, and forget them.
64 65 66 67 68 69 70 71 |
# File 'lib/teek/callback_registry.rb', line 64 def forget_all_for_path(path) containers = @containers_by_path.delete(path) return unless containers containers.each do |container| ids = @entries.delete(container) ids&.each_value { |id| @app.unregister_callback(id) } end end |
#reconcile(container) {|before| ... } ⇒ void
This method returns an undefined value.
53 54 55 56 57 58 59 |
# File 'lib/teek/callback_registry.rb', line 53 def reconcile(container) track(container) before = @entries[container] after = yield(before) (before.values - after.values).each { |id| @app.unregister_callback(id) } @entries[container] = after end |