Reopening an IO Stream vs. just using the new Stream - ruby

In the Ruby-Docs it gives the example of:
f1 = File.new("testfile")
f2 = File.new("testfile")
f2.readlines[0] #=> "This is line one\n"
f2.reopen(f1) #=> #<File:testfile>
f2.readlines[0] #=> "This is line one\n"
My question is why reopen f2 when you could just f2.close and f1.readlines[0]? Are there any advantages to reopening with a new stream vs. just using the new stream?

I talked to some devs on IRB a while back and the response I got was that it was mostly used for changing the $std variables to modify where methods such as puts and print output to...
$stdout.reopen(File.open('log'))
puts 'hello world'
The reason for using this rather than...
$stdout = File.open('log')
...was kinda up in the air though. I had one dev that said that direct assignment didn't play well with some of ruby's C functions. I don't know much about C and can't say much about this, but he pointed me to some minitest source to see an example of it in use. However, apparently even the source has switched over to direct assignment vs. reopening since the dev last looked at it.
In conclusion... from the looks of it IO#reopen might be useless, but I would love to hear an argument against this.
Update
Ok, so I reread over the documentation and saw that there was a second set of opts for reopen:
reopen(path, mode_str) → ios
This actually seems somewhat useful as opposed to the reopen(other_IO) → ios option.

I suspect that the main difference is that with reopen, the new stream would apply not only to subsequent uses of the $std... variable, but also to variables that previously were assigned the value of the $std... variable. This can be good or bad, depending on your situation.
This irb session shows that with reopen, a variable that was assigned previous to the stream change will acquire the newly changed stream. Note that the fileno does not change for either variable, and both variables produce no output:
> $stderr.fileno
=> 2
> stderr_copy = $stderr
=> #<IO:<STDERR>>
> stderr_copy.fileno
=> 2
> $stderr.reopen(File.open('/dev/null', 'w'))
=> #<File:/dev/null>
> stderr_copy.fileno
=> 2
> $stderr.fileno
=> 2
> $stderr.puts 'foo'
=> nil
> stderr_copy.puts 'foo'
=> nil
In contrast, when instead of using reopen, the newly opened /dev/null File object is assigned to $stderr, stderr_copy will retain its original output stream. Only $stderr gets the new fileno, and stderr_copy still produces output:
> $stderr.fileno
=> 2
> stderr_copy = $stderr
=> #<IO:<STDERR>>
> stderr_copy.fileno
=> 2
> $stderr = File.open('/dev/null', 'w')
=> #<File:/dev/null>
> $stderr.fileno
=> 10
> stderr_copy.fileno
=> 2
> $stderr.puts 'foo'
=> nil
> stderr_copy.puts 'foo'
foo
=> nil
If you want to use reopen, but want to save a copy of the original output stream, you can use dup:
> stderr_dup = $stderr.dup
=> #<IO:<STDERR>>
> stderr_dup.fileno
=> 10
> $stderr.reopen(File.open('/dev/null', 'w'))
=> #<File:/dev/null>
> $stderr.fileno
=> 2
> stderr_dup.puts 'foo'
foo
=> nil
> $stderr.puts 'foo'
=> nil

Related

How to allow users to edit given string via $stdin in ruby

I'm searching to allows users to edit an existing string.
Edit the following string: Edit me
# After user delete and add characters
Edit the following string: Edit you
I thought to prepend some data to the $stdin but seems like it's not possible and anyway IMHO it's a too radical solution.
Someone told me to use GNU Readline's Ruby wrapper so I've taken a quick look and I found Readline#pre_input_hook which acts before Readline start taking the input.
I tried:
require 'readline'
Readline.pre_input_hook = -> { "Edit me" }
result = Readline.readline("Edit the following string: ")
puts result
But seems not work.
begin
system("stty raw -echo")
print (acc = "Edit me: ")
loop.each_with_object(acc) do |_,acc|
sym = $stdin.getc
case sym.ord
when 13 # carriage return
break acc
when 127 # backspace
print "\e[1D \e[1D"
acc.slice!(acc.length - 1) if acc.length > 0
else # regular symbol
print sym
acc << sym
end
end
ensure
system("stty -raw echo")
puts
puts "\e[0mEntered: |#{acc}|"
end
Here you go. More info on terminal control sequences. Also, ANSI terminal codes.
I found prompt.ask from tty-prompt fulfilled my need:
$ gem install tty-prompt
$ irb
irb(main):001:0> require "tty-prompt"
=> true
irb(main):002:0> prompt = TTY::Prompt.new
=> #<TTY::Prompt prefix="" quiet=false enabled_color=nil active_color=:green
error_color=:red help_color=:bright_black input=#<IO:<ST...
irb(main):003:0> prompt.ask("What is your name?", default: ENV["USER"])
What is your name? xxx
=> "xxx"
irb(main):004:0> prompt.ask("What is your name?", value: "Mike")
What is your name? Michael
=> "Michael"

