I’m looking for examples of why it’s not a good idea to extend base classes in ruby. I need to show some people why it’s a weapon to be wielded carefully.
Any horror stories you can share?
Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.
Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.
Lost your password? Please enter your email address. You will receive a link and will create a new password via email.
Please briefly explain why you feel this question should be reported.
Please briefly explain why you feel this answer should be reported.
Please briefly explain why you feel this user should be reported.
There was a pretty famous example of monkey-patching going horribly wrong about 2.5 years ago in Rubinius.
The interesting thing about this case is that both the offending code and the victim were highly visible and highly unusual. Usually, the offender is some piece of code written by some PHP script kiddy who got drunk on his 1337 metaprogramming h4X0r skillz. And the failure mode is a simple
ArgumentErrorexception, because the original method and the monkeypatch have different arity.However, in this case, the offender was a library in the stdlib (
mathn) and the failure mode was the Rubinius VM completely blowing up.So, what happened? Well,
mathnmonkeypatches theFixnumclass and changes howFixnumarithmetic works. In particular, it changes both the results and the types of several core methods. E.g.:The problem is of course that in Rubinius, the entire Ruby compiler, the entire Ruby kernel, large parts of the Ruby core library, some parts of the Rubinius VM and other parts of the Rubinius infrastructure, are all written in Ruby. And of course, all of those use
Fixnumarithmetic all over the place.The
Hashclass is written in Ruby and it usesFixnumarithmetic to compute the size of the hash buckets, compute the hash function and so on.Arrayis written in Ruby and needs to compute element sizes and array lengths. The FFI library is written in Ruby and needs to compute memory addresses(!) and structure sizes. Many parts of Rubinius assume that they can do someFixnumarithmetic and then pass the result to some C function as a pointer orint.And since Ruby doesn’t support any kind of selector namespacing or class boxing or similar (although something like that is planned for Ruby 2.0), as soon as some random user code requires the
mathnlibrary, all of those pieces just spectacularly explode, because all of a sudden, the result of aFixnumoperation is no longer aFixnum(which is basically identical to a machineintand can be passed around as such), but aRational(which is a full-fledged Ruby object).Basically, what would happen, is that some code would
require 'mathn'(or you would type that into IRb), and immediately the VM would just die.The solution, in this case, was the safe math plugin for the compiler: when the compiler detects that it is compiling the kernel or other core parts of Rubinius, it automatically rewrites calls to
Fixnummethods into calls to private immutable copies of those methods. [Note: I think in current versions of Rubinius, the problem is solved in a different way.]