Ruby puts Displaying nil in rescue - ruby

I am brand new to Ruby and am coming from a Python background. To help learn the language, I'm porting an existing Python script so I can do a side by side comparison. Thus far, I have a small bit of code and am confused as to why 'nil' prints to the console. Code below:
#!/usr/bin/ruby
require 'date'
backup_dirs = ['/backups/db', '/backups/log']
backup_dirs.each do |backup_dir|
Dir.glob(backup_dir + '/' + '*.*.*.*.*.*').each do |filename|
begin
creation_date = DateTime.strptime(filename.split('.')[3], '%m%d%Y%H%M')
rescue
puts "Skipping #{filename}, invalid file."
end
end
puts 'File is okay.'
end
If the DateTime.strptime method throws an exception, rescue runs and puts prints out the string fine. Except, after it comes a nil. I "googled" and found that puts returns nil. But why does that show only in the rescue area and not in the File is okay. line.
I.e. an example rescue output would be:
Skipping /backups/log/fs.server.dir.999999999999.14.log, invalid file.
nil
And, how do I make it stop displaying that to the console? I'm sure this is a fundamental language thing, just very new to the language so any insight would be much appreciated.
Thanks - Tom

All... sorry. I figured this out. In the actual code I was doing a puts creation_date and that is what was causing the nil to show... as it very well should!
First day learning the language, oops! Did learn a lot about puts though... appreciate all of the answers, sorry for wasting everyones' time.

This is expected behavior. Ruby methods will return the last statement evaluated if return whatever is not specified.
In the sample you have provided you are ending with a puts statement.
puts writes your output then returns nil and subsequently your method returns nil
From irb just run the following two statements to see this in action.
puts 'foo'
'foo'

This is actually because of the #puts method you're calling.
puts(obj, ...) → nil
Writes the given objects to ios as with IO#print. Writes a record separator (typically a
newline) after any that do not already end with a newline sequence. If
called with an array argument, writes each element on a new line. If
called without arguments, outputs a single record separator.
$stdout.puts("this", "is", "a", "test") produces:
this is a test
You can check that in pry/irb:
[9] pry(main)> puts "Hello"
Hello
=> nil

Related

