Class: Woods::RubyAnalyzer::TraceEnricher

Inherits:
Object
  • Object
show all
Defined in:
lib/woods/ruby_analyzer/trace_enricher.rb

Overview

Enriches ExtractedUnit objects with runtime trace data.

Two modes:

  • Recording: wraps a block with TracePoint to capture method calls

  • Merging: enriches existing units with previously collected trace data

Examples:

Recording

trace_data = TraceEnricher.record { MyApp.run }

Merging

TraceEnricher.merge(units: units, trace_data: trace_data)

Class Method Summary collapse

Class Method Details

.merge(units:, trace_data:) ⇒ Array<ExtractedUnit>

Merge trace data into existing units.

Mutates each matching unit’s metadata by adding a :trace key with call count, callers, and return types.

Parameters:

  • units (Array<ExtractedUnit>)

    Units to enrich

  • trace_data (Array<Hash>)

    Trace events (from recording or JSON fixture)

Returns:



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/woods/ruby_analyzer/trace_enricher.rb', line 52

def self.merge(units:, trace_data:)
  return units if trace_data.nil? || trace_data.empty?

  # Index traces by class_name + method_name
  grouped = group_traces(trace_data)

  units.each do |unit|
    class_name, method_name = parse_identifier(unit.identifier)
    next unless class_name && method_name

    key = "#{class_name}##{method_name}"
    next unless grouped.key?(key)

    traces = grouped[key]

    calls = traces.select { |t| fetch_key(t, :event) == 'call' }
    returns = traces.select { |t| fetch_key(t, :event) == 'return' }

    callers = calls.filter_map do |t|
      caller_class = fetch_key(t, :caller_class)
      caller_method = fetch_key(t, :caller_method)
      next unless caller_class

      { 'caller_class' => caller_class, 'caller_method' => caller_method }
    end

    return_types = returns.filter_map do |t|
      fetch_key(t, :return_class)
    end.uniq

    unit.[:trace] = {
      call_count: calls.size,
      callers: callers,
      return_types: return_types
    }
  end
end

.record { ... } ⇒ Array<Hash>

Record method calls during block execution using TracePoint.

Yields:

  • Block to trace

Returns:

  • (Array<Hash>)

    Collected trace events



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/woods/ruby_analyzer/trace_enricher.rb', line 24

def self.record(&block)
  traces = []

  trace = TracePoint.new(:call, :return) do |tp|
    traces << {
      class_name: tp.defined_class&.name || tp.defined_class.to_s,
      method_name: tp.method_id.to_s,
      event: tp.event.to_s,
      path: tp.path,
      line: tp.lineno,
      caller_class: extract_caller_class(tp),
      caller_method: extract_caller_method(tp),
      return_class: tp.event == :return ? safe_return_class(tp) : nil
    }
  end

  trace.enable(&block)
  traces
end