Class: Rubino::Interaction::InputQueue

Inherits:
Object
  • Object
show all
Defined in:
lib/rubino/interaction/input_queue.rb

Overview

Thread-safe hand-off of typed-while-busy input to the REPL loop.

The chat REPL runs one turn synchronously while a background reader thread keeps accepting keystrokes from the TTY (see CLI::ChatCommand#run_turn). Each completed line the reader sees is push-ed here; when the turn returns, the REPL drains the queue and the captured lines become the NEXT user turn — never injected mid-tool.

Mirrors Run::ApprovalGate’s idiom: a plain ::Queue guarded by a Mutex for the multi-line snapshot. Two threads touch it — the reader (push) and the main loop (drain/pending?) — so every read of the backing queue happens under the lock to keep drain and pending? consistent against a concurrent push.

Instance Method Summary collapse

Constructor Details

#initializeInputQueue

Returns a new instance of InputQueue.



19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/rubino/interaction/input_queue.rb', line 19

def initialize
  # An Array (under @mutex) rather than ::Queue: B4 consumes ONE line at a
  # time (FIFO) and an interrupt line must be able to JUMP ahead of items
  # explicitly parked earlier in the same turn (#push_front), neither of
  # which ::Queue supports. The mutex still serialises the reader (push)
  # against the main loop (shift/drain/pending?).
  @lines   = []
  # Deterministic background notices (#push_notice) are held apart from
  # typed lines: #shift never returns them, so a parked notice can't fire
  # a standalone model turn at the idle prompt (#13).
  @notices = []
  @mutex   = Mutex.new
end

Instance Method Details

#drainObject

Removes and returns every queued line, in arrival order — parked background notices first, then typed lines. Empty when nothing is waiting. Atomic against a concurrent #push.



79
80
81
82
83
84
85
86
# File 'lib/rubino/interaction/input_queue.rb', line 79

def drain
  @mutex.synchronize do
    lines    = @notices + @lines
    @notices = []
    @lines   = []
    lines
  end
end

#drain_noticesObject

Removes and returns only the parked background notices. The turn-START injection (Loop, iteration 1) folds notices into the turn the user just submitted without consuming their typed-ahead lines, which must keep running as their own turns (#13).



92
93
94
95
96
97
98
# File 'lib/rubino/interaction/input_queue.rb', line 92

def drain_notices
  @mutex.synchronize do
    notices  = @notices
    @notices = []
    notices
  end
end

#pending?Boolean

True when at least one line or notice is waiting to be drained.

Returns:

  • (Boolean)


101
102
103
# File 'lib/rubino/interaction/input_queue.rb', line 101

def pending?
  @mutex.synchronize { !@lines.empty? || !@notices.empty? }
end

#push(line) ⇒ Object

Records one completed line typed during the turn. Blank/nil lines are dropped so a stray Enter doesn’t manufacture an empty next turn.



35
36
37
38
39
40
# File 'lib/rubino/interaction/input_queue.rb', line 35

def push(line)
  text = normalize(line)
  return if text.nil?

  @mutex.synchronize { @lines.push(text) }
end

#push_front(line) ⇒ Object

Records a line at the FRONT of the queue so it is the NEXT one #shift returns. Used by the interrupt-by-default Enter: the just-submitted line runs immediately next, AHEAD of any items the user explicitly parked (Alt+Enter / “/queued”) earlier in the same turn, which then run in their own order behind it.



47
48
49
50
51
52
# File 'lib/rubino/interaction/input_queue.rb', line 47

def push_front(line)
  text = normalize(line)
  return if text.nil?

  @mutex.synchronize { @lines.unshift(text) }
end

#push_notice(line) ⇒ Object

Records a deterministic background notice (the ‘[background-task] …completed/failed/stopped` lines). Notices are NOT user turns: #shift never returns them, so at the idle prompt a notice doesn’t spend a whole model turn just to restate itself (#13). It rides along on the NEXT real turn instead — #drain (mid-turn steering boundary) and #drain_notices (turn start) both deliver it.



60
61
62
63
64
65
# File 'lib/rubino/interaction/input_queue.rb', line 60

def push_notice(line)
  text = normalize(line)
  return if text.nil?

  @mutex.synchronize { @notices.push(text) }
end

#shiftObject

Removes and returns the OLDEST queued line (FIFO), or nil when empty. The REPL consumes one queued message per turn so several lines parked during one turn each run as their OWN turn, in submission order (B4) —instead of #drain coalescing them into a single newline-joined message. Atomic against a concurrent #push.



72
73
74
# File 'lib/rubino/interaction/input_queue.rb', line 72

def shift
  @mutex.synchronize { @lines.shift }
end