load_tracer 0.1.0 → 0.2.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/Gemfile.lock +1 -1
- data/README.md +12 -0
- data/lib/load_tracer/formatter/default.rb +36 -0
- data/lib/load_tracer/formatter/dot.rb +65 -0
- data/lib/load_tracer/formatter/templates/default.dot.erb +6 -0
- data/lib/load_tracer/version.rb +1 -1
- data/lib/load_tracer.rb +33 -35
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54faee11fca635fb5444d731ba08da06967dd8b26f6164282ae8fe748149840a
|
4
|
+
data.tar.gz: c759a241b21b3c04f34b83274a67abce0a6a3c3c56a6ec92df1c201848859872
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c48aca3dbca0cafe40c9aedf1b97c37c9dd325b77fb6c1be41fa7804fb9625d46c8d375a97e1a7cc08ffd53c224ffc6eb994a76a3cfce52e41a0d11cdd09d34
|
7
|
+
data.tar.gz: 2b980c8f85959c33b4fc98eb3a7717a1e25ab1af8062ab285633003ec745de0ce9fa4340f3d5b91954a947994426c635b2175e8b1494ed600b787f58999acbf1
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -38,6 +38,18 @@ end
|
|
38
38
|
pp report
|
39
39
|
```
|
40
40
|
|
41
|
+
### dot format
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
require 'load_tracer'
|
45
|
+
|
46
|
+
puts LoadTracer.trace(format: :dot) { require 'prime' }
|
47
|
+
```
|
48
|
+
|
49
|
+
```
|
50
|
+
ruby example.rb | dot -Tpng -o example.png | open example.png
|
51
|
+
```
|
52
|
+
|
41
53
|
## License
|
42
54
|
|
43
55
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class LoadTracer
|
2
|
+
class DefaultFormatter
|
3
|
+
def self.export(dependencies:, reverse_dependencies:)
|
4
|
+
report = dependencies.map do |path, deps|
|
5
|
+
FileSpec.new(
|
6
|
+
name: File.basename(path),
|
7
|
+
path: path,
|
8
|
+
dependencies: deps,
|
9
|
+
reverse_dependencies: [],
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
reverse_dependencies.each do |path, rdeps|
|
14
|
+
fs = report.find { |fs| fs.path == path }
|
15
|
+
|
16
|
+
if fs.nil?
|
17
|
+
report << FileSpec.new(
|
18
|
+
name: File.basename(path),
|
19
|
+
path: path,
|
20
|
+
dependencies: [],
|
21
|
+
reverse_dependencies: rdeps,
|
22
|
+
)
|
23
|
+
else
|
24
|
+
fs.reverse_dependencies = rdeps
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
report.each do |fs|
|
29
|
+
fs.dependencies.sort!.uniq!
|
30
|
+
fs.reverse_dependencies.sort!.uniq!
|
31
|
+
end
|
32
|
+
|
33
|
+
report.sort_by(&:name)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
class LoadTracer
|
5
|
+
class DotFormatter
|
6
|
+
def self.export(dependencies:)
|
7
|
+
new(dependencies: dependencies).export
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(dependencies:)
|
11
|
+
@dependencies = dependencies
|
12
|
+
@template = File.read(File.expand_path('templates/default.dot.erb', __dir__))
|
13
|
+
end
|
14
|
+
|
15
|
+
def export
|
16
|
+
graph_data = ERB.new(@template, nil, '-').result(binding)
|
17
|
+
|
18
|
+
graph_data.lines.map(&:rstrip).join("\n")
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def graph_edges
|
24
|
+
@dependencies.flat_map do |from, deps|
|
25
|
+
label1 = File.basename(from)
|
26
|
+
|
27
|
+
deps.map do |to|
|
28
|
+
label2 = File.basename(to)
|
29
|
+
|
30
|
+
[
|
31
|
+
duplicated_label_names.include?(label1) ? node_label(from) : label1,
|
32
|
+
duplicated_label_names.include?(label2) ? node_label(to) : label2,
|
33
|
+
]
|
34
|
+
end
|
35
|
+
end.sort_by(&:first).uniq
|
36
|
+
end
|
37
|
+
|
38
|
+
def duplicated_label_names
|
39
|
+
return @_duplicate_names if @_duplicate_names
|
40
|
+
|
41
|
+
checked = Hash.new
|
42
|
+
@_duplicate_names = []
|
43
|
+
|
44
|
+
@dependencies.each do |from, deps|
|
45
|
+
label1 = File.basename(from)
|
46
|
+
@_duplicate_names << label1 if checked[label1]
|
47
|
+
checked[label1] = true
|
48
|
+
|
49
|
+
deps.each do |to|
|
50
|
+
label2 = File.basename(to)
|
51
|
+
|
52
|
+
@_duplicate_names << label2 if label1 == label2
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
@_duplicate_names.uniq!
|
57
|
+
@_duplicate_names
|
58
|
+
end
|
59
|
+
|
60
|
+
def node_label(absolute_path)
|
61
|
+
s, _, t = Pathname.new(absolute_path).ascend.take(3)
|
62
|
+
s.relative_path_from(t).to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/load_tracer/version.rb
CHANGED
data/lib/load_tracer.rb
CHANGED
@@ -1,25 +1,23 @@
|
|
1
1
|
require 'binding_of_caller'
|
2
|
+
require 'load_tracer/formatter/default'
|
3
|
+
require 'load_tracer/formatter/dot'
|
2
4
|
require 'load_tracer/version'
|
3
5
|
|
4
6
|
module Kernel
|
5
7
|
unless defined?(__original_require__)
|
6
8
|
alias __original_require__ require
|
7
|
-
private :__original_require__
|
8
9
|
end
|
9
10
|
|
10
11
|
unless defined?(__original_require_relative__)
|
11
12
|
alias __original_require_relative__ require_relative
|
12
|
-
private :__original_require_relative__
|
13
13
|
end
|
14
14
|
|
15
15
|
unless defined?(__original_load__)
|
16
16
|
alias __original_load__ load
|
17
|
-
private :__original_load__
|
18
17
|
end
|
19
18
|
|
20
19
|
unless defined?(__original_autoload__)
|
21
20
|
alias __original_autoload__ autoload
|
22
|
-
private :__original_autoload__
|
23
21
|
end
|
24
22
|
|
25
23
|
def require(feature)
|
@@ -44,63 +42,58 @@ class LoadTracer
|
|
44
42
|
|
45
43
|
LOAD_METHODS = %i(require require_relative load autoload)
|
46
44
|
|
47
|
-
def self.trace
|
45
|
+
def self.trace(format: nil)
|
48
46
|
instance = new
|
49
47
|
instance.tracer.enable { yield }
|
50
|
-
instance.report
|
48
|
+
instance.report(format: format)
|
51
49
|
end
|
52
50
|
|
53
51
|
def initialize
|
54
52
|
@dependencies = Hash.new { |hash, key| hash[key] = [] }
|
55
53
|
@reverse_dependencies = Hash.new { |hash, key| hash[key] = [] }
|
54
|
+
@not_found_features = []
|
56
55
|
end
|
57
56
|
|
58
57
|
def tracer
|
59
|
-
TracePoint.new(:
|
58
|
+
TracePoint.new(:return) do |tp|
|
60
59
|
next unless LOAD_METHODS.include?(tp.method_id)
|
61
60
|
next if tp.defined_class != ::Kernel
|
62
61
|
next if tp.path != __FILE__
|
63
62
|
|
64
63
|
case tp.event
|
65
|
-
when :
|
64
|
+
when :return
|
66
65
|
bl = caller_locations[1]
|
67
|
-
|
68
66
|
feature = get_feature(tp)
|
67
|
+
|
68
|
+
if bl.absolute_path.nil?
|
69
|
+
bl = find_caller_of_internal_library(feature)
|
70
|
+
end
|
71
|
+
|
69
72
|
path = find_path(feature) || find_path(File.expand_path(feature, File.dirname(bl.path)))
|
70
73
|
|
71
|
-
|
74
|
+
if path.nil?
|
75
|
+
@not_found_features << feature
|
76
|
+
next
|
77
|
+
end
|
72
78
|
|
73
|
-
@dependencies[bl.
|
74
|
-
@reverse_dependencies[path] << bl.
|
79
|
+
@dependencies[bl.absolute_path] << path
|
80
|
+
@reverse_dependencies[path] << bl.absolute_path
|
75
81
|
end
|
76
82
|
end
|
77
83
|
end
|
78
84
|
|
79
|
-
def report
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
+
def report(format:)
|
86
|
+
case format
|
87
|
+
when :dot
|
88
|
+
DotFormatter.export(
|
89
|
+
dependencies: @dependencies
|
90
|
+
)
|
91
|
+
else
|
92
|
+
DefaultFormatter.export(
|
93
|
+
dependencies: @dependencies,
|
94
|
+
reverse_dependencies: @reverse_dependencies
|
85
95
|
)
|
86
96
|
end
|
87
|
-
|
88
|
-
@reverse_dependencies.each do |path, rdeps|
|
89
|
-
fs = file_specs.find { |fs| fs.path == path }
|
90
|
-
|
91
|
-
if fs.nil?
|
92
|
-
file_specs << FileSpec.new(
|
93
|
-
name: File.basename(path),
|
94
|
-
path: path,
|
95
|
-
dependencies: [],
|
96
|
-
reverse_dependencies: rdeps
|
97
|
-
)
|
98
|
-
else
|
99
|
-
fs.reverse_dependencies = rdeps
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
file_specs
|
104
97
|
end
|
105
98
|
|
106
99
|
private
|
@@ -121,4 +114,9 @@ class LoadTracer
|
|
121
114
|
rescue LoadError
|
122
115
|
nil
|
123
116
|
end
|
117
|
+
|
118
|
+
def find_caller_of_internal_library(feature)
|
119
|
+
index = caller_locations.find_index { |bl| bl.base_label == feature && bl.path == '<internal:prelude>' }
|
120
|
+
caller_locations[index + 1]
|
121
|
+
end
|
124
122
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: load_tracer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shuichi Tamayose
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-01
|
11
|
+
date: 2019-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: binding_of_caller
|
@@ -85,6 +85,9 @@ files:
|
|
85
85
|
- bin/console
|
86
86
|
- bin/setup
|
87
87
|
- lib/load_tracer.rb
|
88
|
+
- lib/load_tracer/formatter/default.rb
|
89
|
+
- lib/load_tracer/formatter/dot.rb
|
90
|
+
- lib/load_tracer/formatter/templates/default.dot.erb
|
88
91
|
- lib/load_tracer/version.rb
|
89
92
|
- load_tracer.gemspec
|
90
93
|
homepage: https://github.com/siman-man/load_tracer
|