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,385 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'lutaml/model'
|
|
4
|
+
require_relative 'base'
|
|
5
|
+
|
|
6
|
+
module Sirena
|
|
7
|
+
module Diagram
|
|
8
|
+
# Represents a class attribute in a UML class diagram.
|
|
9
|
+
#
|
|
10
|
+
# An attribute has a name, type, and visibility modifier that
|
|
11
|
+
# controls its access level.
|
|
12
|
+
class ClassAttribute < Lutaml::Model::Serializable
|
|
13
|
+
# Attribute name
|
|
14
|
+
attribute :name, :string
|
|
15
|
+
|
|
16
|
+
# Attribute type (e.g., 'string', 'int', 'List~String~')
|
|
17
|
+
attribute :type, :string
|
|
18
|
+
|
|
19
|
+
# Visibility: :public (+), :private (-), :protected (#),
|
|
20
|
+
# :package (~)
|
|
21
|
+
attribute :visibility, :string
|
|
22
|
+
|
|
23
|
+
# Initialize with default visibility
|
|
24
|
+
def initialize(*args)
|
|
25
|
+
super
|
|
26
|
+
self.visibility ||= 'public'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Validates the attribute has required fields.
|
|
30
|
+
#
|
|
31
|
+
# @return [Boolean] true if attribute is valid
|
|
32
|
+
def valid?
|
|
33
|
+
!name.nil? && !name.empty?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Returns the UML visibility symbol.
|
|
37
|
+
#
|
|
38
|
+
# @return [String] the visibility symbol (+, -, #, ~)
|
|
39
|
+
def visibility_symbol
|
|
40
|
+
case visibility
|
|
41
|
+
when 'public' then '+'
|
|
42
|
+
when 'private' then '-'
|
|
43
|
+
when 'protected' then '#'
|
|
44
|
+
when 'package' then '~'
|
|
45
|
+
else '+'
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Represents a class method in a UML class diagram.
|
|
51
|
+
#
|
|
52
|
+
# A method has a name, optional parameters, optional return type,
|
|
53
|
+
# and visibility modifier.
|
|
54
|
+
class ClassMethod < Lutaml::Model::Serializable
|
|
55
|
+
# Method name
|
|
56
|
+
attribute :name, :string
|
|
57
|
+
|
|
58
|
+
# Method parameters (e.g., 'x: int, y: int')
|
|
59
|
+
attribute :parameters, :string
|
|
60
|
+
|
|
61
|
+
# Return type (e.g., 'string', 'void')
|
|
62
|
+
attribute :return_type, :string
|
|
63
|
+
|
|
64
|
+
# Visibility: :public (+), :private (-), :protected (#),
|
|
65
|
+
# :package (~)
|
|
66
|
+
attribute :visibility, :string
|
|
67
|
+
|
|
68
|
+
# Initialize with default visibility
|
|
69
|
+
def initialize(*args)
|
|
70
|
+
super
|
|
71
|
+
self.visibility ||= 'public'
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Validates the method has required fields.
|
|
75
|
+
#
|
|
76
|
+
# @return [Boolean] true if method is valid
|
|
77
|
+
def valid?
|
|
78
|
+
!name.nil? && !name.empty?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Returns the UML visibility symbol.
|
|
82
|
+
#
|
|
83
|
+
# @return [String] the visibility symbol (+, -, #, ~)
|
|
84
|
+
def visibility_symbol
|
|
85
|
+
case visibility
|
|
86
|
+
when 'public' then '+'
|
|
87
|
+
when 'private' then '-'
|
|
88
|
+
when 'protected' then '#'
|
|
89
|
+
when 'package' then '~'
|
|
90
|
+
else '+'
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Returns the full method signature.
|
|
95
|
+
#
|
|
96
|
+
# @return [String] method signature with parameters and return type
|
|
97
|
+
def signature
|
|
98
|
+
sig = name
|
|
99
|
+
sig += "(#{parameters})" if parameters && !parameters.empty?
|
|
100
|
+
sig += " #{return_type}" if return_type && !return_type.empty?
|
|
101
|
+
sig
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Represents a class/entity in a UML class diagram.
|
|
106
|
+
#
|
|
107
|
+
# A class entity has an identifier, name, optional stereotype,
|
|
108
|
+
# collection of attributes, and collection of methods.
|
|
109
|
+
class ClassEntity < Lutaml::Model::Serializable
|
|
110
|
+
# Unique identifier for the class
|
|
111
|
+
attribute :id, :string
|
|
112
|
+
|
|
113
|
+
# Display name for the class
|
|
114
|
+
attribute :name, :string
|
|
115
|
+
|
|
116
|
+
# Optional stereotype (e.g., 'interface', 'abstract', 'enum')
|
|
117
|
+
attribute :stereotype, :string
|
|
118
|
+
|
|
119
|
+
# Collection of class attributes
|
|
120
|
+
attribute :attributes, ClassAttribute, collection: true,
|
|
121
|
+
default: -> { [] }
|
|
122
|
+
|
|
123
|
+
# Collection of class methods
|
|
124
|
+
attribute :class_methods, ClassMethod, collection: true,
|
|
125
|
+
default: -> { [] }
|
|
126
|
+
|
|
127
|
+
# Validates the class entity has required fields.
|
|
128
|
+
#
|
|
129
|
+
# @return [Boolean] true if class entity is valid
|
|
130
|
+
def valid?
|
|
131
|
+
!id.nil? && !id.empty? && !name.nil? && !name.empty? &&
|
|
132
|
+
attributes.all?(&:valid?) && class_methods.all?(&:valid?)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Checks if this is an interface.
|
|
136
|
+
#
|
|
137
|
+
# @return [Boolean] true if stereotype is 'interface'
|
|
138
|
+
def interface?
|
|
139
|
+
stereotype == 'interface'
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Checks if this is an abstract class.
|
|
143
|
+
#
|
|
144
|
+
# @return [Boolean] true if stereotype is 'abstract'
|
|
145
|
+
def abstract?
|
|
146
|
+
stereotype == 'abstract'
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Checks if this is an enum.
|
|
150
|
+
#
|
|
151
|
+
# @return [Boolean] true if stereotype is 'enum'
|
|
152
|
+
def enum?
|
|
153
|
+
stereotype == 'enum'
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Represents a relationship between classes in a UML class diagram.
|
|
158
|
+
#
|
|
159
|
+
# A relationship connects two classes with a specific type
|
|
160
|
+
# (inheritance, composition, aggregation, or association) and
|
|
161
|
+
# optional label and cardinality.
|
|
162
|
+
class ClassRelationship < Lutaml::Model::Serializable
|
|
163
|
+
# Source class identifier
|
|
164
|
+
attribute :from_id, :string
|
|
165
|
+
|
|
166
|
+
# Target class identifier
|
|
167
|
+
attribute :to_id, :string
|
|
168
|
+
|
|
169
|
+
# Relationship type: :inheritance, :composition, :aggregation,
|
|
170
|
+
# :association, :dependency, :realization
|
|
171
|
+
attribute :relationship_type, :string
|
|
172
|
+
|
|
173
|
+
# Optional relationship label
|
|
174
|
+
attribute :label, :string
|
|
175
|
+
|
|
176
|
+
# Optional cardinality for source end (e.g., '1', '0..1', '1..*')
|
|
177
|
+
attribute :source_cardinality, :string
|
|
178
|
+
|
|
179
|
+
# Optional cardinality for target end (e.g., '1', '0..1', '1..*')
|
|
180
|
+
attribute :target_cardinality, :string
|
|
181
|
+
|
|
182
|
+
# Initialize with default relationship type
|
|
183
|
+
def initialize(*args)
|
|
184
|
+
super
|
|
185
|
+
self.relationship_type ||= 'association'
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Validates the relationship has required fields.
|
|
189
|
+
#
|
|
190
|
+
# @return [Boolean] true if relationship is valid
|
|
191
|
+
def valid?
|
|
192
|
+
!from_id.nil? && !from_id.empty? &&
|
|
193
|
+
!to_id.nil? && !to_id.empty? &&
|
|
194
|
+
!relationship_type.nil? && !relationship_type.empty?
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Checks if this is an inheritance relationship.
|
|
198
|
+
#
|
|
199
|
+
# @return [Boolean] true if inheritance type
|
|
200
|
+
def inheritance?
|
|
201
|
+
relationship_type == 'inheritance'
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Checks if this is a composition relationship.
|
|
205
|
+
#
|
|
206
|
+
# @return [Boolean] true if composition type
|
|
207
|
+
def composition?
|
|
208
|
+
relationship_type == 'composition'
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Checks if this is an aggregation relationship.
|
|
212
|
+
#
|
|
213
|
+
# @return [Boolean] true if aggregation type
|
|
214
|
+
def aggregation?
|
|
215
|
+
relationship_type == 'aggregation'
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Checks if this is an association relationship.
|
|
219
|
+
#
|
|
220
|
+
# @return [Boolean] true if association type
|
|
221
|
+
def association?
|
|
222
|
+
relationship_type == 'association'
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Checks if this is a dependency relationship.
|
|
226
|
+
#
|
|
227
|
+
# @return [Boolean] true if dependency type
|
|
228
|
+
def dependency?
|
|
229
|
+
relationship_type == 'dependency'
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Checks if this is a realization relationship.
|
|
233
|
+
#
|
|
234
|
+
# @return [Boolean] true if realization type
|
|
235
|
+
def realization?
|
|
236
|
+
relationship_type == 'realization'
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Class diagram model.
|
|
241
|
+
#
|
|
242
|
+
# Represents a complete UML class diagram with classes and their
|
|
243
|
+
# relationships. Class diagrams show the static structure of a
|
|
244
|
+
# system with classes, attributes, methods, and relationships.
|
|
245
|
+
#
|
|
246
|
+
# @example Creating a simple class diagram
|
|
247
|
+
# diagram = ClassDiagram.new(direction: 'TB')
|
|
248
|
+
# diagram.entities << ClassEntity.new(
|
|
249
|
+
# id: 'Animal',
|
|
250
|
+
# name: 'Animal'
|
|
251
|
+
# ).tap do |entity|
|
|
252
|
+
# entity.attributes << ClassAttribute.new(
|
|
253
|
+
# name: 'age',
|
|
254
|
+
# type: 'int',
|
|
255
|
+
# visibility: 'protected'
|
|
256
|
+
# )
|
|
257
|
+
# entity.class_methods << ClassMethod.new(
|
|
258
|
+
# name: 'breathe',
|
|
259
|
+
# visibility: 'public'
|
|
260
|
+
# )
|
|
261
|
+
# end
|
|
262
|
+
# diagram.entities << ClassEntity.new(
|
|
263
|
+
# id: 'Dog',
|
|
264
|
+
# name: 'Dog'
|
|
265
|
+
# ).tap do |entity|
|
|
266
|
+
# entity.class_methods << ClassMethod.new(
|
|
267
|
+
# name: 'bark',
|
|
268
|
+
# visibility: 'public'
|
|
269
|
+
# )
|
|
270
|
+
# end
|
|
271
|
+
# diagram.relationships << ClassRelationship.new(
|
|
272
|
+
# from_id: 'Dog',
|
|
273
|
+
# to_id: 'Animal',
|
|
274
|
+
# relationship_type: 'inheritance'
|
|
275
|
+
# )
|
|
276
|
+
class ClassDiagram < Base
|
|
277
|
+
# Collection of class entities in the diagram
|
|
278
|
+
attribute :entities, ClassEntity, collection: true,
|
|
279
|
+
default: -> { [] }
|
|
280
|
+
|
|
281
|
+
# Collection of relationships between entities
|
|
282
|
+
attribute :relationships, ClassRelationship, collection: true,
|
|
283
|
+
default: -> { [] }
|
|
284
|
+
|
|
285
|
+
# Returns the diagram type identifier.
|
|
286
|
+
#
|
|
287
|
+
# @return [Symbol] :class_diagram
|
|
288
|
+
def diagram_type
|
|
289
|
+
:class_diagram
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Validates the class diagram structure.
|
|
293
|
+
#
|
|
294
|
+
# A class diagram is valid if:
|
|
295
|
+
# - It has at least one entity
|
|
296
|
+
# - All entities are valid
|
|
297
|
+
# - All relationships are valid
|
|
298
|
+
# - All relationship references point to existing entities
|
|
299
|
+
#
|
|
300
|
+
# @return [Boolean] true if class diagram is valid
|
|
301
|
+
def valid?
|
|
302
|
+
return false if entities.nil? || entities.empty?
|
|
303
|
+
return false unless entities.all?(&:valid?)
|
|
304
|
+
return false unless relationships.nil? ||
|
|
305
|
+
relationships.all?(&:valid?)
|
|
306
|
+
|
|
307
|
+
# Validate relationship references
|
|
308
|
+
entity_ids = entities.map(&:id)
|
|
309
|
+
relationships&.each do |rel|
|
|
310
|
+
return false unless entity_ids.include?(rel.from_id)
|
|
311
|
+
return false unless entity_ids.include?(rel.to_id)
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
true
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Finds an entity by its identifier.
|
|
318
|
+
#
|
|
319
|
+
# @param id [String] the entity identifier to find
|
|
320
|
+
# @return [ClassEntity, nil] the entity or nil if not found
|
|
321
|
+
def find_entity(id)
|
|
322
|
+
entities.find { |e| e.id == id }
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Finds all relationships from a specific entity.
|
|
326
|
+
#
|
|
327
|
+
# @param entity_id [String] the source entity identifier
|
|
328
|
+
# @return [Array<ClassRelationship>] relationships from the entity
|
|
329
|
+
def relationships_from(entity_id)
|
|
330
|
+
relationships.select { |r| r.from_id == entity_id }
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Finds all relationships to a specific entity.
|
|
334
|
+
#
|
|
335
|
+
# @param entity_id [String] the target entity identifier
|
|
336
|
+
# @return [Array<ClassRelationship>] relationships to the entity
|
|
337
|
+
def relationships_to(entity_id)
|
|
338
|
+
relationships.select { |r| r.to_id == entity_id }
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Finds all inheritance relationships.
|
|
342
|
+
#
|
|
343
|
+
# @return [Array<ClassRelationship>] inheritance relationships
|
|
344
|
+
def inheritance_relationships
|
|
345
|
+
relationships.select(&:inheritance?)
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# Finds all composition relationships.
|
|
349
|
+
#
|
|
350
|
+
# @return [Array<ClassRelationship>] composition relationships
|
|
351
|
+
def composition_relationships
|
|
352
|
+
relationships.select(&:composition?)
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Finds all aggregation relationships.
|
|
356
|
+
#
|
|
357
|
+
# @return [Array<ClassRelationship>] aggregation relationships
|
|
358
|
+
def aggregation_relationships
|
|
359
|
+
relationships.select(&:aggregation?)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Finds parent classes for a given entity.
|
|
363
|
+
#
|
|
364
|
+
# @param entity_id [String] the entity identifier
|
|
365
|
+
# @return [Array<ClassEntity>] parent entities
|
|
366
|
+
def parent_entities(entity_id)
|
|
367
|
+
parent_ids = relationships
|
|
368
|
+
.select { |r| r.from_id == entity_id && r.inheritance? }
|
|
369
|
+
.map(&:to_id)
|
|
370
|
+
entities.select { |e| parent_ids.include?(e.id) }
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# Finds child classes for a given entity.
|
|
374
|
+
#
|
|
375
|
+
# @param entity_id [String] the entity identifier
|
|
376
|
+
# @return [Array<ClassEntity>] child entities
|
|
377
|
+
def child_entities(entity_id)
|
|
378
|
+
child_ids = relationships
|
|
379
|
+
.select { |r| r.to_id == entity_id && r.inheritance? }
|
|
380
|
+
.map(&:from_id)
|
|
381
|
+
entities.select { |e| child_ids.include?(e.id) }
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
end
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'lutaml/model'
|
|
4
|
+
require_relative 'base'
|
|
5
|
+
|
|
6
|
+
module Sirena
|
|
7
|
+
module Diagram
|
|
8
|
+
# Represents an attribute in an ER diagram entity.
|
|
9
|
+
#
|
|
10
|
+
# An attribute has a name, optional type, and optional key type
|
|
11
|
+
# (PK for primary key, FK for foreign key).
|
|
12
|
+
class ErAttribute < Lutaml::Model::Serializable
|
|
13
|
+
# Attribute name
|
|
14
|
+
attribute :name, :string
|
|
15
|
+
|
|
16
|
+
# Attribute type (e.g., 'string', 'int', 'date')
|
|
17
|
+
attribute :attribute_type, :string
|
|
18
|
+
|
|
19
|
+
# Key type: 'PK' (primary key), 'FK' (foreign key), or nil
|
|
20
|
+
attribute :key_type, :string
|
|
21
|
+
|
|
22
|
+
# Validates the attribute has required fields.
|
|
23
|
+
#
|
|
24
|
+
# @return [Boolean] true if attribute is valid
|
|
25
|
+
def valid?
|
|
26
|
+
!name.nil? && !name.empty?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Checks if this is a primary key.
|
|
30
|
+
#
|
|
31
|
+
# @return [Boolean] true if key_type is 'PK'
|
|
32
|
+
def primary_key?
|
|
33
|
+
key_type == 'PK'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Checks if this is a foreign key.
|
|
37
|
+
#
|
|
38
|
+
# @return [Boolean] true if key_type is 'FK'
|
|
39
|
+
def foreign_key?
|
|
40
|
+
key_type == 'FK'
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Represents an entity in an ER diagram.
|
|
45
|
+
#
|
|
46
|
+
# An entity has an identifier, name, and collection of attributes.
|
|
47
|
+
class ErEntity < Lutaml::Model::Serializable
|
|
48
|
+
# Unique identifier for the entity
|
|
49
|
+
attribute :id, :string
|
|
50
|
+
|
|
51
|
+
# Display name for the entity
|
|
52
|
+
attribute :name, :string
|
|
53
|
+
|
|
54
|
+
# Collection of entity attributes
|
|
55
|
+
attribute :attributes, ErAttribute, collection: true,
|
|
56
|
+
default: -> { [] }
|
|
57
|
+
|
|
58
|
+
# Validates the entity has required fields.
|
|
59
|
+
#
|
|
60
|
+
# @return [Boolean] true if entity is valid
|
|
61
|
+
def valid?
|
|
62
|
+
!id.nil? && !id.empty? && !name.nil? && !name.empty? &&
|
|
63
|
+
attributes.all?(&:valid?)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Represents a relationship between entities in an ER diagram.
|
|
68
|
+
#
|
|
69
|
+
# A relationship connects two entities with cardinality on both ends
|
|
70
|
+
# and a relationship type (identifying or non-identifying).
|
|
71
|
+
class ErRelationship < Lutaml::Model::Serializable
|
|
72
|
+
# Source entity identifier
|
|
73
|
+
attribute :from_id, :string
|
|
74
|
+
|
|
75
|
+
# Target entity identifier
|
|
76
|
+
attribute :to_id, :string
|
|
77
|
+
|
|
78
|
+
# Relationship type: 'identifying' or 'non-identifying'
|
|
79
|
+
attribute :relationship_type, :string
|
|
80
|
+
|
|
81
|
+
# Cardinality from source end (e.g., 'one', 'zero_or_more',
|
|
82
|
+
# 'zero_or_one', 'one_or_more')
|
|
83
|
+
attribute :cardinality_from, :string
|
|
84
|
+
|
|
85
|
+
# Cardinality at target end (e.g., 'one', 'zero_or_more',
|
|
86
|
+
# 'zero_or_one', 'one_or_more')
|
|
87
|
+
attribute :cardinality_to, :string
|
|
88
|
+
|
|
89
|
+
# Optional relationship label
|
|
90
|
+
attribute :label, :string
|
|
91
|
+
|
|
92
|
+
# Initialize with default relationship type
|
|
93
|
+
def initialize(*args)
|
|
94
|
+
super
|
|
95
|
+
self.relationship_type ||= 'non-identifying'
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Validates the relationship has required fields.
|
|
99
|
+
#
|
|
100
|
+
# @return [Boolean] true if relationship is valid
|
|
101
|
+
def valid?
|
|
102
|
+
!from_id.nil? && !from_id.empty? &&
|
|
103
|
+
!to_id.nil? && !to_id.empty? &&
|
|
104
|
+
!relationship_type.nil? && !relationship_type.empty? &&
|
|
105
|
+
!cardinality_from.nil? && !cardinality_from.empty? &&
|
|
106
|
+
!cardinality_to.nil? && !cardinality_to.empty?
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Checks if this is an identifying relationship.
|
|
110
|
+
#
|
|
111
|
+
# @return [Boolean] true if identifying type
|
|
112
|
+
def identifying?
|
|
113
|
+
relationship_type == 'identifying'
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Checks if this is a non-identifying relationship.
|
|
117
|
+
#
|
|
118
|
+
# @return [Boolean] true if non-identifying type
|
|
119
|
+
def non_identifying?
|
|
120
|
+
relationship_type == 'non-identifying'
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# ER diagram model.
|
|
125
|
+
#
|
|
126
|
+
# Represents a complete Entity-Relationship diagram with entities
|
|
127
|
+
# and their relationships. ER diagrams show the data model structure
|
|
128
|
+
# with entities, attributes, and relationships.
|
|
129
|
+
#
|
|
130
|
+
# @example Creating a simple ER diagram
|
|
131
|
+
# diagram = ErDiagram.new
|
|
132
|
+
# diagram.entities << ErEntity.new(
|
|
133
|
+
# id: 'CUSTOMER',
|
|
134
|
+
# name: 'CUSTOMER'
|
|
135
|
+
# ).tap do |entity|
|
|
136
|
+
# entity.attributes << ErAttribute.new(
|
|
137
|
+
# name: 'id',
|
|
138
|
+
# attribute_type: 'int',
|
|
139
|
+
# key_type: 'PK'
|
|
140
|
+
# )
|
|
141
|
+
# entity.attributes << ErAttribute.new(
|
|
142
|
+
# name: 'name',
|
|
143
|
+
# attribute_type: 'string'
|
|
144
|
+
# )
|
|
145
|
+
# end
|
|
146
|
+
# diagram.entities << ErEntity.new(
|
|
147
|
+
# id: 'ORDER',
|
|
148
|
+
# name: 'ORDER'
|
|
149
|
+
# )
|
|
150
|
+
# diagram.relationships << ErRelationship.new(
|
|
151
|
+
# from_id: 'CUSTOMER',
|
|
152
|
+
# to_id: 'ORDER',
|
|
153
|
+
# relationship_type: 'non-identifying',
|
|
154
|
+
# cardinality_from: 'one',
|
|
155
|
+
# cardinality_to: 'zero_or_more',
|
|
156
|
+
# label: 'places'
|
|
157
|
+
# )
|
|
158
|
+
class ErDiagram < Base
|
|
159
|
+
# Collection of entities in the diagram
|
|
160
|
+
attribute :entities, ErEntity, collection: true,
|
|
161
|
+
default: -> { [] }
|
|
162
|
+
|
|
163
|
+
# Collection of relationships between entities
|
|
164
|
+
attribute :relationships, ErRelationship, collection: true,
|
|
165
|
+
default: -> { [] }
|
|
166
|
+
|
|
167
|
+
# Returns the diagram type identifier.
|
|
168
|
+
#
|
|
169
|
+
# @return [Symbol] :er_diagram
|
|
170
|
+
def diagram_type
|
|
171
|
+
:er_diagram
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Validates the ER diagram structure.
|
|
175
|
+
#
|
|
176
|
+
# An ER diagram is valid if:
|
|
177
|
+
# - It has at least one entity
|
|
178
|
+
# - All entities are valid
|
|
179
|
+
# - All relationships are valid
|
|
180
|
+
# - All relationship references point to existing entities
|
|
181
|
+
#
|
|
182
|
+
# @return [Boolean] true if ER diagram is valid
|
|
183
|
+
def valid?
|
|
184
|
+
return false if entities.nil? || entities.empty?
|
|
185
|
+
return false unless entities.all?(&:valid?)
|
|
186
|
+
return false unless relationships.nil? ||
|
|
187
|
+
relationships.all?(&:valid?)
|
|
188
|
+
|
|
189
|
+
# Validate relationship references
|
|
190
|
+
entity_ids = entities.map(&:id)
|
|
191
|
+
relationships&.each do |rel|
|
|
192
|
+
return false unless entity_ids.include?(rel.from_id)
|
|
193
|
+
return false unless entity_ids.include?(rel.to_id)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
true
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Finds an entity by its identifier.
|
|
200
|
+
#
|
|
201
|
+
# @param id [String] the entity identifier to find
|
|
202
|
+
# @return [ErEntity, nil] the entity or nil if not found
|
|
203
|
+
def find_entity(id)
|
|
204
|
+
entities.find { |e| e.id == id }
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Finds all relationships from a specific entity.
|
|
208
|
+
#
|
|
209
|
+
# @param entity_id [String] the source entity identifier
|
|
210
|
+
# @return [Array<ErRelationship>] relationships from the entity
|
|
211
|
+
def relationships_from(entity_id)
|
|
212
|
+
relationships.select { |r| r.from_id == entity_id }
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Finds all relationships to a specific entity.
|
|
216
|
+
#
|
|
217
|
+
# @param entity_id [String] the target entity identifier
|
|
218
|
+
# @return [Array<ErRelationship>] relationships to the entity
|
|
219
|
+
def relationships_to(entity_id)
|
|
220
|
+
relationships.select { |r| r.to_id == entity_id }
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Finds all identifying relationships.
|
|
224
|
+
#
|
|
225
|
+
# @return [Array<ErRelationship>] identifying relationships
|
|
226
|
+
def identifying_relationships
|
|
227
|
+
relationships.select(&:identifying?)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Finds all non-identifying relationships.
|
|
231
|
+
#
|
|
232
|
+
# @return [Array<ErRelationship>] non-identifying relationships
|
|
233
|
+
def non_identifying_relationships
|
|
234
|
+
relationships.select(&:non_identifying?)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'lutaml/model'
|
|
4
|
+
require_relative 'base'
|
|
5
|
+
|
|
6
|
+
module Sirena
|
|
7
|
+
module Diagram
|
|
8
|
+
# Error diagram model.
|
|
9
|
+
#
|
|
10
|
+
# Represents a simple error diagram that displays error messages or
|
|
11
|
+
# error states. Error diagrams are typically used to show parsing
|
|
12
|
+
# errors, syntax errors, or system failure states.
|
|
13
|
+
#
|
|
14
|
+
# @example Creating an error diagram
|
|
15
|
+
# error = Error.new
|
|
16
|
+
# error.message = "Syntax Error"
|
|
17
|
+
class Error < Base
|
|
18
|
+
# Error message to display
|
|
19
|
+
attribute :message, :string
|
|
20
|
+
|
|
21
|
+
# Returns the diagram type identifier.
|
|
22
|
+
#
|
|
23
|
+
# @return [Symbol] :error
|
|
24
|
+
def diagram_type
|
|
25
|
+
:error
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Validates the error diagram structure.
|
|
29
|
+
#
|
|
30
|
+
# Error diagrams are always valid as they have no required content.
|
|
31
|
+
#
|
|
32
|
+
# @return [Boolean] true
|
|
33
|
+
def valid?
|
|
34
|
+
true
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|