Get caller's caller for Ruby exception - ruby

Right now our application is running on Heroku, and in order to save restarts and possible latency the application is designed to cache all errors and send them to stderr and just server a stale cache until the issue is resolved by another push. This prevents application takedown...
However, the logs keep filling up excessively with entire traces which we don't want, so I built a little snippet (that is partially stolen from Rails) to parse caller and only send that line, however with errors being sent from a rescue, we get the wrong line so I was wondering how I could get the caller or the caller or if there was a better way to handle this situation. Here is the snippet:
module StdErr
def error(message)
file = 'Unknown'
line = '0'
if /^(.+?):(\d+)(?::in `(.*)')?/ =~ caller[1]
file = $1
line = $2
end
$stderr.puts "ERROR: #{message} on line #{line} of #{file}"
end
end

If I get it, you want to raise a exception with a backtrace that begins on the caller of the caller. Do it:
def raise_cc(*args)
raise(*args)
rescue Exception
# Raises the same exception, removing the last 2 items
# from backtrace (this method and its caller). The first
# will be the caller of the caller of this method.
raise($!.class, $!.message, $!.backtrace[2..(-1)])
end

caller returns an array and you can go deeper into the call stack by looking at it's later elements (note that you're looking at caller[1])

Related

How to handle exceptions in Find.find

I'm processing files and directories looking for the most recent modified file in each directory. The code I have works but, being new to Ruby, I'm having trouble handling errors correctly.
I use Find.find to get a recursive directory listing, calling my own function newestFile for each directory:
Find.find(ARGV[0]) { |f|
if File.directory?(f)
newestFile(f)
end
}
In the directory tree there are folders I do not have permission to access, so I want to ignore them and go on to the next, but I cannot see how to incorporate the exception handling in to the Find.find "loop".
I tried to put begin..rescue..end around the block but that does not allow me to continue processing the loop.
I also found this SO question: How to continue processing a block in Ruby after an exception? but that handles the error in the loop. I'm trying to recover from an errors occurring in Find.find which would be outside the exception block.
Here's the stack trace of the error:
PS D:\dev\ruby\> ruby .\findrecent.rb "M:/main/*"
C:/Ruby200/lib/ruby/2.0.0/find.rb:51:in `open': Invalid argument - M:/main/<A FOLDER I CAN'T ACCESS> (Errno::EINVAL)
from C:/Ruby200/lib/ruby/2.0.0/find.rb:51:in `entries'
from C:/Ruby200/lib/ruby/2.0.0/find.rb:51:in `block in find'
from C:/Ruby200/lib/ruby/2.0.0/find.rb:42:in `catch'
from C:/Ruby200/lib/ruby/2.0.0/find.rb:42:in `find'
from ./findrecent.rb:17:in `<main>'
How do I add exception handling to this code?
I had a look in the code where the exception is being generated and the method contains the following block:
if s.directory? then
begin
fs = Dir.entries(file)
rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG
next
end
... more code
Performing a horrible hack I added Errno::EINVAL to the list of rescue errors. My code now executes and goes through all the folders but I can't leave that change in the Ruby library code.
Internally find is using Dir.entries, so maybe I need to rewrite my code to process the folders myself, and not rely on find.
I would still like to know if there is a way of handling errors in this sort of code construct as from reading other code this type of small/concise code is used a lot in Ruby.
Do you get this error on your newestFile function or when you try to run File#directory??
If this happens in newestFile you can do something like this:
Find.find(ARGV[0]) do |f|
if File.directory?(f)
newestFile(f) rescue nil
end
end
This just ignores any errors and punts until the next folder. You could also do some nicer output if desired:
Find.find(ARGV[0]) do |f|
if File.directory?(f)
begin
newestFile(f)
rescue
puts "error accessing: #{f}, you might now have permissions"
end
end
end
If the error happens in the File#directory? you need to wrap that section as well:
Find.find(ARGV[0]) do |f|
begin
if File.directory?(f)
newestFile(f)
end
rescue
puts "error accessing: #{f}, you might now have permissions"
end
end
Like you mentioned if the error is occurring in the Find#find itself then you can't catch that from the block. It would have to happen inside of that method.
Can you confirm that the exception is happening in that method and not the subsequent ones by pasting a stack trace of the exception?
Edit
I was going to suggest traversing the directories yourself with something like Dir#entries so you would have that capacity to catch the errors then. One thing I am interested in is if you leave of the * in the call from the command line. I am on MacOS so I can't duplicate 100% what you are seeing but If I allow it to traverse a directory that I don't have access to on my mac it prints debug info about what folders I can't access but continues on. If I give it the * on the other had it seems to do nothing except print the error of the first folder it can't access.
One difference in my experience on the MacOS is that it isn't actually throwing the exception, it is just printing that debug info to the console. But it was interesting that the inclusion of the * made mine stop completely if I didn't have access to a folder.
You can be reactive or proactive, either works, but by testing in advance, your code will run a little faster since you won't be triggering the exception mechanism.
Instead of waiting for a problem to happen then trying to handle the exception, you can find out whether you actually should try to change to a directory or access a file using the File class's owned? and grpowned? methods. From the File documentation:
grpowned?(file_name) → true or false
Returns true if the named file exists and the effective group id of the calling process is the owner of the file. Returns false on Windows.
owned?(file_name) → true or false
Returns true if the named file exists and the effective used id of the calling process is the owner of the file.
That means your code can look like:
Find.find(ARGV[0]) do |f|
if File.directory?(f) && %w[grpowned? owned?].any?{ |m| File.send(m.to_s, f) }
newestFile(f)
end
end
Ruby will check to see if the directory entry is a directory and whether it is owned or grpowned by the current process. Because && is short-circuiting, if it's not a directory the second set of tests won't be triggered.
On some systems the group permissions will give you a better chance of having access rights if there are lots of shared resources, so that gets tested first, and if it returns true, any? will return true and the code will progress. If false is returned because the group permissions don't allow access, then owned? will test the file and the code will skip or step into newestFile. Reverse those two tests for speed depending on the set-up of your system. Or, run the code one with using time ruby /path/to/your/code then twiddle the two and run it again. Compare the resulting times to know which is faster on your system.
There are different schools of thought about whether using exception handling to control program flow is good and different languages prefer different things in their programming styles. To me, it seems like code will always run faster and more safely if I know in advance whether I can do something, rather than try and have it blow up. If it blows up in an expected way, that's one thing, but if it blows up in ways I didn't expect, then I might not have exception handling in place to react correctly, or it might trigger other exceptions that mask the true cause. I'd rather see if I can work my way out of a situation by checking the state, and then if all my attempts failed, have an exception handler that lets me gracefully exit. YMMV.
Finally, in Ruby, we don't name methods using Camelcase, we use snake_case. snake_case_is_easier toReadThanCamelCase.

ruby - how can i still do something when there's error (example: NameError)

i manage to do "something"(like deleting files,etc) when exit or exit! is called from Object.
by changing the method exit and exit! inside Object class. (at_exit is too unreliable)
but then the "something" never execute if there's error such as NameError, etc.
is there way so i can make "something" that still execute if there's error.
(any error possible if necessary).
something like at_exit but works with all errors
thanks in advance for assistance. and forgive me if there's already question ask for this.
i do search a lot before asking here. and found nothing.
edit: i don't know where the original author of the code place the method. since the original author load it from dll files in the exe program the author used for launcher. (i can only edit After the original code take place...). so i think i need another approach for this... but i manage to make a workaround for my problem... by putting begin rescue in other places that send data to the original object. i filter the data send and throw my own error before it reach the main program... so i guess this works too.
Borrowing from an answer on a different thread, and definitely along the lines of what Marek commented, this is how you should handle errors in Ruby:
begin
# something which might raise an exception
rescue SomeExceptionClass => some_variable
# code that deals with some exception
rescue SomeOtherException => some_other_variable
# code that deals with some other exception
else
# code that runs only if *no* exception was raised
ensure
# ensure that this code always runs, no matter what
end
Original credit: Begin, Rescue and Ensure in Ruby?

How to clearly compare old value with new one?

A common piece of code that I already repeated some times, and never liked it, consists in save a value, do an action, and evaluate the value afterwards. Look at the following example
old_files = project.files
project.some_operation_dealing_with_files
if old_files == project.files
puts "not changed"
else
puts "changed"
end
One of the problems on it is that on the first line, when you read old_files = project.files, it is not clear where you want to get. I imagine that if I could eliminate that variable, the code would be better, but I don't know how to achieve this. I'm also, of course, open to suggestions.
TLDR; Is there a way to rewrite the code without the old_file variable?
Make the object aware if the files state changes and provide a query method to test it. Set the did_change status to false at the start of each method that could change the files state.
project.some_operation_with_files
if project.files_did_change?
puts "Changed"
else
puts "No change."
end
Would returning a true/false status or throwing an exception work in your case? When the method returns false you should assume there were no changes in state of project anyway.
if project.some_operation_dealing_with_files
puts "Changed"
else
puts "Not Changed"
end
With an exception you can catch a specific type of error. Again if an error is thrown, project should be idempotent and the error should be handled gracefully.
begin
project.some_operation_dealing_with_files
puts "Changed"
rescue Errno::ENOENT
puts "Not Changed. File not found."
# Clean up and handle the error gracefully
end

Ruby 1.9.3: Recieving Stack Level Too Deep errors when creating a new object

I'm not exactly an expert in Ruby but I'm attempting to trace the issue in some scripts and haven't been able to reach the expert on this particular script to get an idea of why we're receiving this error. I've narrowed things down to this particular function and from what I can tell, the first print statement happens but the end print statement does not. The stack level too deep error only happens when this function exists exactly as below:
def load_power_ports(io_info)
return if !io_info
io_info.each_key do |key|
print key
if !#power_controllers[key.to_s.downcase]
#power_controllers[key.to_s.downcase] = Object.const_get($equipment_table['power_controller'][key.to_s.downcase][0].driver_class_name).new($equipment_table['power_controller'][key.to_s.downcase][0])
end
end
print "end of equipment power block"
rescue Exception => e
raise e.to_s + "\nUnable to create power controller: " + io_info.to_s
end
The print statements are only to see which information is being passed, how far the program is getting and iterations being performed.
Does the constructor of driver_class_name call load_power_ports directly or indirectly? If so, you'd end up having infinite recursion which would give you your stack overflow (oh the irony). That's the only place I can see where you'd likely have that issue.

Ruby Exceptions in a loop

I have a ruby script that loops through a list of shortened urls (around 2,000 - 3,000 at a time). At the moment everything is peachy until a hit a url that is malformed, timedout etc. When an error occurs my script dies. How can I setup my loop to skip to the next record when/if such an error occurs.
my loop looks like this:
blah.foo do |barurl|
mymethod(barurl)
my mymethod looks like this:
def mymethod(barurl)
begin
stuff
...
return expandedurl
rescue
return "Problem expanding link"
end
end
Should my begin/end logic be wrapped around my loop instead of the method?
Because you need to skip the malformed url, you should use the exception message to control the loop
blah.foo do |barurl|
begin
mymethod(barurl)
rescue YourTypeOfException
next
end
end
and inside the method raise the exception
def mymethod(barurl)
stuff
...
raise YourTypeOfException, "this url is not valid"
...
end
I found the existing answers unsatisfying, and reading the documentation suggests to me that the OP had something more like the example suggested there in mind:
[0, 1, 2].map do |i|
10 / i
rescue ZeroDivisionError
nil
end
#=> [nil, 10, 5]
The docs specifically note that a rescue block permits the loop to continue on a caught exception (as indicated by the example).
Yes. All your method does is consume the exception and return another arbitrary object in order to indicate an error.
Your method shouldn't handle its own exceptional conditions. It is just rude on its part to make assumptions about how the caller will react.
blah.foo do |url|
begin
my_method url
rescue
next
end
end
Whether to skip to the next URL or print a message is not a decision the method should be making. Its only concern should be working with the URL.
With that said, you should simply let it propagate and only rescue from it when you can actually deal with it. Don't rescue from a TimeoutError if all you can do is return :timeout.
Do rescue when you need to clean up resources or simply let the user know an error occurred.
Also, rescuing from every possible error just to make them go away is a nice way to introduce bugs. Be as specific as possible.
having exception handling within your method is proper way of doing it, so your implementation is fine
i can only point some ruby sytax sugar to you:
def some_method
# here goes the code
rescue Exception => e
# here goes specific exception/error handling
rescue
# here goes error handling (not Exception handling though!)
else
# do this block when no exceptions are raised
ensure
# do this every time
end
btw you don't need return statements, last value of code block is always returned implicitly
ah i guess i misread your question in the "how to skip next record"
if you want to skip the record after current one that was incorrect you would have to return error code from your parsing method and set up skipping within your loop using break or next keywords
It should be inside the loop, so the loop structure isn't exited on an exception. But it looks like it already is--if you're rescuing inside the method that causes the exception, the loop should already continue normally, because it shouldn't be seeing the exception.

Resources