IRB module with non-blocking asynchronous gets

Is there a way to use the IRB module so that it evaluates code on-demand
only instead of running in a constant loop?

I don’t need to use readline and have IRB wait for input – I can
trigger its need to evaluate from elsewhere in the program. I was
hoping, though, that I could have the gets function from my
IRB::StdioInputMethod subclass return a properly formatted string, let
IRB do its thing, and then give me back control until it was needed
again.

It seems, though, that the eval_input function just keeps executing gets
and never returns control. I understand that’s the intended behavior of
eval_input, but I’m a little surprised there’s not a more asynchronous
way of requesting evaluation.

Am I just approaching the problem from the wrong angle? I keep feeling
like Ruby has to provide a way to do what I’m after, but I haven’t been
able to track it down thus far.

Any help would be appreciated.

On Aug 25, 2007, at 3:09 AM, Shane Liesegang wrote:

Is there a way to use the IRB module so that it evaluates code on-
demand
only instead of running in a constant loop?

I’m working on my glue code speech for Lone Star Rubyconf today so my
first thought was:

#!/usr/bin/env ruby -wKU

class IRbOnDemand
def initialize
@irb = IO.popen(“irb -f --simple-prompt --noreadline”, “r+”)
end

def run(ruby)
@irb.puts ruby
@irb.flush

 @irb.each do |line|
   return eval($1) if line =~ /\A=>\s*(.+)/
 end

end
end

irb = IRbOnDemand.new
result = irb.run %Q{a = %w[words] * 3}
puts “Result: #{result.inspect}”

puts “Doing other things…”
sleep 3

result = irb.run %Q{a}
puts “Result: #{result.inspect}”

END

Is that what you are after? If it is, do we really need IRb at all?
How’s this:

#!/usr/bin/env ruby -wKU

class IRbOnDemand
def initialize
@binding = binding
end

def run(ruby)
eval ruby, @binding
end
end

irb = IRbOnDemand.new
result = irb.run %Q{a = %w[words] * 3}
puts “Result: #{result.inspect}”

puts “Doing other things…”
sleep 3

result = irb.run %Q{a}
puts “Result: #{result.inspect}”

END

James Edward G. II

Hi James. Thanks for the recommendations.

James G. wrote:

Is that what you are after? If it is, do we really need IRb at all?
How’s this:

I could just use eval to run the strings; that’s true. I was hoping to
leverage IRB’s prompt mechanism and the ability to define functions on
the fly. As below:


irb(main):001:0> def test
irb(main):002:1> puts “testing…”
irb(main):003:1> end
=> nil
irb(main):004:0> test
testing…
=> nil
irb(main):005:0>

I’m finding that it’s pretty tricky, though. I managed to make it
non-blocking for a single line by overriding eval_input and having it
break after evaluating a line instead of waiting for input. That seems
to work fine, but when doing any kind of multi-line input, it still goes
into a loop that it doesn’t want to return from.

It seems like IRB, to its core, is based on constantly waiting for
input, and trying to make it work in an on-demand fashion is like
running uphill. I’m still rooting through its source, trying to find the
spots where I can poke at it to make it work that way, but it’s slow
going with a large mass of interrelated code that’s not terribly easy
for humans to parse. :slight_smile:

go with #eval, it makes it dead simple:

arbitrary_code = <<-MUPPETS
def muppets
“muppets”
end
MUPPETS
=> “def muppets\n “muppets”\nend\n”

muppets
NameError: undefined local variable or method `muppets’ for main:Object
from (irb):6

eval(arbitrary_code)
=> nil

muppets
=> “muppets”

is that what you need, basically?


Giles B.

Blog: http://gilesbowkett.blogspot.com
Portfolio: http://www.gilesgoatboy.org
Tumblelog: http://giles.tumblr.com/

Giles B. wrote:

is that what you need, basically?

Close, but it doesn’t let me enter arbitrary lines of input and know
whether they need to be joined to execution or can be executed
individually, the way IRB does.

I’ve got the majority of my desired behavior now, by applying some
patches to RubyLex: http://pastie.caboo.se/91797

When the user presses enter, I call get_statement on the lexer, which
either returns the line and line number if it’s ready for execution or
nil if it’s not.

The only thing this doesn’t currently handle is string continuations –
once again, IRB happily enters an infinite loop when it detects an
incomplete string in a line. I would like to hit this border case as
well, but it’s looking like it’s going to require some pretty deep digs
into the lexing code. It doesn’t terribly excite me, and I don’t
personally use string continuation very much, so I’m tempted to just
call it a halfway-victory.

Unless there are some particularly knowledgeable IRB hackers waiting in
the wings to offer advice…

For anyone still reading this thread (or who stumbles across it in the
future), I have been able to solve the string continuation problem – it
may be a bit inefficient, but I run my own processing on the line before
trying to pass it to the lexer. If I determine that it’s the start of a
string continuation, I set the flags appropriately.

No code in pastie this time, but the algorithm for finding unfinished
strings is pretty straightforward, provided you cover all the cases
provided in Ruby (single quotes, double quotes, %q, %w, %x, %Q, %W, %X,
and heredocs).

I leave it as an exercise to the reader. :slight_smile:

Unless there are some particularly knowledgeable IRB hackers waiting in
the wings to offer advice…

I wish! I know a bunch of IRB tricks and tidbits but this is getting
pretty deep into IRB internals.


Giles B.

Blog: http://gilesbowkett.blogspot.com
Portfolio: http://www.gilesgoatboy.org
Tumblelog: http://giles.tumblr.com/

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs