Class: AppMap::Feature::Base

Inherits:
FeatureStruct show all
Defined in:
lib/appmap/feature.rb

Overview

Base is an abstract base class for features.

Direct Known Subclasses

Cls, Function, Package

Instance Attribute Summary collapse

Attributes inherited from FeatureStruct

#attributes, #location, #name

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, location, attributes) ⇒ Base

Returns a new instance of Base.



50
51
52
53
54
55
# File 'lib/appmap/feature.rb', line 50

def initialize(name, location, attributes)
  super(name, self.class.expand_path(location), attributes)

  @parent = nil
  @children = []
end

Instance Attribute Details

#childrenObject (readonly)

Returns the value of attribute children.



48
49
50
# File 'lib/appmap/feature.rb', line 48

def children
  @children
end

#parentObject (readonly)

Returns the value of attribute parent.



48
49
50
# File 'lib/appmap/feature.rb', line 48

def parent
  @parent
end

Class Method Details

.expand_path(location) ⇒ Object



42
43
44
45
# File 'lib/appmap/feature.rb', line 42

def expand_path(location)
  path, lineno = location.split(':')
  [ path, lineno ].compact.join(':')
end

Instance Method Details

#add_child(child) ⇒ Object



63
64
65
66
# File 'lib/appmap/feature.rb', line 63

def add_child(child)
  @children << child
  child.instance_variable_set('@parent', self)
end

#collect_functions(accumulator = []) ⇒ Object

yield each function to a block.



92
93
94
95
96
97
# File 'lib/appmap/feature.rb', line 92

def collect_functions(accumulator = [])
  accumulator.tap do |_|
    accumulator << self if is_a?(Function)
    children.each { |child| child.collect_functions(accumulator) }
  end
end

#enclosing_type_nameObject

Gets an array containing the type names which enclose this feature.



69
70
71
72
73
74
75
76
# File 'lib/appmap/feature.rb', line 69

def enclosing_type_name
  @enclosing_type_name ||= [].tap do |names|
    p = self
    while (p = p.parent) && p.type?
      names << p.name
    end
  end.reverse
end

#enclosing_type_name?Boolean

true iff this feature has an enclosing type. An example of when this is false: when the parent of the feature is not a type (e.g. it’s a location).

Returns:

  • (Boolean)


80
81
82
# File 'lib/appmap/feature.rb', line 80

def enclosing_type_name?
  !enclosing_type_name.empty?
end

#include_optionObject

The ‘include’ attribute can indicate which elements of the parse subtree to automatically add as features. For example: public_classes, public_modules, public_methods.



87
88
89
# File 'lib/appmap/feature.rb', line 87

def include_option
  (attributes[:include] || '').split(',')
end

#prune(parent = nil) ⇒ Object

Determines if this feature should be dropped from the feature tree. A feature is dropped from the feature tree if it doesn’t add useful information for the user. Performing this operation removes feature nodes that don’t add anything useful to the user. For example, empty classes.



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/appmap/feature.rb', line 125

def prune(parent = nil)
  should_prune = prune? && !parent.nil?
  parent = self unless should_prune
  children.dup.each do |child|
    child.prune(parent)
  end

  # Perform the prune in post-fix traversal order, otherwise the
  # features will get confused about whether they should prune or not.
  if should_prune
    parent.remove_child(self)
    children.each do |child|
      parent.add_child(child)
    end
  end
end

#prune?Boolean

Returns:

  • (Boolean)


177
178
179
# File 'lib/appmap/feature.rb', line 177

def prune?
  false
end

#remove_child(child) ⇒ Object



57
58
59
60
61
# File 'lib/appmap/feature.rb', line 57

def remove_child(child)
  # TODO: Encountered this indexing appland with active_dispatch
  children.delete(child) or warn "Unable to remove #{name.inspect} from parent" # or raise "No such child : #{child}"
  child.instance_variable_set('@parent', nil)
end

#reparent(parent = nil, features_by_type = {}) ⇒ Object

Determines if this feature should be re-parented as a child of a different feature.

A feature is re-parented if the enclosing type of the feature has already been defined in the tree.

Parameters:

  • parent (defaults to: nil)

    the parent of this feature in the compacted tree.



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/appmap/feature.rb', line 147

def reparent(parent = nil, features_by_type = {})
  # Determine if the enclosing type of the feature is defined.
  # Generally, it should be.

  existing_enclosing_type = features_by_type[enclosing_type_name] if enclosing_type_name?
  if existing_enclosing_type
    parent = existing_enclosing_type
  end

  # Determine if this feature is a type which is already defined.
  type_exists = true if type? && features_by_type.key?(type_name)

  # If this feature is a type that's already defined, skip over it and
  # add the children to the existing feature. Otherwise, clone this feature
  # under the parent and use the cloned object as the parent of the compacted 
  # children.
  if type_exists
    features_by_type[type_name]
  else
    clone.tap do |f|
      parent.add_child(f) if parent
      features_by_type[type_name] = f if type?
    end
  end.tap do |updated_parent|
    children.each do |child|
      child.reparent(updated_parent, features_by_type)
    end
  end
end

#to_hObject



111
112
113
114
115
116
117
118
119
# File 'lib/appmap/feature.rb', line 111

def to_h
  super.tap do |map|
    map.delete(:parent)
    class_name = self.class.name.underscore.split('/')[-1]
    map[:type] = TYPE_MAP[class_name] || class_name
    map[:children] = @children.map(&:to_h) unless @children.empty?
    map.delete(:attributes) if map[:attributes].empty?
  end
end

#to_json(*opts) ⇒ Object



107
108
109
# File 'lib/appmap/feature.rb', line 107

def to_json(*opts)
  to_h.to_json(*opts)
end

#type?Boolean

Returns:

  • (Boolean)


99
100
101
# File 'lib/appmap/feature.rb', line 99

def type?
  false
end

#valid?Boolean

Returns:

  • (Boolean)


103
104
105
# File 'lib/appmap/feature.rb', line 103

def valid?
  !name.blank? && !location.blank?
end