As a project to assist in learning Ruby, the decision was made to build a calculator. I believe it’s a common challenge that developers undertake to learn a new language because—despite the initial thought that it is an easy task—it’s challenging and can serve to sharpen your understanding of the language and how the language handles numeric types. Additionally, part of my original goals was to allow for interchangeable user interfaces, even though I only ever wrote the one, using GTK2 to provide a windowed interface.
It definitely turned out to be far more of a challenge than I had anticipated, and I ended up spending something like 40 hours on the project, when I had expected less than 10. Throughout the course of the project, I found that the BigDecimal numeric type handled many of the problems that were being caused by the shortcomings of ECMA Script’s implementation of floating numbers (just try calculating (0.4 – 0.3) in javascript), although, when it came to division, BigDecimal failed immensely. After having spent hours converting all of the mathematical calculations to BigDecimal, I had to go back and change them to Rationals. The later addition of exponents (which include roots, by default (9^0.5 = _/9)) proved to cause even more problems, so a quick conversion to, and back from, floats was required. So many cases of user input possibilities came up, and I had to figure out a system to handle them all, with the least amount of code possible. When it was finished, I had a calculator that was, albeit limited in scope, something I felt I could be proud of.
Similar to the previous post about recursive initialization of Javascript objects, the calculator relies heavily on the Composite Pattern, and this was my first experience implementing that pattern. It turned out to be immensely helpful, although I wish somebody had actually told me about the Composite Pattern before I spent hours brainstorming about a way to accomplish the same thing on my own. (I was unaware that such a pattern existed at the time).
Feel free to review the code here and let me know your thoughts. It requires the ruby/gtk2 package (‘gem install gtk2’) to run, unless you decide to provide a different user interface.