How can I solve undefined method `[]' on Ruby?

I'm trying to get an if statement for users who put incorrect data.
Here's my code:
class Breweries::CLI
def start
puts "Hello!"
puts "---------------------------"
puts "Please enter your location:"
input = gets.strip.downcase
#data = Breweries::API.get_breweries(input)
#objects = Breweries::HoppyCode.all
if input.length < 1
puts "Sorry!!"
puts "```````"
start
else
display_info
end
end
def display_info
puts "You'll love the following spots!"
puts "********************************"
#objects.each.with_index(1) {|brewery, index| puts "#{index}. #{brewery.name}"}
puts "Please make a selection by index number for more information:"
input = gets.strip.downcase
if(input.to_i > 0)
#brewery = #objects[input.to_i - 1]
puts "name: #{#brewery.name}"
puts "street: #{#brewery.street}"
puts "city: #{#brewery.city}"
puts "phone: #{#brewery.phone}"
puts "website_url: #{#brewery.website_url}"
display_info
elsif (input == "quit")
quit
elsif (input == "menu")
start
end
end
def quit
puts "Goodbye. Drink responsibly and enjoy."
end
end
When I put something that would generate an error, it returns the following:
Please enter your location: nvifpejvf80ejvip
Traceback (most recent call last):
2: from bin/breweriesCLI:6:in `<main>'
1: from /home/munificent-format-5297/Development/breweries/lib/breweries/cli.rb:8:in `start' /home/munificent-format-5297/Development/breweries/lib/breweries/api.rb:6:in `get_breweries': undefined method `[]' for nil:NilClass (NoMethodError)
How can I solve the undefined method '[]' error? Here's the API code in case that's necessary.
class Breweries::API
def self.get_breweries(input)
#breweries_hash = HTTParty.get("https://api.openbrewerydb.org/breweries?by_city=#{input}")
breweries_obj = {
name: #breweries_hash[1]["name"],
street: #breweries_hash[3]["street"],
city: #breweries_hash[4]["city"],
phone: #breweries_hash[10]["phone"],
website_url: #breweries_hash[11]["website_url"]
}
Breweries::HoppyCode.new(breweries_obj)
end
end
When the input is invalid, the call to
#breweries_hash = HTTParty.get("...")
returns not the object you expect (I’d suggest it returns an empty hash.) That makes it impossible to get to details in the following lines. Depending on how are you to handle it, you might decide to e. g. early return from this function, or raise, or do something else.
To approach this, start with debugging the issue, like this:
#breweries_hash = HTTParty.get("...")
puts #breweries_hash.inspect
...
That way you’ll see what gets returned and get the ideas of how to handle it.
If I am right, and what is returned is an empty hash, you might want to early return from this function.
#breweries_hash = HTTParty.get("...")
return if #breweries_hash.empty?
...
Identifying the Problem
There are lots of ways to solve for the nil problem, but at a quick glance it seems like part of the problem here is that you're somehow expecting input to return a valid Hash object from your API call, but an empty String or an instance of FalseClass may not do that. Consider the following:
input = gets.strip.downcase # <RETURN> here gets an empty string
input #=> ""
input.to_i > 0 #=> false
Then consider that some downstream of Breweries::API.get_breweries is expecting to operate on a Hash object instead if an instance of NilClass. In this case, that looks like #breweries_hash[1]["name"] and other operations on #breweries_hash.
Some Options
Without knowing more about your code, I don't want to be prescriptive here. But in general, you can do one or more of the following:
Coerce arguments into the expected class in the method call, the method signature, or the method body. For example, for Array objects:
# coerce a String to an Array, raising an exception if it can't
input = ""
Array(input)
#=> [""]
# coerce some Array to a Hash
array = [:name, "foo", :street, "bar"]
Array(array.each_slice 2).to_h
#=> {:name=>"foo", :street=>"bar"}
Explicitly check that you have an Hash object:
fail "#breweries is not a Hash" unless #breweries.is_a? Hash
Raise an exception rather than return 0 if input isn't actually a valid Integer representation in the first place:
input = Integer(gets.strip.downcase)
Check if your Hash or Array object responds to the relevant method calls, and raise a more helpful exception message:
raise sprintf("#brewery: %s", #brewery.class) unless #brewery.respond_to? :[]
There are other things you might do as well. Broadly speaking, you need to adjust your code to check the return value of your call to ensure it's not nil, then branch/raise/rescue appropriately depending on whether or not you ever expect nils as a valid return value from Breweries::API.get_breweries.
A Note on Using Exceptions for Non-Exceptional Circumstances
As a rule of thumb, you should only raise exceptions for truly unexpected circumstances, or when the program should halt because some condition can't (or shouldn't) be handled within the program during runtime. Which is best in your particular use case is really a design decision, and outside the scope of the original question. However, you might want to read Avdi Grimm's Exceptional Ruby for a deeper explanation of when exceptions might better than branching or handlers (or vice versa), but the choice in your code is a little left of center of the problem you're actually dealing with right now.

Range is printed at the end of the "each" iteration

What I'll show is a fulish function, but it's only to explain my problem in a simple way. If I solve the problem in this function, I solved the problem in the original code too.
The problem is simple, and probably the solution too, but I'm new in Ruby and have this doubt. I want to print the values in a range:
def test
(0...5).each do |i|
puts i
end
end
When I call the function, the result that I want is
0
1
2
3
4
but the result that I have is
0
1
2
3
4
0...5
Why this 0...5 is printed together? How can I avoid that?
i don't think the 0..5 is being produced as part of the puts call. Rather, when you call this in your REPL (irb, pry, rails console, etc), you're seeing because it's the last returned value in your code.
Let me show you an example.
Say I save a file called test.rb with the following content:
1.upto(5).each { |i| puts i }
If I call ruby test.rb, I see the expected output,
0
1
2
3
4
If I open irb and run require("./test.rb"), I see the same output.
It's only when I paste the code into irb that I see the additional output (=> 0...5). So I would just ignore this.
In addition to #Max's answer
Whenever any expression is executed in IRB sessions, it will also print the value returned by every expression executed.
In case of method definition it returns the method_name just defined.
> def my_method
?> puts 'this is my method'
?> end
=> :my_method
You see, the :my_method is printed
When the method is invoked, it should print the value returned by the method execution i.e. response of the last expression in the method i.e. puts
> my_method
this is my method
=> nil
but it printed nil because puts always returns nil. I mentioned this because normally developers are astonished when they see their methods returning nil unexpectedly.

Metaprogramming Ruby 2 example from the book isn't working when I try it. Trouble shooting assistance

While reading through a chapter of Metaprogramming Ruby 2, I've come across an example in the book that does not seem to work when I execute the code.
array_explorer.rb
def explore_array(method)
code = "['a','b','c'].#{method}"
puts "Evaluating: #{code}"
eval code
end
loop { p explore_array(gets()) }
The code above is designed to illustrate the power of eval. In the next example the book teaches the major flaw of code injections and refactors the code like so to safeguard:
array_explorer.rb
def explore_array(method, *arguments)
['a','b','c'].send(method, *arguments)
end
loop { p explore_array(gets()) }
When I try to run the above code, the file always gives me this error no matter what array method I try to place in.
array_explorer.rb:2:in `explore_array': undefined method `:size (NoMethodError)
' for ["a", "b", "c"]:Array
I've tried taking out the *arguments portion to whittle it down. I tried using a string as input, a symbol as input, etc. This code doesn't work for some reason. Does anyone know why?
gets reads a line from STDIN; a "line" is defined as a string of characters ending with a newline (\n). Thus, you are trying to invoke the method "size\n", which does not exist. Use chomp to get rid of the newline:
loop { p explore_array(gets.chomp) }
It does not matter in the first example, since you are evaluating the code "['a', 'b', 'c'].size\n", which is still valid.

