Using Ruby to control another process? - ruby

I would like to control a separate process with a classic 1), 2), 3), etc. menu system. Similar to piping an input file in to control the process, I would like to use Ruby to control the process' $stdin and $stdout. I've experimented with IO.popen and Open3.popen3, but cannot seem to get it to work. The documentation examples are not clear (but I'm also quite new to this sort of programming).
The basic idea is:
Open3.popen3("./server") do |stdin,stdout,stderr|
stdout.gets
stdin.puts "1"
stdout.gets
stdin.puts "2"
stdout.gets
end
Currently, the first stdout.gets gets the correct header, but then the program seems to hang. Can anybody offer any advice? I've been googling for a while now but haven't found anything.
Thanks!

IPC (Inter Process Communication) is very complicated. When working with IPC you need to flush the buffer after every write operation. It would look like this:
Open3.popen3("./server") do |stdin,stdout,stderr|
stdout.gets
stdin.puts "1"
stdin.flush
stdout.gets
stdin.puts "2"
stdin.flush
stdout.gets
end
It's ugly, but you can redefine puts as a singleton method on stdin (you can do the same with print and write, if needed):
class <<stdin
old_puts=method(puts)
def puts(str)
old_puts[str]
flush
end
end
This will save allot of trouble if you are doing a lot of IPC.

Related

Understanding how ruby method $stdout works

Here is a simple ruby script, that takes input from a user and provides an output(yes, it will be refactored). I wanted this script to provide output into a text file, rather than console window. That was accomplished, by simply adding $stdout = File.new('out.txt', 'w'), but I thought that this line will just describe a variable which I will use later to tell script to use it to write output into created file.
I cant find much documentation about this method and wondering how does this program knows how to write generated output into that file?
$stdout is a global variable. By default it stores an object of type IO associated with the standard output of the program (which is, by default, the console).
puts is a method of the Kernel module that actually calls $stdout.send() and pass it the list of arguments it receives. As the documentation explains, puts(obj, ...) is equivalent to $stdout.puts(obj, ...).
Your code replaces $stdout with an object of type File that extends class IO. When it is created, your object opens the file out.txt for writing and together with its inheritance from IO it is fully compatible with the default behaviour of $stdout.
Since by default, all the output goes to $stdout, your new definition of $stdout ensures the output is written to the file out.txt without other changes in the code.
$stdout is a global variable (as indicated by $) and according to the documentation, puts is:
Equivalent to
$stdout.puts(obj, ...)
If you assign another object to $stdout, then Kernel#puts will simply send puts to that object. Likewise, print will send write:
class Foo < BasicObject
def puts(*args)
::STDOUT.puts "Foo#puts called with #{args.inspect}"
end
def write(*args)
::STDOUT.puts "Foo#write called with #{args.inspect}"
end
end
$stdout = Foo.new
puts 'hello', 'world'
# Foo#puts called with ["hello", "world"]
print "\n"
# Foo#write called with ["\n"]
Note that if you assign to $stdout, Ruby checks whether the object responds to write. If not, a TypeError will be raised.
It's probably worth highlighting the fact, mentioned by the other posts, that this is a global variable, and modifying it is not thread safe. For instance, if you point $stdout to a new IO stream, anything across the app (on that process thread) that would be logged to stdout will now be logged to your new IO stream. This can lead to multiple streams of unexpected and possibly sensitive input landing in your new IO stream.

What is the point of Kernel#select in Ruby?

I'm working on a ruby script that ultimately starts up a system process that takes quite a while. I need to read from the stderr of this process and react to it depending on what is output.
I'm currently doing it as such:
Open3.popen3(cmd_to_run) do |stdin, stdout, stderr, waitthread|
stderr.each_line do |line|
# look out for specific lines and react to them accordingly
end
end
But I've also seen implementations to achieve something similar but doing it with kernel#select:
Open3.popen3(cmd_to_run) do |stdin, stdout, stderr, waitthread|
io = select([stderr], nil, nil, 30)
if io.nil?
log("Command timed out during Kernel#select")
return
end
io[0][0].each_line do |line|
# look out for specific lines and react to them accordingly
end
end
I've read the pickaxe description of what select does, but I'm confused as to why I should (of if I should) use it? The first method works just the same.
Probably two reasons:
You can use timeout, which you can't with each_line
You can wait for more than one IO object, e. g. io = select([stdout, stderr]) and more than one event (e.g. write event or exception too)

File.open and blocks in Ruby 1.8.7

I'm pretty new to ruby and I'm currently reading the Pickaxe book to get familiar with everything. I came across the File.open section where it discusses taking a block as a parameter to a File.open call then guaranteeing that the file is closed. Now this sounds like an absolutely brilliant way to avoid shooting yourself in the foot and as I'm dangerously low on toes, I figure I'll give it a go. Here is what I wrote (in irb if that matters):
File.open('somefile.txt', 'r').each { |line| puts line }``
My expectation was that the file somefile.txt would get opened, read, printed and closed, right? As far as I can tell wrong. If I use lsof to look at open file handles, it's still open. However, if I do
f = File.open('somefile.txt', 'r').each { |line| puts line }
f.close()
Am I using blocks wrong in this example or have I failed to understand the meaning of File.open when used with a block. I've read section on ruby-doc.org related to File.open but that just seems to confirm that what I'm doing ought to be working as expected.
Can anyone explain what I'm doing wrong?
In order to close file after block, you should pass block to File.open() directly, not to each:
File.open('somefile.txt', 'r') do |f|
f.each_line { |l| puts l }
end
File.open(…).each {…} is just iterating over opened file without closing it.

Equivalent of Scheme's dynamic-wind in Ruby

Ruby has continuations... does it have a dynamic-wind construct like Scheme?
[This answer is written with Scheme programmers in mind (the OP has asked other Scheme questions here before, so that's a safe bet). If you're here because you're a Ruby programmer with no Scheme background, read the footnote for some context. :-)]
MRI doesn't (see below); and if MRI doesn't, that means there is no portable way to use any such functionality even if another implementation provides it.
I actually inspected the MRI 1.9.1 source code, just to be sure. In any case, here is some code to demonstrate that even the normal unwind protection (ensure) doesn't work correctly with continuations on MRI (tested with both 1.8.7 and 1.9.1). (It does work correctly with JRuby (I tested with 1.5), so it goes to show it's an implementation-specific thing. But note that JRuby only provides escape continuations, not general-purpose ones.)
callcc do |cc|
begin
puts 'Body'
cc.call
ensure
puts 'Ensure'
end
end
(To test with MRI 1.9+, you need to either run with the -rcontinuation option, or put require 'continuation' at the top of the file.)
For readers who don't know what a dynamic-wind is, it's a way to specify code to be run when the code being covered is exited (much like ensure), as well as code to to be run when the covered code is re-entered. (This can happen when you use call/cc inside the covered code, and invoke the continuation object after the covered code has been exited.)
Totally contrived example:
def dynamic_wind pre, post, &block
raise 'Replace this with a real implementation, kthx'
end
def redirect_stdout port, &block
saved = $stdout
set_port = lambda {$stdout = port}
reset_port = lambda {$stdout = saved}
dynamic_wind set_port, reset_port, &block
end
cc = nil
# cheap way to nuke all the output ;-)
File.open '/dev/null' do |null|
redirect_stdout null do
callcc {|cc|}
puts 'This should not be shown'
end
puts 'This should be shown'
cc.call
end
So, a properly-functioning dynamic_wind implementation would ensure that $stdout would be set back to the /dev/null stream when the continuation is invoked, so that at all instances where puts 'This should not be shown' is run, that text is indeed not shown.

Why does Kernel#p print to standard out?

Why does Kernel#p print to standard out? Isn't printf debugging supposed to output to standard error?
You can define a global function "q", which works just like "p" except it prints to $stderr.
#!/usr/bin/ruby1.8
module Kernel
def q(*stuff)
stuff.each { |thing| $stderr.print(thing.inspect + "\n")}
end
end
q 'foo' # => "foo"
You may be tempted to use puts instead of print ... + "\n". This code uses print to make it thread-safe: puts can be interrupted between the time it prints its arguments and the time it prints the new-line, causing output from two threads to appear on one line. It's seldom that you have code from multiple threads writing to $stdout/$stderr at the same time, so it's not usually an issue. But this being a debugging tool, you will certainly end up using it to find out what is going on in threads.
Why are you assuming Kernel#p is intended for debugging? It writes to stdout just like Kernel#print or printf in C.
If you want to write to standard error you could do:
$stderr.puts(x.inspect)
By the way, if you really want to use printf debugging I suggest you read this article about debugging techniques

Resources