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 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