Method Visibility and Modules

I was doing some debugging on a Rails application the other day, and I noticed the following in script/console:

>> (MonkeysController.new.methods - ActionController::Base.new.methods).sort
=> ["check_for_buildout", "filter_parameters", "index", "inform_hoptoad",
    "notify_hoptoad", "rescue_action_in_public_with_hoptoad",
    "rescue_action_in_public_without_hoptoad"]

My MonkeysController (name changed to protect the innocent) has only one action, index—but it’s got all these other things publicly available. Clearly, several of them are coming from the Hoptoad plugin, and one (check_for_buildout) is from my own IntegratedBuildouts plugin… I’m less concerned about where these methods came from, however, than I am about why they show up in the public interface of my controller. Ideally, the only methods a controller should advertise are its actions; anything else is subject to abuse (especially if you’ve left the heinous default route in your app).

Obviously, these methods are being added by the inclusion of some modules (include HoptoadNotifier::Catcher, for instance), so I thought a bit and realized that I’ve never really looked for a way to add visibility-limited methods (protected and private) to a class from a module.

So I tried the following:

module Primate
  def m1; end

  protected
  def m2; end

  private
  def m3; end
end

class Monkey
  include Primate

  def c1; end

  protected
  def c2; end

  private
  def c3; end
end

Lo and behold, it works!

(Monkey.new.public_methods - Object.new.public_methods).sort        # [c1, m1]
(Monkey.new.protected_methods - Object.new.protected_methods).sort  # [c2, m2]
(Monkey.new.private_methods - Object.new.private_methods).sort      # [c3, m3]

So, for all you plugin and library writers out there—add the correct visibility markers to your modules! It’s so easy (and obvious) that there’s no excuse not to.

Comments are closed.