views:

100

answers:

4

Ruby has constants and global variables for stdio.

Namely, the consts STDIN, STDOUT, STDERR, and their variable counterparts, $stdin, $stdout, $stderr.

I understand the difference between a constant and a variable. I know the constants are immutably set to the file descriptors at the moment the script was exec'd.

I also understand that you can change (some of) the variables at runtime.

I'm curious regarding practical uses of such feature. Why would you want to do it? What can you achieve?

Seeing some sample code, or even just use cases, extracted from real world projects would be awesome.


Update: From what I gather so far, it seems that when writing your own libraries/programs, you should prefer to use the variables over the constants, so that its users can further muck with it. Right?

A: 

You can send part of your output to a file and then dump it back to the console when you're done.

Azeem.Butt
Care to elaborate on it?
kch
What part of writing to a file do you not understand?
Azeem.Butt
See where I write "practical uses of such feature"?
kch
Logging information out to files is pretty darn practical, and not having to keep track of the original file descriptor yourself before performing the redirection is pretty darn convenient. If you don't see any value there then you must be writing something of exceedingly trivial value yourself.
Azeem.Butt
+1  A: 

Matsumoto's book on it seems to give the answer. Quote from 9.7.1.4 Predefined streams: "Global functions like print and puts write to $stdout by default. If a script alters the value of this global variable, it will change the behavior of those methods."

It sounds to me as if the idea is to simply allow an easy solution to a possibly poorly implemented program.

Mark Wilkins
s/possibly poorly implemented program/supposedly throw-away unix scripts/
kch
mind you that `print` and `puts` actually write to `$>`, which is initially set to `$stdout`, but can be changed. Or so I think.
kch
Ah, sed humor. Always hilarious.
Azeem.Butt
I agree about the throw-away part. Ruby is, after all, a great scripting language.
Mark Wilkins
+1  A: 
$stderr = File.open 'error.log', 'w'

All errors will be written to error.log

Jeffrey Aylesworth
Hum. Makes sense, if you have a config file that determines you should log to a file instead of stderr.
kch
+3  A: 

A more elaborate version of this function is in use in production code:

#!/usr/bin/env ruby -rstringio

def capture_stdout
  $stdout = StringIO.new("", "w")
  begin
    yield
    $stdout.string
  ensure
    $stdout = STDOUT
  end
end

output = capture_stdout do
  print "Line"
  puts " 1"
end

p output     # => "Line 1\n"

It is used in unit tests that want to know what was written to the console using print or puts.

The $ variables let you give Ruby different IO objects for stdout, stdin, stderr:

$stdout = buffer

the constants make it easy to get the $ variables back to their original (when your program started) value:

$stdout = STDOUT
Wayne Conrad
Why don't you initialize buffer to a StringIO instead?
kch
When I wrote this function a gazillion years ago, I was a greenhorn and didn't know about StringIO. Heck, it was Ruby 1.6 then. Today is the first occasion I've had to look at that code since then. But whether or not the code uses StringIO is a peripheral issue.
Wayne Conrad
Indeed it's a peripheral issue, and stringio being less code, it aids the readability of the relevant part of the example.I'm going to accept your answer. I'll edit it to use stringio, feel free to revert it if you don't like my changes.
kch
Thank you, and your edit greatly improves the example.
Wayne Conrad