From: Jean-Julien Fleck on
Hello,

I'm stuck with a problem concerning IO.popen
It's not necessarily related to irb but that's a good point where to start.

I'd like to drive irb from ruby using IO.popen. The problem is: each
time I type something inside irb, I can get one, two or more lines of
output I'd like to store with no way to know beforehand how many there
will be.

The thing is: I don't know how to detect when irb has ended its output
and is waiting for me to give some input.

I tried the following:

def irb(string)
output = ''
puts "Trying to talk to irb"
IO.popen('irb --simple-prompt','r+') do |io|
string.chomp.split.each do |s|
io.puts s
while line = io.gets
line = io.gets
puts line
output += line
end
end
end
return output
end

s = "2+2\n'2'+'2'\n'2'+2"

irb(s)

I was hoping that io.gets would be nil when irb is waiting for input,
but it's not. So this little program gets stuck after the first
statement:

brisingr ~/tmp>ruby essai_irb.rb
Trying to talk to irb
=> 4

Rather, I would like him to return

>> 2+2
=> 4
>> '2'+'2'
=> "22"
>> '2'+2
TypeError: can't convert Fixnum into String
from (irb):4:in `+'
from (irb):4

Does someone have a workaround ?

Thanks,

--
JJ Fleck
PCSI1 Lycée Kléber

From: Caleb Clausen on
On 7/17/10, Jean-Julien Fleck <jeanjulien.fleck(a)gmail.com> wrote:
> Hello,
>
> I'm stuck with a problem concerning IO.popen
> It's not necessarily related to irb but that's a good point where to start.
>
> I'd like to drive irb from ruby using IO.popen. The problem is: each
> time I type something inside irb, I can get one, two or more lines of
> output I'd like to store with no way to know beforehand how many there
> will be.
>
> The thing is: I don't know how to detect when irb has ended its output
> and is waiting for me to give some input.
>
> I tried the following:
>
> def irb(string)
> output = ''
> puts "Trying to talk to irb"
> IO.popen('irb --simple-prompt','r+') do |io|
> string.chomp.split.each do |s|
> io.puts s
> while line = io.gets
> line = io.gets
> puts line
> output += line
> end
> end
> end
> return output
> end
>
> s = "2+2\n'2'+'2'\n'2'+2"
>
> irb(s)
>
> I was hoping that io.gets would be nil when irb is waiting for input,
> but it's not. So this little program gets stuck after the first
> statement:
>
> brisingr ~/tmp>ruby essai_irb.rb
> Trying to talk to irb
> => 4
>
> Rather, I would like him to return
>
>>> 2+2
> => 4
>>> '2'+'2'
> => "22"
>>> '2'+2
> TypeError: can't convert Fixnum into String
> from (irb):4:in `+'
> from (irb):4
>
> Does someone have a workaround ?

You could try to detect whether irb is waiting based an what prompt it
prints. Alternately, you could use non-blocking io with a timeout to
determine when it is no longer printing anything to output. Neither
method is going to be a sure-fire way to determine when irb is
waiting, but you should be able to come up with something that works
well enough most of the time. You could even combine the 2 for greater
reliability.

From: Robert Klemme on
On 07/17/2010 02:05 PM, Jean-Julien Fleck wrote:
> I'm stuck with a problem concerning IO.popen
> It's not necessarily related to irb but that's a good point where to start.
>
> I'd like to drive irb from ruby using IO.popen. The problem is: each
> time I type something inside irb, I can get one, two or more lines of
> output I'd like to store with no way to know beforehand how many there
> will be.
>
> The thing is: I don't know how to detect when irb has ended its output
> and is waiting for me to give some input.
>
> I tried the following:
>
> def irb(string)
> output = ''
> puts "Trying to talk to irb"
> IO.popen('irb --simple-prompt','r+') do |io|
> string.chomp.split.each do |s|
> io.puts s
> while line = io.gets
> line = io.gets
> puts line
> output += line

Note: << is more efficient than += here.

> end
> end
> end
> return output
> end
>
> s = "2+2\n'2'+'2'\n'2'+2"
>
> irb(s)
>
> I was hoping that io.gets would be nil when irb is waiting for input,
> but it's not. So this little program gets stuck after the first
> statement:
>
> brisingr ~/tmp>ruby essai_irb.rb
> Trying to talk to irb
> => 4
>
> Rather, I would like him to return
>
>>> 2+2
> => 4
>>> '2'+'2'
> => "22"
>>> '2'+2
> TypeError: can't convert Fixnum into String
> from (irb):4:in `+'
> from (irb):4
>
> Does someone have a workaround ?

Not a workaround but a solution: since you only want to collect the
output of irb and do not need to react on it you can simply use a second
thread that collects all the output.

def irb_test(strings)
puts "Trying to talk to irb"

IO.popen('irb --simple-prompt','r+') do |io|
rdr = Thread.new { io.read }

io.puts strings

io.close_write
rdr.value
end
end

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
From: Jean-Julien Fleck on
Hello Robert,

> Note: << is more efficient than += here.

Yes. Bad habit on my side: I tend to find it more readable that way so
I don't use << so often. I should rather use it more to find it easier
to read :o)

Correct me if I'm wrong: << will append to the existing String object
whereas += will create a brand new String object by concatenating the
two (the old version and what I want to add), so it could be less
efficient if the string gets too long and/or if there are a lot of
line to append ?

> Not a workaround but a solution: since you only want to collect the output
> of irb and do not need to react on it you can simply use a second thread
> that collects all the output.

Thanks a lot, it works perfectly !

I don't need it now but as I have already been confronted to this
issue (finding workarounds as I had access to the other program and
could modify it), I'll ask anyway: how would you tackle the problem if
you did have to react to the output ? (just some hints would perfectly
please me)

Thanks again,

--
JJ Fleck
PCSI1 Lycée Kléber

From: Robert Klemme on
On 07/17/2010 06:43 PM, Jean-Julien Fleck wrote:
> Hello Robert,
>
>> Note:<< is more efficient than += here.
>
> Yes. Bad habit on my side: I tend to find it more readable that way so
> I don't use<< so often. I should rather use it more to find it easier
> to read :o)
>
> Correct me if I'm wrong:<< will append to the existing String object
> whereas += will create a brand new String object by concatenating the
> two (the old version and what I want to add),

Correct.

> so it could be less
> efficient if the string gets too long and/or if there are a lot of
> line to append ?

I don't think so. You have two cost factors: allocating the underlying
memory to hold the sequence of characters (or bytes if you will) and the
allocation of an object, registration with GC etc. The first cost
factor needs to be paid regardless of variant used while the second
costs factor is not incurred with the << version. Memory allocation
overhead is usually kept low by using a smart algorithm for calculating
the size of the next memory chunk to allocate so allocations become
rarer with more append operations of identical sized strings. IMHO the
version with the single read that slurps in everything is even more
efficient because you loop in C and not in Ruby. (Note that Ruby's
multithreading is not negatively affected; the single read does not
block other threads.)

>> Not a workaround but a solution: since you only want to collect the output
>> of irb and do not need to react on it you can simply use a second thread
>> that collects all the output.
>
> Thanks a lot, it works perfectly !
>
> I don't need it now but as I have already been confronted to this
> issue (finding workarounds as I had access to the other program and
> could modify it), I'll ask anyway: how would you tackle the problem if
> you did have to react to the output ? (just some hints would perfectly
> please me)

There is expect ("ri IO#expect"). Documentation is a tad sparse but
there's likely more to find on the web.

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/