Embedding irb


(Gilles Filippini) #1

Hi,

It must be a basic question, but I didn’t find any answer:
How to embed an IRB session into a ruby app ?

Thanks in advance.

_gilles.


(Joel VanderWerf) #2

Gilles Filippini wrote:

Hi,

It must be a basic question, but I didn’t find any answer:
How to embed an IRB session into a ruby app ?

This is one way to do it for a command line app (i.e., without changing
stdin/out). It works well, but I do wish IRB made this a bit easier. The
obj argument to start_session will be self when the session starts up.

require 'irb’
require ‘irb/completion’

module IRB
def IRB.start_session(obj)
unless $irb
IRB.initialize nil
## maybe set some opts here, as in parse_opts in irb/init.rb?
IRB.load_modules
end

workspace = WorkSpace.new(obj)

if @CONF[:SCRIPT] ## normally, set by parse_opts
  $irb = Irb.new(workspace, @CONF[:SCRIPT])
else
  $irb = Irb.new(workspace)
end

@CONF[:IRB_RC].call($irb.context) if @CONF[:IRB_RC]
@CONF[:MAIN_CONTEXT] = $irb.context

trap("INT") do
  $irb.signal_handle
end

catch(:IRB_EXIT) do
  $irb.eval_input
end
print "\n"

## might want to reset your app's interrupt handler here

end
end


(Bob X) #3

Into an FXRuby app would be even better. : )

Joel VanderWerf wrote:

···

Gilles Filippini wrote:

Hi,

It must be a basic question, but I didn’t find any answer:
How to embed an IRB session into a ruby app ?

This is one way to do it for a command line app (i.e., without changing
stdin/out). It works well, but I do wish IRB made this a bit easier. The
obj argument to start_session will be self when the session starts up.

require 'irb’
require ‘irb/completion’

module IRB
def IRB.start_session(obj)
unless $irb
IRB.initialize nil
## maybe set some opts here, as in parse_opts in irb/init.rb?
IRB.load_modules
end

workspace = WorkSpace.new(obj)

if @CONF[:SCRIPT] ## normally, set by parse_opts
  $irb = Irb.new(workspace, @CONF[:SCRIPT])
else
  $irb = Irb.new(workspace)
end

@CONF[:IRB_RC].call($irb.context) if @CONF[:IRB_RC]
@CONF[:MAIN_CONTEXT] = $irb.context

trap("INT") do
  $irb.signal_handle
end

catch(:IRB_EXIT) do
  $irb.eval_input
end
print "\n"

## might want to reset your app's interrupt handler here

end
end


(Gilles Filippini) #4

Joel VanderWerf wrote:

Gilles Filippini wrote:

Hi,

It must be a basic question, but I didn’t find any answer:
How to embed an IRB session into a ruby app ?

This is one way to do it for a command line app (i.e., without changing
stdin/out). It works well, but I do wish IRB made this a bit easier. The
obj argument to start_session will be self when the session starts up.

require 'irb’
require ‘irb/completion’

[snip]

Thanks a lot. It’s just what I was looking for.
In the meantime I’ve found this:

http://www.ruby-talk.org/blade/31020

require "irb/main"
IRB.start

How is it different from your solution?
Thanks for your help.

_gilles.


(Joel VanderWerf) #5

Gilles Filippini wrote:

    http://www.ruby-talk.org/blade/31020

require "irb/main"
IRB.start

How is it different from your solution?

Hm, there doesn’t seem to be an irb/main on my system, and my irb
install is only a few days old.

The reasons for the IRB.start_session as opposed to just IRB.start
(which is what start_session is based on) are:

  1. start_session doesn’t expect to be invoked from the command line,
    though if you do, and pass it a script file, it will execute it,

  2. it leaves the Irb object in $irb, and only calls IRB initialization
    when the first Irb object is created, so you can exit the IRB
    session
    (with ^D), and then the app can can start_session again later,

  3. you can specify the object to be used as self when the session
    starts
    i.e., IRB.start_session([1,2,3]) starts a session in which self is
    [1,2,3].


