On Sat, May 10, 2003 at 10:03:39AM +0900, Simon Strandgaard wrote:
> BTW:  pipe is a unix thing.. How do I make a ruby-sandbox on windows?
> I don't own a windows box myself. What issues should I be aware of ???

I don't use Windows either, but Windows does support processes with pipes
between them, based on what I've seen posted here. It works as well as you
can expect any O.S. feature in Windows to work :-)

Even DOS had pipes of a sort:

C:\> type autoexec.bat | more

(which it implemented by running command 1 outputting to a temporary file,
then running command 2 reading from the temporary file)

> ruby-in-a-sandbox: Im working on editor project where I "think" I need
> this feature (my knowledge about unix-TTYs is minimal).

And you need it because you want the vim-like feature: "run this program and
capture its output into the editor buffer"?

My argument is that the capturing belongs at the point where you run the
external program, just as if you were writing your code in C++ directly, not
in some framework where you have to jump through hoops with pipes and
threads (which are dangerous here)

How about adding a FAQ to your tutorial? Here's one I contribute.

  Q. How do I capture the entire output of an external program into a Ruby
     string? I tried
           string = `someprog args`   # or %x{someprog args}
     but anything generated on stderr by that program is still sent to
     my own program's stderr

  A. If you don't mind mixing the child's stdout and stderr, you might just
     get away with using shell redirection:
           string = `someprog args 2>&1`

     However this doesn't work under Windows, and if 'someprog' itself
     doesn't exist, the shell itself will still write to stderr.

     When you use backticks, Ruby is actually spawning a child (fork/exec)
     and collecting its stdout using a pipe. You can do this explicitly and
     collect both stdout and stderr through separate pipes:

       require 'open3'
       outstr = errstr = nil
       inp, out, err = Open3.popen3("ls -l /nonexistent /dev/null")
       t1 = Thread.new { outstr = out.read }
       t2 = Thread.new { errstr = err.read }
       # If you want to send to the child's stdin, write to 'inp' here
       inp.close
       t1.join 
       t2.join 

       puts "stdout said: #{outstr.inspect}"
       puts "stderr said: #{errstr.inspect}"

       # stdout said: "crw-rw-rw-  1 root  wheel    2,   2 May 10 08:26 /dev/null\n"
       # stderr said: "ls: /nonexistent: No such file or directory\n"

     This is essentially the same as you'd do if writing C++ to achieve
     the same goal: create some pipes, fork, attach one end of the pipes
     to fd 0/1/2 in the child, and exec the target program. In the parent
     you'd have to read from both output pipes using a select() loop or
     threads. Ruby's threads are actually implemented using select()
     behind the scenes.

Then you can show how simple it is to embed Ruby in C++ (presumably the same
as the 4 or 5 lines you need when embedding Ruby in C), and just deal with
these side-issues as they arise.

> Another case: many unix-applications is today being started from the
> desktop, and no console output is visible. You will need to redirect
> rubys output to the applications status-box/log-window.

But by the same argument: you will have to redirect all your C++ program's
output to the applications status-box/log-window, or else write your C++
program so that it never writes to stdout or stderr. So I don't see how
embedded Ruby is any different; it's just a part of your C++ program.
There's nothing special about a Ruby script which says it *must* write to
stdout or stderr.

If you were writing your editor entirely in C++, or entirely in Ruby for
that matter, you'd have the same issue. So this isn't really anything
specific to embedding Ruby.

On a different point, I hope that catching exceptions is an area you have
covered or will cover in the tutorial?

Regards,

Brian.