load_tracer 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eeeeceacbdb0493b22b4bc6555f8132aca31920261fc2aa2a17d96627c972a63
4
- data.tar.gz: 844f00b03f9f2d871bce7e0705c1bac12c99923838ff047dec41127a0ccd54c5
3
+ metadata.gz: 54faee11fca635fb5444d731ba08da06967dd8b26f6164282ae8fe748149840a
4
+ data.tar.gz: c759a241b21b3c04f34b83274a67abce0a6a3c3c56a6ec92df1c201848859872
5
5
  SHA512:
6
- metadata.gz: b389012cc1aee3159a000c86675095529401e8761db4dbfb5db79d574bce7794857509d8e29fa7014d41a2521a279834923ef92bc3a6d0c12b69df645755033c
7
- data.tar.gz: 43bfbda30363e9ca0e686b378d6f551f1897460c4794d18272158e51125e43e8612e3954b017dc7bdca76e70d2db2632da6bcc20fc381d68f5d3ac6ebd89811d
6
+ metadata.gz: 2c48aca3dbca0cafe40c9aedf1b97c37c9dd325b77fb6c1be41fa7804fb9625d46c8d375a97e1a7cc08ffd53c224ffc6eb994a76a3cfce52e41a0d11cdd09d34
7
+ data.tar.gz: 2b980c8f85959c33b4fc98eb3a7717a1e25ab1af8062ab285633003ec745de0ce9fa4340f3d5b91954a947994426c635b2175e8b1494ed600b787f58999acbf1
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- load_tracer (0.1.0)
4
+ load_tracer (0.2.0)
5
5
  binding_of_caller (~> 0.8.0)
6
6
 
7
7
  GEM
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
@@ -0,0 +1,6 @@
1
+ digraph file_dependencies {
2
+ graph [ dpi = 200 ]
3
+ <%- graph_edges.each do |edge| %>
4
+ <%= %Q("#{edge.first}" -> "#{edge.last}") -%>
5
+ <% end %>
6
+ }
@@ -1,3 +1,3 @@
1
1
  class LoadTracer
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
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(:call) do |tp|
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 :call
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
- raise LoadError.new("cannot load such file -- #{feature}") if path.nil?
74
+ if path.nil?
75
+ @not_found_features << feature
76
+ next
77
+ end
72
78
 
73
- @dependencies[bl.path] << path
74
- @reverse_dependencies[path] << bl.path
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
- file_specs = @dependencies.map do |path, deps|
81
- FileSpec.new(
82
- name: File.basename(path),
83
- path: path,
84
- dependencies: deps,
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.1.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-29 00:00:00.000000000 Z
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