Ruby compatibility error encoding

I'm having a problem. Let's look:
C:\temp> ruby script.rb
script.rb => Powershell output
puts "ę" => ę #irb \xA9
puts "\xA9" => ▯
puts "ę"=="\xA9" => false
input = $stdin.gets.chomp => input=="ę"
puts "e#{input}e" => eęe
puts "ę"==input => false
puts "ę#{input}" => Encoding::Compatibility Error Utf8 & CP852
irb => #command line in ruby
puts "ę"=="\xA9" => true
input = $stdin.gets.chomp => input=="ę"
puts "ę"==input => true && "\xA9"==input => true
puts "ę#{input}" => ęę
It looks like powershell's input uses other font for all special characters than ruby and notepad++(?). Can i change that so it will work when i type in prompt(when asked) and does not show an error?
Edit: Sorry for misdirection. I added invoke and specified that file has extension ".rb" not ".txt"
Edit2: Ok, I've researched some more information and I've been trying do some encoding(UTF8) to a variable. Somethin' strange occured.
puts "ę#{input.encoding}" => ęCP852
puts "\xA9" => UTF-8
Encoding to CP852 has revealed that encoding pass on bytes. I learned that value of "ę"=20+99=119, "ą" = 20 + 85, 20 = C4
Ok. got it ".encoding" - shows what encoding i use. And that resolve this problem.
puts "ę#{input.encode "UTF-8"}" => ęę
Thanks everyone for your input.
If your input in prompt( either cmd or powershell) is causing problems due to incompatibility of using differents encodings just try to encode it via methods in script.encode "UTF-8" #in case of Ruby language If you dont know what methods do that just google your_language_name encoding

What happens to the object I assign to $stdout in Ruby? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
EDIT: Don't bother reading this question, I just can't delete it. It's based on broken code and there's (almost) nothing to learn here.
I am redirecting console output in my Ruby program and although it works perfectly there is one thing I'm curious about:
Here's my code
capture = StringIO.new
$stdout = capture
puts "Hello World"
It looks like even though I'm assigning my capture object to $stdout, $stdout contains a new and different object after the assignment, but at least the type is correct.
In other words:
$stdout.to_s # => #<IO:0x2584b30>
capture = StringIO.new
$stdout = capture
$stdout.to_s # => #<StringIO:0x4fda948>
capture.to_s # => #<StringIO:0x4e3b220>
Subsequently $stdout.string contains "Hello World", but capture.string is empty.
Is there something happening behind the scenes or am I missing something here?
EDIT: This might be specific to certain versions only. I'm using Ruby 2.0.0-p247 on Windows 8.1
It works as expected.
>> capture = StringIO.new
=> #<StringIO:0x00000001ea8c00>
>> $stdout = capture
>> $stdout.to_s
>> capture.to_s
Above two line does not print anything because $stdout is now disconnected from terminal.
So I used $stderr.puts in following lines (can also use STDOUT.puts as Stefan commented):
>> $stderr.puts $stdout.to_s
#<StringIO:0x00000001ea8c00>
>> $stderr.puts capture.to_s
#<StringIO:0x00000001ea8c00>
$stdout.to_s, capture.to_s give me same result.
I used ruby 1.9.3. (Same for 2.0.0)
Are you sure there is no other manipulation of $stdout or capturehappening in between?
For me, output looks different. Both capture and $stdout are the same object and subsequently answer to string with the same response (ruby 1.9.2):
require 'stringio'
$stdout.to_s # => #<IO:0x2584b30>
capture = StringIO.new
$stdout = capture
puts $stdout.to_s # => #<StringIO:0x89a38c0>
puts capture.to_s # => #<StringIO:0x89a38c0>
puts "redirected"
$stderr.puts $stdout.string # => '#<StringIO:0x89a38c0>\n#<StringIO:0x89a38c0>\nredirected'
$stderr.puts capture.string # => '#<StringIO:0x89a38c0>\n#<StringIO:0x89a38c0>\nredirected'
Although this question was the result of overlooking a change to the value of $stdout, Ruby does have the ability to override assignment to global vars in this way, at least in the C api, using hooked variables.
$stdout actually does make use of this to check whether the new value is appropriate (it checks whether the new value responds to write) and raises an exception if it doesn’t.
If you really wanted (you don’t) you could create an extension that defines a global variable that automatically stores a different object than the value assigned, perhaps by called dup on it and using that instead:
#include "ruby.h"
VALUE foo;
static void foo_setter(VALUE val, ID id, VALUE *var){
VALUE dup_val = rb_funcall(val, rb_intern("dup"), 0);
*var = dup_val;
}
void Init_hooked() {
rb_define_hooked_variable("$foo", &foo, 0, foo_setter);
}
You could then use it like:
2.0.0-p247 :001 > require './ext/hooked'
=> true
2.0.0-p247 :002 > s = Object.new
=> #<Object:0x00000100b20560>
2.0.0-p247 :003 > $foo = s
=> #<Object:0x00000100b20560>
2.0.0-p247 :004 > s.to_s
=> "#<Object:0x00000100b20560>"
2.0.0-p247 :005 > $foo.to_s
=> "#<Object:0x00000100b3bea0>"
2.0.0-p247 :006 > s == $foo
=> false
Of course this is very similar to simply creating a setter method in a class that dups the vale and stores that, which you can do in plain Ruby:
def foo=(new_foo)
#foo = new_foo.dup
end
Since using global variables is generally bad design, it seems reasonable that this isn’t possible in Ruby for globals.

Retrieve a file in Ruby

So what I am trying to do is pass a file name into a method and and check if the file is closed. What I am struggling to do is getting a file object from the file name without actually opening the file.
def file_is_closed(file_name)
file = # The method I am looking for
file.closed?
end
I have to fill in the commented part. I tried using the load_file method from the YAML module but I think that gives the content of the file instead of the actual file.
I couldn't find a method in the File module to call. Is there a method maybe that I don't know?
File#closed? returns whether that particular File object is closed, so there is no method that is going to make your current attempted solution work:
f1 = File.new("test.file")
f2 = File.new("test.file")
f1.close
f1.closed? # => true # Even though f2 still has the same file open
It would be best to retain the File object that you're using in order to ask it if it is closed, if possible.
If you really want to know if your current Ruby process has any File objects open for a particular path, something like this feels hack-ish but should mostly work:
def file_is_closed?(file_name)
ObjectSpace.each_object(File) do |f|
if File.absolute_path(f) == File.absolute_path(file_name) && !f.closed?
return false
end
end
true
end
I don't stand by that handling corner cases well, but it seems to work for me in general:
f1 = File.new("test.file")
f2 = File.new("test.file")
file_is_closed?("test.file") # => false
f1.close
file_is_closed?("test.file") # => false
f2.close
file_is_closed?("test.file") # => true
If you want to know if any process has the file open, I think you'll need to resort to something external like lsof.
For those cases where you no longer have access to the original file objects in Ruby (after fork + exec, for instance), a list of open file descriptors is available in /proc/pid/fd. Each file there is named for the file descriptor number, and is a symlink to the opened file, pipe, or socket:
# Returns hash in form fd => filename
def open_file_descriptors
Hash[
Dir.glob( File.join( '/proc', Process.pid.to_s, 'fd', '*' ) ).
map { |fn| [File.basename(fn).to_i, File.readlink(fn)] rescue [nil, nil] }.
delete_if { |fd, fn| fd.nil? or fd < 3 }
]
end
# Return IO object for the named file, or nil if it's not open
def io_for_path(path)
fd, fn = open_file_descriptors.find {|k,v| path === v}
fd.nil? ? nil : IO.for_fd(fd)
end
# close an open file
file = io_for_path('/my/open/file')
file.close unless file.nil?
The open_file_descriptors method parses the fd directory and returns a hash like {3 => '/my/open/file'}. It is then a simple matter to get the file descriptor number for the desired file, and have Ruby produce an IO object for it with for_fd.
This assumes you are on Linux, of course.

How to create a IO object from a file?

Another seems stupid question, but haven't found a clear example.
I need a IO object as parameter for a function, actually its the new function in this class:
I used this way , but seems some problems when using a file descriptor as a IO after wrote something:
irb(main):001:0> f= File.open("result.txt","w")
=> #<File:result.txt>
irb(main):002:0> i=IO.new(f.to_i,"w")
=> #<IO:0x3b5cb90>
irb(main):003:0> i.write "hello the world"
=> 15
irb(main):004:0> i.close
=> nil
irb(main):005:0> f.close
Errno::EBADF: Bad file descriptor - result.txt
from (irb):5:in `close'
from (irb):5
from :0
So I only need to close either i or f once? or there is a standard way to do this?
FILE is a subclass of IO
irb(main):001:0> File.superclass
=> IO
In your case, i and f refer to the same object. Hence the observation. You can use the File object for TestRunner.
Couldn't you do something like this:
File.open("result.txt", 'w') do |f|
t = TestRunner.new(your_suite, NORMAL, f)
t.start
end
This will start the test running using the result.txt file io object. It will automatically close the file even if an exception occurs.

Resources