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.