If you have a problem or need to report a bug please email : support@dsprobotics.com
There are 3 sections to this support area:
DOWNLOADS: access to product manuals, support files and drivers
HELP & INFORMATION: tutorials and example files for learning or finding pre-made modules for your projects
USER FORUMS: meet with other users and exchange ideas, you can also get help and assistance here
NEW REGISTRATIONS - please contact us if you wish to register on the forum
Users are reminded of the forum rules they sign up to which prohibits any activity that violates any laws including posting material covered by copyright
To the rescue! A Ruby tip.
5 posts
• Page 1 of 1
To the rescue! A Ruby tip.
In a recent conversation with Spogg I mentioned a functionality in Ruby, of which he thinks not many may know and therefore asked me to write a post about it.
When writing code, it is inevitable to cause an error at some point. Any higher language can catch such errors, which means, they are brought to the programmer's attention, before they can do any harm. Those are called "exceptions" and the process of dealing with exceptions "exception handling".
As any other high level language, Ruby also has a mechanism for exception handling. It is very convenient, flexible and at the same time very easy to use.
Whenever there is critical code, of which you must ensure that it runs no matter what, the standard behavior of Ruby will prevent that, because, if an exception is raised, Ruby tells you about the error in the info pane and then stops execution.
A better way is to use the "begin end" structure. Place the code section, that is critical, in this block. As an example, we will provoke a TypeError.
Immediately, Ruby tells you that it can't convert a fixnum (3) into an array, and then stops execution. Now we have a mess, because the variable number was already instantiated, before the exception was raised, but because the execution was stopped, it is now of NilClass. Not at all what we need! A number is, what the rest of the program expects.
With this simple structure, we already managed one big step: Ruby does not print the error to the info pane and stops, but continues to run. However, under rescue, we have no code. We don't handle the exception! number is still an instance of NilClass.
The rescue keyword is important. Every exception that is raised within the begin block, will be routed to it, instead of Ruby's standard behavior. We need a way to handle the exceptions, and that is done with the exception class. It will allow us to handle an exception based on its type. Ruby already provides a fine list of exceptions that are bundled under StandardError. When we tell rescue to catch StandardError, it will catch ALL standard errors that are defined, like TypeError, NameError, ZeroDivisionError, and so many more. If you write rescue with no exception specification, Ruby assumes StandardError automatically. The following codes are the same in functionality
Let's use our rescue block now. In the simplest form, we just notify ourselves of the exception. But Ruby does that already without writing a begin end block. The next simple form would be to give a default value out, when an exception arrives.
That's better. Number is now 0 instead of nil, and the rest of the program can deal with that. Ruby continues to run flawlessly.
But there's more. Sometimes you don't want to catch ALL exceptions, as following code might already deal with some of them. In that case we just select a subclass of StandardError. In our example, it would be a TypeError, we're interested in.
Now only a type error will be caught and dealt with, but all others be raised. And you can go into much deeper detail. You can specify more than one exception to be caught in a rescue line.
Now our rescue block will react to Type and Name errors. Often times, different exceptions need different handling. No problem. Just add another rescue line (actually as many as you need).
But you didn't think that was it, did you? When rescuing an exception, it might be possible to run the code, that failed before! Therefore you use the keyword retry.
But that still isn't it. Sometimes you need to make sure that some code is run under all circumstances. It could be difficult to repeat the code in all rescue blocks. And so there is another helpful keyword: ensure. It does exactly what it says. It ensures that whatever code is under it, will be executed, EVEN IF AN EXCEPTION WAS NOT HANDLED BY OUR RESCUE CODES! This is important to understand. Ensure will run, even if Ruby has to stop after an unhandled exception. This is why many Ruby programmers use begin end to open files. It is dangerous and even destructive to open files, but not closing them after use. But an unhandled exception will lead to exactly that. It can be avoided with ensure, like so.
If now an unhandled exception is raised, the file will be closed, before Ruby stops. However, ensure will always be executed, even if there was no exception, or if all exception were handled. That means, you have to make sure, that the code under ensure will not be executed elsewhere in the begin end block! In this example the file should not be closed in the rescue block!
I still have more for you. Sometimes, instead of just defining which exceptions to catch, you may want to access the exception in the rescue block (especially useful with your own exception, see below). You do that by referencing a local variable.
Last but not least, as well as handling exceptions, you can also raise one! This is helpful for various reasons, for example to re-raise an exception that was already handled. Some higher layer code might need to be informed of the error, even if it was handled.
raise without any argument in the rescue block will re-raise the same eexception, here a ZeroDivisionError.
Here you raise a RuntimeError with the message you can read there. You can also create your own exceptions that you can then raise. Exceptions are classes, just like everything else in Ruby. Just make sure to inherit from StandardError
You can overwrite the default message as well.
I probably forgot about something. But this will give you a start into exception handling. Have fun programming!
When writing code, it is inevitable to cause an error at some point. Any higher language can catch such errors, which means, they are brought to the programmer's attention, before they can do any harm. Those are called "exceptions" and the process of dealing with exceptions "exception handling".
As any other high level language, Ruby also has a mechanism for exception handling. It is very convenient, flexible and at the same time very easy to use.
Whenever there is critical code, of which you must ensure that it runs no matter what, the standard behavior of Ruby will prevent that, because, if an exception is raised, Ruby tells you about the error in the info pane and then stops execution.
A better way is to use the "begin end" structure. Place the code section, that is critical, in this block. As an example, we will provoke a TypeError.
- Code: Select all
ary = [] #creates an array class instance
number = ary + 3
Immediately, Ruby tells you that it can't convert a fixnum (3) into an array, and then stops execution. Now we have a mess, because the variable number was already instantiated, before the exception was raised, but because the execution was stopped, it is now of NilClass. Not at all what we need! A number is, what the rest of the program expects.
- Code: Select all
ary = [] #creates an array class instance
begin
number = ary + 3
rescue
end
With this simple structure, we already managed one big step: Ruby does not print the error to the info pane and stops, but continues to run. However, under rescue, we have no code. We don't handle the exception! number is still an instance of NilClass.
The rescue keyword is important. Every exception that is raised within the begin block, will be routed to it, instead of Ruby's standard behavior. We need a way to handle the exceptions, and that is done with the exception class. It will allow us to handle an exception based on its type. Ruby already provides a fine list of exceptions that are bundled under StandardError. When we tell rescue to catch StandardError, it will catch ALL standard errors that are defined, like TypeError, NameError, ZeroDivisionError, and so many more. If you write rescue with no exception specification, Ruby assumes StandardError automatically. The following codes are the same in functionality
- Code: Select all
begin
rescue
end
- Code: Select all
begin
rescue StandardError
end
Let's use our rescue block now. In the simplest form, we just notify ourselves of the exception. But Ruby does that already without writing a begin end block. The next simple form would be to give a default value out, when an exception arrives.
- Code: Select all
ary = [] #creates an array class instance
begin
number = ary + 3
rescue
number = 0
end
That's better. Number is now 0 instead of nil, and the rest of the program can deal with that. Ruby continues to run flawlessly.
But there's more. Sometimes you don't want to catch ALL exceptions, as following code might already deal with some of them. In that case we just select a subclass of StandardError. In our example, it would be a TypeError, we're interested in.
- Code: Select all
ary = [] #creates an array class instance
begin
number = ary + 3
rescue TypeError
number = 0
end
Now only a type error will be caught and dealt with, but all others be raised. And you can go into much deeper detail. You can specify more than one exception to be caught in a rescue line.
- Code: Select all
ary = [] #creates an array class instance
begin
number = ary + 3
rescue TypeError, NameError
number = 0
end
Now our rescue block will react to Type and Name errors. Often times, different exceptions need different handling. No problem. Just add another rescue line (actually as many as you need).
- Code: Select all
ary = [] #creates an array class instance
begin
number = ary + 3
rescue TypeError, NameError
number = 0
rescue ZeroDivisionError
#some code to prevent division by zero#
end
But you didn't think that was it, did you? When rescuing an exception, it might be possible to run the code, that failed before! Therefore you use the keyword retry.
- Code: Select all
ary = [] #creates an array class instance
begin
number = ary + 3
rescue TypeError
ary = 0
retry #since we changed the array to a number, the code "number = ary + 3" will work now.
end
But that still isn't it. Sometimes you need to make sure that some code is run under all circumstances. It could be difficult to repeat the code in all rescue blocks. And so there is another helpful keyword: ensure. It does exactly what it says. It ensures that whatever code is under it, will be executed, EVEN IF AN EXCEPTION WAS NOT HANDLED BY OUR RESCUE CODES! This is important to understand. Ensure will run, even if Ruby has to stop after an unhandled exception. This is why many Ruby programmers use begin end to open files. It is dangerous and even destructive to open files, but not closing them after use. But an unhandled exception will lead to exactly that. It can be avoided with ensure, like so.
- Code: Select all
f = File.open("somefile")
#the file is now open and ready to be used#
begin
#code that works with the file#
rescue
#code that handles errors#
ensure
f.close unless f.nil?
#if f is nil, the file is unavailable, else close f#
end
If now an unhandled exception is raised, the file will be closed, before Ruby stops. However, ensure will always be executed, even if there was no exception, or if all exception were handled. That means, you have to make sure, that the code under ensure will not be executed elsewhere in the begin end block! In this example the file should not be closed in the rescue block!
I still have more for you. Sometimes, instead of just defining which exceptions to catch, you may want to access the exception in the rescue block (especially useful with your own exception, see below). You do that by referencing a local variable.
- Code: Select all
ary = [] #creates an array class instance
begin
number = ary + 3
rescue => exc #works also with "rescue TypeError => exc" etc., but is redundant
watch "I caught this", exc.message
number = 0
end
Last but not least, as well as handling exceptions, you can also raise one! This is helpful for various reasons, for example to re-raise an exception that was already handled. Some higher layer code might need to be informed of the error, even if it was handled.
- Code: Select all
ary = [] #creates an array class instance
begin
number = ary + 3
rescue TypeError, NameError
number = 0
rescue ZeroDivisionError
number = 0
raise
end
raise without any argument in the rescue block will re-raise the same eexception, here a ZeroDivisionError.
- Code: Select all
a = 0
begin
number = 6 / a
rescue TypeError, NameError
number = 0
rescue ZeroDivisionError
number = 6
raise "You idiot once again tried to divide by zero! Will you ever learn?"
end
Here you raise a RuntimeError with the message you can read there. You can also create your own exceptions that you can then raise. Exceptions are classes, just like everything else in Ruby. Just make sure to inherit from StandardError
- Code: Select all
class IdiotError < StandardError
def initialize(msg="You will never learn! Divided by Zero again!")
super
end
end
a = 0
begin
b = 3 / a
rescue
raise IdiotError
end
You can overwrite the default message as well.
- Code: Select all
class IdiotError < StandardError
def initialize(msg="You will never learn! Divided by Zero again!")
super
end
end
a = 0
begin
b = 3 / a
rescue
raise IdiotError, "OK, I was a bit harsh. But please stop dividing by Zero!"
end
I probably forgot about something. But this will give you a start into exception handling. Have fun programming!
"There lies the dog buried" (German saying translated literally)
- tulamide
- Posts: 2714
- Joined: Sat Jun 21, 2014 2:48 pm
- Location: Germany
Re: To the rescue! A Ruby tip.
Excellent Help Tutorial !
Thanks Tulamide
Thanks Tulamide
- RJHollins
- Posts: 1571
- Joined: Thu Mar 08, 2012 7:58 pm
Re: To the rescue! A Ruby tip.
And here is an actual example of how rescue and the StandardError method :backtrace helped me to find a pesky, rare issue.
I used raise here to let you see the original error message I got. It made no sense. "Hey, in the mouseMoveMethod you tried to compare a float with a float!"
It also shows, what I forgot about in the first post. When having a method, you can add a rescue block at the end. The whole method definition then acts like a begin, rescue, end block.
Back to the error. In rescue I accessed the exception via local variable and called its method :backtrace. This is -simply spoken- like a raw version of the message, that was constructed. It gives me a litle bit more detail, as it tells me that actually the error is not in the method itself, but in the sort method, that I called in line 77! Now that I knew it must have been something that made the sort comparison impossible, I could check precisely for the objects used for sorting. Obviously 0.0 and 1.0 are valid, and for reasons I omit here, @angle was also valid all the time. So it could only be @y_delta. But I still didn't know why. So I placed some watch methods, to see the content of the object. In rare cases, @y_delta can become so small, that it can't be represented with a float anymore. It becomes undefined, and the description for that is NaN ("not a number"). Comparing any float with NaN is an unordered result and must therefore raise an exception in a sorting method. However, the designer of the message belonging to the ArgumentError did a bad job by just showing the classes. At that point @y_delta was already undefined, so it should have shown "comparison of NaN with Float failed". Since NaN is not a class, but a value in Ruby, it slipped through.
The solution is now very easy. The FloatClass has a method :nan?, which returns true if the value is NaN, and I just need to check for NaN (and replace it) before passing it to the sort algorithm.
I used raise here to let you see the original error message I got. It made no sense. "Hey, in the mouseMoveMethod you tried to compare a float with a float!"
It also shows, what I forgot about in the first post. When having a method, you can add a rescue block at the end. The whole method definition then acts like a begin, rescue, end block.
Back to the error. In rescue I accessed the exception via local variable and called its method :backtrace. This is -simply spoken- like a raw version of the message, that was constructed. It gives me a litle bit more detail, as it tells me that actually the error is not in the method itself, but in the sort method, that I called in line 77! Now that I knew it must have been something that made the sort comparison impossible, I could check precisely for the objects used for sorting. Obviously 0.0 and 1.0 are valid, and for reasons I omit here, @angle was also valid all the time. So it could only be @y_delta. But I still didn't know why. So I placed some watch methods, to see the content of the object. In rare cases, @y_delta can become so small, that it can't be represented with a float anymore. It becomes undefined, and the description for that is NaN ("not a number"). Comparing any float with NaN is an unordered result and must therefore raise an exception in a sorting method. However, the designer of the message belonging to the ArgumentError did a bad job by just showing the classes. At that point @y_delta was already undefined, so it should have shown "comparison of NaN with Float failed". Since NaN is not a class, but a value in Ruby, it slipped through.
The solution is now very easy. The FloatClass has a method :nan?, which returns true if the value is NaN, and I just need to check for NaN (and replace it) before passing it to the sort algorithm.
"There lies the dog buried" (German saying translated literally)
- tulamide
- Posts: 2714
- Joined: Sat Jun 21, 2014 2:48 pm
- Location: Germany
Re: To the rescue! A Ruby tip.
Oh wow tulamide!
When I requested that you post the info, I had no idea of how much work it would cause you. I’m personally very grateful for you taking the time to do this for us.
BTW I somehow suspect this code is for a knob. I must be psychic
When I requested that you post the info, I had no idea of how much work it would cause you. I’m personally very grateful for you taking the time to do this for us.
BTW I somehow suspect this code is for a knob. I must be psychic
-
Spogg - Posts: 3358
- Joined: Thu Nov 20, 2014 4:24 pm
- Location: Birmingham, England
Re: To the rescue! A Ruby tip.
Great tutorial.. ...thanks Tulamide
BV MUSIC SYDNEY AUSTRALIA..Songwriting and Software development
Headquartershttps://www.bvmusicsydneyaustralia.com/
Spotifyhttps://open.spotify.com/artist/7JO8QM40mVmHb7pAwKPJi0
Donatationhttps://www.paypal.com/donate/?hosted_button_id=HEUR8R7K8GZ4L
Headquartershttps://www.bvmusicsydneyaustralia.com/
Spotifyhttps://open.spotify.com/artist/7JO8QM40mVmHb7pAwKPJi0
Donatationhttps://www.paypal.com/donate/?hosted_button_id=HEUR8R7K8GZ4L
- billv
- Posts: 1157
- Joined: Tue Aug 31, 2010 3:34 pm
- Location: Australia
5 posts
• Page 1 of 1
Who is online
Users browsing this forum: Google [Bot] and 82 guests