Tuesday, November 3, 2009

Recipe 10.5. Fixing Bugs in Someone Else's Class










Recipe 10.5. Fixing Bugs in Someone Else's Class



Problem


You're using a class that's got a bug in one of its methods. You know where the bug is and how
to fix it, but you can't or don't want to change the source file itself.




Solutions


Extend the class from within your program and overwrite the buggy method with an implementation that fixes the bug. Create an alias for the buggy version of the method, so you can still access it if necessary.


Suppose you're trying to use the buggy method in the Multiplier class defined below:



class Multiplier
def double_your_pleasure(pleasure)
return pleasure * 3 # FIXME: Actually triples your pleasure.
end
end

m = Multiplier.new
m.double_your_pleasure(6) # => 18



Reopen the class, alias the buggy method to another name, then redefine it with a correct implementation:



class Multiplier
alias :double_your_pleasure_BUGGY :double_your_pleasure
def double_your_pleasure(pleasure)
return pleasure * 2
end
end
m.double_your_pleasure(6) # => 12

m.double_your_pleasure_BUGGY(6) # => 18





Discussion


In many programming languages a class, function, or method can't be modified after its initial definition. In other languages, this behavior is possible but not encouraged. For Ruby programmers, the ability to reprogram
classes on the fly is just another technique for the toolbox, to be used when necessary. It's most commonly used to add new code to a class, but it can also be used to deploy a drop-in replacement for buggy or slow implementation of a method.


Since Ruby is (at least right now) a purely interpreted language, you should be able to find the source code of any Ruby class used by your program. If a method in one of those classes has a bug, you should be able to copy and paste the original Ruby implementation into your code and fix the bug in the new copy.[1] This is not an elegant technique, but it's often better than distributing a slightly modified version of the entire class or library (that is, copying and pasting a whole file).

[1] Bugs in Ruby C extensions are much more difficult to patch. You might be able to write equivalent Ruby code, but there's probably a reason why the original code was written in C. Since C doesn't share Ruby's attitude towards redefining functions on the fly, you'll need to fix the bug in the original C code and recompile the extension.


When you fix the buggy behavior, you should also send your fix to the maintainer of the software that contains the bug. The sooner you can get the fix out of your code, the better. If the software package is abandoned, you should at least post the fix online so others can find it.


If a method isn't buggy, but simply doesn't do what you'd like it to do, add a new method to the class (or create a subclass) instead of redefining the old one. Methods you don't know about may use the behavior of the method as it is. Of course, there could be methods that rely on the buggy behavior of a buggy method, but that's less likely.




See Also


  • Throughout this book we use techniques like this to work around bugs and performance problems in the Ruby standard library (although most of the bugs have been fixed in Ruby 1.9); see, for instance, Recipe 2.7, "Taking Logarithms," Recipe 2.16, "Generating Prime Numbers," and Recipe 6.18, "Deleting a File"

  • Recipe 10.14, "Aliasing Methods"













No comments:

Post a Comment