Updated October 2026: Clarified that access control is a Ruby (not Rails) feature, corrected how private and protected methods behave with subclasses and receivers, and added runnable examples plus modern idioms (
private def,private attr_reader,private_constant,private_class_method).
In Ruby on Rails, access control plays a crucial role in determining which methods or attributes of a class can be accessed from outside the class. Access control is a feature of the Ruby language itself (Rails just uses it), and Ruby provides three access control levels: private, protected, and public. Let’s explore each of these levels and their implications in Rails development.
I. Private Methods
- Definition: A private method can only be called without an explicit receiver, i.e. from inside an instance method of the object itself. Since Ruby 2.7,
self.my_private_methodis also allowed (a literalselfreceiver). Calling it on any other receiver, such asobj.my_private_methodfrom outside, raisesNoMethodError. - Subclasses: Private methods are inherited, so a subclass can call them (with an implicit receiver). Private in Ruby means “only the object itself”, not “only this class”.
- Usage: Private methods are typically used for internal implementation details that should not be exposed to external code.
- Example:
class Account def initialize(balance) @balance = balance end def summary "Balance: #{formatted_balance}" # implicit receiver: OK end def summary_with_self "Balance: #{self.formatted_balance}" # literal self: OK since Ruby 2.7 end private def formatted_balance format("%.2f", @balance) end end class SavingsAccount < Account def report "Savings -> #{formatted_balance}" # subclasses can call it too end end account = Account.new(100) account.summary # => "Balance: 100.00" SavingsAccount.new(42).report # => "Savings -> 42.00" account.formatted_balance # NoMethodError: private method 'formatted_balance' called for an instance of Account
II. Protected Methods
- Definition: A protected method can be called with an explicit receiver, but only from inside an instance method of the same class (or a subclass), and only when the receiver is also an instance of that class family. In other words, objects of the same kind can call each other’s protected methods; outside code cannot.
- Usage: Protected methods are typically used to compare or combine two instances of a class without making the underlying data public, e.g. in
==,<=>oreql?. - Example:
class Money include Comparable def initialize(cents) @cents = cents end def <=>(other) cents <=> other.cents # other.cents works: called from inside Money end protected attr_reader :cents end a = Money.new(500) b = Money.new(300) a > b # => true a.cents # NoMethodError: protected method 'cents' called for an instance of Money
If cents were private, other.cents would raise NoMethodError, because a private method cannot be called on another object.
III. Public Methods
- Definition: Public methods can be called from anywhere, both within and outside the class. They define the interface of the class and are accessible by any object. Methods are public by default (except
initialize, which is always private). - Usage: Public methods are used to expose functionality that should be accessible to external code.
- Example:
class MyClass def my_public_method # Implementation details end end
IV. Modern Idioms
Besides the private / protected sections above, you can mark visibility inline:
require "digest"
require "securerandom"
class User
ROLES = %w[admin member].freeze
private_constant :ROLES # User::ROLES raises NameError outside the class
attr_reader :name
private attr_reader :password # Ruby 3.0+: attr_* return the method names
def initialize(name, password)
@name = name
@password = password
end
def self.build(name)
new(name, generate_password)
end
private_class_method def self.generate_password # User.generate_password raises NoMethodError
SecureRandom.hex(8)
end
private def digest # `def` returns the method name, so this works
Digest::SHA256.hexdigest(password)
end
end
private def ...works becausedefreturns the method name as a symbol.private attr_reader :passwordworks since Ruby 3.0, whenattr_reader/attr_writer/attr_accessorstarted returning an array of the defined method names andprivate/protected/publicstarted accepting an array.private_constanthides a constant from outside the class.private_class_methodmakes a class method (def self.foo) private; the plainprivatekeyword does not affectdef self.methods.
V. When to Use Each Access Control Level
- Private: Use private methods for internal implementation details that should not be accessible outside the object. In Rails, controller helpers such as
set_postorpost_paramsshould be private so they are not exposed as actions. - Protected: Use protected methods when instances of the same class need to access each other’s internals (comparisons, equality, merging), while keeping them hidden from external code.
- Public: Use public methods to define the interface of the class and expose functionality to external code.
VI. Conclusion
Understanding Ruby’s access control levels—private, protected, and public—provides essential insights into defining the visibility and accessibility of methods within your Rails application. By appropriately applying these access control levels, you can maintain encapsulation, ensure code integrity, and improve the overall design of your Rails application.
Public comments are closed, but I love hearing from readers. Feel free to contact me with your thoughts.