Skip to content

Commit 7341e7f

Browse files
authored
Add visibility arrows (#49)
* Clarify edge options * For focus_pack use nil to indicate that no focus is happening * Organize options object and cli * Indentation * Add visibility arrows (and option to hide them) * Remove broader penwidth of nested arrow * Bump gem version
1 parent f5a9cf1 commit 7341e7f

25 files changed

Lines changed: 441 additions & 93 deletions

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
visualize_packs (0.5.16)
4+
visualize_packs (0.5.17)
55
packs-specification
66
parse_packwerk (>= 0.20.0)
77
sorbet-runtime

bin/visualize_packs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,23 @@ options = Options.new
1212
OptionParser.new do |opt|
1313
opt.on('--no-legend', "Don't show legend") { |o| options.show_legend = false }
1414

15+
opt.on('--no-dependency-arrows', "Don't show accepted dependency arrows") { |o| options.show_dependencies = false }
16+
opt.on('--no-privacy-boxes', "Don't show privacy enforcement box on a pack") { |o| options.show_privacy = false }
1517
opt.on('--no-layers', "Don't show architectural layers") { |o| options.show_layers = false }
16-
17-
opt.on('--no-teams', "Don't show team colors") { |o| options.show_teams = false }
18-
19-
opt.on('--no-dependency-arrows', "Don't show accepted dependencies") { |o| options.show_dependencies = false }
18+
opt.on('--no-visibility-arrows', "Don't show visibility arrows") { |o| options.show_visibility = false }
2019

2120
opt.on('--no-todo-arrows', "Don't show pack todos") { |o| options.show_todos = false }
2221
opt.on("--only-todo-types=", "Show only the selected types of todos. Comma-separated list of #{EdgeTodoTypes.values.map &:serialize}") { |o| options.only_todo_types = o.to_s.split(",").uniq.map { EdgeTodoTypes.deserialize(_1) } }
23-
opt.on('--no-privacy-boxes', "Don't show privacy enforcement box on a pack") { |o| options.show_privacy = false }
2422

25-
opt.on('--no-nesting-arrows', "Don't draw relationships between parents and nested packs") { |o| options.show_nested_relationships = false }
26-
opt.on('--roll-nested-into-parent-packs', "Don't show nested packs (not counting root). Connect edges to top-level pack instead") { |o| options.roll_nested_into_parent_packs = true }
23+
opt.on('--no-teams', "Don't show team colors") { |o| options.show_teams = false }
2724

2825
opt.on('--focus-pack=', "Focus on a specific pack(s). Comma-separated list of packs. Wildcards supported: 'packs/*'") { |o| options.focus_pack = o.to_s.split(",") }
2926
opt.on('--focus-pack-edge-mode=', "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) }
3027
opt.on('--exclude-packs=', "Exclude listed packs from diagram. If used with include you will get all included that are not excluded. Wildcards support: 'packs/ignores/*'") { |o| options.exclude_packs = o.to_s.split(",") }
3128

29+
opt.on('--roll-nested-into-parent-packs', "Don't show nested packs (not counting root). Connect edges to top-level pack instead") { |o| options.roll_nested_into_parent_packs = true }
30+
opt.on('--no-nesting-arrows', "Don't draw relationships between parents and nested packs") { |o| options.show_nested_relationships = false }
31+
3232
opt.on('--remote-base-url=', "Link pack packs to a URL (affects graphviz SVG generation)") { |o| options.remote_base_url = o }
3333

3434
opt.on_tail("-h", "--help", "Show this message") do

diagram_examples.png

164 KB
Loading

lib/graph.dot.erb

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ digraph package_diagram {
3333
<%- end -%>
3434
<%- grouped_packages[layer_name].each do |package| -%>
3535
"<%= package.name -%>" [
36-
fontsize=<%= options.focus_pack.any? && options.focus_pack.any? {|p| File.fnmatch(p, package.name)} ? 18.0 : 12.0 -%>
36+
fontsize=<%= options.focus_pack && options.focus_pack.any? {|p| File.fnmatch(p, package.name)} ? 18.0 : 12.0 -%>
3737
<%- if options.remote_base_url %>
3838
URL="<%= options.remote_base_url %>/<%= package.name == '.' ? '' : package.name -%>"
3939
<%- end %>
@@ -109,20 +109,32 @@ digraph package_diagram {
109109
<%- end -%>
110110
<%- end -%>
111111
<%- if options.show_nested_relationships -%>
112-
<%- all_packages.each do |package| -%>
113-
<%- all_packages.each do |nested_package| -%>
114-
<%- if nested_package.name.include?("#{package.name}/") && !(nested_package.name == package.name) -%>
115-
"<%= package.name -%>" -> "<%= nested_package.name -%>" [ color=purple penwidth=3 ]
112+
<%- all_packages.each do |package| -%>
113+
<%- all_packages.each do |nested_package| -%>
114+
<%- if nested_package.name.include?("#{package.name}/") && !(nested_package.name == package.name) -%>
115+
"<%= package.name -%>" -> "<%= nested_package.name -%>" [ color=purple ]
116+
<%- end -%>
117+
<%- end -%>
118+
<%- end -%>
119+
<%- end -%>
120+
<%- if options.show_visibility -%>
121+
<%- all_packages.each do |package| -%>
122+
<%- (package.config['visible_to'] || []).each do |other_package_name| -%>
123+
"<%= package.name -%>" -> "<%= other_package_name -%>" [ color=blue constraint=false ]
116124
<%- end -%>
117125
<%- end -%>
118126
<%- end -%>
119-
<%- end -%>
120127
<%- if options.show_legend -%>
121128
subgraph cluster_legend {
122129
fontsize=16
123130
label="Edges Styles and Arrow Heads"
124131
A [ fontsize=12 shape=box label="package"]
125132
B [ fontsize=12 shape=box label="package"]
133+
K [ fontsize=12 shape=box label="package"]
134+
L [ fontsize=12 shape=box label="package"]
135+
M [ fontsize=12 shape=box label="package"]
136+
N [ fontsize=12 shape=box label="package"]
137+
126138
C [ fontsize=12 shape=box label="package"]
127139
D [ fontsize=12 shape=box label="package"]
128140
E [ fontsize=12 shape=box label="package"]
@@ -131,14 +143,15 @@ digraph package_diagram {
131143
H [ fontsize=12 shape=box label="package"]
132144
I [ fontsize=12 shape=box label="package"]
133145
J [ fontsize=12 shape=box label="package"]
134-
K [ fontsize=12 shape=box label="package"]
135-
L [ fontsize=12 shape=box label="package"]
146+
136147
A -> B [label="accepted dependency" color=darkgreen]
148+
K -> L [label="nested package" color=purple]
149+
M -> N [label="visibile to" color=darkgreen]
150+
137151
C -> D [label="privacy todo" color=darkred style=dashed arrowhead=crow]
138152
E -> F [label="architecture todo" color=darkred style=dashed arrowhead=invodot]
139153
G -> H [label="visibility todo" color=darkred style=dashed arrowhead=obox]
140154
I -> J [label="dependency todo" color=darkred style=dashed arrowhead=odot]
141-
K -> L [label="nested package" color=purple penwidth=3]
142155
}
143156
<%- end -%>
144157
<%- if options.show_teams && all_team_names != [] -%>

lib/visualize_packs.rb

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ def self.code_owner(package)
5757
sig { params(options: Options, max_todo_count: T.nilable(Integer)).returns(String) }
5858
def self.diagram_title(options, max_todo_count)
5959
app_name = File.basename(Dir.pwd)
60-
focus_edge_info = options.focus_pack.any? && options.show_only_edges_to_focus_pack != FocusPackEdgeDirection::All ? "showing only edges to/from focus pack" : "showing all edges between visible packs"
61-
focus_info = options.focus_pack.any? ? "Focus on #{limited_sentence(options.focus_pack)} (#{focus_edge_info})" : "All packs"
60+
focus_edge_info = options.focus_pack && options.show_only_edges_to_focus_pack != FocusPackEdgeDirection::All ? "showing only edges to/from focus pack" : "showing all edges between visible packs"
61+
focus_info = options.focus_pack ? "Focus on #{limited_sentence(options.focus_pack)} (#{focus_edge_info})" : "All packs"
6262
skipped_info =
6363
[
6464
options.show_legend ? nil : "hiding legend",
@@ -68,6 +68,7 @@ def self.diagram_title(options, max_todo_count)
6868
EdgeTodoTypes.values.size == options.only_todo_types.size ? nil : "only #{limited_sentence(options.only_todo_types.map &:serialize)} todos",
6969
options.show_privacy ? nil : "hiding privacy",
7070
options.show_teams ? nil : "hiding teams",
71+
options.show_visibility ? nil : "hiding visibility",
7172
options.roll_nested_into_parent_packs ? "hiding nested packs" : nil,
7273
options.show_nested_relationships ? nil : "hiding nested relationships",
7374
options.exclude_packs.empty? ? nil : "excluding pack#{options.exclude_packs.size > 1 ? 's' : ''}: #{limited_sentence(options.exclude_packs)}",
@@ -173,7 +174,7 @@ def self.filtered(packages, options)
173174
focus_pack = options.focus_pack
174175
exclude_packs = options.exclude_packs
175176

176-
return packages unless focus_pack.any? || exclude_packs.any?
177+
return packages unless focus_pack || exclude_packs.any?
177178

178179
nested_packages = all_nested_packages(packages.map { |p| p.name })
179180

@@ -185,7 +186,7 @@ def self.filtered(packages, options)
185186
result = T.let([], T::Array[T.nilable(String)])
186187
result = packages.map { |pack| pack.name }
187188

188-
if !focus_pack.empty?
189+
if focus_pack && !focus_pack.empty?
189190
result = []
190191
result += packages.map { |pack| pack.name }.select { |p| match_packs?(p, focus_pack) }
191192
if options.show_dependencies
@@ -299,8 +300,8 @@ def self.remove_nested_packs(packages, options)
299300
morphed_packages.reject { |p| nested_packages.keys.include?(p.name) }
300301
end
301302

302-
sig { params(pack: String, packs_name_with_wildcards: T::Array[String]).returns(T::Boolean) }
303+
sig { params(pack: String, packs_name_with_wildcards: T.nilable(T::Array[String])).returns(T::Boolean) }
303304
def self.match_packs?(pack, packs_name_with_wildcards)
304-
packs_name_with_wildcards.any? {|p| File.fnmatch(p, pack)}
305+
!packs_name_with_wildcards || packs_name_with_wildcards.any? {|p| File.fnmatch(p, pack)}
305306
end
306307
end

lib/visualize_packs/options.rb

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,35 @@ class EdgeTodoTypes < T::Enum
1212

1313
class FocusPackEdgeDirection < T::Enum
1414
enums do
15-
None = new
16-
All = new
17-
In = new
18-
Out = new
19-
InOut = new
15+
None = new # don't include non-focus packs and thus show no edges to/from them
16+
All = new # include non-focus packs and show all edges between all visible nodes
17+
In = new # include non-focus packs and show edges that go towards focus packs (and show all edges between focus packs)
18+
Out = new # include non-focus packs and show edges that go away from focus packs (and show all edges between focus packs)
19+
InOut = new # include non-focus packs and show edges that go towards or away from focus packs (and show all edges between focus packs)
2020
end
2121
end
2222

2323
class Options < T::Struct
2424
extend T::Sig
2525

2626
prop :show_legend, T::Boolean, default: true
27-
prop :show_layers, T::Boolean, default: true
27+
2828
prop :show_dependencies, T::Boolean, default: true
29+
prop :show_privacy, T::Boolean, default: true
30+
prop :show_layers, T::Boolean, default: true
31+
prop :show_visibility, T::Boolean, default: true
32+
2933
prop :show_todos, T::Boolean, default: true
3034
prop :only_todo_types, T::Array[EdgeTodoTypes], default: EdgeTodoTypes.values
31-
prop :show_privacy, T::Boolean, default: true
35+
3236
prop :show_teams, T::Boolean, default: true
3337

34-
prop :focus_pack, T::Array[String], default: []
38+
prop :focus_pack, T.nilable(T::Array[String]), default: nil
3539
prop :show_only_edges_to_focus_pack, FocusPackEdgeDirection, default: FocusPackEdgeDirection::All
40+
prop :exclude_packs, T::Array[String], default: []
3641

3742
prop :roll_nested_into_parent_packs, T::Boolean, default: false
3843
prop :show_nested_relationships, T::Boolean, default: true
3944

40-
prop :exclude_packs, T::Array[String], default: []
41-
4245
prop :remote_base_url, T.nilable(String)
4346
end

spec/diagram_generation_helper.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ test_names+=("no_dependencies"); test_params[${test_names[-1]}]="--no-dependency
2323
test_names+=("no_todos"); test_params[${test_names[-1]}]="--no-todo-arrows"
2424
test_names+=("no_privacy"); test_params[${test_names[-1]}]="--no-privacy-boxes"
2525
test_names+=("no_nested"); test_params[${test_names[-1]}]="--no-nesting-arrows"
26+
test_names+=("no_visibility"); test_params[${test_names[-1]}]="--no-visibility-arrows"
2627

2728
test_names+=("only_todo_types"); test_params[${test_names[-1]}]="--only-todo-types=architecture,visibility"
2829
test_names+=("roll_nested_into_parent_packs"); test_params[${test_names[-1]}]="--roll-nested-into-parent-packs"

spec/sample_app1/Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: ../..
33
specs:
4-
visualize_packs (0.5.16)
4+
visualize_packs (0.5.17)
55
packs-specification
66
parse_packwerk (>= 0.20.0)
77
sorbet-runtime
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
enforce_dependencies: true
22
enforce_privacy: true
3+
enforce_visibility: true
34
layer: app
45
dependencies:
56
- packs/utility
67
- .
7-
owner: UI Team
8+
owner: UI Team
9+
visible_to:
10+
- packs/models

spec/sample_app1/test_output/focussed_on_packs_ui.dot

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,18 @@ digraph package_diagram {
155155
# headlabel="visibility"
156156
arrowhead=obox
157157
penwidth=3.25 ]
158-
"packs/models" -> "packs/models/packs/model_a" [ color=purple penwidth=3 ]
158+
"packs/models" -> "packs/models/packs/model_a" [ color=purple ]
159+
"packs/models/packs/model_a" -> "packs/models" [ color=blue constraint=false ]
159160
subgraph cluster_legend {
160161
fontsize=16
161162
label="Edges Styles and Arrow Heads"
162163
A [ fontsize=12 shape=box label="package"]
163164
B [ fontsize=12 shape=box label="package"]
165+
K [ fontsize=12 shape=box label="package"]
166+
L [ fontsize=12 shape=box label="package"]
167+
M [ fontsize=12 shape=box label="package"]
168+
N [ fontsize=12 shape=box label="package"]
169+
164170
C [ fontsize=12 shape=box label="package"]
165171
D [ fontsize=12 shape=box label="package"]
166172
E [ fontsize=12 shape=box label="package"]
@@ -169,14 +175,15 @@ digraph package_diagram {
169175
H [ fontsize=12 shape=box label="package"]
170176
I [ fontsize=12 shape=box label="package"]
171177
J [ fontsize=12 shape=box label="package"]
172-
K [ fontsize=12 shape=box label="package"]
173-
L [ fontsize=12 shape=box label="package"]
178+
174179
A -> B [label="accepted dependency" color=darkgreen]
180+
K -> L [label="nested package" color=purple]
181+
M -> N [label="visibile to" color=darkgreen]
182+
175183
C -> D [label="privacy todo" color=darkred style=dashed arrowhead=crow]
176184
E -> F [label="architecture todo" color=darkred style=dashed arrowhead=invodot]
177185
G -> H [label="visibility todo" color=darkred style=dashed arrowhead=obox]
178186
I -> J [label="dependency todo" color=darkred style=dashed arrowhead=odot]
179-
K -> L [label="nested package" color=purple penwidth=3]
180187
}
181188
subgraph cluster_teams_legend {
182189
fontsize=16

0 commit comments

Comments
 (0)