IRB as test interface to existing object

Hello,

I have some object classes to which I want to add an interactive
command-line shell for testing. At the moment, at the end of the source
file
I have something like

if FILE == $0
obj = MyClass.new(*ARGV)
CLI.run(obj, "MyClass> ")
end

Now, I had started writing a CLI module which reads lines from stdin,
splits
them into an array, runs obj.send(*args), and prints the response. Then
it
occurred to me that this is what IRB is for. But I don’t know how to
start
IRB in such a way that the default “self” receiver is an object which I
created.

Here’s an example of what I’d like to end up with:

class Foo
attr_reader :addr
def initialize(addr)
@addr = addr
end

def do_stuff(n)
n.times { puts “doing stuff with #{@addr}” }
end
end

if FILE == $0
obj = Foo.new(*ARGV)
prompt = "Foo(#{obj.addr})> "
IRB.run(obj, prompt) # <<<— what do I put here?
end

$ ruby foo.rb 127.0.0.1
Foo(127.0.0.1)> do_stuff 3
doing stuff with 127.0.0.1
doing stuff with 127.0.0.1
doing stuff with 127.0.0.1
nil
Foo(127.0.0.1)>

Reading the irb manpage, I can see that you can use the ‘irb’ command
(interactively) to set the default receiver in a subshell:

irb(main):012:0> obj = Foo.new(“127.0.0.1”)
=> #<Foo:0xb7c46730 @addr=“127.0.0.1”>
irb(main):013:0> irb obj
irb#1(#Foo:0xb7c46730):001:0> do_stuff 3
doing stuff with 127.0.0.1
doing stuff with 127.0.0.1
doing stuff with 127.0.0.1
=> 3

But I can’t work out how to invoke an initial IRB instance with my own
‘main’ object from the start. I’ve tried going through the source, but I
get
lost in Contexts and Workspaces well before I get to subirb.rb :slight_smile: I’d
also
prefer to implement it in a way which uses a ‘standard’ interface to
Irb,
such that it won’t break with a future version of Ruby.

Any clues gratefully received.

Thanks,

Brian.

Brian C. wrote:

def initialize(addr)
prompt = "Foo(#{obj.addr})> "

=> 3

Brian.

The following drops into (or back into) an irb session. (Customizing
the prompt is another matter, and I don’t remember how to do that, but
it might be findable on ruby-talk.)

(I use typically this in a long running process. I set my INT handler
and top-level exception handlers to start_session on some main object.
So ^C drops into irb, and ^D jumps back out.)

#!/usr/bin/env ruby

require ‘irb’
require ‘irb/completion’

module IRB
def IRB.parse_opts
# Don’t touch ARGV, which belongs to the app which called this
module.
end

def IRB.start_session(*args)
unless $irb
IRB.setup nil
## maybe set some opts here, as in parse_opts in irb/init.rb?
end

 workspace = WorkSpace.new(*args)

 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

 custom_configuration if defined?(IRB.custom_configuration)

 catch :IRB_EXIT do
   $irb.eval_input
 end

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

end
end

class Object
include IRB::ExtendCommandBundle # so that Marshal.dump works
end

if FILE == $0
x = Object.new
puts “\nStarted irb shell for x”
IRB.start_session(x)
puts “\nStarted irb shell for x with current binding”
IRB.start_session(binding, x)
puts “\nRestarted irb shell for x with current binding”
$irb.eval_input
puts “\nExited irb shell”
p x
end

On Sun, Mar 18, 2007 at 01:42:42AM +0900, Joel VanderWerf wrote:

The following drops into (or back into) an irb session.

This works perfectly, thank you (great to get command history!)

It also looks suspiciously like IRB.start in /usr/lib/ruby/1.8/irb.rb -
shame that doesn’t take a second optional parameter for the workspace.

(Customizing
the prompt is another matter, and I don’t remember how to do that, but
it might be findable on ruby-talk.)

Just defining a to_s method on the object works well enough for me. I
have
also found more details about IRB.conf in “man irb” (actually “man
irb1.8”
on this Ubuntu box)

Thanks again,

Brian.