visualize_packs 0.5.19 → 0.5.21
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/bin/visualize_packs +11 -3
- data/lib/graph.dot.erb +16 -9
- data/lib/visualize_packs/options.rb +4 -3
- data/lib/visualize_packs.rb +120 -45
- 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: 72b6b3b6121cd9e3a2f30a4b927882be78fab2bbff55d2168100552cdf5b8276
|
4
|
+
data.tar.gz: 2281e4d89e00c73e18ed025b8b636d2cbafef28addc4955ff4f82ea22ddc442d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b9affb4f63a85e56125c00e412373d60c705f793c8da118d0e11899b1dcff0e143cade00d4d2562d23d075e3f2a58d9be594cc751e6ee67038a5d591e6c1c27
|
7
|
+
data.tar.gz: e99623cfaaa59016939a675a500f12357da8401d2fb3ac6e87c37b1a1f861c5976665e59e830f2fc44fe38c33e7cc6926721e264a9516e2dd57df20e1bff95dc
|
data/bin/visualize_packs
CHANGED
@@ -17,11 +17,12 @@ OptionParser.new do |opt|
|
|
17
17
|
opt.on('--no-layers', "Don't show architectural layers") { |o| options.show_layers = false }
|
18
18
|
opt.on('--no-visibility-arrows', "Don't show visibility arrows") { |o| options.show_visibility = false }
|
19
19
|
|
20
|
-
opt.on('--no-todo-
|
21
|
-
opt.on("--
|
22
|
-
opt.on("--use-todos-for-layout", "Show only the selected types of todos. Comma-separated list of #{EdgeTodoTypes.values.map &:serialize}") { |o| options.
|
20
|
+
opt.on('--no-todo-edges', "Don't show todos for package relationships") { |o| options.show_relationship_todos = false }
|
21
|
+
opt.on("--edge-todo-types=STRING", "Show only the selected types of relationship todos. Comma-separated list of #{EdgeTodoTypes.values.map &:serialize}") { |o| options.relationship_todo_types = o.to_s.split(",").uniq.map { EdgeTodoTypes.deserialize(_1) } }
|
22
|
+
opt.on("--use-edge-todos-for-layout", "Show only the selected types of relationship todos. Comma-separated list of #{EdgeTodoTypes.values.map &:serialize}") { |o| options.use_relationship_todos_for_layout = true }
|
23
23
|
|
24
24
|
opt.on('--no-teams', "Don't show team colors") { |o| options.show_teams = false }
|
25
|
+
opt.on('--no-node-todos', "Don't show package-based todos") { |o| options.show_node_todos = false }
|
25
26
|
|
26
27
|
opt.on('--focus-pack=STRING', "Focus on a specific pack(s). Comma-separated list of packs. Wildcards supported: 'packs/*'") { |o| options.focus_pack = o.to_s.split(",") }
|
27
28
|
opt.on('--focus-pack-edge-mode=STRING', "If focus-pack is set, this shows only between focussed packs (when set to none) or the edges into / out of / in and out of the focus packs to non-focus packs (which will be re-added to the graph). One of #{FocusPackEdgeDirection.values.map &:serialize}") { |o| options.show_only_edges_to_focus_pack = FocusPackEdgeDirection.deserialize(o) }
|
@@ -34,6 +35,13 @@ OptionParser.new do |opt|
|
|
34
35
|
|
35
36
|
opt.on('--title=STRING', "Set a custom diagram title") { |o| options.title = o }
|
36
37
|
|
38
|
+
opt.on('-V', '--version', "Show version") do
|
39
|
+
spec_path = File.expand_path("../visualize_packs.gemspec", __dir__)
|
40
|
+
spec = Gem::Specification::load(spec_path)
|
41
|
+
puts "Version #{spec.version}"
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
|
37
45
|
opt.on_tail("-h", "--help", "Show this message") do
|
38
46
|
puts opt
|
39
47
|
exit
|
data/lib/graph.dot.erb
CHANGED
@@ -46,11 +46,18 @@ digraph package_diagram {
|
|
46
46
|
<table border='0' cellborder='1' cellspacing='0' cellpadding='4'>
|
47
47
|
<tr> <td port='private'> <%= package.name -%> </td> </tr>
|
48
48
|
</table>
|
49
|
-
|
49
|
+
</td></tr>
|
50
|
+
<%- if options.show_node_todos && node_protection.(package.name) != "" -%>
|
51
|
+
<tr><td CELLPADDING='1' ALIGN='RIGHT'><%= node_protection.(package.name) %></td></tr>
|
52
|
+
<%- end -%>
|
53
|
+
</table>
|
50
54
|
>
|
51
55
|
<%- else -%><
|
52
56
|
<table border='0' cellborder='1' cellspacing='0' cellpadding='4'>
|
53
57
|
<tr> <td align='left'> <%= package.name -%> </td> </tr>
|
58
|
+
<%- if options.show_node_todos && node_protection.(package.name) != "" -%>
|
59
|
+
<tr><td CELLPADDING='1' ALIGN='RIGHT'><%= node_protection.(package.name) %></td></tr>
|
60
|
+
<%- end -%>
|
54
61
|
</table>
|
55
62
|
>
|
56
63
|
<%- end -%>
|
@@ -79,16 +86,16 @@ digraph package_diagram {
|
|
79
86
|
<%- end -%>
|
80
87
|
<%- end -%>
|
81
88
|
<%- end -%>
|
82
|
-
<%- if options.
|
89
|
+
<%- if options.show_relationship_todos -%>
|
83
90
|
<%- all_packages.each do |package| -%>
|
84
|
-
<%- filtered_todos = package.violations.select { options.
|
91
|
+
<%- filtered_todos = package.violations.select { options.relationship_todo_types.include?(EdgeTodoTypes.deserialize(_1.type)) } -%>
|
85
92
|
<%- todos_by_package = filtered_todos.group_by(&:to_package_name) -%>
|
86
93
|
<%- todos_by_package.keys.each do |todos_to_package| -%>
|
87
94
|
<%- todo_types = todos_by_package[todos_to_package].group_by(&:type) -%>
|
88
95
|
<%- todo_types.keys.each do |todo_type| -%>
|
89
96
|
<%- if show_edge.call(package.name, todos_to_package) -%>
|
90
97
|
"<%= package.name -%>" -> "<%= todos_to_package -%>"<%= todo_type == 'privacy' ? ':private' : '' -%> [
|
91
|
-
<%- if !options.
|
98
|
+
<%- if !options.use_relationship_todos_for_layout -%>
|
92
99
|
constraint=false
|
93
100
|
<%- end -%>
|
94
101
|
# headlabel="<%= todo_type -%>"
|
@@ -143,23 +150,23 @@ digraph package_diagram {
|
|
143
150
|
N [ fontsize=12 shape=box label="package"]
|
144
151
|
M -> N [label="visibile to" <%= VisualizePacks::ArrowHead::ConfiguredVisibileTo.serialize %>]
|
145
152
|
<%- end -%>
|
146
|
-
<%- if options.
|
147
|
-
<%- if options.
|
153
|
+
<%- if options.show_relationship_todos -%>
|
154
|
+
<%- if options.relationship_todo_types.include?(EdgeTodoTypes::Privacy) -%>
|
148
155
|
C [ fontsize=12 shape=box label="package"]
|
149
156
|
D [ fontsize=12 shape=box label="package"]
|
150
157
|
C -> D [label="privacy todo" <%= VisualizePacks::ArrowHead::PrivacyTodo.serialize %>]
|
151
158
|
<%- end -%>
|
152
|
-
<%- if options.
|
159
|
+
<%- if options.relationship_todo_types.include?(EdgeTodoTypes::Architecture) -%>
|
153
160
|
E [ fontsize=12 shape=box label="package"]
|
154
161
|
F [ fontsize=12 shape=box label="package"]
|
155
162
|
E -> F [label="architecture todo" <%= VisualizePacks::ArrowHead::ArchitectureTodo.serialize %>]
|
156
163
|
<%- end -%>
|
157
|
-
<%- if options.
|
164
|
+
<%- if options.relationship_todo_types.include?(EdgeTodoTypes::Visibility) -%>
|
158
165
|
G [ fontsize=12 shape=box label="package"]
|
159
166
|
H [ fontsize=12 shape=box label="package"]
|
160
167
|
G -> H [label="visibility todo" <%= VisualizePacks::ArrowHead::VisibilityTodo.serialize %>]
|
161
168
|
<%- end -%>
|
162
|
-
<%- if options.
|
169
|
+
<%- if options.relationship_todo_types.include?(EdgeTodoTypes::Dependency) -%>
|
163
170
|
I [ fontsize=12 shape=box label="package"]
|
164
171
|
J [ fontsize=12 shape=box label="package"]
|
165
172
|
I -> J [label="dependency todo" <%= VisualizePacks::ArrowHead::DependencyTodo.serialize %>]
|
@@ -30,11 +30,12 @@ class Options < T::Struct
|
|
30
30
|
prop :show_layers, T::Boolean, default: true
|
31
31
|
prop :show_visibility, T::Boolean, default: true
|
32
32
|
|
33
|
-
prop :
|
34
|
-
prop :
|
35
|
-
prop :
|
33
|
+
prop :show_relationship_todos, T::Boolean, default: true
|
34
|
+
prop :relationship_todo_types, T::Array[EdgeTodoTypes], default: EdgeTodoTypes.values
|
35
|
+
prop :use_relationship_todos_for_layout, T::Boolean, default: false
|
36
36
|
|
37
37
|
prop :show_teams, T::Boolean, default: true
|
38
|
+
prop :show_node_todos, T::Boolean, default: true
|
38
39
|
|
39
40
|
prop :focus_pack, T.nilable(T::Array[String]), default: nil
|
40
41
|
prop :show_only_edges_to_focus_pack, FocusPackEdgeDirection, default: FocusPackEdgeDirection::All
|
data/lib/visualize_packs.rb
CHANGED
@@ -31,6 +31,7 @@ module VisualizePacks
|
|
31
31
|
|
32
32
|
show_edge = show_edge_builder(options, all_package_names)
|
33
33
|
node_color = node_color_builder()
|
34
|
+
node_protection = package_based_todos_text_maker()
|
34
35
|
max_todo_count = max_todo_count(all_packages, show_edge, options)
|
35
36
|
|
36
37
|
title = diagram_title(options, max_todo_count)
|
@@ -70,26 +71,33 @@ module VisualizePacks
|
|
70
71
|
def self.diagram_title(options, max_todo_count)
|
71
72
|
return "<<b>#{options.title}</b>>" if options.title
|
72
73
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
options.
|
81
|
-
options.
|
82
|
-
|
83
|
-
options.
|
84
|
-
options.
|
85
|
-
options.
|
86
|
-
options.
|
87
|
-
options.
|
88
|
-
options.
|
74
|
+
focus_info = if options.focus_pack
|
75
|
+
"Focus on #{limited_sentence(options.focus_pack)} (Edge mode: #{options.show_only_edges_to_focus_pack.serialize})"
|
76
|
+
else
|
77
|
+
"All packs"
|
78
|
+
end
|
79
|
+
|
80
|
+
hidden_aspects = [
|
81
|
+
options.show_legend ? nil : "legend",
|
82
|
+
options.show_layers ? nil : "layers",
|
83
|
+
options.show_dependencies ? nil : "dependencies",
|
84
|
+
options.show_relationship_todos ? nil : "edge todos",
|
85
|
+
options.show_privacy ? nil : "privacy",
|
86
|
+
options.show_teams ? nil : "teams",
|
87
|
+
options.show_node_todos ? nil : "node todos",
|
88
|
+
options.show_visibility ? nil : "visibility",
|
89
|
+
options.roll_nested_into_parent_packs ? "nested packs" : nil,
|
90
|
+
options.show_nested_relationships ? nil : "nested relationships",
|
89
91
|
].compact.join(', ').strip
|
90
|
-
|
91
|
-
|
92
|
-
|
92
|
+
hidden_aspects_title = hidden_aspects != '' ? "Hiding #{hidden_aspects}" : nil
|
93
|
+
|
94
|
+
todo_types = EdgeTodoTypes.values.size == options.relationship_todo_types.size ? nil : "Only #{options.relationship_todo_types.map &:serialize} todos",
|
95
|
+
|
96
|
+
exclusions = options.exclude_packs.empty? ? nil : "Excluding pack#{options.exclude_packs.size > 1 ? 's' : ''}: #{limited_sentence(options.exclude_packs)}",
|
97
|
+
|
98
|
+
main_title = [focus_info, hidden_aspects_title, todo_types, exclusions].compact.join('. ')
|
99
|
+
|
100
|
+
if options.show_relationship_todos && max_todo_count
|
93
101
|
sub_title = "<br/><font point-size='12'>Widest todo edge is #{max_todo_count} todo#{max_todo_count > 1 ? 's' : ''}</font>"
|
94
102
|
end
|
95
103
|
"<<b>#{main_title}</b>#{sub_title}>"
|
@@ -115,6 +123,8 @@ module VisualizePacks
|
|
115
123
|
case options.show_only_edges_to_focus_pack
|
116
124
|
when FocusPackEdgeDirection::All then
|
117
125
|
true
|
126
|
+
when FocusPackEdgeDirection::None then
|
127
|
+
match_packs?(start_node, options.focus_pack) && match_packs?(end_node, options.focus_pack)
|
118
128
|
when FocusPackEdgeDirection::InOut then
|
119
129
|
match_packs?(start_node, options.focus_pack) || match_packs?(end_node, options.focus_pack)
|
120
130
|
when FocusPackEdgeDirection::In then
|
@@ -142,13 +152,13 @@ module VisualizePacks
|
|
142
152
|
sig { params(all_packages: T::Array[ParsePackwerk::Package], show_edge: T.proc.params(arg0: String, arg1: String).returns(T::Boolean), options: Options).returns(T.nilable(Integer)) }
|
143
153
|
def self.max_todo_count(all_packages, show_edge, options)
|
144
154
|
todo_counts = {}
|
145
|
-
if options.
|
155
|
+
if options.show_relationship_todos
|
146
156
|
all_packages.each do |package|
|
147
157
|
todos_by_package = package.violations&.group_by(&:to_package_name)
|
148
158
|
todos_by_package&.keys&.each do |todos_to_package|
|
149
159
|
todo_types = todos_by_package&& todos_by_package[todos_to_package]&.group_by(&:type)
|
150
160
|
todo_types&.keys&.each do |todo_type|
|
151
|
-
if options.
|
161
|
+
if options.relationship_todo_types.include?(EdgeTodoTypes.deserialize(todo_type))
|
152
162
|
if show_edge.call(package.name, todos_to_package)
|
153
163
|
key = "#{package.name}->#{todos_to_package}:#{todo_type}"
|
154
164
|
todo_counts[key] = todo_types && todo_types[todo_type]&.count
|
@@ -200,36 +210,32 @@ module VisualizePacks
|
|
200
210
|
result = T.let([], T::Array[T.nilable(String)])
|
201
211
|
result = packages.map { |pack| pack.name }
|
202
212
|
|
203
|
-
if focus_pack
|
213
|
+
if focus_pack
|
204
214
|
result = []
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
215
|
+
focus_pack_name = packages.map { |pack| pack.name }.select { |p| match_packs?(p, focus_pack) }
|
216
|
+
result += focus_pack_name
|
217
|
+
|
218
|
+
dependents = options.show_dependencies ? dependents_on(packages, focus_pack_name) : []
|
219
|
+
dependencies = options.show_dependencies ? dependencies_of(packages, focus_pack_name) : []
|
220
|
+
todos_out = options.show_relationship_todos ? todos_out(packages, focus_pack_name, options) : []
|
221
|
+
todos_in = options.show_relationship_todos ? todos_in(packages, focus_pack_name, options) : []
|
222
|
+
|
223
|
+
case options.show_only_edges_to_focus_pack
|
224
|
+
when FocusPackEdgeDirection::All, FocusPackEdgeDirection::InOut then
|
225
|
+
result += dependents + dependencies + todos_out + todos_in
|
226
|
+
when FocusPackEdgeDirection::In then
|
227
|
+
result += dependents + todos_in
|
228
|
+
when FocusPackEdgeDirection::Out then
|
229
|
+
result += dependencies + todos_out
|
230
|
+
when FocusPackEdgeDirection::None then
|
231
|
+
# nothing to do
|
216
232
|
end
|
217
|
-
|
218
|
-
if options.show_dependencies
|
219
|
-
result += packages_by_name[p].dependencies
|
220
|
-
end
|
221
|
-
if options.show_todos && [FocusPackEdgeDirection::All, FocusPackEdgeDirection::Out, FocusPackEdgeDirection::InOut].include?(options.show_only_edges_to_focus_pack)
|
222
|
-
result += (packages_by_name[p].violations || []).inject([]) do |res, todo|
|
223
|
-
res << todo.to_package_name if options.only_todo_types.include?(EdgeTodoTypes.deserialize(todo.type))
|
224
|
-
res
|
225
|
-
end
|
226
|
-
end
|
227
|
-
end
|
228
|
-
result = result.uniq
|
233
|
+
|
229
234
|
parent_packs = result.inject([]) do |res, package_name|
|
230
235
|
res << nested_packages[package_name]
|
231
236
|
res
|
232
237
|
end
|
238
|
+
|
233
239
|
result = (result + parent_packs).uniq.compact
|
234
240
|
end
|
235
241
|
|
@@ -318,4 +324,73 @@ module VisualizePacks
|
|
318
324
|
def self.match_packs?(pack, packs_name_with_wildcards)
|
319
325
|
!packs_name_with_wildcards || packs_name_with_wildcards.any? {|p| File.fnmatch(p, pack)}
|
320
326
|
end
|
327
|
+
|
328
|
+
sig { params(all_packages: T::Array[ParsePackwerk::Package], focus_packs_names: T::Array[String]).returns(T::Array[String]) }
|
329
|
+
def self.dependencies_of(all_packages, focus_packs_names)
|
330
|
+
focus_packs = all_packages.select { focus_packs_names.include?(_1.name)}
|
331
|
+
|
332
|
+
focus_packs.inject([]) do |result, pack|
|
333
|
+
result += pack.dependencies
|
334
|
+
result
|
335
|
+
end.uniq
|
336
|
+
end
|
337
|
+
|
338
|
+
sig { params(all_packages: T::Array[ParsePackwerk::Package], focus_packs_names: T::Array[String]).returns(T::Array[String]) }
|
339
|
+
def self.dependents_on(all_packages, focus_packs_names)
|
340
|
+
all_packages.select { |pack| pack.dependencies.any? { focus_packs_names.include?(_1) }}.map &:name
|
341
|
+
end
|
342
|
+
|
343
|
+
sig { params(all_packages: T::Array[ParsePackwerk::Package], focus_packs_names: T::Array[String], options: Options).returns(T::Array[String]) }
|
344
|
+
def self.todos_in(all_packages, focus_packs_names, options)
|
345
|
+
all_packages.select do |p|
|
346
|
+
(p.violations || []).inject([]) do |res, todo|
|
347
|
+
res << todo.to_package_name if options.relationship_todo_types.include?(EdgeTodoTypes.deserialize(todo.type))
|
348
|
+
res
|
349
|
+
end.any? { |v| focus_packs_names.include?(v) }
|
350
|
+
end.map { |pack| pack.name }
|
351
|
+
end
|
352
|
+
|
353
|
+
sig { params(all_packages: T::Array[ParsePackwerk::Package], focus_packs_names: T::Array[String], options: Options).returns(T::Array[String]) }
|
354
|
+
def self.todos_out(all_packages, focus_packs_names, options)
|
355
|
+
all_packages.inject([]) do |result, p|
|
356
|
+
focus_packs_names.include?(p.name) && (p.violations || []).each do |todo|
|
357
|
+
result << todo.to_package_name if options.relationship_todo_types.include?(EdgeTodoTypes.deserialize(todo.type))
|
358
|
+
end
|
359
|
+
result
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
sig { params(protection: String, package_name: String, rubocop_config: T.any(NilClass, T::Boolean, T::Hash[String, T.untyped]), rubocop_todo: T.any(NilClass, T::Boolean, T::Hash[String, T.untyped])).returns(T.nilable(Integer)) }
|
364
|
+
def self.package_based_todos_for(protection, package_name, rubocop_config, rubocop_todo)
|
365
|
+
rubocop_config = {} if rubocop_config.is_a?(TrueClass) || rubocop_config.is_a?(FalseClass) || rubocop_config.is_a?(NilClass)
|
366
|
+
rubocop_todo = {} if rubocop_todo.is_a?(TrueClass) || rubocop_todo.is_a?(FalseClass) || rubocop_todo.is_a?(NilClass)
|
367
|
+
|
368
|
+
raise ArgumentError unless ['Packs/ClassMethodsAsPublicApis', 'Packs/DocumentedPublicApis', 'Packs/RootNamespaceIsPackName', 'Packs/TypedPublicApis'].include?(protection)
|
369
|
+
return nil unless (rubocop_config.dig(protection)&.dig('Enabled'))
|
370
|
+
|
371
|
+
(rubocop_todo.dig(protection)&.dig('Exclude') || []).inject(0) do |result, todo|
|
372
|
+
result += 1 if todo.start_with?("#{package_name}/")
|
373
|
+
result
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
sig { returns(T.untyped) }
|
378
|
+
def self.package_based_todos_text_maker
|
379
|
+
->(package_name) {
|
380
|
+
[
|
381
|
+
'Packs/ClassMethodsAsPublicApis',
|
382
|
+
'Packs/DocumentedPublicApis',
|
383
|
+
'Packs/RootNamespaceIsPackName',
|
384
|
+
'Packs/TypedPublicApis'
|
385
|
+
].map do |protection|
|
386
|
+
rubocop_config = File.exist?("#{package_name}/.rubocop.yml") ? YAML.load_file("#{package_name}/.rubocop.yml") : {}
|
387
|
+
rubocop_todo = File.exist?(".rubocop_todo.yml") ? YAML.load_file(".rubocop_todo.yml") : {}
|
388
|
+
|
389
|
+
todo_value = package_based_todos_for(protection, package_name, rubocop_config, rubocop_todo)
|
390
|
+
abbreviation = T.must(protection.split('/')[1]).chars[0]
|
391
|
+
|
392
|
+
"#{abbreviation}: #{todo_value}" if todo_value
|
393
|
+
end.compact.join(", ")
|
394
|
+
}
|
395
|
+
end
|
321
396
|
end
|
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.21
|
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-09-
|
11
|
+
date: 2023-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|