I have been interested in Design By Contract. I don't have access to Eiffel. I want to try them out in Ruby. I looked into the frameworks provided by Ruby, but I don't fully understand how it works. I read that they have a lot of limitation. Frameworks are in fact little languages that one has to learn on top of the regular language.
What can we do?
We can try it using the most minimal implementation of the idea.
Let's use design by contracts as an example.
The basic idea is that you check conditions before and after calling a function. This is something that we can implement easily.
Let's start with a simple function that we want to add contracts to. Here is a function that adds two unsignged bytes.
1 2 3 |
|
Let's add a precondition that the sum should be of integers.
1 2 3 4 5 6 |
|
Now let's add a postcondition. Let's say that the return should be an integer that fits within a C byte range. So we need to take the result, do a modulo operation and get a value that fits.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
At this point we have done the basics of Design By Contract. Yet looking at it, it is hard to read. We don't have any indicator of what we are doing. Let's abstract the conditional lines.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
This is a good step. It is cleaner. I would like to clearly point out when it is a precondition or postcondition. Especially, we want the error message to let us know immediately.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
This is a lot better. It is clear that we are writing a contract for this function. There is one last item that bothers me, which is having to repeat result
a second time. We can make it slightly easier with the following modifications.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
This puts the post_condition
at the end, as the last step of the function. I think it is cleaner. You may disagree and choose to skip it.
Now we have a design by contract micro library. This is enough to try it out in your projects and see if it makes sense for you. With these few lines of code, we can bring in the value that design by contract has.
There is no elaborate API to learn either. The conditions are performed in plain Ruby, as long as you make sure the conditional returns a boolean.
The contract clauses are clearly marked. They are as close to the code as possible. Having the clauses there is marking that this function has enough business value that it is worth to add pre and post conditions.
The micro library is small, understandable, and hackable. It is not using obscure or hard to understand metaprograming code. Because of this, a developer or a team can easily extend it as adoption increases.
For example, the micro library lacks an invariant
test for objects. It shouldn't be too hard to add it if it is necessary.
It also allows to properly assess whether adopting a library makes sense. Now that you have tried Design by Contract for a while, you can correctly determine if adopting a fully fleshed framework is worth your time or not.
I could wrap this micro library in a gem. I have decided not to. Instead, you can copy and paste the library, and grow it yourself. Besides of this hackability, it also reduces your security exposure. Once you copy in the template micro library, you don't have to worry that someone has added a security vulnerability or malicious code.