(Gilles Filippini) #6

Bob X wrote:

Into an FXRuby app would be even better. : )

That’s what I’d like to do actually.
I think IRB should run in a separate thread. But then I don’t know how
to redirect IRB’s standard input, output and error.
Any help appreciated.

_gilles.


(Gilles Filippini) #7

Joel VanderWerf wrote:

Gilles Filippini wrote:

    http://www.ruby-talk.org/blade/31020

require "irb/main"
IRB.start

How is it different from your solution?

Hm, there doesn’t seem to be an irb/main on my system, and my irb
install is only a few days old.

I’ve tried it:
- on Windows => OK.
- on GNU/Linux => no irb/main but
require "irb"
IRB.start
is OK.

The reasons for the IRB.start_session as opposed to just IRB.start
(which is what start_session is based on) are:

  1. start_session doesn’t expect to be invoked from the command line,
    though if you do, and pass it a script file, it will execute it,

  2. it leaves the Irb object in $irb, and only calls IRB initialization
    when the first Irb object is created, so you can exit the IRB
    session
    (with ^D), and then the app can can start_session again later,

  3. you can specify the object to be used as self when the session
    starts
    i.e., IRB.start_session([1,2,3]) starts a session in which self is
    [1,2,3].

Thanks for the details.

_gilles.


(Joel VanderWerf) #8

Gilles Filippini wrote:

Bob X wrote:

Into an FXRuby app would be even better. : )

That’s what I’d like to do actually.
I think IRB should run in a separate thread. But then I don’t know how
to redirect IRB’s standard input, output and error.
Any help appreciated.

_gilles.

If you can figure out how to associate an input and output stream with a
text editing window, then, IIUC, you can set readline to work on those
streams, using rl_instream and rl_outstream:

http://cnswww.cns.cwru.edu/~chet/readline/readline.html#SEC28

(Assuming you’ve set irb up to use readline.) I have no idea how to
accomplish the first part, though. Maybe look at the source for a
terminal emulator?


(Gilles Filippini) #9

Joel VanderWerf wrote:

Gilles Filippini wrote:

Bob X wrote:

Into an FXRuby app would be even better. : )

That’s what I’d like to do actually.
I think IRB should run in a separate thread. But then I don’t know how
to redirect IRB’s standard input, output and error.
Any help appreciated.

_gilles.

If you can figure out how to associate an input and output stream with a
text editing window, then, IIUC, you can set readline to work on those
streams, using rl_instream and rl_outstream:

http://cnswww.cns.cwru.edu/~chet/readline/readline.html#SEC28

Thanks for the pointer. Anyway, I don’t think readline streams
redirection is enough because IRB uses statements such as print and
printf for its output. So I have to redirect standard streams. I’ve
tried this:

@input = IO.pipe
@output = IO.pipe
@err = IO.pipe
@irb = Thread.new {
STDIN.reopen(@input[0])
STDOUT.reopen(@output[1])
STDERR.reopen(@input[1])
IRB.start
}

but it freezes my app :confused:

(Assuming you’ve set irb up to use readline.) I have no idea how to
accomplish the first part, though. Maybe look at the source for a
terminal emulator?

I’ll use FXScintilla. As soon as I’ll have figured out how to manage IRB
standard streams it shouldn’t be difficult to branch them on the text
editor. SciTE already does this with Scintilla.

_gilles.


(ts) #10

I'll use FXScintilla. As soon as I'll have figured out how to manage IRB
standard streams it shouldn't be difficult to branch them on the text
editor. SciTE already does this with Scintilla.

Well, you can try

pigeon% cat b.rb
#!/usr/bin/ruby
require 'irb'
class << STDOUT
   def write(s)
      $stderr.puts "HERE #{s}"
   end
end
IRB.start
pigeon%

pigeon% b.rb
irb(main):001:0> aaa
HERE NameError
HERE :
HERE undefined local variable or method `aaa' for #<Object:0x401c6ce0>
HERE
HERE from (irb):1
HERE
irb(main):002:0> HERE
pigeon%

Guy Decoux