这是一个代码示例:
class Foo
def self.create_method
def example_method
"foo"
end
end
private
create_method
end
Foo.public_instance_methods(false) # => [:example_method]
是否可以检测到从类create_method
私有区调用类方法Foo
?
在上面的示例中,该信息可用于使example_method
公开或私有,具体取决于调用create_method
的位置。
答案 0 :(得分:2)
虽然有点hacky,但有可能:
class Foo
def self.create_method
define_method :example_method do
visibility = case caller(0).first[/block in (\w+)'/, 1].to_sym
when ->(m) { Foo.private_methods.include? m }
:private
when ->(m) { Foo.protected_methods.include? m }
:protected
when ->(m) { Foo.public_methods.include? m }
:public
else :unknown
end
puts "Visibility: #{visibility}"
end
end
private_class_method :create_method
end
Foo.send :create_method
Foo.new.example_method
#⇒ Visibility: private
在这里,我们通过case
块检查调用者的可见性。请注意,您不能简单地将案例移动到另一个辅助方法而不做任何修改,因为它依赖于caller
。希望它有所帮助。
答案 1 :(得分:2)
我写了更统一的解决方案,它能够找出任何来电者的可见范围。
我的主要想法是确定两件事:
self
绑定)我使用binding_of_caller gem来实现这一目标。
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"
答案 2 :(得分:2)
为了确定,我用ruby代码仔细检查过,但我可能会遗漏一些东西。我找不到任何方法从类中获取当前声明的可见范围。如果在没有方法名参数的情况下声明可见性方法(私有,公共或受保护),则会将当前范围设置为类范围声明,除非我们在后续语句中声明其他可见范围。
您可以查看此代码以进一步调查 - https://github.com/ruby/ruby/blob/c5c5e96643fd674cc44bf6c4f6edd965aa317c9e/vm_method.c#L1386
我找不到任何直接引用cref-&gt; visi的方法,您可以查看此代码作为参考 - https://github.com/ruby/ruby/blob/48cb7391190612c77375f924c1e202178f09f559/eval_intern.h#L236
以下是Stackoverflow上最早的帖子之一的类似答案 - https://stackoverflow.com/a/28055622/390132
所以这是简化的解决方案,我提出了 -
class Foo
def self.create_method
def example_method
"foo"
end
visibility = if self.private_method_defined? :test_method
:private
elsif self.public_method_defined? :test_method
:public
elsif self.protected_method_defined? :test_method
:protected
end
send visibility, :example_method
end
private
# As Ruby doesn't associate visibility flag along with the caller
# reference rather with the actual method which are subsequently
# declared. So we can take that as an advantage and create a test method
# and later from :create_method scope check that particular method
# visibility and change the generated method visibility accordingly.
# Create a test method to verify the actual visibility when calling 'create_method' method
def test_method; end
create_method
end
puts "Public methods: #{Foo.public_instance_methods(false)}"
# []
puts "Private methods: #{Foo.private_instance_methods(false)}"
# [:test_method, :example_method]
puts "Protected methods: #{Foo.protected_instance_methods(false)}"
# []
答案 3 :(得分:0)
尝试https://github.com/ruby-prof/ruby-prof
有特色:
调用树配置文件 - 以适合的calltree格式输出结果 用于KCacheGrind分析工具。
这可能会对你有所帮助