deps_grapher 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +21 -0
- data/README.md +327 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/deps_grapher +96 -0
- data/bin/setup +8 -0
- data/deps_grapher.gemspec +34 -0
- data/lefthook.yml +14 -0
- data/lib/deps_grapher/ast_processor.rb +211 -0
- data/lib/deps_grapher/ast_processor_policy.rb +45 -0
- data/lib/deps_grapher/cache_file.rb +48 -0
- data/lib/deps_grapher/cli.rb +29 -0
- data/lib/deps_grapher/command/analyzer.rb +89 -0
- data/lib/deps_grapher/command/init.rb +49 -0
- data/lib/deps_grapher/configuration.rb +99 -0
- data/lib/deps_grapher/context.rb +48 -0
- data/lib/deps_grapher/cytoscape/cose.rb +36 -0
- data/lib/deps_grapher/cytoscape/fcose.rb +36 -0
- data/lib/deps_grapher/cytoscape/klay.rb +27 -0
- data/lib/deps_grapher/cytoscape/template.erb +61 -0
- data/lib/deps_grapher/cytoscape.rb +88 -0
- data/lib/deps_grapher/dsl.rb +50 -0
- data/lib/deps_grapher/edge.rb +58 -0
- data/lib/deps_grapher/errors.rb +8 -0
- data/lib/deps_grapher/event.rb +63 -0
- data/lib/deps_grapher/graph.rb +71 -0
- data/lib/deps_grapher/graphile/generator.rb +42 -0
- data/lib/deps_grapher/graphile/graphile.erb +119 -0
- data/lib/deps_grapher/graphile/graphile.temp.erb +23 -0
- data/lib/deps_grapher/html_writer.rb +16 -0
- data/lib/deps_grapher/input.rb +40 -0
- data/lib/deps_grapher/layer/registry.rb +32 -0
- data/lib/deps_grapher/layer.rb +75 -0
- data/lib/deps_grapher/logging.rb +21 -0
- data/lib/deps_grapher/matcher.rb +28 -0
- data/lib/deps_grapher/node.rb +65 -0
- data/lib/deps_grapher/plugin_dsl.rb +12 -0
- data/lib/deps_grapher/plugin_loader.rb +29 -0
- data/lib/deps_grapher/source.rb +51 -0
- data/lib/deps_grapher/source_cache/class_name_extractor.rb +60 -0
- data/lib/deps_grapher/source_cache/registry.rb +64 -0
- data/lib/deps_grapher/source_cache.rb +49 -0
- data/lib/deps_grapher/version.rb +5 -0
- data/lib/deps_grapher/vis/box.rb +21 -0
- data/lib/deps_grapher/vis/dot.rb +17 -0
- data/lib/deps_grapher/vis/template.erb +36 -0
- data/lib/deps_grapher/vis.rb +69 -0
- data/lib/deps_grapher/visualizer/base.rb +74 -0
- data/lib/deps_grapher/visualizer/color/registry.rb +30 -0
- data/lib/deps_grapher/visualizer/color.rb +62 -0
- data/lib/deps_grapher/visualizer/command_option.rb +22 -0
- data/lib/deps_grapher/visualizer/downloader.rb +42 -0
- data/lib/deps_grapher/visualizer/js_option.rb +39 -0
- data/lib/deps_grapher/visualizer/registry.rb +37 -0
- data/lib/deps_grapher/visualizer.rb +11 -0
- data/lib/deps_grapher.rb +42 -0
- metadata +135 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../cytoscape"
|
4
|
+
|
5
|
+
module DepsGrapher
|
6
|
+
class Cytoscape
|
7
|
+
class Klay < self
|
8
|
+
command_option "cy:klay", default: true
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def required_js
|
13
|
+
super.tap do |js|
|
14
|
+
js << "https://unpkg.com/klayjs/klay.js"
|
15
|
+
js << "https://raw.githubusercontent.com/cytoscape/cytoscape.js-klay/master/cytoscape-klay.js"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def layout_options
|
20
|
+
Visualizer::JsOption.new(
|
21
|
+
name: :klay,
|
22
|
+
nodeDimensionsIncludeLabels: true
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<title>Cytoscape</title>
|
6
|
+
<meta name=”viewport” content=”width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no″>
|
7
|
+
<% required_js.each do |js| -%>
|
8
|
+
<script src="<%= File.basename js %>"></script>
|
9
|
+
<% end -%>
|
10
|
+
<style>
|
11
|
+
#cy {
|
12
|
+
width: 100vw;
|
13
|
+
height: 100vh;
|
14
|
+
}
|
15
|
+
</style>
|
16
|
+
<script>
|
17
|
+
const backgroundColorMap = <%= color_map(:background).to_json %>;
|
18
|
+
document.addEventListener('DOMContentLoaded', function () {
|
19
|
+
const cy = cytoscape({
|
20
|
+
container: document.getElementById('cy'),
|
21
|
+
elements: <%= data.to_json %>,
|
22
|
+
layout: <%= layout_options %>,
|
23
|
+
style: [
|
24
|
+
{
|
25
|
+
selector: 'node',
|
26
|
+
style: {
|
27
|
+
'background-color': function(ele) {
|
28
|
+
return backgroundColorMap[ele.data('layer')];
|
29
|
+
},
|
30
|
+
'label': 'data(label)',
|
31
|
+
'font-size': '6%',
|
32
|
+
'width': function(ele) {
|
33
|
+
return Math.max(<%= min_width %>, ele.data('deps_count') * <%= coefficient %>);
|
34
|
+
},
|
35
|
+
'height': function(ele) {
|
36
|
+
return Math.max(<%= min_height %>, ele.data('deps_count') * <%= coefficient %>);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
},
|
40
|
+
<% Layer.names.each do |layer_name| -%>
|
41
|
+
{
|
42
|
+
selector: 'edge[layer="<%= layer_name %>"]',
|
43
|
+
style: {
|
44
|
+
'width': 1,
|
45
|
+
'line-color': '<%= arrow_color(layer_name) %>',
|
46
|
+
'target-arrow-color': '<%= arrow_color(layer_name) %>',
|
47
|
+
'target-arrow-shape': 'triangle',
|
48
|
+
'curve-style': 'bezier'
|
49
|
+
}
|
50
|
+
},
|
51
|
+
<% end -%>
|
52
|
+
],
|
53
|
+
});
|
54
|
+
<%= advanced_render %>
|
55
|
+
});
|
56
|
+
</script>
|
57
|
+
</head>
|
58
|
+
<body>
|
59
|
+
<div id="cy"></div>
|
60
|
+
</body>
|
61
|
+
</html>
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "visualizer/base"
|
4
|
+
|
5
|
+
module DepsGrapher
|
6
|
+
class Cytoscape < Visualizer::Base
|
7
|
+
def required_js
|
8
|
+
[
|
9
|
+
"https://unpkg.com/cytoscape/dist/cytoscape.min.js",
|
10
|
+
"https://unpkg.com/layout-base/layout-base.js"
|
11
|
+
]
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def template_path
|
17
|
+
File.expand_path File.join("cytoscape", "template.erb"), __dir__
|
18
|
+
end
|
19
|
+
|
20
|
+
def min_width
|
21
|
+
"10"
|
22
|
+
end
|
23
|
+
|
24
|
+
def min_height
|
25
|
+
"10"
|
26
|
+
end
|
27
|
+
|
28
|
+
def coefficient
|
29
|
+
"0.2 * 10"
|
30
|
+
end
|
31
|
+
|
32
|
+
def layout_options
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
def advanced_render
|
37
|
+
""
|
38
|
+
end
|
39
|
+
|
40
|
+
def data
|
41
|
+
[].tap do |array|
|
42
|
+
@nodes.each do |node|
|
43
|
+
next if skip_node?(node)
|
44
|
+
|
45
|
+
array << convert_node(node)
|
46
|
+
end
|
47
|
+
|
48
|
+
@edges.each do |edge|
|
49
|
+
next if skip_edge?(edge)
|
50
|
+
|
51
|
+
array << convert_edge(edge)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def convert_node(node)
|
57
|
+
{
|
58
|
+
group: :nodes,
|
59
|
+
data: {
|
60
|
+
id: node.id,
|
61
|
+
layer: node.layer,
|
62
|
+
label: node.label,
|
63
|
+
deps_count: node.deps_count
|
64
|
+
}
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def convert_edge(edge)
|
69
|
+
{
|
70
|
+
group: :edges,
|
71
|
+
data: {
|
72
|
+
id: edge.id,
|
73
|
+
source: edge.from.id,
|
74
|
+
target: edge.to.id,
|
75
|
+
layer: edge.from.layer
|
76
|
+
}
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def skip_node?(node)
|
81
|
+
options[:layers].present? && !options[:layers].include?(node.layer)
|
82
|
+
end
|
83
|
+
|
84
|
+
def skip_edge?(edge)
|
85
|
+
skip_node?(edge.from) || skip_node?(edge.to)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DepsGrapher
|
4
|
+
class DSL
|
5
|
+
def initialize(obj)
|
6
|
+
@obj = obj
|
7
|
+
end
|
8
|
+
|
9
|
+
def respond_to_missing?(symbol, include_private = false)
|
10
|
+
["#{symbol}=", symbol].each do |method_name|
|
11
|
+
return true if @obj.respond_to?(method_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def method_missing(symbol, *args, &block)
|
20
|
+
["#{symbol}=", symbol].each do |method_name|
|
21
|
+
next unless @obj.respond_to?(method_name)
|
22
|
+
|
23
|
+
parameters = @obj.method(method_name).parameters
|
24
|
+
|
25
|
+
return @obj.send(method_name) if parameters.empty?
|
26
|
+
|
27
|
+
if parameters.size == 1
|
28
|
+
parameter = parameters.first
|
29
|
+
case parameter[0]
|
30
|
+
when :req, :opt
|
31
|
+
return @obj.send(method_name, args.first.freeze)
|
32
|
+
when :block
|
33
|
+
return @obj.send(method_name, &block)
|
34
|
+
else
|
35
|
+
raise ArgumentError, "unsupported parameter type: #{parameter[0]}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
next unless parameters.size == 2
|
40
|
+
|
41
|
+
arg_type_1st, arg_type_2nd = parameters.flat_map(&:first)
|
42
|
+
return @obj.send(method_name, args.first.freeze, &block) if %i[req opt].include?(arg_type_1st) && arg_type_2nd == :block
|
43
|
+
|
44
|
+
raise ArgumentError, "unsupported parameter types: #{arg_type_1st}, #{arg_type_2nd}"
|
45
|
+
end
|
46
|
+
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DepsGrapher
|
4
|
+
class Edge
|
5
|
+
class << self
|
6
|
+
def fetch(from, to)
|
7
|
+
id = generate_id from, to
|
8
|
+
registry.fetch id
|
9
|
+
end
|
10
|
+
|
11
|
+
def add(from, to)
|
12
|
+
return nil if from.nil? || to.nil?
|
13
|
+
|
14
|
+
id = generate_id from, to
|
15
|
+
|
16
|
+
return nil if registry.key?(id) || from.id == to.id
|
17
|
+
|
18
|
+
to.parent = from.id
|
19
|
+
to.increment_deps_count!
|
20
|
+
|
21
|
+
registry[id] = new id, from, to
|
22
|
+
end
|
23
|
+
|
24
|
+
def all
|
25
|
+
registry.values
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def generate_id(from, to)
|
31
|
+
"e:#{from.id}:#{to.id}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def registry
|
35
|
+
@registry ||= {}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private_class_method :new
|
40
|
+
|
41
|
+
attr_accessor :id, :from, :to
|
42
|
+
|
43
|
+
def initialize(id, from, to)
|
44
|
+
@id = id
|
45
|
+
@from = from
|
46
|
+
@to = to
|
47
|
+
end
|
48
|
+
|
49
|
+
def eql?(other)
|
50
|
+
other.is_a?(self.class) && id == other.id
|
51
|
+
end
|
52
|
+
alias == eql?
|
53
|
+
|
54
|
+
def hash
|
55
|
+
id.hash
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DepsGrapher
|
4
|
+
class Event
|
5
|
+
class << self
|
6
|
+
def generate_key(name, const_name, method)
|
7
|
+
return nil if const_name.nil? && method.nil?
|
8
|
+
|
9
|
+
key = name.to_s.dup
|
10
|
+
key << ".#{const_name}" if const_name
|
11
|
+
key << ".#{method}" if method
|
12
|
+
key
|
13
|
+
end
|
14
|
+
|
15
|
+
def add(name:, const_name:, location:, method: nil)
|
16
|
+
key = generate_key name, const_name, method
|
17
|
+
|
18
|
+
return nil if key.nil?
|
19
|
+
|
20
|
+
registry[key] ||= new(
|
21
|
+
name: name,
|
22
|
+
const_name: const_name,
|
23
|
+
location: location,
|
24
|
+
method: method
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def registry
|
31
|
+
@registry ||= {}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private_class_method :new
|
36
|
+
|
37
|
+
attr_accessor :name, :const_name, :location, :method
|
38
|
+
|
39
|
+
def initialize(name:, const_name:, location:, method:)
|
40
|
+
@name = name
|
41
|
+
@const_name = const_name
|
42
|
+
@location = location
|
43
|
+
@method = method
|
44
|
+
@skip_processing = false
|
45
|
+
end
|
46
|
+
|
47
|
+
def key
|
48
|
+
self.class.generate_key name, const_name, method
|
49
|
+
end
|
50
|
+
|
51
|
+
def processing!
|
52
|
+
@skip_processing = false
|
53
|
+
end
|
54
|
+
|
55
|
+
def skip_processing!
|
56
|
+
@skip_processing = true
|
57
|
+
end
|
58
|
+
|
59
|
+
def processing?
|
60
|
+
!@skip_processing
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DepsGrapher
|
4
|
+
class NullGraph
|
5
|
+
def add_edge(_); end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Graph
|
9
|
+
def initialize
|
10
|
+
@graph = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_edge(edge)
|
14
|
+
@graph[edge.from] ||= []
|
15
|
+
@graph[edge.from] << edge.to
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_path(source_path_matcher, target_path_matcher)
|
19
|
+
paths = []
|
20
|
+
visited = {}
|
21
|
+
|
22
|
+
@graph.each_key do |start_node|
|
23
|
+
next if source_path_matcher && !source_path_matcher.match?(start_node.class_name)
|
24
|
+
|
25
|
+
dfs start_node, target_path_matcher, visited, [start_node], paths
|
26
|
+
end
|
27
|
+
|
28
|
+
nodes = Set.new
|
29
|
+
edges = Set.new
|
30
|
+
|
31
|
+
paths.each do |path|
|
32
|
+
collect! path, nodes, edges
|
33
|
+
end
|
34
|
+
|
35
|
+
[nodes, edges]
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def dfs(current, target_path_matcher, visited, path, paths)
|
41
|
+
return if visited[current]
|
42
|
+
|
43
|
+
visited[current] = true
|
44
|
+
|
45
|
+
if target_path_matcher.match?(current.class_name)
|
46
|
+
paths << path
|
47
|
+
else
|
48
|
+
@graph[current]&.each do |node|
|
49
|
+
dfs node, target_path_matcher, visited, path + [node], paths
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
visited[current] = false
|
54
|
+
end
|
55
|
+
|
56
|
+
def collect!(path, nodes, edges)
|
57
|
+
current_node = nil
|
58
|
+
path.each do |node|
|
59
|
+
nodes << node
|
60
|
+
|
61
|
+
if current_node.nil?
|
62
|
+
current_node = node
|
63
|
+
next
|
64
|
+
end
|
65
|
+
|
66
|
+
edges << Edge.fetch(current_node, node)
|
67
|
+
current_node = node
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DepsGrapher
|
4
|
+
module Graphile
|
5
|
+
class Generator
|
6
|
+
def initialize(config)
|
7
|
+
@config = config
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(dest)
|
11
|
+
if dest
|
12
|
+
File.write dest, read_template(temp: false)
|
13
|
+
return dest
|
14
|
+
end
|
15
|
+
|
16
|
+
path = nil
|
17
|
+
|
18
|
+
# unlink this temp file by GC
|
19
|
+
Tempfile.open("graphile_generator", config.cache_dir) do |f|
|
20
|
+
f.write read_template(temp: true)
|
21
|
+
path = f.path
|
22
|
+
end
|
23
|
+
|
24
|
+
path
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
attr_reader :config
|
30
|
+
|
31
|
+
def read_template(temp:)
|
32
|
+
b = config.instance_eval { binding }
|
33
|
+
ERB.new(File.read(resolve_path(temp)), trim_mode: "-").result(b)
|
34
|
+
end
|
35
|
+
|
36
|
+
def resolve_path(temp)
|
37
|
+
file_name = temp ? "graphile.temp.erb" : "graphile.erb"
|
38
|
+
File.expand_path(file_name, __dir__)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# directory settings
|
4
|
+
# customize for your project
|
5
|
+
output_dir File.expand_path File.join("tmp", "deps_grapher", "graph")
|
6
|
+
cache_dir File.expand_path File.join("tmp", "deps_grapher", "cache")
|
7
|
+
|
8
|
+
root_dir = "<%= root_dir %>"
|
9
|
+
lib_root_dir = File.join(root_dir, "lib")
|
10
|
+
layer_root_dir = File.join(lib_root_dir, "deps_grapher")
|
11
|
+
|
12
|
+
# default visualizer setting
|
13
|
+
visualizer "<%= visualizer %>"
|
14
|
+
|
15
|
+
ast_processor_policy do
|
16
|
+
exclude_const /\ADepsGrapher\z/
|
17
|
+
|
18
|
+
# advanced_const_resolver do |ast_node|
|
19
|
+
# # some advanced logic using ast_node of parser gem
|
20
|
+
# # this block should return a string of const name or nil
|
21
|
+
# # if return nil, the const name will be resolved by default logic
|
22
|
+
# end
|
23
|
+
end
|
24
|
+
|
25
|
+
layer do
|
26
|
+
name :deps_grapher
|
27
|
+
visible true
|
28
|
+
|
29
|
+
source do
|
30
|
+
root lib_root_dir
|
31
|
+
exclude_pattern %r{/(command|cytoscape|vis|visualizer)}
|
32
|
+
end
|
33
|
+
|
34
|
+
color do
|
35
|
+
background "#FF5252"
|
36
|
+
border "#EF5350"
|
37
|
+
font "#FF5252"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
layer do
|
42
|
+
name :command
|
43
|
+
visible true
|
44
|
+
|
45
|
+
source do
|
46
|
+
root File.join(layer_root_dir, "command")
|
47
|
+
end
|
48
|
+
|
49
|
+
color do
|
50
|
+
background "#512DA8"
|
51
|
+
border "#673AB7"
|
52
|
+
font "#512DA8"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
layer do
|
57
|
+
name :cytoscape
|
58
|
+
visible true
|
59
|
+
|
60
|
+
source do
|
61
|
+
root layer_root_dir
|
62
|
+
glob_pattern ["cytoscape.rb", "cytoscape/**/*.rb"]
|
63
|
+
end
|
64
|
+
|
65
|
+
color do
|
66
|
+
background "#448AFF"
|
67
|
+
border "#42A5F5"
|
68
|
+
font "#448AFF"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
layer do
|
73
|
+
name :vis
|
74
|
+
visible true
|
75
|
+
|
76
|
+
source do
|
77
|
+
root layer_root_dir
|
78
|
+
glob_pattern ["vis.rb", "vis/**/*.rb"]
|
79
|
+
end
|
80
|
+
|
81
|
+
color do
|
82
|
+
background "#00B8D4"
|
83
|
+
border "#00ACC1"
|
84
|
+
font "#00B8D4"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
layer do
|
89
|
+
name :visualizer
|
90
|
+
visible true
|
91
|
+
|
92
|
+
source do
|
93
|
+
root layer_root_dir
|
94
|
+
glob_pattern ["visualizer.rb", "visualizer/**/*.rb"]
|
95
|
+
end
|
96
|
+
|
97
|
+
color do
|
98
|
+
background "#00C853"
|
99
|
+
border "#4CAF50"
|
100
|
+
font "#00C853"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
with_plugin do |plugin_dir|
|
105
|
+
layer do
|
106
|
+
name :plugin
|
107
|
+
visible true
|
108
|
+
|
109
|
+
source do
|
110
|
+
root plugin_dir
|
111
|
+
end
|
112
|
+
|
113
|
+
color do
|
114
|
+
background "#607D8B"
|
115
|
+
border "#78909C"
|
116
|
+
font "#607D8B"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
output_dir File.join(Dir.home, ".deps_grapher", "graph")
|
4
|
+
cache_dir File.join(Dir.home, ".deps_grapher", "cache")
|
5
|
+
cache_ttl 60 * 5 # 5 minutes
|
6
|
+
|
7
|
+
root_dir = "<%= root_dir %>"
|
8
|
+
|
9
|
+
layer do
|
10
|
+
name :<%= File.basename(root_dir) %>
|
11
|
+
visible true
|
12
|
+
|
13
|
+
source do
|
14
|
+
root root_dir
|
15
|
+
exclude_pattern %r{/(?:vendor|spec)/}
|
16
|
+
end
|
17
|
+
|
18
|
+
color do
|
19
|
+
background "#448AFF"
|
20
|
+
border "#42A5F5"
|
21
|
+
font "#448AFF"
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DepsGrapher
|
4
|
+
class HtmlWriter
|
5
|
+
attr_reader :path
|
6
|
+
|
7
|
+
def initialize(output_dir)
|
8
|
+
FileUtils.mkdir_p output_dir
|
9
|
+
@path = File.join output_dir, "index.html"
|
10
|
+
end
|
11
|
+
|
12
|
+
def write(content)
|
13
|
+
File.write path, content
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DepsGrapher
|
4
|
+
class Input
|
5
|
+
def initialize(config)
|
6
|
+
@config = config
|
7
|
+
end
|
8
|
+
|
9
|
+
def files
|
10
|
+
FileUtils.rm_rf File.dirname(config.cache_dir) if config.clean
|
11
|
+
|
12
|
+
set_layers_visibility!
|
13
|
+
extract_files_from_layers
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :config
|
19
|
+
|
20
|
+
def set_layers_visibility!
|
21
|
+
layer_visibility = config.visualizer_options[:layers]
|
22
|
+
|
23
|
+
config.layers.each_value do |layer|
|
24
|
+
layer.visible = layer_visibility.include? layer.name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def extract_files_from_layers
|
29
|
+
config.layers.values.select(&:visible).each_with_object([]) do |layer, files|
|
30
|
+
source = layer.source
|
31
|
+
|
32
|
+
source.files.each do |file|
|
33
|
+
next if file == config.path
|
34
|
+
|
35
|
+
files << file
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DepsGrapher
|
4
|
+
class Layer
|
5
|
+
module Registry
|
6
|
+
class << self
|
7
|
+
def fetch(file_path)
|
8
|
+
registry[file_path] || Layer::Default
|
9
|
+
end
|
10
|
+
alias [] fetch
|
11
|
+
|
12
|
+
def register(file_path, layer)
|
13
|
+
registry[file_path] ||= layer
|
14
|
+
end
|
15
|
+
|
16
|
+
def exist?(file_path)
|
17
|
+
registry.key? file_path
|
18
|
+
end
|
19
|
+
|
20
|
+
def all
|
21
|
+
registry.values
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def registry
|
27
|
+
@registry ||= {}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|