2019-08-02 09:02 — By Erik van Eykelen
While reading the excellent Ruby Style Guide I came across best practices I often forget while writing Ruby. In order to plasticize my brain, and perhaps help you prevent making the same mistakes, I wrote down ten of them. I’ll probably create a follow-up post with additional mistakes.
nil
checksDon’t do explicit non-
nil
checks unless you’re dealing with boolean values – style guide
There’s no need to call !obj.nil?
or test for nil
inequality unless you want to check whether a Boolean value is really not nil
i.e. whether it has been set to true
or false
.
I suspect I sometimes add a useless nil?
to make it explicit to the reader[1]
that I really mean to test for nil
Ref
[1]
: I always write code thinking John Carmack will review my code and yell at me – I swear this helps.
For Rails there are additional methods that test for nil
such as blank?
which kind of softens the “badness” of making explicit (non-)nil
checks since they test more than just nil
. If you look at blank.rb you’ll see that Rails adds blank?
to 9 classes. Frankly I’ve always had a slight dislike of blank?
because it kind of says “I don’t really know what kind of state this variable is in so I test for a broad set of conditions”.
__send__
Prefer
__send__
oversend
, assend
may overlap with existing methods – style guide
Since send
is often used as a method name it’s likely to override Ruby’s BasicObject send
. If you use __send__
then you take away the ambiguity.
method_missing
Avoid using
method_missing
for metaprogramming because backtraces become messy, the behavior is not listed in#methods
, and misspelled method calls might silently work – style guide
The Ruby Style Guide article explains how to use method_missing
if there’s really no alternative.
Since Ruby 1.9 there is Proc#curry
which can be used in cases where you would otherwise have used method_missing
. This article gives examples of curry
. The article also explains the difference between partial application and currying.
Use non-capturing groups when you don’t use the captured result – style guide
The article gives two examples:
# bad
/(first|second)/
# good
/(?:first|second)/
A good example of using non-capturing groups is extracting the domain name from a URL.
For instance this regex (https?:\/\/)([^\/\?\&]+)
yields two match groups:
1. https://
2. example.com
When you turn the first group into a non-capturing group via (?:https?:\/\/)([^\/\?\&]+)
it only yields what you’re looking for:
1. example.com
Granted, this tip is not Ruby-specific but still it’s something I tend to forget.
Prefer
Time.now
overTime.new
when retrieving the current system time – style guide
and
Don’t use
DateTime
unless you need to account for historical calendar reform - and if you do, explicitly specify the start argument to clearly state your intentions.
Although the style guide itself doesn’t provide the reason for choosing Time
over DateTime
, this Gist does.
When using named format string tokens, favor
%<name>s
over%{name}
because it encodes information about the type of the value – style guide
The article gives two examples:
# bad
format('Hello, %{name}', name: 'John')
# good
format('Hello, %<name>s', name: 'John')
This left me scratching my head for a second because I didn’t know about the “good” way, but it makes total sense now because just like sprintf the s
in %<name>s
indicates you’re dealing with a string. Passing an integer will throw an ArgumentError
.
gsub
Don’t use
String#gsub
in scenarios in which you can use a faster and more specialized alternative – style guide
You have three choices: tr
, sub
, and gsub
:
tr
tr
is the most bare bones one. It stands for translate characters
, and is named after the tr
command in Unix.
It can do things like:
"hello".tr('el', 'ip') #=> "hippo"
and
"hello".tr('a-y', 'b-z') #=> "ifmmp"
sub
Ruby doc states: [..] Returns a copy of str with the first occurrence of pattern replaced by the second argument
.
It can do things like this:
hello".sub(/[aeiou]/, '*') #=> "h*llo"
Nice touch: sub()
supports blocks:
"hello".sub(/./) { |s| s.ord.to_s + ' ' } #=> "104 ello"
gsub
This is the Swiss Army knife of string replacement. The docs state: Returns a copy of str with all occurrences of pattern substituted for the second argument
.
The Ruby Style Guide makes the case for not abusing gsub because 1) tr
and sub
can be substantially faster, and 2) it’s a good habit to use the smallest, fastest, and most specialized tool available.
I ran a naive benchmark on a 10k line text file to see the difference between the three methods. The sub
methods wins (of course, it only substitutes a single character per line in this benchmark), then comes tr
, and then gsub
.
to_s
Don’t use
Object#to_s
on interpolated objects. It’s invoked on them automatically – style guide
I find myself making this mistake in my quest to be specific (again, to the reader of my code) but it’s unnecessary.
Prefer single-quoted strings when you don’t need string interpolation or special symbols such as
\t
,\n
,'
, etc – style guide
This is one of the reasons why you should be running Rubocop early and often (and automated) in your projects because repairing this formatting flaw late in the game is a lot of work.
The reason is, I guess, two-fold: 1) you make it clear you’re not intending to do string interpolation, and 2) you make it slightly easier for the parser to perform an optimization (it knows it doesn’t have the deal with interpolation).
Prefer string interpolation and string formatting instead of string concatenation – style guide
The examples speak for themselves. The main reason why email_with_name = "#{user.name} <#{user.email}>"
is better than email_with_name = user.name + ' <' + user.email + '>'
is that the latter creates 3 strings and the former just 1.
In the next installment I will discuss the next 10 mistakes I often make.
Edit: I’ve posted part 2.