Class: Brut::TUI::EventLoop
- Inherits:
-
Object
- Object
- Brut::TUI::EventLoop
- Defined in:
- lib/brut/tui/event_loop.rb
Overview
An event loop used to power any TUI, including those that just print out messages. This is intended to be used across multiple threads, with this running on the "main" thread that is allowed to write to the screen.
Instance Method Summary collapse
-
#<<(event) ⇒ Object
Queue an event for later processing.
-
#initialize(tick: true) ⇒ EventLoop
constructor
Create a new EventLoop.
-
#run ⇒ Object
Start the event loop.
-
#subscribe(event_class, subscriber) ⇒ Object
Subscribe to a specific event.
-
#subscribe_to_all(subscriber) ⇒ Object
Subscribe to all events.
Constructor Details
Instance Method Details
#<<(event) ⇒ Object
Queue an event for later processing. This is safe to do from another thread.
22 23 24 |
# File 'lib/brut/tui/event_loop.rb', line 22 def <<(event) @queue << event end |
#run ⇒ Object
Start the event loop. Don't call this more than once. It will block and continue running until an event is received that returns true for Brut::TUI::Events::BaseEvent#exit? or Brut::TUI::Events::BaseEvent#drain_then_exit?.
If Brut::TUI::Events::BaseEvent#exit? returns true, the loop is exited and any events left in the queue are unprocessed, essentially ignored/discarded.
If Brut::TUI::Events::BaseEvent#drain_then_exit? returns true, anything currently in the queue is processed before exiting. If any subscriber adds events to the queue they will not be processed. If no event handler produces errors, the CLI should exit cleanly. If, however, any of the event handlers themselves produce errors, those errors will be handled, but the script will exit nonzero.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/brut/tui/event_loop.rb', line 67 def run debug "EventLoop: starting" start = Time.now loop do event = @queue.pop(timeout: 0.05) debug "EventLoop: got event #{event.class.name}\n #{event.inspect}" if event errors = @event_bus.notify(event) debug "EventLoop: notified subscribers of #{event.class.name}, got #{errors.length} errors" # future screen rendering here handle_errors_from_notify(errors) if event.drain_then_exit? debug "EventLoop: exiting" all_errors = [] @queue.size.times do event = @queue.pop(timeout: 0.05) if event debug "EventLoop (exiting): got event #{event.class.name}\n #{event.inspect}" errors = @event_bus.notify(event) all_errors = all_errors + errors # future screen rendering here end end handle_errors_from_notify(all_errors, immediate: true) break elsif event.exit? debug "EventLoop: exiting" break end end if @tick errors = @event_bus.notify(Brut::TUI::Events::Tick.new(Time.now - start)) handle_errors_from_notify(errors) end end end |
#subscribe(event_class, subscriber) ⇒ Object
Subscribe to a specific event. This requires that subscriber implement the handler method
exposed by Brut::TUI::Events::BaseEvent.handler_method_name. The method's arguments must be
one of three forms:
- no-args (or a single
:restarg, like(*)) - the method is called when the event occurs, no arguments are passed, thus no information about the event is available. - single required arg (e.g.
(event)) - the event instance is passed. - keyword args (e.g.
(description:, command:)) - the event is splatted viadeconstruct_keysand passed in for any keyword arg. If required keyword args aren't available from the event, an exception is raised. If optional keyword args aren't available from the event, their default values are provided. Each event should document what keyword args are available.
In all cases, if the method raises an exception, it is captured and sent as a Brut::TUI::Events::Exception event, potentially to be
handled by other subscribers. See #run for how this interacts with the loop.
41 42 43 |
# File 'lib/brut/tui/event_loop.rb', line 41 def subscribe(event_class, subscriber) @event_bus.subscribe(event_class, subscriber) end |
#subscribe_to_all(subscriber) ⇒ Object
Subscribe to all events. subscriber will only be notified if it
implements an event's Brut::TUI::Events::BaseEvent.handler_method_name or if the subscriber implements
on_any_event. If both are implemented, only the more specific method is called. See #subscribe for a description of
how the method is invoked. If a specific method is not provided, on_any_event is invoked with
the event instance. There is no keyword splatting in this case.
52 53 54 |
# File 'lib/brut/tui/event_loop.rb', line 52 def subscribe_to_all(subscriber) @event_bus.subscribe_to_all(subscriber) end |