When to use private and protected?

Any style guidelines on when to use private and protected in a large Rails application?

I find that I rarely use these keywords.

Also, I maybe wrong, but the use of Concerns with private/protected did not seem to be as simple as in a regular class.

Check out the Learn appā€™s source code. Youā€™ll see we use private constantly.

Almost all my classes have private methods. I use that to keep my public API small while still breaking up my classes into many small methods.

Agreed with @benorenstein on making frequent and aggressive use of private. I donā€™t find occasion to use protected. Iā€™m sure a computer science purist could point out places where I should be using protected methods, but I seem to be served fine between just public and private.

Basically every method should start out as private, and then be promoted to public as needed. As for protected, itā€™s significantly less useful in Ruby than other languages, because private isnā€™t actually ā€œprivateā€.

In most languages, private would restrict access outside of where itā€™s defined. The notable difference between this and what Ruby does is subclasses. Usually protected means itā€™s not part of the public API of an object, but is part of the API available to subclasses.

In Ruby, private means that the method cannot be called with a subject (ironically including self), so you must use the implicit self to call the method. Protected allows you to call the method with a target, but only from within the same class. The only time Iā€™ve ever found that this is useful is for methods used in comparison. So for example:

class Foo
  def initialize(items)
    @items = items
  end

  private

  attr_reader :items
end

However, if I wanted to add an == method, itā€™d probably look like this:

def ==(other)
  items == other.items
end

However, items now needs to be protected for me to call it from inside of ==

Hi Sean. Why would you have a private attr_reader on :items rather than simply referring to @items throughout the class?

Also, what is the the meaning of private within a Rails concern module?

Hey Justin,

Sorry for the delayed response. As a rule of thumb, I never access ivars outside of constructors, getters, and setters. By following this rule, it becomes trivial to move a local variable to an instance variable, or move a property from one object to another (adding a delegator), without having to change unrelated code. For example, if my code looked like this:

class Foo
  def initialize(items)
    @items = items
  end

  def fooable_items
    @items.filter(&:fooable?)
  end
end

and I wanted to refactor it to this:

class Foo
  def initialize(bar)
    @bar = bar
  end

  def fooable_items
    bar.items.filter(&:fooable?)
  end
end

class Bar
  def initialize(items)
    @items = items
  end

  attr_reader :items
end

My fooable_items method had to change as a result of me changing where @items lives. However, if Iā€™d written it like this:

class Foo
  def initialize(items)
    @items = items
  end

  def fooable_items
    items.select(&:fooable?)
  end

  private

  attr_reader :items
end

then once I move items to Bar, I only have to change the attr_reader, instead of all of my code.

class Foo
  extend Forwardable

  def initialize(bar)
    @bar = bar
  end

  def fooable_items
    items.select(&:fooable?)
  end

  private

  attr_reader :bar 
  delegate items: :bar 
end
1 Like

Also, the meaning of private in a module is the same as in anywhere else. It canā€™t be called on a target, so it must be called with the implicit form of self. That means that private methods in a module will still be visible to classes that mix in the module.