4 ms·
Depends on your definition of "standard Ruby". "Contract" is really just a very clever method available in class scope on classes which use this library. It's
by mhink 12y ago
Depends on your definition of "standard Ruby". "Contract" is really just a very clever method available in class scope on classes which use this library. It's certainly inspired by method signature syntax in other languages, but at its core, it's all implemented using metaprogramming facilities provided by Ruby's "Module" and "Class" classes. It's a bit unorthodox, but it's also a great example of how Ruby can be "molded" to be more expressive while still being pretty much the same language.
As far as the use case is concerned: more or less, it's executable documentation. In an expressive way, it both explains what to expect from the method, and gives useful error messages when those expectations are not met- which saves you the trouble of figuring it out yourself.
One of the most annoying things in Ruby is trying to figure out why a NoMethodError is getting thrown 40 frames down in the call stack, in code you didn't personally write. Worse, a method just doesn't behave the way you'd expect, and you're not sure why. Generally speaking, the unexpected behavior is because the "implicit contract" of some method along the way changed, and the methods around it weren't updated to handle the new return value. Boom, now somewhere in your call stack you're getting a "NoMethodError: Undefined method 'foobar' for nil:NilClass" and it's going to take 30 minutes of tracing in pry to figure out where something's being called wrong.
As far as doing the same thing using "is_a?" and friends: you certainly could, but that adds a lot of noise to your method body. When I'm looking at a method, I want to cut through that noise and answer the question: "what does this method actually do?" That said, I still want the benefits of validating my arguments and return values. This gives me both.