Ruby inherits the philosophy of “there’s more than one way to do it,” or TMTOWTDI, from Perl. Of course, TMTOWTDI is worthless unless at least a handful of those ways can be written clearly not just for the author, but (perhaps more importantly) for future readers and editors. So, how do you make the best use of the many ways Ruby and Rails allow you to do things?
Before Ruby, the experience I had in dynamic, interpreted languages was with Python – a language with a totally opposite motto, “There should be one, and preferably only one, obvious way to do it.” As such, Ruby and Rails were somewhat of a shock. The first thing that comes to mind about loose dynamically-typed languages like Perl and Ruby is usually, “But it’ll be so easy to write bad code!”
And it is! As the author of Eloquent Ruby says, Ruby is a “language for grownups,” meaning that writing ugly, hard-to-maintain code is certainly possible, but this freedom allows for beautifully expressive, concise, and readable code.
Here are some guidelines that I and other Highgroovers use to make sure we achieve the latter whenever we can:
reducego a long way towards making code understandable in less time, as does the ubiquitous
#size. Which one you use depends on why the number of elements is needed, and what “sounds right” in the given section of code. Synonyms also make it easier to
primes(n)when it could just be
As an example of putting these into action, let’s write a method that turns a hypothetical Rails model into a hash of its reports; we need to convert every report to a hash, combine them, and return it. Here’s the straight imperative way:
It gets the job done, but it ain’t pretty; again, the
#merge! method seems out of place, since it implies a change in state and the only thing really changing is the return value, which should be expected.
Here’s a more functional way:
This is a bit shorter, and the fact that
@reports is the “subject” being method chained and then returned makes it clearer that the return value is just some transformation of the model’s reports. But let’s try to make it almost readable English:
This is much shorter, and translates easily to its purpose: Take this instance’s reports, map them to hashes, and reduce them by merging them together. It’s also nearly point-free since the
reduce blocks just take method symbols.
Of course, personal tastes will vary on whether this is the ideal way of representing this or that method. But for me, the guidelines above have led to generally clearer, more comprehensible code during review.
How do you make sure your code is clear and concise?