Search code examples
rubyoopconceptual

Ruby - How to test for nil or empty string from console(stdin)


I'm very new to ruby so please bear with me... In a text on Ruby there is a code example that does this:

str = gets
exit if str.nil? || str.empty?
str.chomp!
temp, scale = str.split(" ")

My queries are as follows:

Given that gets will only return up to and including the cr why test for the empty string?

If you test the following:

puts nil.to_s.empty?
puts "".to_s.empty?
puts "".length            #the empty string      : equates to 0
puts nil.to_s.length      #the 'to string' method: equates to 0

Both will evaluate to true and have zero length. However, the only thing in the stream, if there is only a cr, is the carriage return itself. str will have a length of 1 in the following if you just hit the enter key.

print "enter a string or hit the enter key : "
str = gets
puts str.length
puts str

Furthermore, nil in ruby is an object. How the heck am I suppose to capture that from stdin?

I now see that the chomp! is misplaced within the text, the author's mistake not mine:

str = gets
str.chomp!
exit if str.nil? || str.empty? #test for zero length string will now work
temp, scale = str.split(" ")

Granted, I come from java and some common lisp and therefore may be too antiquated to grok this, but I still don't see how the test for nil is appropriate in this context. Maybe there are some conceptual differences in streams that are further along than my reading. Thanks in advance.

Edit:

To clear up some confusion here is the code re-written with only the change to placement of the chomp! statement:

#temperature-converter.rb
#code changed by poster, otherwise as written by author
print "Please enter a temperature and scale (C or F) : "
STDOUT.flush   #self explanatory....
str = gets
str.chomp!     #new placement of method call -- edit by poster
exit if str.nil? || str.empty?
#chomp! original position -- by author
temp, scale = str.split(" ")
abort "#{temp} is not a valid number." if temp !~ /-?\d+/
temp = temp.to_f
case scale
    when "C", "c"
        f = 1.8 * temp + 32
    when "F", "f"
        c = (5.0/9.0) * (temp - 32)
else
abort "Must specify C or F."
end
if f.nil?
    puts "#{c} degrees C"
else
    puts "#{f} degrees F"
end

And the output:

=>ruby temperature-converter.rb
Please enter a temperature and scale (C or F) : 30 c
86.0 degrees F
=>ruby temperature-converter.rb
Please enter a temperature and scale (C or F) : j c
j is not a valid number.
=>ruby temperature-converter.rb
Please enter a temperature and scale (C or F) : 30 p
Must specify C or F.
=>ruby temperature-converter.rb
Please enter a temperature and scale (C or F) :    #just the enter key
=>                                                 #did this just exit?!

but the answer selected by me is correct, if you use Ctl+D(U) or Ctl+Z(W) eof can be emulated and a in '<main>': undefined method 'chomp!' for nil:NilClass (NoMethodError) is error is thrown. Still, the string is never empty so checking the condition doesn't make sense to me.


Solution

  • From the documentation, gets can return a string or nil :

    Returns (and assigns to $_) the next line from the list of files in ARGV (or $*), or from standard input if no files are present on the command line. Returns nil at end of file.

    From Standard input

    see @MarkoAvlijaš' answer with CTRL+D.

    From ARGV

    You can create an empty file called empty.txt, and launch :

    ruby your_script.rb empty.txt
    

    Nothing should happen.

    If you remove the second line though :

    your_script.rb:3:in `<main>': undefined method `chomp!' for nil:NilClass (NoMethodError)
    

    I don't see how str could be empty with the above code. At the very least, there will be a newline if str isn't nil.

    Note that if you use chomp! before checking that str isn't nil, you might get a NoMethodError.