2

ドキュメントの目的で 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 はありません。これが理にかなっていることを願っています!

4

1 に答える 1

1

そのため、「使用済み」が何を意味するかに関して、ここにはいくつかのあいまいさがあります。最後に b.rb (これも使用されます) が呼び出されるため、明らかに d が使用されD.newます。「require プロセス中以外に、そのファイルからコードが実行された」ことを意味するために「使用された」と警告する場合、次のコードは、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​​gems ファイル) を片付けるだけです。

これはテスト例では実際には機能しません: B.new が実行されるとき、b.rb からのコード行は実際には実行されません。ただし、B (および C、D など) に初期化メソッド (または呼び出されるコード行) がある場合は、目的の結果が得られるはずです。それはかなり単純化されたものであり、あらゆる種類のものにだまされる可能性があります. 特に、(たとえば) B でメソッドを呼び出したが、そのメソッドの実装が b.rb にない場合 (たとえば、attr_accessor で定義されたアクセサー)、b.rb はログに記録されません。

call イベントをより適切に使用できるかもしれませんが、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 を返します。実際に実行されるコードについてしか認識していないという基本的な制限は依然としてあります。

于 2013-04-07T10:32:01.510 に答える