Make a if condition more simple with a scope

Ruby on Rails / Refactoring

Quite often it’s easy to simplify if condition by using simple scopes. Let’s demonstrate it in sample classes:

class User < ActiveRecord::Base
  has_many :posts
end
class NotificationService
  def initialize(company:)
    @company = company
  end

  def notify_user(email:)
    user = User.find_by(email: email)

    if user.present? && user.posts.any?
      Mailer.new_posts_reminder(user.id).deliver
    end
  end

  private
  attr_reader :company
end

We have User model and each user can have many posts. NotificationService is used to remind users with posts about writing new posts by sending a simple e-mail message to them.

We don’t have to check users.posts.any? because it can be checked on a database level. Let’s create scope for users with posts:

class User < ActiveRecord::Base
  has_many :posts

  scope :with_posts, -> { joins(:posts) }
end

You can also define scope as a class method, is up to you. If you don’t know which way you should choose check article about it. Now we can use updated database query and refactor our condition:

class NotificationService
  def initialize(company:)
    @company = company
  end

  def notify_user(email:)
    user = User.with_posts.find_by(email: email)

    user && Mailer.new_posts_reminder(user.id).deliver
  end

  private
  attr_reader :company
end

Now our code is simpler and shorter. Also, the query itself is self-explanatory. Small refactoring, big difference. This solution is also nice because you may want to change the code responsible for determining if a user has posts in the future. With the old solution you would have to change all places where you are checking if the user has posts but with the new solution, all you need to do is to change the scope in the User model.