我想检测Ruby文件直接引用的所有文件以用于文档目的。读取基本需求列表并不完整,因为有些文件是可传递导入的,而其他文件是导入但从未使用过的。例如:
a.rb:
require 'b'
require 'e'
class A; end
B.new; C.new
b.rb:
require 'c'
require 'd'
class B; end
C.new; D.new
c.rb:
class C; end
(d.rb and e.rb are just like c.rb)
然后我要为a.rb
获取的列表是b.rb, c.rb
。没有D或E,因为它们没有被直接引用。希望这是有道理的!
答案 0 :(得分:1)
所以这里有一些关于使用''手段。显然使用了d,因为b.rb(也是使用的)最后调用D.new
。如果我们发出警告,请使用'表示"代码是从该文件执行的,而不是在需求过程中#34;然后下面的代码是关闭的,因为我可以得到ruby 1.9.3
require 'set'
def analyze(filename)
require_depth = 0
files = Set.new
set_trace_func( lambda do |event, file, line, id, binding, classname|
case event
when 'call'then require_depth += 1 if id == :require && classname == Kernel
when 'return' then require_depth -= 1 if id == :require && classname == Kernel
when 'line'
files << file if require_depth == 0
end
end)
load filename
set_trace_func nil
files.reject {|f| f == __FILE__ || f =~ %r{/lib/ruby/site_ruby}}
end
您可以通过运行analyse 'a.rb'
来使用它(假设所涉及的所有文件都在加载路径上)。这样做是使用ruby的set_trace_func来聆听正在发生的事情。第一部分是粗略尝试忽略调用require期间发生的所有事情。然后我们累积执行ruby的每一行的文件名。最后一行只是清理垃圾(例如补丁所需的rubygems文件)。
这对于测试示例实际上并不起作用:当B.new运行时,实际上没有执行来自b.rb的代码行。但是,如果B(和C,D等)具有初始化方法(或某些被调用的代码行),那么您应该得到所需的结果。这是非常简单的东西,可能被各种各样的东西所欺骗。特别是如果你在(例如)B上调用一个方法,但该方法的实现不在b.rb中(例如用attr_accessor定义的访问器),那么b.rb不会被记录
您可以更好地使用通话事件,但我不认为使用set_trace_func可以完成更多工作。
如果您使用的是ruby 2.0,则可以使用TracePoint替换set_trace_func
。它具有稍微不同的语义,特别是当我们跟踪方法调用时,它更容易获得它所调用的类
require 'set'
def analyze(filename)
require_depth = 0
files = Set.new
classes_to_files = {}
trace = TracePoint.new(:call, :line, :return, :c_call, :class) do |tp|
case tp.event
when :class
classes_to_files[tp.self] = tp.path
when :call, :c_call then
if tp.method_id == :require && tp.defined_class == Kernel
require_depth += 1
else
if require_depth == 0
if path = classes_to_files[tp.self] || classes_to_files[tp.self.class]
files << path
end
end
end
when :return then require_depth -= 1 if tp.method_id == :require && tp.defined_class == Kernel
when :line
if require_depth == 0
files << tp.path
end
end
end
trace.enable
load filename
trace.disable
files.reject {|f| f == __FILE__ || f =~ %r{/lib/ruby/site_ruby}}
end
确实为测试示例返回a,b,c。它仍然受到基本的限制,它只知道实际执行的代码。