Class: GunDog::TraceStack

Inherits:
Array
  • Object
show all
Defined in:
lib/gun_dog/trace_stack.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(klass) ⇒ TraceStack

Returns a new instance of TraceStack.



28
29
30
31
32
# File 'lib/gun_dog/trace_stack.rb', line 28

def initialize(klass)
  @klass = klass
  @traced_klass_entry_points = Array.new
  @collaborating_classes = Set.new
end

Instance Attribute Details

#collaborating_classesObject (readonly)

Returns the value of attribute collaborating_classes.



3
4
5
# File 'lib/gun_dog/trace_stack.rb', line 3

def collaborating_classes
  @collaborating_classes
end

#klassObject (readonly)

Returns the value of attribute klass.



3
4
5
# File 'lib/gun_dog/trace_stack.rb', line 3

def klass
  @klass
end

Class Method Details

.from_array(klass:, stack:) ⇒ Object



15
16
17
18
19
20
21
22
# File 'lib/gun_dog/trace_stack.rb', line 15

def self.from_array(klass:, stack: )
  ts = new(klass)
  stack.each do |f|
    ts << GunDog::MethodOwnerStackFrame.new(Utilities.get_class(f['klass']), f['method_name'])
  end

  ts
end

.from_json(json) ⇒ Object



5
6
7
8
9
10
11
12
13
# File 'lib/gun_dog/trace_stack.rb', line 5

def self.from_json(json)
  ts = new(json['klass'])

  ts.instance_eval do
    @collaborating_classes = Set.new(json['collaborating_classes'].map { |k| Utilities.get_class(k) })
  end

  ts
end

Instance Method Details

#<<(frame) ⇒ Object



81
82
83
84
85
# File 'lib/gun_dog/trace_stack.rb', line 81

def <<(frame)
  collaborating_classes.add(frame.klass) if preceded_by_traced_klass?
  @traced_klass_entry_points << length if frame_owned_by_traced_klass?(frame)
  super(frame)
end

#as_jsonObject



91
92
93
# File 'lib/gun_dog/trace_stack.rb', line 91

def as_json
  map(&:as_json)
end

#call_stackObject



68
69
70
71
# File 'lib/gun_dog/trace_stack.rb', line 68

def call_stack
  # the stack excluding the gundog sentinel (element zero) and ourselves (element -1)
  self.slice(1 .. -2) || TraceStack.new(klass)
end

#classes_in_stackObject



24
25
26
# File 'lib/gun_dog/trace_stack.rb', line 24

def classes_in_stack
  self.group_by(&:klass).keys.to_set
end

#cyclical_stack?Boolean

Returns:

  • (Boolean)


41
42
43
44
45
46
# File 'lib/gun_dog/trace_stack.rb', line 41

def cyclical_stack?
  # if the set of classes contained in the trace since we entered our own
  # class is a superset of the set of our class then it is a cyclical stack
  # (ie, it contains calls both within and without of the class)
  since_first_klass_entry.classes_in_stack > self_set
end

#dynamic_stack?Boolean

Returns:

  • (Boolean)


48
49
50
51
# File 'lib/gun_dog/trace_stack.rb', line 48

def dynamic_stack?
  methods = since_first_klass_entry.map { |f| f.method_name.to_s }
  methods.include?('method_missing') || methods.include?('send') || methods.include?('__send__')
end

#frame_owned_by_traced_klass?(frame) ⇒ Boolean

Returns:

  • (Boolean)


87
88
89
# File 'lib/gun_dog/trace_stack.rb', line 87

def frame_owned_by_traced_klass?(frame)
  frame.klass == klass || frame.klass.singleton_class == klass.singleton_class
end

#internal_stack?Boolean

Returns:

  • (Boolean)


34
35
36
37
38
39
# File 'lib/gun_dog/trace_stack.rb', line 34

def internal_stack?
  # if the set of classes contained in the trace since we entered our own
  # class is equivalent to the set of our own class then it is an internal
  # trace
  since_first_klass_entry.classes_in_stack == self_set
end

#popObject



73
74
75
76
77
78
79
# File 'lib/gun_dog/trace_stack.rb', line 73

def pop
  super.tap do |popped|
    if length == @traced_klass_entry_points.last
      @traced_klass_entry_points.pop
    end
  end
end

#preceded_by_traced_klass?Boolean

Returns:

  • (Boolean)


53
54
55
56
# File 'lib/gun_dog/trace_stack.rb', line 53

def preceded_by_traced_klass?
  traced_klasses = self.map(&:klass)
  traced_klasses.include?(klass) || traced_klasses.include?(klass.singleton_class)
end

#self_setObject



58
59
60
# File 'lib/gun_dog/trace_stack.rb', line 58

def self_set
  [klass].to_set
end

#since_first_klass_entryObject



62
63
64
65
66
# File 'lib/gun_dog/trace_stack.rb', line 62

def since_first_klass_entry
  # the stack since the first call to a traced method excluding the current
  # frame
  self.slice((@traced_klass_entry_points.first || 1) .. -2) || GunDog::TraceStack.new(@klass)
end