I’ve seen a lot of Rails developers abuse Active Support’s
present? method in views and controllers. Most of the times what they want to check is that an object is not
present? checks if an item is not empty and I think the usage of
present? for this purpose is overkill.
For an idea of what I mean, here’s a snippet I found in an actual open-source project.
Let’s look at how
present? is implemented:
According to the documentation
An object is present if it’s not blank.
It returns a boolean and it calls
empty? under the hood looks like this:
The critical part here is
blank? checks the object on which it’s called whether it responds to the
empty?, to do this it has to check whether the object is not an instance
of some list of classes that implement
empty?. Let’s turn to IRB for an idea of how the list may look like:
Here we’re checking 17 objects whether or not they implement an
empty? method; this is not inside of
a Rails app. The list is much longer in a Rails app.
For classes that do implement
present? does more work than checking for
nil. And since at runtime, a lot of classes implement
empty? and you have the
respond_to? check, you can expect it to be slower. But more importantly, with that many different implementations on different classes, it’s hard to keep in mind what it does. But we can avoid it by only getting rid of it, backed by tests and making sure
nil is what we expect for the absence of an object. The same is true for when
present? is used in views.
params check above could instead be simply modified to avoid
Here’s a simple benchmark to back for some idea of how slower we can get our apps.
This is all pernickety. But recently I found a tweet that summarises the rest of my thoughts:
ActiveSupport's #present?/#blank? adds additional uncertainty on what type an object is.— Janko Marohnić (@jankomarohnic) January 24, 2020
If it's clear that an object can either be a truthy object (array, string, number) or nil, then just use `if object`. It's shorter and increases confidence.
Active Support is excellent and has so many useful methods that I wish were part of Ruby Core, but some of these methods come with some overhead cost, in most cases, we can prevent these. And seemingly minimal, watching out for little things like this may help with some speed gains.