Skip to content

Instantly share code, notes, and snippets.

@zetachang
Last active December 16, 2015 04:49
Show Gist options
  • Save zetachang/5379588 to your computer and use it in GitHub Desktop.
Save zetachang/5379588 to your computer and use it in GitHub Desktop.
A proof of concept which wrap IRB in a different feed-driven API
require 'irb'
module IRB
WAITING_FOR_INPUT_IDENTIFIER = "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
class MyInputMethod < StdioInputMethod
def gets
puts WAITING_FOR_INPUT_IDENTIFIER
print @prompt + "\n"
line = @stdin.gets
@line[@line_no += 1] = line
end
end
def self.start_session(binding)
IRB.setup(nil)
workspace = WorkSpace.new(binding)
irb = Irb.new(workspace, MyInputMethod.new)
@CONF[:MAIN_CONTEXT] = irb.context
catch(:IRB_EXIT) do
irb.eval_input
end
end
end
class IrbWrapper
def initialize(binding)
@rd, @wr = IO.pipe # for sending input to IRB process
@rd2, @wr2 = IO.pipe # for receiving result from IRB process
@irb_pid = Process.fork do
STDIN.reopen(@rd)
STDOUT.reopen(@wr2)
STDERR.reopen(@wr2)
# Make IRB believe it's talking to terminal, so the prompt string will be returned back
STDIN.instance_eval("def tty?; true; end")
IRB.start_session(binding)
end
@rd2.gets # Read the identifier when first prompted
end
# Should be used carefully between REP round, since read is blocking
def get_prompt()
@rd2.gets.chomp()
end
# The given block will recieve a line when IRB output one until done
def send_input(input, &block)
@wr.write(input)
loop do
str = @rd2.gets
break if str.chomp == IRB::WAITING_FOR_INPUT_IDENTIFIER
if str.include?(IRB::WAITING_FOR_INPUT_IDENTIFIER)
yield str.chomp[0..(-IRB::WAITING_FOR_INPUT_IDENTIFIER.length-1)] + "\n"
break
end
yield str
end
end
def exit
@wr.write("exit\n")
Process.waitpid(@irb_pid)
end
end
rep = IrbWrapper.new(binding)
puts "Welcome :-)"
loop do # Loop
print rep.get_prompt
str = gets # Read
if str.chomp == "exit"
rep.exit
puts "bye"
exit 0
end
rep.send_input(str) do |line| # Eval and Print
print line
end
end
@zetachang
Copy link
Author

Simply download the file, run ruby rep.rb and you got a interactive ruby shell built upon IRB.

@zetachang
Copy link
Author

Make improvement today to provide a more consistent communication between IRB and wrapper class by using our own input method to inject a custom identifier IRB::WAITING_FOR_INPUT_IDENTIFIER before IRB call gets.

@zetachang
Copy link
Author

[Update] The halting status triggered by gets could be solved by using IO.select to detect if input or output available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment