Unexpected errors output order - ruby

Having this code:
# $stderr.sync = false
warn 'warn'
$stderr.puts 'err'
fail
When I run it like this ruby test.rb 2>err.log, output is ok:
warn
err
test.rb:3:in `<main>': unhandled exception
But when I uncomment the first line — the output is different:
warn
test.rb:3:in `<main>': unhandled exception
err
Why buffered output to the $stderr redirected to file has the wrong order?
If we do not redirect to the file — the order is ok (exception is the last line of the output).
warn was added thanks to the comment below.
This is not the same question as this one, here I do not mix stdout and stderr.
Checked platforms:
Windows10: Ruby v3.0.2.107 (2021-07-07) [x64-mingw32]
Linux: Ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux]

Related

ruby file locking error Errno::EBADF in solaris

Am trying to lock one executable script to make sure it doesn't run second time when there is another process running. Here is my code,
if $0 == __FILE__
if File.new(__FILE__).flock(File::LOCK_EX | File::LOCK_NB)
main()
end
end
and getting below error,
# ruby /tmp/test.rb
/tmp/test.rb:397:in `flock': Bad file number - /tmp/test.rb (Errno::EBADF)
from /tmp/test.rb:397:in `<main>'
#
Am using ruby version 1.9.3,
# ruby --version
ruby 1.9.3p551 (2014-11-13 revision 48407) [sparc-solaris2.11]
#
But its working perfect in Linux environments.
Found the solution. In Solaris we need to open file with read/write mode only then the exclusive lock will happen.
if $0 == __FILE__
if File.new(__FILE__, 'r+').flock(File::LOCK_EX | File::LOCK_NB)
main()
end
end

Ruby cmd line script executes code non-consecutively

I've built a ruby script that is supposed to run in the terminal.
$ ruby script.rb
I have some code specific to newer versions of ruby so I added a ruby version check towards the top of the page:
abort("You're using ruby #{RUBY_VERSION}. Please use version 2.1 or newer") if (RUBY_VERSION.to_f < 2.1)
I double checked the code line in irb and seems to work when changing the ruby version via RVM.
However, when I run the ruby script file under, say ruby 1.8.7, the script blows up with the following error:
$ ruby script.rb
script.rb:6: odd number list for Hash
option1: 'some options',
^
script.rb:6: syntax error, unexpected ':', expecting '}'
option1: 'some options',
^
script.rb:6: syntax error, unexpected ',', expecting $end
This would be expected behavior if I didn't have the version check on the top of the file.
Why doesn't the version check execute before the next lines of code? Is there a way to force execution of the ruby check before continuing with the rest of the code?
My full file is:
#!/usr/bin/env ruby
abort("You're using ruby #{RUBY_VERSION}. Please use version 2.1 or newer") if (RUBY_VERSION.to_f < 2.1)
options = {
option1: 'some options',
option2: 'some more options',
option3: 'other options'
}
That error is on the ruby parser. In ruby 1.8.7 hashes with symbols must be written with hashrockets { :option => 'some options'} because the shorthand { option: '' } was only introduced in ruby 1.9
To explain it better, ruby has to parse the whole file before executing anything on it. So your version check wont be executed because your file has invalid ruby 1.8 syntax.

why does `ruby -v` return true in rails, rather than return the version?

