sirena 0.1.0
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 +7 -0
- data/.github/workflows/build_deploy.yml +59 -0
- data/.github/workflows/links.yml +85 -0
- data/.github/workflows/rake.yml +15 -0
- data/.github/workflows/release.yml +27 -0
- data/.gitignore +68 -0
- data/.rspec +3 -0
- data/.rubocop.yml +14 -0
- data/.rubocop_todo.yml +70 -0
- data/ARCHITECTURE.md +744 -0
- data/Gemfile +12 -0
- data/LICENSE +25 -0
- data/README.adoc +357 -0
- data/Rakefile +11 -0
- data/docs/.gitignore +1 -0
- data/docs/Gemfile +13 -0
- data/docs/_config.yml +182 -0
- data/docs/_diagram_types/architecture-diagram.adoc +314 -0
- data/docs/_diagram_types/block-diagram.adoc +345 -0
- data/docs/_diagram_types/c4-diagram.adoc +559 -0
- data/docs/_diagram_types/class-diagram.adoc +816 -0
- data/docs/_diagram_types/er-diagram.adoc +719 -0
- data/docs/_diagram_types/error-diagram.adoc +114 -0
- data/docs/_diagram_types/examples/flowchart-examples.adoc +29 -0
- data/docs/_diagram_types/flowchart.adoc +488 -0
- data/docs/_diagram_types/gantt-chart.adoc +502 -0
- data/docs/_diagram_types/git-graph.adoc +600 -0
- data/docs/_diagram_types/index.adoc +192 -0
- data/docs/_diagram_types/info-diagram.adoc +103 -0
- data/docs/_diagram_types/kanban-diagram.adoc +262 -0
- data/docs/_diagram_types/mindmap.adoc +603 -0
- data/docs/_diagram_types/packet-diagram.adoc +378 -0
- data/docs/_diagram_types/pie-chart.adoc +335 -0
- data/docs/_diagram_types/quadrant-chart.adoc +406 -0
- data/docs/_diagram_types/radar-chart.adoc +528 -0
- data/docs/_diagram_types/requirement-diagram.adoc +416 -0
- data/docs/_diagram_types/sankey-diagram.adoc +357 -0
- data/docs/_diagram_types/sequence-diagram.adoc +664 -0
- data/docs/_diagram_types/state-diagram.adoc +658 -0
- data/docs/_diagram_types/timeline.adoc +352 -0
- data/docs/_diagram_types/treemap-diagram.adoc +462 -0
- data/docs/_diagram_types/user-journey.adoc +602 -0
- data/docs/_features/index.adoc +129 -0
- data/docs/_guides/cli-reference.adoc +203 -0
- data/docs/_guides/index.adoc +56 -0
- data/docs/_guides/installation.adoc +100 -0
- data/docs/_guides/quick-start.adoc +132 -0
- data/docs/_pages/comparison.adoc +441 -0
- data/docs/_pages/compatibility.adoc +300 -0
- data/docs/_pages/index.adoc +39 -0
- data/docs/_references/index.adoc +103 -0
- data/docs/_tutorials/index.adoc +57 -0
- data/docs/index.adoc +166 -0
- data/docs/lychee.toml +54 -0
- data/examples/.gitignore +10 -0
- data/examples/README.adoc +196 -0
- data/examples/README.md +64 -0
- data/examples/architecture/01-basic-services.mmd +9 -0
- data/examples/architecture/01-basic-services.svg +37 -0
- data/examples/architecture/02-service-groups.mmd +16 -0
- data/examples/architecture/02-service-groups.svg +55 -0
- data/examples/architecture/README.adoc +79 -0
- data/examples/block/01-basic-blocks.mmd +13 -0
- data/examples/block/01-basic-blocks.svg +44 -0
- data/examples/block/02-block-shapes.mmd +13 -0
- data/examples/block/02-block-shapes.svg +47 -0
- data/examples/block/README.adoc +85 -0
- data/examples/c4/01-context-diagram.mmd +10 -0
- data/examples/c4/01-context-diagram.svg +45 -0
- data/examples/c4/02-container-diagram.mmd +24 -0
- data/examples/c4/02-container-diagram.svg +105 -0
- data/examples/c4/README.adoc +92 -0
- data/examples/class_diagram/01-basic-classes.mmd +61 -0
- data/examples/class_diagram/01-basic-classes.svg +117 -0
- data/examples/class_diagram/02-relationships.mmd +61 -0
- data/examples/class_diagram/02-relationships.svg +129 -0
- data/examples/class_diagram/README.adoc +93 -0
- data/examples/er_diagram/01-basic-entities.mmd +64 -0
- data/examples/er_diagram/01-basic-entities.svg +5 -0
- data/examples/er_diagram/02-cardinality.mmd +57 -0
- data/examples/er_diagram/02-cardinality.svg +125 -0
- data/examples/er_diagram/README.adoc +88 -0
- data/examples/error/01-basic-error.mmd +1 -0
- data/examples/error/01-basic-error.svg +13 -0
- data/examples/error/02-error-display.mmd +1 -0
- data/examples/error/02-error-display.svg +13 -0
- data/examples/error/README.adoc +71 -0
- data/examples/error_message_example.svg +13 -0
- data/examples/flowchart/00-original.mmd +13 -0
- data/examples/flowchart/00-original.svg +5 -0
- data/examples/flowchart/01-basic-flow.mmd +7 -0
- data/examples/flowchart/01-basic-flow.svg +52 -0
- data/examples/flowchart/01-basic-flow.yml +13 -0
- data/examples/flowchart/02*.svg +87 -0
- data/examples/flowchart/02-node-shapes.mmd +9 -0
- data/examples/flowchart/02-node-shapes.svg +33 -0
- data/examples/flowchart/03-edge-types.mmd +7 -0
- data/examples/flowchart/03-edge-types.svg +53 -0
- data/examples/flowchart/04-subgraphs.mmd +9 -0
- data/examples/flowchart/04-subgraphs.svg +33 -0
- data/examples/flowchart/05-styling.mmd +9 -0
- data/examples/flowchart/05-styling.svg +33 -0
- data/examples/flowchart/06-complex-flow.mmd +8 -0
- data/examples/flowchart/06-complex-flow.svg +59 -0
- data/examples/flowchart/README.adoc +167 -0
- data/examples/gantt/01-simple-timeline.* +14 -0
- data/examples/gantt/01-simple-timeline.mmd +6 -0
- data/examples/gantt/01-simple-timeline.svg +26 -0
- data/examples/gantt/02-task-dependencies.mmd +6 -0
- data/examples/gantt/02-task-dependencies.svg +26 -0
- data/examples/gantt/README.adoc +86 -0
- data/examples/git_graph/01-linear-history.mmd +12 -0
- data/examples/git_graph/01-linear-history.svg +26 -0
- data/examples/git_graph/02-branching.mmd +12 -0
- data/examples/git_graph/02-branching.svg +26 -0
- data/examples/git_graph/README.adoc +73 -0
- data/examples/info/02-showinfo.mmd +1 -0
- data/examples/info/02-showinfo.svg +10 -0
- data/examples/info/README.adoc +58 -0
- data/examples/info_showinfo_example.svg +10 -0
- data/examples/kanban/01-simple-board.mmd +8 -0
- data/examples/kanban/01-simple-board.svg +43 -0
- data/examples/kanban/02-workflow.mmd +8 -0
- data/examples/kanban/02-workflow.svg +43 -0
- data/examples/kanban/README.adoc +79 -0
- data/examples/mindmap/01-simple-tree.mmd +19 -0
- data/examples/mindmap/01-simple-tree.svg +61 -0
- data/examples/mindmap/02-knowledge-map.mmd +19 -0
- data/examples/mindmap/02-knowledge-map.svg +61 -0
- data/examples/mindmap/README.adoc +77 -0
- data/examples/packet/01-basic-packet.* +17 -0
- data/examples/packet/01-basic-packet.mmd +4 -0
- data/examples/packet/01-basic-packet.svg +82 -0
- data/examples/packet/README.adoc +58 -0
- data/examples/pie/01-simple-chart.mmd +5 -0
- data/examples/pie/01-simple-chart.svg +17 -0
- data/examples/pie/02-labeled-slices.mmd +6 -0
- data/examples/pie/02-labeled-slices.svg +19 -0
- data/examples/pie/README.adoc +75 -0
- data/examples/quadrant/01-basic-quadrant.mmd +13 -0
- data/examples/quadrant/01-basic-quadrant.svg +33 -0
- data/examples/quadrant/02-positioned-items.mmd +14 -0
- data/examples/quadrant/02-positioned-items.svg +35 -0
- data/examples/quadrant/README.adoc +84 -0
- data/examples/radar/01-simple-radar.* +5 -0
- data/examples/radar/01-simple-radar.mmd +3 -0
- data/examples/radar/01-simple-radar.svg +25 -0
- data/examples/radar/02-multiple-curves.mmd +4 -0
- data/examples/radar/02-multiple-curves.svg +43 -0
- data/examples/radar/README.adoc +75 -0
- data/examples/requirement/01-basic-requirements.mmd +23 -0
- data/examples/requirement/01-basic-requirements.svg +49 -0
- data/examples/requirement/02-risk-levels.mmd +23 -0
- data/examples/requirement/02-risk-levels.svg +49 -0
- data/examples/requirement/README.adoc +85 -0
- data/examples/sankey/01-simple-flow.mmd +7 -0
- data/examples/sankey/01-simple-flow.svg +34 -0
- data/examples/sankey/02-multi-stage.mmd +11 -0
- data/examples/sankey/02-multi-stage.svg +44 -0
- data/examples/sankey/README.adoc +74 -0
- data/examples/sequence/01-basic-sequence.mmd +27 -0
- data/examples/sequence/01-basic-sequence.svg +5 -0
- data/examples/sequence/02-activations.mmd +17 -0
- data/examples/sequence/02-activations.svg +78 -0
- data/examples/sequence/README.adoc +86 -0
- data/examples/state_diagram/01-simple-states.mmd +29 -0
- data/examples/state_diagram/01-simple-states.svg +5 -0
- data/examples/state_diagram/02-composite.mmd +19 -0
- data/examples/state_diagram/02-composite.svg +81 -0
- data/examples/state_diagram/README.adoc +90 -0
- data/examples/timeline/01-simple-timeline.mmd +11 -0
- data/examples/timeline/01-simple-timeline.svg +36 -0
- data/examples/timeline/02-periods.mmd +15 -0
- data/examples/timeline/02-periods.svg +47 -0
- data/examples/timeline/README.adoc +78 -0
- data/examples/treemap/01-basic-treemap.mmd +12 -0
- data/examples/treemap/01-basic-treemap.svg +59 -0
- data/examples/treemap/README.adoc +59 -0
- data/examples/user_journey/01-simple-journey.mmd +23 -0
- data/examples/user_journey/01-simple-journey.svg +5 -0
- data/examples/user_journey/02-multi-actor.mmd +18 -0
- data/examples/user_journey/02-multi-actor.svg +129 -0
- data/examples/user_journey/README.adoc +81 -0
- data/examples/xychart/01-line-chart.mmd +5 -0
- data/examples/xychart/01-line-chart.svg +43 -0
- data/examples/xychart/02-bar-chart.mmd +7 -0
- data/examples/xychart/02-bar-chart.svg +48 -0
- data/examples/xychart/README.adoc +80 -0
- data/exe/sirena +7 -0
- data/lib/sirena/cli.rb +138 -0
- data/lib/sirena/commands/batch.rb +117 -0
- data/lib/sirena/commands/render.rb +80 -0
- data/lib/sirena/commands/types.rb +29 -0
- data/lib/sirena/commands/version.rb +24 -0
- data/lib/sirena/diagram/architecture.rb +46 -0
- data/lib/sirena/diagram/base.rb +61 -0
- data/lib/sirena/diagram/block.rb +81 -0
- data/lib/sirena/diagram/c4.rb +328 -0
- data/lib/sirena/diagram/class_diagram.rb +385 -0
- data/lib/sirena/diagram/er_diagram.rb +238 -0
- data/lib/sirena/diagram/error.rb +38 -0
- data/lib/sirena/diagram/flowchart.rb +160 -0
- data/lib/sirena/diagram/gantt.rb +71 -0
- data/lib/sirena/diagram/git_graph.rb +36 -0
- data/lib/sirena/diagram/info.rb +38 -0
- data/lib/sirena/diagram/kanban.rb +178 -0
- data/lib/sirena/diagram/mindmap.rb +54 -0
- data/lib/sirena/diagram/packet.rb +79 -0
- data/lib/sirena/diagram/pie.rb +115 -0
- data/lib/sirena/diagram/quadrant.rb +138 -0
- data/lib/sirena/diagram/radar.rb +52 -0
- data/lib/sirena/diagram/requirement.rb +133 -0
- data/lib/sirena/diagram/sankey.rb +217 -0
- data/lib/sirena/diagram/sequence.rb +242 -0
- data/lib/sirena/diagram/state_diagram.rb +237 -0
- data/lib/sirena/diagram/timeline.rb +171 -0
- data/lib/sirena/diagram/treemap.rb +84 -0
- data/lib/sirena/diagram/user_journey.rb +149 -0
- data/lib/sirena/diagram/xy_chart.rb +76 -0
- data/lib/sirena/diagram.rb +8 -0
- data/lib/sirena/diagram_registry.rb +101 -0
- data/lib/sirena/engine.rb +292 -0
- data/lib/sirena/parser/architecture.rb +41 -0
- data/lib/sirena/parser/base.rb +41 -0
- data/lib/sirena/parser/block.rb +72 -0
- data/lib/sirena/parser/c4.rb +53 -0
- data/lib/sirena/parser/class_diagram.rb +63 -0
- data/lib/sirena/parser/er_diagram.rb +40 -0
- data/lib/sirena/parser/error.rb +49 -0
- data/lib/sirena/parser/flowchart.rb +71 -0
- data/lib/sirena/parser/gantt.rb +60 -0
- data/lib/sirena/parser/git_graph.rb +95 -0
- data/lib/sirena/parser/grammars/architecture.rb +145 -0
- data/lib/sirena/parser/grammars/block.rb +190 -0
- data/lib/sirena/parser/grammars/c4.rb +226 -0
- data/lib/sirena/parser/grammars/class_diagram.rb +284 -0
- data/lib/sirena/parser/grammars/common.rb +84 -0
- data/lib/sirena/parser/grammars/er_diagram.rb +114 -0
- data/lib/sirena/parser/grammars/error.rb +40 -0
- data/lib/sirena/parser/grammars/flowchart.rb +298 -0
- data/lib/sirena/parser/grammars/gantt.rb +252 -0
- data/lib/sirena/parser/grammars/git_graph.rb +167 -0
- data/lib/sirena/parser/grammars/info.rb +58 -0
- data/lib/sirena/parser/grammars/kanban.rb +83 -0
- data/lib/sirena/parser/grammars/mindmap.rb +115 -0
- data/lib/sirena/parser/grammars/packet.rb +73 -0
- data/lib/sirena/parser/grammars/pie.rb +128 -0
- data/lib/sirena/parser/grammars/quadrant.rb +199 -0
- data/lib/sirena/parser/grammars/radar.rb +150 -0
- data/lib/sirena/parser/grammars/requirement.rb +188 -0
- data/lib/sirena/parser/grammars/sankey.rb +104 -0
- data/lib/sirena/parser/grammars/sequence.rb +247 -0
- data/lib/sirena/parser/grammars/state_diagram.rb +172 -0
- data/lib/sirena/parser/grammars/timeline.rb +142 -0
- data/lib/sirena/parser/grammars/treemap.rb +120 -0
- data/lib/sirena/parser/grammars/xy_chart.rb +120 -0
- data/lib/sirena/parser/info.rb +49 -0
- data/lib/sirena/parser/kanban.rb +97 -0
- data/lib/sirena/parser/mindmap.rb +106 -0
- data/lib/sirena/parser/packet.rb +76 -0
- data/lib/sirena/parser/pie.rb +49 -0
- data/lib/sirena/parser/quadrant.rb +57 -0
- data/lib/sirena/parser/radar.rb +104 -0
- data/lib/sirena/parser/requirement.rb +70 -0
- data/lib/sirena/parser/sankey.rb +64 -0
- data/lib/sirena/parser/sequence.rb +51 -0
- data/lib/sirena/parser/state_diagram.rb +69 -0
- data/lib/sirena/parser/timeline.rb +57 -0
- data/lib/sirena/parser/transforms/architecture.rb +97 -0
- data/lib/sirena/parser/transforms/block.rb +254 -0
- data/lib/sirena/parser/transforms/c4.rb +347 -0
- data/lib/sirena/parser/transforms/class_diagram.rb +352 -0
- data/lib/sirena/parser/transforms/er_diagram.rb +169 -0
- data/lib/sirena/parser/transforms/error.rb +58 -0
- data/lib/sirena/parser/transforms/flowchart.rb +293 -0
- data/lib/sirena/parser/transforms/gantt.rb +215 -0
- data/lib/sirena/parser/transforms/git_graph.rb +160 -0
- data/lib/sirena/parser/transforms/info.rb +58 -0
- data/lib/sirena/parser/transforms/kanban.rb +176 -0
- data/lib/sirena/parser/transforms/mindmap.rb +227 -0
- data/lib/sirena/parser/transforms/packet.rb +63 -0
- data/lib/sirena/parser/transforms/pie.rb +143 -0
- data/lib/sirena/parser/transforms/quadrant.rb +177 -0
- data/lib/sirena/parser/transforms/radar.rb +126 -0
- data/lib/sirena/parser/transforms/requirement.rb +272 -0
- data/lib/sirena/parser/transforms/sankey.rb +122 -0
- data/lib/sirena/parser/transforms/sequence.rb +342 -0
- data/lib/sirena/parser/transforms/state_diagram.rb +292 -0
- data/lib/sirena/parser/transforms/timeline.rb +177 -0
- data/lib/sirena/parser/transforms/treemap.rb +81 -0
- data/lib/sirena/parser/transforms/xy_chart.rb +132 -0
- data/lib/sirena/parser/treemap.rb +98 -0
- data/lib/sirena/parser/user_journey.rb +120 -0
- data/lib/sirena/parser/xy_chart.rb +114 -0
- data/lib/sirena/parser.rb +8 -0
- data/lib/sirena/renderer/architecture.rb +251 -0
- data/lib/sirena/renderer/base.rb +251 -0
- data/lib/sirena/renderer/block.rb +286 -0
- data/lib/sirena/renderer/c4.rb +490 -0
- data/lib/sirena/renderer/class_diagram.rb +499 -0
- data/lib/sirena/renderer/er_diagram.rb +417 -0
- data/lib/sirena/renderer/error.rb +131 -0
- data/lib/sirena/renderer/flowchart.rb +301 -0
- data/lib/sirena/renderer/gantt.rb +331 -0
- data/lib/sirena/renderer/git_graph.rb +368 -0
- data/lib/sirena/renderer/info.rb +93 -0
- data/lib/sirena/renderer/kanban.rb +295 -0
- data/lib/sirena/renderer/mindmap.rb +396 -0
- data/lib/sirena/renderer/packet.rb +239 -0
- data/lib/sirena/renderer/pie.rb +235 -0
- data/lib/sirena/renderer/quadrant.rb +292 -0
- data/lib/sirena/renderer/radar.rb +323 -0
- data/lib/sirena/renderer/requirement.rb +371 -0
- data/lib/sirena/renderer/sankey.rb +255 -0
- data/lib/sirena/renderer/sequence.rb +424 -0
- data/lib/sirena/renderer/state_diagram.rb +328 -0
- data/lib/sirena/renderer/timeline.rb +304 -0
- data/lib/sirena/renderer/treemap.rb +152 -0
- data/lib/sirena/renderer/user_journey.rb +331 -0
- data/lib/sirena/renderer/xy_chart.rb +452 -0
- data/lib/sirena/renderer.rb +8 -0
- data/lib/sirena/svg/circle.rb +41 -0
- data/lib/sirena/svg/document.rb +103 -0
- data/lib/sirena/svg/element.rb +65 -0
- data/lib/sirena/svg/ellipse.rb +33 -0
- data/lib/sirena/svg/group.rb +71 -0
- data/lib/sirena/svg/line.rb +49 -0
- data/lib/sirena/svg/path.rb +76 -0
- data/lib/sirena/svg/polygon.rb +43 -0
- data/lib/sirena/svg/polyline.rb +35 -0
- data/lib/sirena/svg/rect.rb +57 -0
- data/lib/sirena/svg/style.rb +44 -0
- data/lib/sirena/svg/text.rb +72 -0
- data/lib/sirena/svg.rb +19 -0
- data/lib/sirena/text_measurement.rb +71 -0
- data/lib/sirena/theme/builtin/dark.yml +70 -0
- data/lib/sirena/theme/builtin/default.yml +80 -0
- data/lib/sirena/theme/builtin/high_contrast.yml +70 -0
- data/lib/sirena/theme/builtin/light.yml +70 -0
- data/lib/sirena/theme/color_palette.rb +48 -0
- data/lib/sirena/theme/effect_styles.rb +28 -0
- data/lib/sirena/theme/registry.rb +41 -0
- data/lib/sirena/theme/shape_styles.rb +28 -0
- data/lib/sirena/theme/spacing_config.rb +24 -0
- data/lib/sirena/theme/typography.rb +30 -0
- data/lib/sirena/theme.rb +69 -0
- data/lib/sirena/transform/architecture.rb +273 -0
- data/lib/sirena/transform/base.rb +199 -0
- data/lib/sirena/transform/block.rb +215 -0
- data/lib/sirena/transform/c4.rb +288 -0
- data/lib/sirena/transform/class_diagram.rb +296 -0
- data/lib/sirena/transform/er_diagram.rb +204 -0
- data/lib/sirena/transform/error.rb +39 -0
- data/lib/sirena/transform/flowchart.rb +161 -0
- data/lib/sirena/transform/gantt.rb +253 -0
- data/lib/sirena/transform/git_graph.rb +283 -0
- data/lib/sirena/transform/info.rb +39 -0
- data/lib/sirena/transform/kanban.rb +180 -0
- data/lib/sirena/transform/mindmap.rb +251 -0
- data/lib/sirena/transform/packet.rb +185 -0
- data/lib/sirena/transform/pie.rb +62 -0
- data/lib/sirena/transform/quadrant.rb +167 -0
- data/lib/sirena/transform/radar.rb +227 -0
- data/lib/sirena/transform/requirement.rb +233 -0
- data/lib/sirena/transform/sankey.rb +212 -0
- data/lib/sirena/transform/sequence.rb +143 -0
- data/lib/sirena/transform/state_diagram.rb +228 -0
- data/lib/sirena/transform/timeline.rb +139 -0
- data/lib/sirena/transform/treemap.rb +120 -0
- data/lib/sirena/transform/user_journey.rb +207 -0
- data/lib/sirena/transform/xy_chart.rb +273 -0
- data/lib/sirena/transform.rb +8 -0
- data/lib/sirena/version.rb +5 -0
- data/lib/sirena.rb +328 -0
- data/lib/tasks/benchmark.rake +532 -0
- data/lib/tasks/examples.rake +468 -0
- data/lib/tasks/generate_mermaid_fixtures.rake +363 -0
- data/lib/tasks/mermaid_fixtures.rake +46 -0
- data/scripts/extract_mermaid_tests.rb +493 -0
- data/scripts/rename_to_sirena.rb +73 -0
- data/sirena.gemspec +47 -0
- metadata +529 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
require_relative "../diagram/gantt"
|
|
5
|
+
require "date"
|
|
6
|
+
|
|
7
|
+
module Sirena
|
|
8
|
+
module Transform
|
|
9
|
+
# Gantt chart transformer for converting gantt models to renderable structure.
|
|
10
|
+
#
|
|
11
|
+
# Handles date calculations, dependency resolution, and timeline positioning.
|
|
12
|
+
#
|
|
13
|
+
# @example Transform a Gantt chart
|
|
14
|
+
# transform = GanttTransform.new
|
|
15
|
+
# data = transform.to_graph(gantt_diagram)
|
|
16
|
+
class GanttTransform < Base
|
|
17
|
+
# Converts a Gantt diagram to a layout structure with calculated positions.
|
|
18
|
+
#
|
|
19
|
+
# @param diagram [Diagram::GanttChart] the Gantt diagram to transform
|
|
20
|
+
# @return [Hash] data structure for rendering
|
|
21
|
+
# @raise [TransformError] if diagram is invalid
|
|
22
|
+
def to_graph(diagram)
|
|
23
|
+
@diagram = diagram
|
|
24
|
+
@task_map = build_task_map(diagram)
|
|
25
|
+
|
|
26
|
+
# Calculate dates for all tasks
|
|
27
|
+
calculate_task_dates
|
|
28
|
+
|
|
29
|
+
# Determine timeline range
|
|
30
|
+
timeline = calculate_timeline
|
|
31
|
+
|
|
32
|
+
{
|
|
33
|
+
id: "gantt",
|
|
34
|
+
title: diagram.title,
|
|
35
|
+
date_format: diagram.date_format,
|
|
36
|
+
axis_format: diagram.axis_format,
|
|
37
|
+
tick_interval: diagram.tick_interval,
|
|
38
|
+
excludes: diagram.excludes,
|
|
39
|
+
today_marker: diagram.today_marker,
|
|
40
|
+
sections: transform_sections(diagram, timeline),
|
|
41
|
+
timeline: timeline,
|
|
42
|
+
metadata: {
|
|
43
|
+
section_count: diagram.sections.length,
|
|
44
|
+
task_count: total_task_count(diagram)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def build_task_map(diagram)
|
|
52
|
+
map = {}
|
|
53
|
+
diagram.sections.each do |section|
|
|
54
|
+
section.tasks.each do |task|
|
|
55
|
+
map[task.id] = task if task.id
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
map
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def calculate_task_dates
|
|
62
|
+
# First pass: calculate tasks with explicit dates
|
|
63
|
+
@diagram.sections.each do |section|
|
|
64
|
+
section.tasks.each do |task|
|
|
65
|
+
calculate_task_date(task) if task.start_date && !task.after_task
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Second pass: resolve dependencies
|
|
70
|
+
max_iterations = 100
|
|
71
|
+
iteration = 0
|
|
72
|
+
loop do
|
|
73
|
+
changed = false
|
|
74
|
+
@diagram.sections.each do |section|
|
|
75
|
+
section.tasks.each do |task|
|
|
76
|
+
if !task.calculated_start && (task.after_task || task.until_task)
|
|
77
|
+
if resolve_task_dependency(task)
|
|
78
|
+
changed = true
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
iteration += 1
|
|
85
|
+
break if !changed || iteration >= max_iterations
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def calculate_task_date(task)
|
|
90
|
+
return if task.calculated_start
|
|
91
|
+
|
|
92
|
+
if task.start_date
|
|
93
|
+
task.calculated_start = parse_date(task.start_date)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if task.end_date
|
|
97
|
+
task.calculated_end = parse_date(task.end_date)
|
|
98
|
+
elsif task.duration && task.calculated_start
|
|
99
|
+
task.calculated_end = add_duration(task.calculated_start, task.duration)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
task.calculated_start
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def resolve_task_dependency(task)
|
|
106
|
+
if task.after_task
|
|
107
|
+
ref_task = @task_map[task.after_task]
|
|
108
|
+
return false unless ref_task && ref_task.calculated_end
|
|
109
|
+
|
|
110
|
+
task.calculated_start = ref_task.calculated_end
|
|
111
|
+
if task.duration
|
|
112
|
+
task.calculated_end = add_duration(task.calculated_start, task.duration)
|
|
113
|
+
elsif task.end_date
|
|
114
|
+
task.calculated_end = parse_date(task.end_date)
|
|
115
|
+
elsif task.until_task
|
|
116
|
+
until_task = @task_map[task.until_task]
|
|
117
|
+
task.calculated_end = until_task.calculated_start if until_task
|
|
118
|
+
end
|
|
119
|
+
return true
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
if task.until_task && task.start_date
|
|
123
|
+
task.calculated_start = parse_date(task.start_date)
|
|
124
|
+
until_task = @task_map[task.until_task]
|
|
125
|
+
if until_task && until_task.calculated_start
|
|
126
|
+
task.calculated_end = until_task.calculated_start
|
|
127
|
+
return true
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
false
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def parse_date(date_str)
|
|
135
|
+
# Handle various date formats
|
|
136
|
+
Date.parse(date_str)
|
|
137
|
+
rescue ArgumentError
|
|
138
|
+
# Default to today if parsing fails
|
|
139
|
+
Date.today
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def add_duration(start_date, duration_str)
|
|
143
|
+
# Parse duration string (e.g., "30d", "2w", "48h", "1M")
|
|
144
|
+
value = duration_str.to_i
|
|
145
|
+
unit = duration_str[-1]
|
|
146
|
+
|
|
147
|
+
case unit
|
|
148
|
+
when "d"
|
|
149
|
+
start_date + value
|
|
150
|
+
when "w"
|
|
151
|
+
start_date + (value * 7)
|
|
152
|
+
when "h"
|
|
153
|
+
# Convert hours to days (rough approximation)
|
|
154
|
+
start_date + (value / 24.0).ceil
|
|
155
|
+
when "M"
|
|
156
|
+
# Add months (rough approximation)
|
|
157
|
+
start_date >> value
|
|
158
|
+
else
|
|
159
|
+
start_date + value # Default to days
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def calculate_timeline
|
|
164
|
+
min_date = nil
|
|
165
|
+
max_date = nil
|
|
166
|
+
|
|
167
|
+
@diagram.sections.each do |section|
|
|
168
|
+
section.tasks.each do |task|
|
|
169
|
+
if task.calculated_start
|
|
170
|
+
min_date = task.calculated_start if !min_date || task.calculated_start < min_date
|
|
171
|
+
end
|
|
172
|
+
if task.calculated_end
|
|
173
|
+
max_date = task.calculated_end if !max_date || task.calculated_end > max_date
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Default to a reasonable range if no dates found
|
|
179
|
+
min_date ||= Date.today
|
|
180
|
+
max_date ||= Date.today + 30
|
|
181
|
+
|
|
182
|
+
# Add padding
|
|
183
|
+
min_date -= 1
|
|
184
|
+
max_date += 1
|
|
185
|
+
|
|
186
|
+
{
|
|
187
|
+
start_date: min_date,
|
|
188
|
+
end_date: max_date,
|
|
189
|
+
total_days: (max_date - min_date).to_i
|
|
190
|
+
}
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def transform_sections(diagram, timeline)
|
|
194
|
+
diagram.sections.map.with_index do |section, section_index|
|
|
195
|
+
{
|
|
196
|
+
id: "section_#{section_index}",
|
|
197
|
+
name: section.name,
|
|
198
|
+
tasks: transform_tasks(section, timeline, section_index)
|
|
199
|
+
}
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def transform_tasks(section, timeline, section_index)
|
|
204
|
+
section.tasks.map.with_index do |task, task_index|
|
|
205
|
+
{
|
|
206
|
+
id: task.id || "task_#{section_index}_#{task_index}",
|
|
207
|
+
description: task.description,
|
|
208
|
+
tags: task.tags,
|
|
209
|
+
done: task.done?,
|
|
210
|
+
active: task.active?,
|
|
211
|
+
critical: task.critical?,
|
|
212
|
+
milestone: task.milestone?,
|
|
213
|
+
start_date: task.calculated_start,
|
|
214
|
+
end_date: task.calculated_end,
|
|
215
|
+
start_x: calculate_x_position(task.calculated_start, timeline),
|
|
216
|
+
width: calculate_width(task.calculated_start, task.calculated_end, timeline),
|
|
217
|
+
click_href: task.click_href,
|
|
218
|
+
click_callback: task.click_callback
|
|
219
|
+
}
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def calculate_x_position(date, timeline)
|
|
224
|
+
return 0 unless date
|
|
225
|
+
|
|
226
|
+
days_from_start = (date - timeline[:start_date]).to_i
|
|
227
|
+
(days_from_start.to_f / timeline[:total_days]) * 800 # 800 is the timeline width
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def calculate_width(start_date, end_date, timeline)
|
|
231
|
+
return 20 unless start_date && end_date # Minimum width for milestones
|
|
232
|
+
|
|
233
|
+
duration_days = (end_date - start_date).to_i
|
|
234
|
+
return 10 if duration_days <= 0 # Milestone or zero duration
|
|
235
|
+
|
|
236
|
+
(duration_days.to_f / timeline[:total_days]) * 800
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def total_task_count(diagram)
|
|
240
|
+
diagram.sections.sum { |s| s.tasks.length }
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Extend GanttTask to hold calculated dates
|
|
247
|
+
module Sirena
|
|
248
|
+
module Diagram
|
|
249
|
+
class GanttTask
|
|
250
|
+
attr_accessor :calculated_start, :calculated_end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sirena
|
|
4
|
+
module Transform
|
|
5
|
+
# Transforms a GitGraph diagram into a positioned layout structure.
|
|
6
|
+
#
|
|
7
|
+
# Unlike other transformers that use ELK layout, GitGraph uses a custom
|
|
8
|
+
# layout algorithm that assigns commits to horizontal lanes (Y positions)
|
|
9
|
+
# and sequential X positions based on commit order.
|
|
10
|
+
#
|
|
11
|
+
# The layout algorithm handles:
|
|
12
|
+
# - Branch lane assignment (main branch at top)
|
|
13
|
+
# - Commit positioning (chronological X coordinates)
|
|
14
|
+
# - Parent-child relationships for drawing connections
|
|
15
|
+
# - Merge point tracking for drawing merge arrows
|
|
16
|
+
# - Cherry-pick visualization
|
|
17
|
+
#
|
|
18
|
+
# @example Transform a git graph
|
|
19
|
+
# transform = Transform::GitGraph.new
|
|
20
|
+
# layout = transform.to_graph(diagram)
|
|
21
|
+
class GitGraph
|
|
22
|
+
# Spacing between commits horizontally
|
|
23
|
+
COMMIT_SPACING = 80
|
|
24
|
+
|
|
25
|
+
# Spacing between branch lanes vertically
|
|
26
|
+
LANE_SPACING = 60
|
|
27
|
+
|
|
28
|
+
# Radius of commit circles
|
|
29
|
+
COMMIT_RADIUS = 8
|
|
30
|
+
|
|
31
|
+
# Default branch colors (cycling through these)
|
|
32
|
+
DEFAULT_COLORS = %w[
|
|
33
|
+
#2563eb #7c3aed #db2777 #ea580c #ca8a04
|
|
34
|
+
#16a34a #0891b2 #4f46e5 #c026d3 #dc2626
|
|
35
|
+
].freeze
|
|
36
|
+
|
|
37
|
+
# Transforms the diagram into a layout structure.
|
|
38
|
+
#
|
|
39
|
+
# @param diagram [Diagram::GitGraph] the git graph diagram
|
|
40
|
+
# @return [Hash] layout data with commits, branches, and connections
|
|
41
|
+
def to_graph(diagram)
|
|
42
|
+
# Build commit lookup and parent tracking
|
|
43
|
+
commits_by_id = build_commit_lookup(diagram.commits)
|
|
44
|
+
branch_info = build_branch_info(diagram)
|
|
45
|
+
|
|
46
|
+
# Assign lanes to branches
|
|
47
|
+
lane_assignments = assign_lanes(diagram, branch_info)
|
|
48
|
+
|
|
49
|
+
# Position commits
|
|
50
|
+
positioned_commits = position_commits(
|
|
51
|
+
diagram.commits,
|
|
52
|
+
commits_by_id,
|
|
53
|
+
lane_assignments
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# Build connections between commits
|
|
57
|
+
connections = build_connections(
|
|
58
|
+
positioned_commits,
|
|
59
|
+
commits_by_id
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Build branch metadata
|
|
63
|
+
branches = build_branch_metadata(
|
|
64
|
+
diagram.branches,
|
|
65
|
+
lane_assignments,
|
|
66
|
+
positioned_commits
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
{
|
|
70
|
+
commits: positioned_commits,
|
|
71
|
+
branches: branches,
|
|
72
|
+
connections: connections,
|
|
73
|
+
width: calculate_width(positioned_commits),
|
|
74
|
+
height: calculate_height(lane_assignments)
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
# Builds a lookup hash of commits by ID.
|
|
81
|
+
#
|
|
82
|
+
# @param commits [Array<Diagram::GitGraph::Commit>] commits
|
|
83
|
+
# @return [Hash<String, Diagram::GitGraph::Commit>] commit lookup
|
|
84
|
+
def build_commit_lookup(commits)
|
|
85
|
+
lookup = {}
|
|
86
|
+
commits.each_with_index do |commit, idx|
|
|
87
|
+
# Use index as fallback ID if no ID specified
|
|
88
|
+
key = commit.id || "commit_#{idx}"
|
|
89
|
+
lookup[key] = commit
|
|
90
|
+
end
|
|
91
|
+
lookup
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Builds branch information from diagram.
|
|
95
|
+
#
|
|
96
|
+
# @param diagram [Diagram::GitGraph] diagram
|
|
97
|
+
# @return [Hash] branch info with parent relationships
|
|
98
|
+
def build_branch_info(diagram)
|
|
99
|
+
info = {}
|
|
100
|
+
|
|
101
|
+
# Start with main branch
|
|
102
|
+
info["main"] = {
|
|
103
|
+
parent: nil,
|
|
104
|
+
order: 0,
|
|
105
|
+
created_at: nil
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# Add other branches
|
|
109
|
+
diagram.branches.each do |branch|
|
|
110
|
+
info[branch.name] = {
|
|
111
|
+
parent: branch.parent_branch || "main",
|
|
112
|
+
order: branch.order || info.size,
|
|
113
|
+
created_at: branch.created_at_commit
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
info
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Assigns lanes (Y positions) to branches.
|
|
121
|
+
#
|
|
122
|
+
# Main branch gets lane 0, child branches get lanes below parent.
|
|
123
|
+
#
|
|
124
|
+
# @param diagram [Diagram::GitGraph] diagram
|
|
125
|
+
# @param branch_info [Hash] branch information
|
|
126
|
+
# @return [Hash<String, Integer>] branch name to lane number
|
|
127
|
+
def assign_lanes(diagram, branch_info)
|
|
128
|
+
lanes = {}
|
|
129
|
+
next_lane = 0
|
|
130
|
+
|
|
131
|
+
# Assign main branch to lane 0
|
|
132
|
+
lanes["main"] = next_lane
|
|
133
|
+
next_lane += 1
|
|
134
|
+
|
|
135
|
+
# Sort branches by order then by creation
|
|
136
|
+
sorted_branches = diagram.branches.sort_by do |b|
|
|
137
|
+
[branch_info[b.name][:order] || 999, b.name]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Assign lanes to other branches
|
|
141
|
+
sorted_branches.each do |branch|
|
|
142
|
+
lanes[branch.name] = next_lane
|
|
143
|
+
next_lane += 1
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
lanes
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Positions commits with X and Y coordinates.
|
|
150
|
+
#
|
|
151
|
+
# @param commits [Array<Diagram::GitGraph::Commit>] commits
|
|
152
|
+
# @param commits_by_id [Hash] commit lookup
|
|
153
|
+
# @param lane_assignments [Hash] branch to lane mapping
|
|
154
|
+
# @return [Array<Hash>] positioned commits
|
|
155
|
+
def position_commits(commits, commits_by_id, lane_assignments)
|
|
156
|
+
positioned = []
|
|
157
|
+
|
|
158
|
+
commits.each_with_index do |commit, idx|
|
|
159
|
+
# X position is based on commit order
|
|
160
|
+
x = idx * COMMIT_SPACING + COMMIT_SPACING
|
|
161
|
+
|
|
162
|
+
# Y position is based on branch lane
|
|
163
|
+
branch = commit.branch_name || "main"
|
|
164
|
+
lane = lane_assignments[branch] || 0
|
|
165
|
+
y = lane * LANE_SPACING + LANE_SPACING
|
|
166
|
+
|
|
167
|
+
commit_id = commit.id || "commit_#{idx}"
|
|
168
|
+
|
|
169
|
+
positioned << {
|
|
170
|
+
id: commit_id,
|
|
171
|
+
x: x,
|
|
172
|
+
y: y,
|
|
173
|
+
branch: branch,
|
|
174
|
+
lane: lane,
|
|
175
|
+
type: commit.type || "NORMAL",
|
|
176
|
+
tag: commit.tag,
|
|
177
|
+
parent_ids: commit.parent_ids,
|
|
178
|
+
is_merge: commit.is_merge || false,
|
|
179
|
+
merge_branch: commit.merge_branch,
|
|
180
|
+
is_cherry_pick: commit.is_cherry_pick || false,
|
|
181
|
+
cherry_pick_parent: commit.cherry_pick_parent,
|
|
182
|
+
# Store original commit for reference
|
|
183
|
+
original: commit
|
|
184
|
+
}
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
positioned
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Builds connections between commits.
|
|
191
|
+
#
|
|
192
|
+
# @param positioned_commits [Array<Hash>] positioned commits
|
|
193
|
+
# @param commits_by_id [Hash] commit lookup
|
|
194
|
+
# @return [Array<Hash>] connections with from/to commits and type
|
|
195
|
+
def build_connections(positioned_commits, commits_by_id)
|
|
196
|
+
connections = []
|
|
197
|
+
commit_positions = positioned_commits.each_with_object({}) do |c, h|
|
|
198
|
+
h[c[:id]] = c
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
positioned_commits.each do |commit|
|
|
202
|
+
# Connect to parent commits
|
|
203
|
+
commit[:parent_ids].each do |parent_id|
|
|
204
|
+
parent = commit_positions[parent_id]
|
|
205
|
+
next unless parent
|
|
206
|
+
|
|
207
|
+
connection_type = if commit[:is_merge]
|
|
208
|
+
:merge
|
|
209
|
+
elsif commit[:is_cherry_pick]
|
|
210
|
+
:cherry_pick
|
|
211
|
+
else
|
|
212
|
+
:normal
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
connections << {
|
|
216
|
+
from: parent[:id],
|
|
217
|
+
to: commit[:id],
|
|
218
|
+
from_x: parent[:x],
|
|
219
|
+
from_y: parent[:y],
|
|
220
|
+
to_x: commit[:x],
|
|
221
|
+
to_y: commit[:y],
|
|
222
|
+
from_branch: parent[:branch],
|
|
223
|
+
to_branch: commit[:branch],
|
|
224
|
+
type: connection_type
|
|
225
|
+
}
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
connections
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Builds branch metadata for rendering.
|
|
233
|
+
#
|
|
234
|
+
# @param branches [Array<Diagram::GitGraph::Branch>] branches
|
|
235
|
+
# @param lane_assignments [Hash] lane assignments
|
|
236
|
+
# @param positioned_commits [Array<Hash>] positioned commits
|
|
237
|
+
# @return [Array<Hash>] branch metadata
|
|
238
|
+
def build_branch_metadata(branches, lane_assignments, positioned_commits)
|
|
239
|
+
metadata = []
|
|
240
|
+
|
|
241
|
+
# Add main branch
|
|
242
|
+
metadata << {
|
|
243
|
+
name: "main",
|
|
244
|
+
lane: lane_assignments["main"] || 0,
|
|
245
|
+
color: DEFAULT_COLORS[0]
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
# Add other branches with cycling colors
|
|
249
|
+
branches.each_with_index do |branch, idx|
|
|
250
|
+
metadata << {
|
|
251
|
+
name: branch.name,
|
|
252
|
+
lane: lane_assignments[branch.name] || (idx + 1),
|
|
253
|
+
color: DEFAULT_COLORS[(idx + 1) % DEFAULT_COLORS.length]
|
|
254
|
+
}
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
metadata
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Calculates total width needed for the layout.
|
|
261
|
+
#
|
|
262
|
+
# @param positioned_commits [Array<Hash>] positioned commits
|
|
263
|
+
# @return [Numeric] total width in pixels
|
|
264
|
+
def calculate_width(positioned_commits)
|
|
265
|
+
return COMMIT_SPACING * 2 if positioned_commits.empty?
|
|
266
|
+
|
|
267
|
+
max_x = positioned_commits.map { |c| c[:x] }.max
|
|
268
|
+
max_x + COMMIT_SPACING
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Calculates total height needed for the layout.
|
|
272
|
+
#
|
|
273
|
+
# @param lane_assignments [Hash] lane assignments
|
|
274
|
+
# @return [Numeric] total height in pixels
|
|
275
|
+
def calculate_height(lane_assignments)
|
|
276
|
+
return LANE_SPACING * 2 if lane_assignments.empty?
|
|
277
|
+
|
|
278
|
+
max_lane = lane_assignments.values.max
|
|
279
|
+
(max_lane + 1) * LANE_SPACING + LANE_SPACING
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
require_relative '../diagram/info'
|
|
5
|
+
|
|
6
|
+
module Sirena
|
|
7
|
+
module Transform
|
|
8
|
+
# Info diagram transformer for converting info models to renderable structure.
|
|
9
|
+
#
|
|
10
|
+
# Info diagrams have no complex layout requirements - they simply display
|
|
11
|
+
# an informational message. This transformer prepares basic data for rendering.
|
|
12
|
+
#
|
|
13
|
+
# @example Transform an info diagram
|
|
14
|
+
# transform = InfoTransform.new
|
|
15
|
+
# data = transform.to_graph(info_diagram)
|
|
16
|
+
class InfoTransform < Base
|
|
17
|
+
# Converts an info diagram to a simple data structure.
|
|
18
|
+
#
|
|
19
|
+
# Info diagrams don't need layout computation. This method validates
|
|
20
|
+
# the diagram and returns a structure for the renderer.
|
|
21
|
+
#
|
|
22
|
+
# @param diagram [Diagram::Info] the info diagram to transform
|
|
23
|
+
# @return [Hash] data structure for rendering
|
|
24
|
+
# @raise [TransformError] if diagram is invalid
|
|
25
|
+
def to_graph(diagram)
|
|
26
|
+
raise TransformError, 'Invalid diagram' unless diagram.valid?
|
|
27
|
+
|
|
28
|
+
{
|
|
29
|
+
id: diagram.id || 'info',
|
|
30
|
+
title: diagram.title,
|
|
31
|
+
show_info: diagram.show_info || false,
|
|
32
|
+
metadata: {
|
|
33
|
+
diagram_type: :info
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|