What does the nil returned by puts signify?

When I use the puts command, especially in IRB I get a nil returned as part of the statement execution.
I was curious to know what it means on a general note.
Please find below a sample:-
ruby-1.8.7-p334 :021 > puts 3/2
1
=> nil
Your inputs would be really handy.
Thanks.
Every method in Ruby returns a value. puts doesn't really have any sort of useful value to return, so it returns nil.

Catching line numbers in ruby exceptions

Consider the following ruby code
test.rb:
begin
puts
thisFunctionDoesNotExist
x = 1+1
rescue Exception => e
p e
end
For debugging purposes, I would like the rescue block to know that the error occurred in line 4 of this file. Is there a clean way of doing that?
p e.backtrace
I ran it on an IRB session which has no source and it still gave relevant info.
=> ["(irb):11:in `foo'",
"(irb):17:in `irb_binding'",
"/usr/lib64/ruby/1.8/irb/workspace.rb:52:in `irb_binding'",
"/usr/lib64/ruby/1.8/irb/workspace.rb:52"]
If you want a nicely parsed backtrace, the following regex might be handy:
p x.backtrace.map{ |x|
x.match(/^(.+?):(\d+)(|:in `(.+)')$/);
[$1,$2,$4]
}
[
["(irb)", "11", "foo"],
["(irb)", "48", "irb_binding"],
["/usr/lib64/ruby/1.8/irb/workspace.rb", "52", "irb_binding"],
["/usr/lib64/ruby/1.8/irb/workspace.rb", "52", nil]
]
( Regex /should/ be safe against weird characters in function names or directories/filenames )
( If you're wondering where foo camefrom, i made a def to grab the exception out :
>>def foo
>> thisFunctionDoesNotExist
>> rescue Exception => e
>> return e
>>end
>>x = foo
>>x.backtrace
You can access the backtrace from an Exception object. To see the entire backtrace:
p e.backtrace
It will contain an array of files and line numbers for the call stack. For a simple script like the one in your question, it would just contain one line.
["/Users/dan/Desktop/x.rb:4"]
If you want the line number, you can examine the first line of the backtrace, and extract the value after the colon.
p e.backtrace[0].split(":").last
Usually the backtrace contains a lot of lines from external gems
It's much more convenient to see only lines related to the project itself
My suggestion is to filter the backtrace by the project folder name
puts e.backtrace.select { |x| x.match(/HERE-IS-YOUR-PROJECT-FOLDER-NAME/) }
And then you can parse filtered lines to extract line numbers as suggested in other answers.
Throwing my $0.02 in on this old thread-- here's a simple solution that maintains all the original data:
print e.backtrace.join("\n")
It is possible that in Ruby 1.9.3 you will be able to get access to not only this information in a more structured, reliable, and simpler way without using regular expressions to cut strings.
The basic idea is to introduce a call frame object which gives access to information about the call stack.
See http://wiki.github.com/rocky/rb-threadframe/, which alas, requires patching Ruby 1.9. In RubyKaigi 2010 (late August 2010) a meeting is scheduled to discuss introducing a frame object into Ruby.
Given this, the earliest this could happen is in Ruby 1.9.3.

Resources