Проблема с Module # method_defined? в том, что он может возвращать true для косвенных определений методов (других включенных модулей, унаследованных методов, если module является классом), а также для закрытых методов. Это означает, что вы (и, что важно, любой, кто касается кода), должен быть очень осторожным в том, что вы делаете с этим модулем.
Таким образом, вы могли бы использовать этот подход, но вам необходимо четко указать будущим сопровождающим, что любой метод в модуле автоматически является внешним интерфейсом. Лично я бы выбрал что-то более явное, например, простой белый список разрешенных имен методов API, например:
require 'set'
module CoolModule
ALLOWED_API_METHODS = Set[
:foo,
:bar,
...
]
def self.api_allowed? meth
ALLOWED_API_METHODS.include? meth.to_sym
end
end
Да, вы должны вести список, но это не так неприглядно, это документация с явным интерфейсом; и означает, что вы не получите ничего от более позднего кодера, решившего, что ему нужно добавить некоторые служебные методы в модуль для удобства, и, таким образом, случайно экспортировать их в ваш внешний API.
В качестве альтернативы единственному списку вы можете использовать метод define_for_api и использовать его вместо def для объявления методов интерфейса API
module CoolModule
@registered_api_methods = Set.new
def self.define_for_api meth, &block
define method meth, &block
@registered_api_methods << meth
end
def self.api_allowed? meth
@registered_api_methods.include? meth.to_sym
end
def api_dispatch meth, *args
raise ArgumentError unless self.class.api_allowed? meth
send(meth *args)
end
define_for_api :foo do |*args|
do_something_common
...
end
define_for_api :bar do
do_something_common
...
end
# this one is just ordinary method internal to module
private
def do_something_common
end
end