Class: Terminalwire::V2::Server::Handler
- Inherits:
-
Object
- Object
- Terminalwire::V2::Server::Handler
- Defined in:
- lib/terminalwire/v2/server/handler.rb
Overview
The framework-agnostic server entrypoint: performs the handshake, runs your CLI with a Terminalwire-backed context, handles errors, and exits the client. A Rails or Rack adapter just builds a transport and calls this.
Your CLI can be ANY of:
* a block / callable run(context, args) — works with OptionParser, GLI,
dry-cli, or hand-rolled parsing. The block runs inside `Server.redirect`,
so $stdout/$stderr/$stdin and bare puts/gets already target the client.
* a Thor class via `cli_class:` (it gets Thor's dedicated shell adapter).
# OptionParser (or anything using the standard IO globals):
Handler.new do |ctx, args|
opts = {}
OptionParser.new { |o| o.on("--name NAME") { |v| opts[:name] = v } }.parse!(args)
puts "hello #{opts[:name]}"
end
# Thor:
Handler.new(cli_class: MyThorCLI)
Constant Summary collapse
- DEFAULT_ERROR_MESSAGE =
"An error occurred. Please try again."
Instance Method Summary collapse
-
#call(transport:, request: {}) ⇒ Object
Run one session over the given transport.
-
#initialize(cli_class: nil, run: nil, report: nil, verbose: false, error_message: DEFAULT_ERROR_MESSAGE) {|context, args| ... } ⇒ Handler
constructor
A new instance of Handler.
Constructor Details
#initialize(cli_class: nil, run: nil, report: nil, verbose: false, error_message: DEFAULT_ERROR_MESSAGE) {|context, args| ... } ⇒ Handler
Returns a new instance of Handler.
32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/terminalwire/v2/server/handler.rb', line 32 def initialize(cli_class: nil, run: nil, report: nil, verbose: false, error_message: DEFAULT_ERROR_MESSAGE, &block) @cli_class = cli_class @run = run || block @report = report @verbose = verbose @error_message = return if @cli_class || @run raise ArgumentError, "provide a Thor cli_class:, a run: callable, or a block" end |
Instance Method Details
#call(transport:, request: {}) ⇒ Object
Run one session over the given transport. Returns the exit status.
request is the incoming HTTP connection profile from the Rack env
({ host:, ip:, user_agent:, headers: }) — threaded in so URL helpers can use
the host (v1 set cli.default_url_options[:host] the same way) and so server
code / about can see who connected.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/terminalwire/v2/server/handler.rb', line 50 def call(transport:, request: {}) runtime = Runtime.new(transport: transport).handshake context = Context.new(runtime) context.request = request arguments = context.program_arguments status = 0 begin begin dispatch(context, arguments, request[:host]) rescue Interrupt, Interrupted status = 130 rescue SystemExit => e # A command that called `exit`/`abort` raises SystemExit (not a # StandardError). Without this it would slip past the rescue below, the # ensure would send the client `exit(0)` — reporting success — and the # exception would then silently kill the CLI thread. Honor the real code. status = e.status rescue StandardError => e status = handle_error(e, context) ensure # Teardown must not be interrupted. A late Ctrl-C (delivered as an async # Interrupted via Thread#raise) landing here would abort the exit-frame # write or runtime close and hang the client — the very failure the # interrupt machinery exists to avoid. Mask async interrupts for the # duration so the exit frame always flushes and the runtime always closes. Thread.handle_interrupt(Interrupt => :never, Interrupted => :never) do context.exit(status) runtime.close end end rescue Interrupt, Interrupted # An interrupt that fired in a rescue clause above, before the mask took # hold, surfaces here. Teardown still ran in the ensure, so just report it. status = 130 end status end |