I’ve seen a lot Rails developers abuse ActiveSuport’s present? method in views and controllers. Most of the times what they actually want to check is that an object is not nil. Rails’ 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 I’d prefer not to name.

if params[:reply_to].present?
  @comment.reply_to = params[:reply_to].to_i
  ...
end

Let’s look at how present? is implemented:

# File activesupport/lib/active_support/core_ext/object/blank.rb, line 26
def present?
  !blank?
end

According to the documentation…

An object is present if it’s not blank.

This returns a boolean; it calls blank? and blank? under the hood looks like this:

 def blank?
    respond_to?(:empty?) ? !!empty? : !self
  end

The critical part here is respond_to?(:empty?) where 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:

ObjectSpace.each_object(Class).select { |klass| klass.instance_methods.include? :empty? }

 # => [Thread::SizedQueue, Thread::Queue, #<Class:FileTest>, #<Class:#<Object:0x00007fdc4b0905b8>>, Hash, Array, Warning::buffer, Symbol, String, SortedSet, Set, Gem::RequestSet::Lockfile::Tokenizer, Gem::Resolver::RequirementList, #<Class:#<Object:0x00007fdc4d0b5280>>, #<Class:#<Hash:0x00007fdc4d0e7230>>, #<Class:#<String:0x00007fdc4d0e6c40>>, #<Class:#<String:0x00007fdc4d0e6cb8>>]

Here we’re checking 17 objects whether or not they implement an empty? method! This is not inside of a Rails app. But this one is!

For classes that do implement empty?, 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 simply 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.

The params check above could instead be simply modified to avoid present?

if params[:reply_to]
  @comment.reply_to = params[:reply_to].to_i
  ...
end

Here’s a simple benchmark to back for some idea of how slower we can get our apps.

require 'active_support/all'
require 'benchmark/ips'

params = { reply_to: "Emmanuel" }

Benchmark.ips do |x|
  x.report("prezent") { 10000.times { params.present? }}
  x.report("abzent") { 10000.times { params }}

  x.compare!
end

Warming up --------------------------------------
             prezent   128.000  i/100ms
              abzent   251.000  i/100ms
Calculating -------------------------------------
             prezent      1.273k (± 1.5%) i/s -      6.400k in   5.028756s
              abzent      2.520k (± 1.7%) i/s -     12.801k in   5.081013s

Comparison:
              abzent:     2520.1 i/s
             prezent:     1273.0 i/s - 1.98x  slower

This is all pernickety. But recently I found a tweet that summarises the rest of my thoughts:

ActiveSupport is great 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.

PS:

In search of a remote Ruby/Rails job. Hire me.