This seems like a silly question, but it's got me stumped. If I do this in the rails console:
system('ruby -v')
ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-darwin14]
=> true
So it shows the version, but returns true. So I do this in an .erb file:
<%= system('ruby -v') %>
and it just prints "true" on the web page. I've searched google, though the words involved just make it harder since they are used in so many places I've found it hard to craft a query that will return what I want. Thus proving google is not an AI. :-)
system executes the command and returns true if there was no error. So in this case the version is printed to the standard output and your program receives true which is included in your Erb output (check your log files, you might find the output of ruby -v in them.
What you probably want is ` (that’s a backtick). This is a method, but is called by using the backticks like quotes:
2.2.1 :001 > `ruby -v`
=> "ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-darwin13]\n"
Note how this returns the output of the call to ruby -v rather than printing it.
system prints out the result of the command and returns whether it was a success or not. Try the `` syntax:
<%= `ruby -v` %>

What is the clearest way to open a file and do "rescue" when it cannot be opened in Ruby

I am now dealing with this issue by using the following code
begin
File.open(filename, 'r')
rescue
print "failed to open #{filename}\n"
exit
end
Is there any way to do it more easily like Perl 'open (IN, $filename) || die "failed to open $filename\n"' Thanks.
File.open("doesnotexist.txt", 'r')
Is enough. If the file does not exist, this will raise an Exception. This is not caught, so the program exits.
# =>test6.rb:1:in `initialize': No such file or directory # rb_sysopen - doesnotexist.txt (Errno::ENOENT)
I'm not sure what you're trying to accomplish other than trying to write Perl with Ruby. You have to consider the fact that Perl's open returns "nonzero on success, the undefined value otherwise. If the open involved a pipe, the return value happens to be the pid of the subprocess."
Ruby's File::open however raises an exception Errno::ENOENT which is completely different behavior than returning some Ruby equivalent to undefined (I guess nil).
Write Ruby if your tool is Ruby. Write Perl if your tool is Perl. Don't write Perl if your tool is Ruby. Don't write Ruby if your tool is Perl.
UPDATE:
As #steenslag answered, simply not rescueing the exception is sort of an equivalent since the exception will act as an implicit die equivalent of Perl.
File.open filename
However you are now restricted to outputting the exception's message value as the output. For example:
in `initialize': No such file or directory # rb_sysopen - filename (Errno::ENOENT)
If you need to output your own message you'll have to catch the exception and handle it. This is the behavior of File.open so you'll have to use it as intended. I would also argue you should explicitly specify the exception type to catch, as well as write to $stderr:
begin
File.open filename do |f|
puts f.gets
# ...
end
rescue Errno::ENOENT => e
$stderr.puts "Caught the exception: #{e}"
exit -1
end
Which would output the message specified to standard error and exit the program with an non-zero status (which is the behavior of die):
Caught the exception: No such file or directory # rb_sysopen - filename
For the record I absolutely love Perl. Ruby actually shares a lot of similar characteristics of Perl (among other languages and paradigms). But when I'm writing Perl I use Perl idioms, and when I write Ruby I use Ruby idioms. The reason I'm stressing "don't write Perl with Ruby" so much is because languages have their idioms for a reason and you can end up making your colleagues mad if you don't do things "the Ruby way" or "the Perl way" with each respective language.

Unit tests stops half way when adding certain assertions

I am trying to write some unit testing for my ruby script. However, it is not working as I think it should work, and with certain tests, it just stops half way through the unit test.
This is the method I am currently testing.
#!/usr/bin/env ruby
require 'ptools'
require 'test/unit'
class InputValidators
# checks whether the input file exist.
def input_file_validator(input_file)
begin
raise ArgumentError, "Error: Input file \"#{input_file}\" does not exist. \n" unless File.exist?(input_file)
raise ArgumentError, "Error: Input file is empty. Please correct this and try again. \n" if File.zero?(input_file)
raise ArgumentError, "Error: Input file is in binary format - only text based input files are supported. \n" if File.binary?(input_file)
rescue Exception => e
puts # a empty line
puts e.message
puts # a empty line
Process.exit(true)
end
end
end
class UnitTests < Test::Unit::TestCase
def test_input_file_validator_1
test_validators = InputValidators.new
assert_equal(nil, test_validators.input_file_validator("./test_inputs/genetic.fna")) #file is present
assert_raise( SystemExit ) {test_validators.input_file_validator("./test_inputs/missing_input.fna")} # file doesn't exist
# assert_equal(nil, test_validators.input_file_validator("./test_inputs/empty_file.fna")) # empty file
# assert_equal(nil, test_validators.input_file_validator("./test_inputs/binary_file.fna")) # a binary file
end
end
Now, if I leave the script as above, the unit test work perfectly...
Current Output:
Run options:
# Running tests:
[1/1] UnitTests#test_input_file_validator_1
Error: Input file "./test_inputs/missing_input.fna" does not exist.
Finished tests in 0.004222s, 236.8797 tests/s, 473.7593 assertions/s.
1 tests, 2 assertions, 0 failures, 0 errors, 0 skips
ruby -v: ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-linux]
However, if I even uncomment one of the other asserts, the unit test just stops and doesn't complete.
Output (when uncommenting just one or both of the assertions in the above script):
Run options:
# Running tests:
[1/1] UnitTests#test_input_file_validator_1
Error: Input file "./test_inputs/missing_input.fna" does not exist.
Error: Input file is empty. Please correct this and try again.
I have no idea what I am doing wrong so any help on this would be most appreciated.
Let me know if you need more info.
well, if you run exit and you are not rescuing from that exception, your process just stops running.
i guess that assert_raise does actually capture that error or does some other magic to complete the process. running at_exit hooks might be some of those magic tricks.
despite all this, it's considered a bad practice to use exceptions for workflows. so i would not recommend raising an error and then catching it immediately just to exit the process. i usually just use abort with a message.

Resources