This blog is part of our Rails 6 series. Rails 6.0.0.rc1 was recently released.

Rails 6 adds :private option to delegate method. After this addition, we can delegate methods in the private scope.

Let’s checkout how it works.

Rails 6.0.0.beta2

Let’s create two models named as Address and Order. Let’s also delegate validate_state method in Order to Address.

class Address < ApplicationRecord
  validates :first_name, :last_name, :state, presence: true

  DELIVERABLE_STATES = ['New York']

  def validate_state
    unless DELIVERABLE_STATES.include?(state)
      errors.add(:state, :invalid)
    end
  end
end

class Order < ApplicationRecord
  belongs_to :address

  delegate :validate_state, to: :address
end

>> Order.first
SELECT "orders".* FROM "orders"
ORDER BY "orders"."id" ASC LIMIT $1  [["LIMIT", 1]]

=> #<Order id: 1, amount: 0.1e2, address_id: 1, created_at: "2019-03-21 10:02:58", updated_at: "2019-03-21 10:17:44">

>> Address.first
SELECT "addresses".* FROM "addresses"
ORDER BY "addresses"."id" ASC LIMIT $1  [["LIMIT", 1]]

=> #<Address id: 1, first_name: "Amit", last_name: "Choudhary", state: "California", created_at: "2019-03-21 10:02:47", updated_at: "2019-03-21 10:02:47">

>> Order.first.validate_state
SELECT "orders".* FROM "orders"
ORDER BY "orders"."id" ASC LIMIT $1  [["LIMIT", 1]]

SELECT "addresses".* FROM "addresses"
WHERE "addresses"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]

=> ["is invalid"]

Now, let’s add private: true to the delegation.

class Order < ApplicationRecord
  belongs_to :address

  delegate :validate_state, to: :address, private: true
end

>> Order.first.validate_state
SELECT "orders".* FROM "orders"
ORDER BY "orders"."id" ASC LIMIT $1  [["LIMIT", 1]]

=> Traceback (most recent call last):
        1: from (irb):7
NoMethodError (private method 'validate_state' called for #<Order:0x00007fb9d72fc1f8>
Did you mean?  validate)

As we can see, Rails now raises an exception of private method called if private option is set with delegate method.

Here is the relevant pull request.