visualize_packs 0.5.4 → 0.5.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/bin/visualize_packs +16 -2
- data/lib/graph.dot.erb +17 -13
- data/lib/options.rb +2 -1
- data/lib/visualize_packs.rb +33 -36
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd8d9c383e056e9ca083b753f77d14ff951d2f0245d3cccb8570eb4a6ac5cf72
|
4
|
+
data.tar.gz: 98edaf0dd58faabbfc57e3d42b24ca43c5f5ea65227627f519815811a03970a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 541e54ff0460769bccc2d7e8788a57a86c1fbb5929af3cc7b01bbcac36daf0f26bf7378745e89bf189c51fa63b3484c3b34d6493e85e1603cf976f1b6780945e
|
7
|
+
data.tar.gz: d0622a5d36ee4816b497f6fb2488be4ee1a86679729432f8304b27ce1120ff16e28fd1baefc790a1372b227932d9cd54ca5c13e7221f91271d33e717ff01f682
|
data/README.md
CHANGED
@@ -16,7 +16,7 @@ This will generate a local dependency diagram for every pack in your app
|
|
16
16
|
find . -iname 'package.yml' | sed 's/\/package.yml//g' | sed 's/\.\///' | xargs -I % sh -c "bundle exec visualize_packs --only=% > %/packs.dot && dot %/packs.dot -Tpng -o %/packs.png"
|
17
17
|
```
|
18
18
|
|
19
|
-
If your app is large and has many packages and
|
19
|
+
If your app is large and has many packages and todos, the above graphs will likely be too big. Try this version to get only the edges to and from the focus package for each diagram:
|
20
20
|
|
21
21
|
```
|
22
22
|
find . -iname 'package.yml' | sed 's/\/package.yml//g' | sed 's/\.\///' | xargs -I % sh -c "bundle exec visualize_packs --only=% --only-edges-to-focus > %/packs.dot && dot %/packs.dot -Tpng -o %/packs.png"
|
data/bin/visualize_packs
CHANGED
@@ -10,20 +10,34 @@ require_relative '../lib/options'
|
|
10
10
|
|
11
11
|
options = Options.new
|
12
12
|
|
13
|
+
supported_todo_types = %w[
|
14
|
+
privacy
|
15
|
+
architecture
|
16
|
+
visibility
|
17
|
+
dependency
|
18
|
+
].sort.freeze
|
19
|
+
|
20
|
+
def validated_list(o, valid_arguments)
|
21
|
+
list = o.to_s.split(",").uniq
|
22
|
+
raise OptionParser::InvalidArgument, o unless (list - valid_arguments).empty?
|
23
|
+
list
|
24
|
+
end
|
25
|
+
|
13
26
|
OptionParser.new do |opt|
|
14
27
|
opt.on('--no-legend', "Don't show legend") { |o| options.show_legend = false }
|
15
28
|
opt.on('--no-layers', "Don't show architectural layers") { |o| options.show_layers = false }
|
16
29
|
opt.on('--no-dependencies', "Don't show accepted dependencies") { |o| options.show_dependencies = false }
|
17
30
|
opt.on('--no-todos', "Don't show package todos") { |o| options.show_todos = false }
|
31
|
+
opt.on('--only-todo-types=privacy,architecture,etc', "Show only these types of todos (supported types: #{supported_todo_types.join(', ')})") { |o| options.only_todo_types = validated_list(o, supported_todo_types) }
|
18
32
|
opt.on('--no-privacy', "Don't show privacy enforcement") { |o| options.show_privacy = false }
|
19
33
|
opt.on('--no-teams', "Don't show team colors") { |o| options.show_teams = false }
|
20
34
|
|
21
35
|
opt.on('--focus-on=PACKAGE', "Focus on a specific package") { |o| options.focus_package = o }
|
22
36
|
opt.on('--only-edges-to-focus', "If focus is set, this shows only the edges to/from the focus node instead of all edges in the focussed graph. This only has effect when --focus-on is set.") { |o| options.show_only_edges_to_focus_package = true }
|
23
37
|
|
24
|
-
opt.on('--
|
38
|
+
opt.on('--roll-nested-into-parent-packs', "Don't show nested packages (not counting root). Connect edges to top-level package instead") { |o| options.roll_nested_into_parent_packs = true }
|
25
39
|
opt.on('--focus_folder=FOLDER', "Draw package diagram only for packages in FOLDER") { |o| options.focus_folder = o }
|
26
|
-
opt.on('--no_nested_relationships', "Don't draw nested
|
40
|
+
opt.on('--no_nested_relationships', "Don't draw relationships between parents and nested packs") { |o| options.show_nested_relationships = false }
|
27
41
|
|
28
42
|
opt.on('--exclude-packs=pack1,pack2,etc', "Exclude these packs from diagram") { |o| options.exclude_packs = o.to_s.split(",") }
|
29
43
|
|
data/lib/graph.dot.erb
CHANGED
@@ -81,26 +81,30 @@ digraph package_diagram {
|
|
81
81
|
<%- end -%>
|
82
82
|
<%- if options.show_todos -%>
|
83
83
|
<%- all_packages.each do |package| -%>
|
84
|
-
<%-
|
85
|
-
<%-
|
86
|
-
<%-
|
87
|
-
|
88
|
-
|
89
|
-
|
84
|
+
<%- filtered_todos = package.violations -%>
|
85
|
+
<%- if options.only_todo_types.any? -%>
|
86
|
+
<%- filtered_todos = filtered_todos.select { options.only_todo_types.include?(_1.type) } -%>
|
87
|
+
<%- end -%>
|
88
|
+
<%- todos_by_package = filtered_todos.group_by(&:to_package_name) -%>
|
89
|
+
<%- todos_by_package.keys.each do |todos_to_package| -%>
|
90
|
+
<%- todo_types = todos_by_package[todos_to_package].group_by(&:type) -%>
|
91
|
+
<%- todo_types.keys.each do |todo_type| -%>
|
92
|
+
<%- if show_edge.call(package.name, todos_to_package) -%>
|
93
|
+
"<%= package.name -%>" -> "<%= todos_to_package -%>"<%= todo_type == 'privacy' ? ':private' : '' -%> [ color=darkred style=dashed
|
90
94
|
constraint=false
|
91
|
-
# headlabel="<%=
|
92
|
-
<%- if
|
95
|
+
# headlabel="<%= todo_type -%>"
|
96
|
+
<%- if todo_type == 'privacy' -%>
|
93
97
|
arrowhead=crow
|
94
|
-
<%- elsif
|
98
|
+
<%- elsif todo_type == 'architecture' -%>
|
95
99
|
arrowhead=invodot
|
96
|
-
<%- elsif
|
100
|
+
<%- elsif todo_type == 'visibility' -%>
|
97
101
|
arrowhead=obox
|
98
|
-
<%- elsif
|
102
|
+
<%- elsif todo_type == 'dependency' -%>
|
99
103
|
arrowhead=odot
|
100
104
|
<%- end -%>
|
101
105
|
<%-
|
102
106
|
max_edge_width = 10
|
103
|
-
edge_width = (
|
107
|
+
edge_width = (todo_types[todo_type].count / max_todo_count.to_f * max_edge_width).to_i
|
104
108
|
-%>
|
105
109
|
penwidth=<%= edge_width -%>
|
106
110
|
]
|
@@ -135,7 +139,7 @@ digraph package_diagram {
|
|
135
139
|
K [ fontsize=12 shape=box label="package"]
|
136
140
|
L [ fontsize=12 shape=box label="package"]
|
137
141
|
A -> B [label="accepted dependency" color=darkgreen]
|
138
|
-
C -> D [label="
|
142
|
+
C -> D [label="privacy todo" color=darkred style=dashed arrowhead=crow]
|
139
143
|
E -> F [label="architecture todo" color=darkred style=dashed arrowhead=invodot]
|
140
144
|
G -> H [label="visibility todo" color=darkred style=dashed arrowhead=obox]
|
141
145
|
I -> J [label="dependency todo" color=darkred style=dashed arrowhead=odot]
|
data/lib/options.rb
CHANGED
@@ -8,13 +8,14 @@ class Options < T::Struct
|
|
8
8
|
prop :show_layers, T::Boolean, default: true
|
9
9
|
prop :show_dependencies, T::Boolean, default: true
|
10
10
|
prop :show_todos, T::Boolean, default: true
|
11
|
+
prop :only_todo_types, T::Array[String], default: []
|
11
12
|
prop :show_privacy, T::Boolean, default: true
|
12
13
|
prop :show_teams, T::Boolean, default: true
|
13
14
|
|
14
15
|
prop :focus_package, T.nilable(String)
|
15
16
|
prop :show_only_edges_to_focus_package, T::Boolean, default: false
|
16
17
|
|
17
|
-
prop :
|
18
|
+
prop :roll_nested_into_parent_packs, T::Boolean, default: false
|
18
19
|
prop :focus_folder, T.nilable(String)
|
19
20
|
prop :show_nested_relationships, T::Boolean, default: true
|
20
21
|
|
data/lib/visualize_packs.rb
CHANGED
@@ -14,13 +14,13 @@ module VisualizePacks
|
|
14
14
|
all_packages = filtered(packages, options.focus_package, options.focus_folder, options.exclude_packs).sort_by {|x| x.name }
|
15
15
|
all_package_names = all_packages.map &:name
|
16
16
|
|
17
|
-
all_packages = remove_nested_packs(all_packages) if options.
|
17
|
+
all_packages = remove_nested_packs(all_packages) if options.roll_nested_into_parent_packs
|
18
18
|
|
19
19
|
show_edge = show_edge_builder(options, all_package_names)
|
20
20
|
node_color = node_color_builder()
|
21
|
-
|
21
|
+
max_todo_count = max_todo_count(all_packages, show_edge)
|
22
22
|
|
23
|
-
title = diagram_title(options,
|
23
|
+
title = diagram_title(options, max_todo_count)
|
24
24
|
|
25
25
|
architecture_layers = (raw_config['architecture_layers'] || []) + ["NotInLayer"]
|
26
26
|
grouped_packages = architecture_layers.inject({}) do |result, key|
|
@@ -52,7 +52,7 @@ module VisualizePacks
|
|
52
52
|
package.config.dig("metadata", "owner") || package.config["owner"]
|
53
53
|
end
|
54
54
|
|
55
|
-
def self.diagram_title(options,
|
55
|
+
def self.diagram_title(options, max_todo_count)
|
56
56
|
app_name = File.basename(Dir.pwd)
|
57
57
|
focus_edge_info = options.focus_package && options.show_only_edges_to_focus_package ? "showing only edges to/from focus pack" : "showing all edges between visible packs"
|
58
58
|
focus_info = options.focus_package || options.focus_folder ? "Focus on #{[options.focus_package, options.focus_folder].compact.join(' and ')} (#{focus_edge_info})" : "All packs"
|
@@ -62,28 +62,26 @@ module VisualizePacks
|
|
62
62
|
options.show_layers ? nil : "hiding layers",
|
63
63
|
options.show_dependencies ? nil : "hiding dependencies",
|
64
64
|
options.show_todos ? nil : "hiding todos",
|
65
|
+
options.only_todo_types.empty? ? nil : "only #{limited_sentence(options.only_todo_types)} todos",
|
65
66
|
options.show_privacy ? nil : "hiding privacy",
|
66
67
|
options.show_teams ? nil : "hiding teams",
|
67
|
-
options.
|
68
|
+
options.roll_nested_into_parent_packs ? "hiding nested packs" : nil,
|
68
69
|
options.show_nested_relationships ? nil : "hiding nested relationships",
|
69
|
-
options.exclude_packs.empty? ? nil : "excluding pack#{options.exclude_packs.size > 1 ? 's' : ''}: #{
|
70
|
+
options.exclude_packs.empty? ? nil : "excluding pack#{options.exclude_packs.size > 1 ? 's' : ''}: #{limited_sentence(options.exclude_packs)}",
|
70
71
|
].compact.join(', ').strip
|
71
72
|
main_title = "#{app_name}: #{focus_info}#{skipped_info != '' ? ' - ' + skipped_info : ''}"
|
72
73
|
sub_title = ""
|
73
|
-
if options.show_todos &&
|
74
|
-
sub_title = "<br/><font point-size='12'>Widest todo edge is #{
|
74
|
+
if options.show_todos && max_todo_count
|
75
|
+
sub_title = "<br/><font point-size='12'>Widest todo edge is #{max_todo_count} todo#{max_todo_count > 1 ? 's' : ''}</font>"
|
75
76
|
end
|
76
77
|
"<<b>#{main_title}</b>#{sub_title}>"
|
77
78
|
end
|
78
79
|
|
79
|
-
def self.
|
80
|
-
|
81
|
-
|
82
|
-
exclude_packs.first
|
83
|
-
when 2
|
84
|
-
exclude_packs.join(" and ")
|
80
|
+
def self.limited_sentence(list)
|
81
|
+
if list.size <= 2
|
82
|
+
list.join(" and ")
|
85
83
|
else
|
86
|
-
"#{
|
84
|
+
"#{list[0, 2].join(", ")}, and #{list.size - 2} more"
|
87
85
|
end
|
88
86
|
end
|
89
87
|
|
@@ -115,22 +113,22 @@ module VisualizePacks
|
|
115
113
|
end
|
116
114
|
end
|
117
115
|
|
118
|
-
def self.
|
119
|
-
|
116
|
+
def self.max_todo_count(all_packages, show_edge)
|
117
|
+
todo_counts = {}
|
120
118
|
all_packages.each do |package|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
if show_edge.call(package.name,
|
126
|
-
key = "#{package.name}->#{
|
127
|
-
|
128
|
-
#
|
119
|
+
todos_by_package = package.violations.group_by(&:to_package_name)
|
120
|
+
todos_by_package.keys.each do |todos_to_package|
|
121
|
+
todo_types = todos_by_package[todos_to_package].group_by(&:type)
|
122
|
+
todo_types.keys.each do |todo_type|
|
123
|
+
if show_edge.call(package.name, todos_to_package)
|
124
|
+
key = "#{package.name}->#{todos_to_package}:#{todo_type}"
|
125
|
+
todo_counts[key] = todo_types[todo_type].count
|
126
|
+
# todo_counts[key] += 1
|
129
127
|
end
|
130
128
|
end
|
131
129
|
end
|
132
130
|
end
|
133
|
-
|
131
|
+
todo_counts.values.max
|
134
132
|
end
|
135
133
|
|
136
134
|
def self.filtered(packages, filter_package, filter_folder, exclude_packs)
|
@@ -161,13 +159,12 @@ module VisualizePacks
|
|
161
159
|
def self.all_nested_packages(all_package_names)
|
162
160
|
all_package_names.reject { |p| p == '.' }.inject({}) do |result, package|
|
163
161
|
package_map_tally = all_package_names.map { |other_package| Pathname.new(package).parent.to_s.include?(other_package) }
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
end
|
162
|
+
|
163
|
+
acc = []
|
164
|
+
all_package_names.each_with_index { |pack, idx| acc << pack if package_map_tally[idx] }
|
165
|
+
acc.sort_by(&:length)
|
166
|
+
result[package] = acc.first unless acc.empty?
|
167
|
+
|
171
168
|
result
|
172
169
|
end
|
173
170
|
end
|
@@ -203,7 +200,7 @@ module VisualizePacks
|
|
203
200
|
nested_packages[d] || d
|
204
201
|
end.uniq.reject { |p| p == package.name }
|
205
202
|
|
206
|
-
|
203
|
+
morphed_todos = package.violations.map do |v|
|
207
204
|
ParsePackwerk::Violation.new(
|
208
205
|
type: v.type,
|
209
206
|
to_package_name: nested_packages[v.to_package_name] || v.to_package_name,
|
@@ -221,10 +218,10 @@ module VisualizePacks
|
|
221
218
|
metadata: package.metadata,
|
222
219
|
dependencies: morphed_dependencies,
|
223
220
|
config: package.config,
|
224
|
-
violations:
|
221
|
+
violations: morphed_todos
|
225
222
|
)
|
226
223
|
# add dependencies TO nested packages to top-level package
|
227
|
-
# add
|
224
|
+
# add todos TO nested packages to top-level package
|
228
225
|
end
|
229
226
|
end
|
230
227
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: visualize_packs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gusto Engineers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-08-
|
11
|
+
date: 2023-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|