Rails 6 delegate to, with private: true option


Private methods are great way to restrict what methods of a class can be invoked from the outside. These provide a great degree of control when designing classes.

One common pattern we like to follow is to make every other method except perform as private in our service classes.

Example service class:

class UserNotificationService 
  def initialize(user:)
    @user = user
  end
  
  def perform
    send_text_notification
    send_email_notification
  end
  
  private
  
  attr_reader :user
  
  def send_text_notification
    # Service to send text notification 
  end
  
  def send_email_notification
    # Service to send email notification 
  end 
end

Note, all methods other then perform including the attr_reader are private as we don’t plan to access them outside this class.

Until now there was no convenient way of making methods created using the delegate to syntax as private.

Consider a goal of creating a service with only one public method(perform), you would expect the following code to do the trick but it does not work:

class UserNotificationService
  # ... 
  def perform
    # ...
  end
  # ...
  private
  delegate :name, :email, :ssn, to: :user
  # ... 
end

Only option you had was to use the following syntax which is not the most elegant and conventional:

class UserNotificationService
  # ... 
  def perform
    # ...
  end
  # ...
  private *delegate(:name, :email, :ssn, to: :user)
  # ... 
end

Rails 6 adds option to pass private: true to delegate. Now, we can achieve our above goal using the following:

class UserNotificationService 
  # ...
  def perform
    # ...
  end
  # ...
  delegate :name, :email, :ssn, to: :user, private: true
  # ... 
end

This change is backwards compatible and your code with private *delegate... will still work.

Note: protected option is not supported yet, there is plan to add that in the future.