Last active
December 16, 2015 04:49
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
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
.
[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
Simply download the file, run
ruby rep.rb
and you got a interactive ruby shell built upon IRB.