appmap 0.23.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +17 -8
- data/.travis.yml +6 -0
- data/CHANGELOG.md +19 -0
- data/README.md +29 -12
- data/Rakefile +3 -3
- data/appmap.gemspec +3 -1
- data/exe/appmap +6 -18
- data/lib/appmap.rb +47 -6
- data/lib/appmap/algorithm/prune_class_map.rb +2 -0
- data/lib/appmap/algorithm/stats.rb +4 -2
- data/lib/appmap/class_map.rb +143 -0
- data/lib/appmap/command/record.rb +8 -6
- data/lib/appmap/command/stats.rb +2 -0
- data/lib/appmap/command/upload.rb +4 -2
- data/lib/appmap/event.rb +168 -0
- data/lib/appmap/hook.rb +151 -0
- data/lib/appmap/middleware/remote_recording.rb +14 -20
- data/lib/appmap/rails/action_handler.rb +10 -6
- data/lib/appmap/rails/sql_handler.rb +10 -8
- data/lib/appmap/railtie.rb +31 -18
- data/lib/appmap/rspec.rb +238 -261
- data/lib/appmap/trace.rb +88 -0
- data/lib/appmap/version.rb +1 -1
- data/package-lock.json +90 -92
- data/spec/abstract_controller4_base_spec.rb +1 -1
- data/spec/abstract_controller_base_spec.rb +7 -3
- data/spec/config_spec.rb +25 -0
- data/spec/fixtures/hook/attr_accessor.rb +5 -0
- data/spec/fixtures/hook/class_method.rb +17 -0
- data/spec/fixtures/hook/constructor.rb +7 -0
- data/spec/fixtures/hook/exception_method.rb +11 -0
- data/spec/fixtures/hook/instance_method.rb +23 -0
- data/spec/fixtures/rails4_users_app/app/controllers/api/users_controller.rb +3 -3
- data/spec/fixtures/rails4_users_app/config/database.yml +2 -1
- data/spec/fixtures/rails4_users_app/docker-compose.yml +2 -0
- data/spec/fixtures/rails_users_app/.ruby-version +1 -1
- data/spec/fixtures/rails_users_app/app/controllers/api/users_controller.rb +2 -2
- data/spec/fixtures/rails_users_app/config/database.yml +2 -1
- data/spec/fixtures/rails_users_app/create_app +1 -0
- data/spec/fixtures/rails_users_app/docker-compose.yml +4 -0
- data/spec/fixtures/rails_users_app/spec/models/user_spec.rb +1 -1
- data/spec/hook_spec.rb +357 -0
- data/spec/rails_spec_helper.rb +25 -16
- data/spec/railtie_spec.rb +1 -1
- data/spec/record_sql_rails_pg_spec.rb +1 -2
- data/spec/remote_recording_spec.rb +117 -0
- data/spec/spec_helper.rb +1 -0
- data/test/cli_test.rb +7 -36
- data/test/fixtures/cli_record_test/appmap.yml +2 -1
- data/test/fixtures/cli_record_test/lib/cli_record_test/main.rb +4 -2
- data/test/test_helper.rb +0 -42
- metadata +46 -62
- data/exe/_appmap-record-self +0 -49
- data/lib/appmap/command/inspect.rb +0 -14
- data/lib/appmap/config.rb +0 -65
- data/lib/appmap/config/directory.rb +0 -65
- data/lib/appmap/config/file.rb +0 -13
- data/lib/appmap/config/named_function.rb +0 -21
- data/lib/appmap/config/package_dir.rb +0 -52
- data/lib/appmap/config/path.rb +0 -25
- data/lib/appmap/feature.rb +0 -262
- data/lib/appmap/inspect.rb +0 -91
- data/lib/appmap/inspect/inspector.rb +0 -99
- data/lib/appmap/inspect/parse_node.rb +0 -170
- data/lib/appmap/inspect/parser.rb +0 -15
- data/lib/appmap/parser.rb +0 -60
- data/lib/appmap/rspec/parse_node.rb +0 -41
- data/lib/appmap/rspec/parser.rb +0 -15
- data/lib/appmap/trace/event_handler/rack_handler_webrick.rb +0 -65
- data/lib/appmap/trace/tracer.rb +0 -356
- data/spec/fixtures/rails_users_app/bin/_appmap-record-self +0 -29
- data/spec/rack_handler_webrick_spec.rb +0 -59
- data/test/config_test.rb +0 -149
- data/test/explict_inspect_test.rb +0 -29
- data/test/fixtures/active_record_like/active_record.rb +0 -2
- data/test/fixtures/active_record_like/active_record/aggregations.rb +0 -4
- data/test/fixtures/active_record_like/active_record/association.rb +0 -4
- data/test/fixtures/active_record_like/active_record/associations/join_dependency.rb +0 -6
- data/test/fixtures/active_record_like/active_record/associations/join_dependency/join_base.rb +0 -8
- data/test/fixtures/active_record_like/active_record/associations/join_dependency/join_part.rb +0 -8
- data/test/fixtures/active_record_like/active_record/caps/caps.rb +0 -4
- data/test/fixtures/ignore_non_ruby_file/class.rb +0 -3
- data/test/fixtures/ignore_non_ruby_file/non-ruby.txt +0 -1
- data/test/fixtures/includes_excludes/lib/a/a_1.rb +0 -6
- data/test/fixtures/includes_excludes/lib/a/a_2.rb +0 -6
- data/test/fixtures/includes_excludes/lib/a/x/x_1.rb +0 -8
- data/test/fixtures/includes_excludes/lib/b/b_1.rb +0 -6
- data/test/fixtures/includes_excludes/lib/root_1.rb +0 -4
- data/test/fixtures/inspect_multiple_subdirs/module_a.rb +0 -2
- data/test/fixtures/inspect_multiple_subdirs/module_a/class_a.rb +0 -5
- data/test/fixtures/inspect_multiple_subdirs/module_b.rb +0 -2
- data/test/fixtures/inspect_multiple_subdirs/module_b/class_b.rb +0 -5
- data/test/fixtures/inspect_multiple_subdirs/module_b/class_c.rb +0 -5
- data/test/fixtures/inspect_package/module_a/module_b/class_in_module.rb +0 -6
- data/test/fixtures/parse_file/defs_static_function.rb +0 -96
- data/test/fixtures/parse_file/function_within_class.rb +0 -36
- data/test/fixtures/parse_file/include_public_methods.rb +0 -127
- data/test/fixtures/parse_file/instance_function.rb +0 -17
- data/test/fixtures/parse_file/modules.rb +0 -71
- data/test/fixtures/parse_file/sclass_static_function.rb +0 -88
- data/test/fixtures/parse_file/toplevel_class.rb +0 -13
- data/test/fixtures/parse_file/toplevel_function.rb +0 -14
- data/test/fixtures/trace_test/trace_program_1.rb +0 -44
- data/test/implicit_inspect_test.rb +0 -33
- data/test/include_exclude_test.rb +0 -48
- data/test/prerecorded_trace_test.rb +0 -76
- data/test/trace_test.rb +0 -92
data/lib/appmap/inspect.rb
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
require 'appmap/inspect/inspector'
|
|
2
|
-
require 'appmap/inspect/parser'
|
|
3
|
-
|
|
4
|
-
module AppMap
|
|
5
|
-
# Inspect identifies features from a Ruby file.
|
|
6
|
-
module Inspect
|
|
7
|
-
class << self
|
|
8
|
-
# Detect features from a source code repository. The manner in which the features are detected in the
|
|
9
|
-
# code is defined and tuned by a path configuration object. The path configuration tells the
|
|
10
|
-
# feature detector what it should do when it encounters code that may be a "sub-feature",
|
|
11
|
-
# for example a public instance method of a class.
|
|
12
|
-
#
|
|
13
|
-
# rubocop:disable Metrics/AbcSize
|
|
14
|
-
# rubocop:disable Metrics/MethodLength
|
|
15
|
-
# @appmap
|
|
16
|
-
def detect_features(config_spec)
|
|
17
|
-
child_features = -> { config_spec.children.map(&Inspect.method(:detect_features)).flatten.compact }
|
|
18
|
-
parse_file = -> { inspect_file(config_spec.mode, file_path: config_spec.path) }
|
|
19
|
-
|
|
20
|
-
feature_builders = Hash.new { |_, key| raise "Unable to build features for #{key.inspect}" }
|
|
21
|
-
feature_builders[AppMap::Config::Directory] = child_features
|
|
22
|
-
feature_builders[AppMap::Config::File] = parse_file
|
|
23
|
-
feature_builders[AppMap::Config::PackageDir] = lambda {
|
|
24
|
-
AppMap::Feature::Package.new(config_spec.package_name, config_spec.path, {}).tap do |package|
|
|
25
|
-
child_features.call.each do |child|
|
|
26
|
-
package.add_child(child)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
}
|
|
30
|
-
feature_builders[AppMap::Config::NamedFunction] = lambda {
|
|
31
|
-
# Loads named functions by finding the requested gem, finding the file within the gem,
|
|
32
|
-
# parsing that file, and then inspecting the module/class scope for the requested method.
|
|
33
|
-
# We can't 'require' the specified code, because if we do that, it can change the
|
|
34
|
-
# behavior of the program.
|
|
35
|
-
|
|
36
|
-
gem = Gem.loaded_specs[config_spec.gem_name]
|
|
37
|
-
return [] unless gem
|
|
38
|
-
|
|
39
|
-
gem_dir = gem.gem_dir
|
|
40
|
-
file_path = File.join(gem_dir, config_spec.file_path)
|
|
41
|
-
|
|
42
|
-
parse_nodes, comments = Parser.new(file_path: file_path).parse
|
|
43
|
-
features = ImplicitInspector.new(file_path, parse_nodes, comments).inspect_file
|
|
44
|
-
|
|
45
|
-
class_names = config_spec.class_names.dup
|
|
46
|
-
until class_names.empty?
|
|
47
|
-
class_name = class_names.shift
|
|
48
|
-
feature = features.find { |f| f.to_h[:type] == 'class' && f.name == class_name }
|
|
49
|
-
raise "#{class_name.inspect} not found" unless feature
|
|
50
|
-
|
|
51
|
-
features = feature.children
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
function = features.find { |f| f.to_h[:type] == 'function' && f.name == config_spec.method_name && f.static == config_spec.static }
|
|
55
|
-
|
|
56
|
-
# If the configuration specifier has an id, use it as the handler id.
|
|
57
|
-
# This is how we can associate custom handler logic with the named function.
|
|
58
|
-
function.handler_id = config_spec.id.to_s if config_spec.id
|
|
59
|
-
|
|
60
|
-
AppMap::Feature::Package.new(config_spec.gem_name, "#{gem_dir}:0", {}).tap do |pkg|
|
|
61
|
-
parent = pkg
|
|
62
|
-
class_names = config_spec.class_names.dup
|
|
63
|
-
until class_names.empty?
|
|
64
|
-
class_name = class_names.shift
|
|
65
|
-
cls = AppMap::Feature::Cls.new(class_name, "#{gem_dir}:0", {})
|
|
66
|
-
parent.children << cls
|
|
67
|
-
parent = cls
|
|
68
|
-
end
|
|
69
|
-
parent.children << function
|
|
70
|
-
end
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
feature_builders[config_spec.class].call
|
|
74
|
-
end
|
|
75
|
-
# rubocop:enable Metrics/AbcSize
|
|
76
|
-
# rubocop:enable Metrics/MethodLength
|
|
77
|
-
|
|
78
|
-
# Inspect a specific file for features.
|
|
79
|
-
#
|
|
80
|
-
# @appmap
|
|
81
|
-
def inspect_file(strategy, file_path: nil)
|
|
82
|
-
parse_nodes, comments = Parser.new(file_path: file_path).parse
|
|
83
|
-
inspector_class = {
|
|
84
|
-
implicit: ImplicitInspector,
|
|
85
|
-
explicit: ExplicitInspector
|
|
86
|
-
}[strategy] or raise "Invalid strategy : #{strategy.inspect}"
|
|
87
|
-
inspector_class.new(file_path, parse_nodes, comments).inspect_file
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
module AppMap
|
|
2
|
-
module Inspect
|
|
3
|
-
# Inspector is an abstract class for extracting features from a Ruby program.
|
|
4
|
-
class Inspector
|
|
5
|
-
attr_reader :file_path, :parse_nodes, :comments
|
|
6
|
-
|
|
7
|
-
def initialize(file_path, parse_nodes, comments)
|
|
8
|
-
@file_path = file_path
|
|
9
|
-
@parse_nodes = parse_nodes
|
|
10
|
-
@comments = comments
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# ImplicitInspector extracts features from a Ruby program, creating a feature for each public class and method.
|
|
15
|
-
class ImplicitInspector < Inspector
|
|
16
|
-
def inspect_file
|
|
17
|
-
features = []
|
|
18
|
-
features_by_ast_node = {}
|
|
19
|
-
parse_nodes.select(&:public?).each do |parse_node|
|
|
20
|
-
feature = parse_node.to_feature({})
|
|
21
|
-
features_by_ast_node[parse_node.node] = feature
|
|
22
|
-
if feature
|
|
23
|
-
if (enclosing_type_node = parse_node.enclosing_type_node) &&
|
|
24
|
-
(parent_feature = features_by_ast_node[enclosing_type_node])
|
|
25
|
-
parent_feature.add_child(feature)
|
|
26
|
-
else
|
|
27
|
-
features << feature
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
features.keep_if(&:valid?)
|
|
33
|
-
features
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# ExplicitInspector extracts features from a Ruby program, requiring the use of @appmap annotations to mark each
|
|
38
|
-
# relevant class and method.
|
|
39
|
-
class ExplicitInspector < Inspector
|
|
40
|
-
def inspect_file
|
|
41
|
-
nodes_by_line = parse_nodes.each_with_object({}) { |node, h| h[node.node.loc.line] = node }
|
|
42
|
-
|
|
43
|
-
features_by_ast_node = {}
|
|
44
|
-
features = []
|
|
45
|
-
|
|
46
|
-
comments.select { |c| c.text.index('@appmap') }.each do |c|
|
|
47
|
-
c.text.split("\n").select { |l| l.index('@appmap') }.each do |feature|
|
|
48
|
-
tokens = feature.split
|
|
49
|
-
tokens.delete_if { |t| %w[# @appmap].member?(t) }
|
|
50
|
-
attributes = tokens.inject({}) do |memo, token|
|
|
51
|
-
key, value = token.split('=')
|
|
52
|
-
memo.tap do |attrs|
|
|
53
|
-
attrs[key.to_sym] = value
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
parse_node = nodes_by_line[c.location.last_line + 1]
|
|
58
|
-
if parse_node
|
|
59
|
-
feature = parse_node.to_feature(attributes)
|
|
60
|
-
features_by_ast_node[parse_node.node] = feature
|
|
61
|
-
if feature
|
|
62
|
-
if (enclosing_type_node = parse_node.enclosing_type_node) &&
|
|
63
|
-
(parent_feature = features_by_ast_node[enclosing_type_node])
|
|
64
|
-
parent_feature.add_child(feature)
|
|
65
|
-
else
|
|
66
|
-
features << feature
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# At this point there's a parse_node and an associated feature.
|
|
70
|
-
# If the feature is a class which has the option 'include=public_methods',
|
|
71
|
-
# scan the rest of the class body for public methods and create features
|
|
72
|
-
# for them.
|
|
73
|
-
|
|
74
|
-
if parse_node.type == :class && feature.include_option.member?('public_methods')
|
|
75
|
-
begin_node = parse_node.node.children.find { |n| n.respond_to?(:type) && n.type == :begin }
|
|
76
|
-
if begin_node
|
|
77
|
-
public_methods = begin_node
|
|
78
|
-
.children
|
|
79
|
-
.select { |n| n.respond_to?(:type) && n.type == :def }
|
|
80
|
-
.map { |n| ParseNode.from_node(n, file_path, parse_node.ancestors + [ parse_node.node, begin_node ]) }
|
|
81
|
-
.select(&:public?)
|
|
82
|
-
public_methods.map { |m| m.to_feature([]) }.compact.each do |f|
|
|
83
|
-
feature.add_child(f)
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
else
|
|
89
|
-
warn "No parse node found at #{file_path}:#{c.location.last_line + 1}"
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
features.keep_if(&:valid?)
|
|
95
|
-
features
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
require 'appmap/feature'
|
|
2
|
-
require 'forwardable'
|
|
3
|
-
|
|
4
|
-
module AppMap
|
|
5
|
-
module Inspect
|
|
6
|
-
# ParseNodeStruct wraps a generic AST parse node.
|
|
7
|
-
ParseNodeStruct = Struct.new(:node, :file_path, :ancestors) do
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
# ParseNode wraps a generic AST parse node.
|
|
11
|
-
class ParseNode < ParseNodeStruct
|
|
12
|
-
extend Forwardable
|
|
13
|
-
|
|
14
|
-
def_delegators :node, :type, :location
|
|
15
|
-
|
|
16
|
-
class << self
|
|
17
|
-
# Build a ParseNode from an AST node.
|
|
18
|
-
def from_node(node, file_path, ancestors)
|
|
19
|
-
case node.type
|
|
20
|
-
when :class, :module
|
|
21
|
-
ClassParseNode.new(node, file_path, ancestors.dup)
|
|
22
|
-
when :def
|
|
23
|
-
InstanceMethodParseNode.new(node, file_path, ancestors.dup)
|
|
24
|
-
when :defs
|
|
25
|
-
StaticMethodParseNode.new(node, file_path, ancestors.dup) \
|
|
26
|
-
if StaticMethodParseNode.static?(node)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def public?
|
|
32
|
-
preceding_send = preceding_sibling_nodes
|
|
33
|
-
.reverse
|
|
34
|
-
.select { |n| n.respond_to?(:type) && n.type == :send }
|
|
35
|
-
.find { |n| %i[public protected private].member?(n.children[1]) }
|
|
36
|
-
preceding_send.nil? || preceding_send.children[1] == :public
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Gets the AST node of the module or class which encloses this node.
|
|
40
|
-
def enclosing_type_node
|
|
41
|
-
ancestors.reverse.find do |a|
|
|
42
|
-
%i[class module].include?(a.type)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def parent_node
|
|
47
|
-
ancestors[-1]
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def preceding_sibling_nodes
|
|
51
|
-
return [] unless parent_node
|
|
52
|
-
index_of_this_node = parent_node.children.index { |c| c == node }
|
|
53
|
-
parent_node.children[0...index_of_this_node]
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
protected
|
|
57
|
-
|
|
58
|
-
def extract_class_name(node)
|
|
59
|
-
node.children[0].children[1].to_s
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def extract_module_name(node)
|
|
63
|
-
node.children[0].children[1].to_s
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# A Ruby class.
|
|
68
|
-
class ClassParseNode < ParseNode
|
|
69
|
-
def to_feature(attributes)
|
|
70
|
-
AppMap::Feature::Cls.new(extract_class_name(node), "#{file_path}:#{location.line}", attributes)
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# Abstract representation of a method.
|
|
75
|
-
class MethodParseNode < ParseNode
|
|
76
|
-
def to_feature(attributes)
|
|
77
|
-
AppMap::Feature::Function.new(name, "#{file_path}:#{location.line}", attributes).tap do |a|
|
|
78
|
-
a.static = static?
|
|
79
|
-
a.class_name = class_name
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def enclosing_names
|
|
84
|
-
ancestors.select do |a|
|
|
85
|
-
%i[class module].include?(a.type)
|
|
86
|
-
end.map do |a|
|
|
87
|
-
send("extract_#{a.type}_name", a)
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# A method defines as a :def AST node.
|
|
93
|
-
class InstanceMethodParseNode < MethodParseNode
|
|
94
|
-
def name
|
|
95
|
-
node.children[0].to_s
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# class_name should be inferred from the enclosing type.
|
|
99
|
-
def class_name
|
|
100
|
-
enclosing_names.join('::')
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
# An instance method defined in an sclass is a static method.
|
|
104
|
-
#
|
|
105
|
-
# TODO: Well, not strictly true. A singleton class can be defined on a class or
|
|
106
|
-
# on an instance. In fact, to Ruby a class method is really just an instance method
|
|
107
|
-
# on a class. So, this needs fixing to try and determine if the singleton class is
|
|
108
|
-
# defined on an instance or on a class. This may actually be hard (impossible?) to do
|
|
109
|
-
# from static parsing.
|
|
110
|
-
def static?
|
|
111
|
-
result = ancestors[-1].type == :sclass ||
|
|
112
|
-
(ancestors[-1].type == :begin && ancestors[-2] && ancestors[-2].type == :sclass)
|
|
113
|
-
!!result
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
# A method defines as a :defs AST node.
|
|
118
|
-
# For example:
|
|
119
|
-
#
|
|
120
|
-
# class Main
|
|
121
|
-
# def Main.main_func; end
|
|
122
|
-
# def explain
|
|
123
|
-
# some_func.tap do |s|
|
|
124
|
-
# def s.inspect; self; end
|
|
125
|
-
# end
|
|
126
|
-
# end
|
|
127
|
-
# end
|
|
128
|
-
class StaticMethodParseNode < MethodParseNode
|
|
129
|
-
class << self
|
|
130
|
-
def static?(node)
|
|
131
|
-
%i[self const].member?(node.children[0].type)
|
|
132
|
-
end
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def name
|
|
136
|
-
node.children[1].to_s
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# class_name is specified as `nil` if it should be inferred from the
|
|
140
|
-
# enclosing type.
|
|
141
|
-
def class_name
|
|
142
|
-
case (defs_type = node.children[0].type)
|
|
143
|
-
when :self
|
|
144
|
-
class_name_from_enclosing_type
|
|
145
|
-
when :const
|
|
146
|
-
class_name_from_declaration
|
|
147
|
-
else
|
|
148
|
-
raise "Unrecognized 'defs' method type : #{defs_type.inspect}"
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def static?
|
|
153
|
-
true
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
protected
|
|
157
|
-
|
|
158
|
-
def class_name_from_enclosing_type
|
|
159
|
-
enclosing_names.join('::')
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
def class_name_from_declaration
|
|
163
|
-
ancestor_names = enclosing_names
|
|
164
|
-
ancestor_names.pop
|
|
165
|
-
ancestor_names << node.children[0].children[1]
|
|
166
|
-
ancestor_names.join('::')
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
require 'appmap/parser'
|
|
2
|
-
require 'appmap/inspect/parse_node'
|
|
3
|
-
|
|
4
|
-
module AppMap
|
|
5
|
-
module Inspect
|
|
6
|
-
# Parser processes a Ruby into a list of parse nodes and a list of comments.
|
|
7
|
-
class Parser < ::AppMap::Parser
|
|
8
|
-
protected
|
|
9
|
-
|
|
10
|
-
def build_parse_node(node, file_path, ancestors)
|
|
11
|
-
ParseNode.from_node(node, file_path, ancestors)
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
data/lib/appmap/parser.rb
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
require 'parser/current'
|
|
2
|
-
require 'set'
|
|
3
|
-
|
|
4
|
-
module AppMap
|
|
5
|
-
class Parser
|
|
6
|
-
def initialize(file_path: nil, code: nil)
|
|
7
|
-
@file_path = file_path
|
|
8
|
-
@code = code
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def to_s
|
|
12
|
-
"Parse code #{file_path.inspect}"
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# Parse the contents of a file into a list of features.
|
|
16
|
-
def parse
|
|
17
|
-
parse_tree, comments = parse_code_and_comments
|
|
18
|
-
parse_nodes = build_parse_nodes parse_tree
|
|
19
|
-
[ parse_nodes, comments ]
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
protected
|
|
23
|
-
|
|
24
|
-
def file_path
|
|
25
|
-
@file_path || '<inline>'
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def code
|
|
29
|
-
@code ||= File.read(file_path)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def parse_code_and_comments
|
|
33
|
-
::Parser::CurrentRuby.parse_with_comments(code)
|
|
34
|
-
rescue ::Parser::SyntaxError, EncodingError
|
|
35
|
-
warn "Unable to parse #{file_path.inspect} : #{$!.message}"
|
|
36
|
-
[ [], [] ]
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# rubocop:disable Metrics/MethodLength
|
|
40
|
-
def build_parse_nodes(parse_tree)
|
|
41
|
-
parse_nodes = []
|
|
42
|
-
visit_methods = lambda do |node, ancestors|
|
|
43
|
-
return unless node.respond_to?(:type)
|
|
44
|
-
|
|
45
|
-
parse_node = build_parse_node(node, file_path, ancestors)
|
|
46
|
-
parse_nodes << parse_node if parse_node
|
|
47
|
-
|
|
48
|
-
ancestors << node
|
|
49
|
-
node.children.each do |child|
|
|
50
|
-
visit_methods.call(child, ancestors)
|
|
51
|
-
end
|
|
52
|
-
ancestors.pop
|
|
53
|
-
end
|
|
54
|
-
visit_methods.call(parse_tree, [])
|
|
55
|
-
parse_nodes
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
# rubocop:enable Metrics/MethodLength
|
|
59
|
-
end
|
|
60
|
-
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
require 'forwardable'
|
|
2
|
-
|
|
3
|
-
module AppMap
|
|
4
|
-
module RSpec
|
|
5
|
-
# ParseNodeStruct wraps a generic AST parse node.
|
|
6
|
-
ParseNodeStruct = Struct.new(:node, :file_path, :ancestors) do
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
# ParseNode wraps a generic AST parse node.
|
|
10
|
-
class ParseNode < ParseNodeStruct
|
|
11
|
-
extend Forwardable
|
|
12
|
-
|
|
13
|
-
def_delegators :node, :type, :location
|
|
14
|
-
|
|
15
|
-
class << self
|
|
16
|
-
# Build a ParseNode from an AST node.
|
|
17
|
-
def from_node(node, file_path, ancestors)
|
|
18
|
-
case node.type
|
|
19
|
-
when :block
|
|
20
|
-
BlockParseNode.new(node, file_path, ancestors.dup)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# A Ruby block.
|
|
27
|
-
class BlockParseNode < ParseNode
|
|
28
|
-
def to_s
|
|
29
|
-
"RSpec block at #{file_path} #{first_line}:#{last_line}"
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def first_line
|
|
33
|
-
node.location.first_line
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def last_line
|
|
37
|
-
node.location.last_line
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|