DEV Community

Augusts Bautra
Augusts Bautra

Posted on

Identify and clean up .present? calls on collections

Over the holidays I experimented with an old question I had. "How to find the places in codebase where .present? or .any? is called on a collection?". The answer is important because calling those methods instead of .exists? forces the records in the collection to get loaded from DB into Ruby memory which, depending on the size of the collection, can be a huge performance hit.

After looking into Rails' source, I came up with a monkeypatch that only touches method definitions on ActiveRecord collections. The module that needs prepending is ActiveRecord::Relation.

# in config/application.rb

module CollectionQueryingSafety
  def present?
    raise_warning_about_performance_problems(__method__)
  end

  def blank?
    raise_warning_about_performance_problems(__method__)
  end

  private

    def raise_warning_about_performance_problems(method_name)
      raise(
        ".#{ method_name } called on an AR collection. "\
        "See if .exists? can be used instead."
      )
    end
end

unless Rails.env.production?
  ActiveRecord::Relation.prepend(CollectionQueryingSafety)
end
Enter fullscreen mode Exit fullscreen mode

This snippet will raise errors in non-production environments. Given decent coverage, running your test suite should show most places where suboptimal calls occur.

For example, there could be offensive code like this:

# bad
@my_model.some_has_many_association.present?

# good
@my_model.some_has_many_association.exists?
Enter fullscreen mode Exit fullscreen mode

Now, besides .present? and .blank? there are other array methods - .any?, .none?, and .many? - which in most cases should not be called on collections.
Unfortunately, since these methods accept blocks, I did not feel confident overriding them outright, but in the cases where no block is passed to .any?, .exists? is a much faster and correct alternative.

Top comments (0)