Я написал больше унифицированного решения, он может узнать область видимости любого вызывающего абонента.
Моя основная идея состояла в том, чтобы определить 2 вещи:
- объекта вызывающего абонента (
self
связывание вызывающего абонента) имя
- метод объекта вызывающего
Я использовал binding_of_caller камень для достижения этой цели.
class Foo
class << self
def visibility_scope
binding_of_caller = binding.of_caller(1)
caller_method = binding_of_caller.eval('__method__')
caller_object = binding_of_caller.eval('self')
# It's asking if caller is a module, since Class is inherited from Module
if caller_object.is_a?(Module)
return visibility_scope_for(caller_object.singleton_class, caller_method)
end
# First we should check object.singleton_class, since methods from there are called before
# class instance methods from object.class
visibility = visibility_scope_for(caller_object.singleton_class, caller_method)
return visibility if visibility
# Then we check instance methods, that are stored in object.class
visibility = visibility_scope_for(caller_object.class, caller_method)
return visibility if visibility
fail 'Visibility is undefined'
end
private
def visibility_scope_for(object, method_name)
%w(public protected private).each do |scope|
if object.send("#{scope}_method_defined?", method_name)
return scope
end
end
nil
end
end
end
Добавьте некоторые методы испытаний:
class Foo
class << self
# This method is private in instance and public in class
def twin_method
visibility_scope
end
def class_public_method
visibility_scope
end
protected
def class_protected_method
visibility_scope
end
private
def class_private_method
visibility_scope
end
end
def instance_public_method
self.class.visibility_scope
end
protected
def instance_protected_method
self.class.visibility_scope
end
private
def twin_method
self.class.visibility_scope
end
def instance_private_method
self.class.visibility_scope
end
end
# singleton methods
foo = Foo.new
foo.singleton_class.class_eval do
def public_singleton_method
Foo.visibility_scope
end
protected
def protected_singleton_method
Foo.visibility_scope
end
private
def private_singleton_method
Foo.visibility_scope
end
end
class Bar
class << self
private
def class_private_method
Foo.visibility_scope
end
end
protected
def instance_protected_method
Foo.visibility_scope
end
end
Тест
# check ordinary method
Foo.class_public_method
=> "public"
Foo.send(:class_protected_method)
=> "protected"
Foo.send(:class_private_method)
=> "private"
Foo.new.instance_public_method
=> "public"
Foo.new.send(:instance_protected_method)
=> "protected"
Foo.new.send(:instance_private_method)
=> "private"
# check class and instance methods with the same name
Foo.twin_method
=> "public"
Foo.new.send(:twin_method)
=> "private"
# check methods from different objects
Bar.send(:class_private_method)
=> "private"
Bar.new.send(:instance_protected_method)
=> "protected"
# check singleton methods
foo.public_singleton_method
=> "public"
foo.send(:protected_singleton_method)
=> "protected"
foo.send(:private_singleton_method)
=> "private"
Наконец-то я нашел способ сделать это и изменил свой ответ –