Skip to content

Commit

Permalink
source and dependency should be render within subgraph
Browse files Browse the repository at this point in the history
The source and dependency are rendered under the appropriate subgraph.
There was a problem that Graphviz could not be rendered if the same subgraph was rendered multiple times.
  • Loading branch information
alpaca-tc committed May 9, 2024
1 parent 2a474e9 commit adf4dc6
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 46 deletions.
91 changes: 55 additions & 36 deletions lib/diver_down/web/definition_to_dot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,7 @@ def to_s
if @only_module
render_only_modules
else
definition.sources.sort_by(&:source_name).each do
insert_source(_1)
end
render_sources
end
end
io.puts '}'
Expand Down Expand Up @@ -268,13 +266,62 @@ def render_only_modules
end
end

def insert_source(source)
if module_store.get(source.source_name).empty?
io.puts %("#{source.source_name}" #{build_attributes(label: source.source_name, id: @metadata_store.issue_source_id(source))})
else
insert_modules(source)
def render_sources
by_modules = definition.sources.group_by do |source|
module_store.get(source.source_name)
end

# Remove duplicated prefix modules
# from [["A"], ["A", "B"]] to [["A", "B"]]
uniq_modules = by_modules.keys.uniq
uniq_modules = uniq_modules.reject do |modules|
uniq_modules.any? { _1[0..modules.size - 1] == modules && _1.length > modules.size }
end

uniq_modules.each do |full_modules|
# Render module and source
if full_modules.empty?
sources = by_modules[full_modules].sort_by(&:source_name)

sources.each do |source|
insert_source(source)
insert_dependencies(source)
end
else
buf = swap_io do
indexes = (0..(full_modules.length - 1)).to_a

chain_yield(indexes) do |index, next_proc|
module_names = full_modules[0..index]
module_name = module_names[-1]

io.puts %(subgraph "#{module_label(module_names)}" {)
io.indented do
io.puts %(id="#{@metadata_store.issue_modules_id(module_names)}")
io.puts %(label="#{module_name}")

sources = (by_modules[module_names] || []).sort_by(&:source_name)
sources.each do |source|
insert_source(source)
insert_dependencies(source)
end

next_proc&.call
end
io.puts '}'
end
end

io.write buf.string
end
end
end

def insert_source(source)
io.puts %("#{source.source_name}" #{build_attributes(label: source.source_name, id: @metadata_store.issue_source_id(source))})
end

def insert_dependencies(source)
source.dependencies.each do
attributes = {}
ltail = module_label(*module_store.get(source.source_name))
Expand Down Expand Up @@ -313,34 +360,6 @@ def insert_source(source)
end
end

def insert_modules(source)
buf = swap_io do
all_module_names = module_store.get(source.source_name)
indexes = (0..(all_module_names.length - 1)).to_a

chain_yield(indexes) do |index, next_proc|
module_names = all_module_names[0..index]
module_name = module_names[-1]

io.puts %(subgraph "#{module_label(module_names)}" {)
io.indented do
io.puts %(id="#{@metadata_store.issue_modules_id(module_names)}")
io.puts %(label="#{module_name}")

if next_proc
next_proc.call
else
# last. equals indexes[-1] == index
io.puts %("#{source.source_name}" #{build_attributes(label: source.source_name, id: @metadata_store.issue_source_id(source))})
end
end
io.puts '}'
end
end

io.write buf.string
end

def chain_yield(values, &block)
*head, tail = values

Expand Down
70 changes: 60 additions & 10 deletions spec/diver_down/web/definition_to_dot_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -206,16 +206,12 @@ def build_definition(title: 'title', sources: [])
id="graph_1"
label="A"
"a.rb" [label="a.rb" id="graph_2"]
"a.rb" -> "b.rb" [id="graph_3" ltail="cluster_A" lhead="cluster_B" minlen="3"]
}
"a.rb" -> "b.rb" [id="graph_3" ltail="cluster_A" lhead="cluster_B" minlen="3"]
subgraph "cluster_B" {
id="graph_4"
label="B"
"b.rb" [label="b.rb" id="graph_5"]
}
subgraph "cluster_B" {
id="graph_4"
label="B"
"c.rb" [label="c.rb" id="graph_6"]
}
}
Expand Down Expand Up @@ -323,16 +319,12 @@ def build_definition(title: 'title', sources: [])
id="graph_1"
label="A"
"a.rb" [label="a.rb" id="graph_2"]
"a.rb" -> "b.rb" [id="graph_3" ltail="cluster_A" lhead="cluster_B" minlen="3"]
}
"a.rb" -> "b.rb" [id="graph_3" ltail="cluster_A" lhead="cluster_B" minlen="3"]
subgraph "cluster_B" {
id="graph_4"
label="B"
"b.rb" [label="b.rb" id="graph_5"]
}
subgraph "cluster_B" {
id="graph_4"
label="B"
"c.rb" [label="c.rb" id="graph_6"]
}
}
Expand Down Expand Up @@ -535,6 +527,64 @@ def build_definition(title: 'title', sources: [])
]
)
end

it 'returns concentrate digraph with multiple modules if concentrate = true' do
definition = build_definition(
sources: [
{
source_name: 'a.rb',
},
{
source_name: 'b.rb',
},
{
source_name: 'c.rb',
},
{
source_name: 'd.rb',
},
]
)

module_store.set('b.rb', ['A'])
module_store.set('c.rb', ['A', 'C'])
module_store.set('d.rb', ['B'])

instance = described_class.new(definition, module_store, concentrate: true)
expect(instance.to_s).to eq(<<~DOT)
strict digraph "title" {
concentrate=true
"a.rb" [label="a.rb" id="graph_1"]
subgraph "cluster_A" {
id="graph_2"
label="A"
"b.rb" [label="b.rb" id="graph_3"]
subgraph "cluster_A::C" {
id="graph_4"
label="C"
"c.rb" [label="c.rb" id="graph_5"]
}
}
subgraph "cluster_B" {
id="graph_6"
label="B"
"d.rb" [label="d.rb" id="graph_7"]
}
}
DOT

expect(instance.metadata).to eq(
[
{ id: 'graph_1', type: 'source', source_name: 'a.rb', modules: [] },
{ id: 'graph_2', type: 'module', modules: [{ module_name: 'A' }] },
{ id: 'graph_3', type: 'source', source_name: 'b.rb', modules: [{ module_name: 'A' }] },
{ id: 'graph_4', type: 'module', modules: [{ module_name: 'A' }, { module_name: 'C' }] },
{ id: 'graph_5', type: 'source', source_name: 'c.rb', modules: [{ module_name: 'A' }, { module_name: 'C' }] },
{ id: 'graph_6', type: 'module', modules: [{ module_name: 'B' }] },
{ id: 'graph_7', type: 'source', source_name: 'd.rb', modules: [{ module_name: 'B' }] },
]
)
end
end
end
end
Expand Down

0 comments on commit adf4dc6

Please sign in to comment.