Joel VanderWerf
3/17/2007 4:43:00 PM
Brian Candler wrote:
> 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 :-) 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.
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
--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407