From 1d70fabfa32ab8dbb330bbfc1f929c79880c7f8b Mon Sep 17 00:00:00 2001 From: Josh Cheek Date: Wed, 16 Dec 2020 16:56:34 -0600 Subject: [PATCH] `BetterErrors.use_pry!` works on threaded servers Co-authored-by: Robin Daugherty --- lib/better_errors/repl/pry.rb | 54 +++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/lib/better_errors/repl/pry.rb b/lib/better_errors/repl/pry.rb index 4390ec7e..5b1b6b58 100644 --- a/lib/better_errors/repl/pry.rb +++ b/lib/better_errors/repl/pry.rb @@ -37,15 +37,36 @@ def print(*args) end def initialize(binding, exception) - @fiber = Fiber.new do - @pry.repl binding - end + @input_queue = Queue.new + @output_queue = Queue.new @input = BetterErrors::REPL::Pry::Input.new @output = BetterErrors::REPL::Pry::Output.new - @pry = ::Pry.new input: @input, output: @output - @pry.hooks.clear_all if defined?(@pry.hooks.clear_all) - store_last_exception exception - @fiber.resume + @thread = Thread.new do + Thread.current.abort_on_exception = true + @fiber = Fiber.new do + @pry.repl binding + end + @pry = ::Pry.new input: @input, output: @output + @pry.hooks.clear_all if defined?(@pry.hooks.clear_all) + store_last_exception exception + @fiber.resume + + loop do + command = @input_queue.shift + break if command == :stop + local ::Pry.config, color: false, pager: false do + @fiber.resume "#{command}\n" + end + # NOTE: indent_level here is not what we seem to expect it to be ¯\_(ツ)_/¯ + indent_level = @pry.instance_variable_get(:@indent).indent_level + @output_queue << [@output.read_buffer, *prompt(indent_level)] + end + end + end + + def stop + @input_queue << :stop + @thread.join end def store_last_exception(exception) @@ -54,23 +75,20 @@ def store_last_exception(exception) end def send_input(str) - local ::Pry.config, color: false, pager: false do - @fiber.resume "#{str}\n" - [@output.read_buffer, *prompt] - end + @input_queue << str + @output_queue.shift end - def prompt - if indent = @pry.instance_variable_get(:@indent) and !indent.indent_level.empty? - ["..", indent.indent_level] - else + private + + def prompt(indent_level) + if indent_level.empty? [">>", ""] + else + ["..", indent_level] end - rescue - [">>", ""] end - private def local(obj, attrs) old_attrs = {} attrs.each do |k, v|