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.
Files changed (382) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build_deploy.yml +59 -0
  3. data/.github/workflows/links.yml +85 -0
  4. data/.github/workflows/rake.yml +15 -0
  5. data/.github/workflows/release.yml +27 -0
  6. data/.gitignore +68 -0
  7. data/.rspec +3 -0
  8. data/.rubocop.yml +14 -0
  9. data/.rubocop_todo.yml +70 -0
  10. data/ARCHITECTURE.md +744 -0
  11. data/Gemfile +12 -0
  12. data/LICENSE +25 -0
  13. data/README.adoc +357 -0
  14. data/Rakefile +11 -0
  15. data/docs/.gitignore +1 -0
  16. data/docs/Gemfile +13 -0
  17. data/docs/_config.yml +182 -0
  18. data/docs/_diagram_types/architecture-diagram.adoc +314 -0
  19. data/docs/_diagram_types/block-diagram.adoc +345 -0
  20. data/docs/_diagram_types/c4-diagram.adoc +559 -0
  21. data/docs/_diagram_types/class-diagram.adoc +816 -0
  22. data/docs/_diagram_types/er-diagram.adoc +719 -0
  23. data/docs/_diagram_types/error-diagram.adoc +114 -0
  24. data/docs/_diagram_types/examples/flowchart-examples.adoc +29 -0
  25. data/docs/_diagram_types/flowchart.adoc +488 -0
  26. data/docs/_diagram_types/gantt-chart.adoc +502 -0
  27. data/docs/_diagram_types/git-graph.adoc +600 -0
  28. data/docs/_diagram_types/index.adoc +192 -0
  29. data/docs/_diagram_types/info-diagram.adoc +103 -0
  30. data/docs/_diagram_types/kanban-diagram.adoc +262 -0
  31. data/docs/_diagram_types/mindmap.adoc +603 -0
  32. data/docs/_diagram_types/packet-diagram.adoc +378 -0
  33. data/docs/_diagram_types/pie-chart.adoc +335 -0
  34. data/docs/_diagram_types/quadrant-chart.adoc +406 -0
  35. data/docs/_diagram_types/radar-chart.adoc +528 -0
  36. data/docs/_diagram_types/requirement-diagram.adoc +416 -0
  37. data/docs/_diagram_types/sankey-diagram.adoc +357 -0
  38. data/docs/_diagram_types/sequence-diagram.adoc +664 -0
  39. data/docs/_diagram_types/state-diagram.adoc +658 -0
  40. data/docs/_diagram_types/timeline.adoc +352 -0
  41. data/docs/_diagram_types/treemap-diagram.adoc +462 -0
  42. data/docs/_diagram_types/user-journey.adoc +602 -0
  43. data/docs/_features/index.adoc +129 -0
  44. data/docs/_guides/cli-reference.adoc +203 -0
  45. data/docs/_guides/index.adoc +56 -0
  46. data/docs/_guides/installation.adoc +100 -0
  47. data/docs/_guides/quick-start.adoc +132 -0
  48. data/docs/_pages/comparison.adoc +441 -0
  49. data/docs/_pages/compatibility.adoc +300 -0
  50. data/docs/_pages/index.adoc +39 -0
  51. data/docs/_references/index.adoc +103 -0
  52. data/docs/_tutorials/index.adoc +57 -0
  53. data/docs/index.adoc +166 -0
  54. data/docs/lychee.toml +54 -0
  55. data/examples/.gitignore +10 -0
  56. data/examples/README.adoc +196 -0
  57. data/examples/README.md +64 -0
  58. data/examples/architecture/01-basic-services.mmd +9 -0
  59. data/examples/architecture/01-basic-services.svg +37 -0
  60. data/examples/architecture/02-service-groups.mmd +16 -0
  61. data/examples/architecture/02-service-groups.svg +55 -0
  62. data/examples/architecture/README.adoc +79 -0
  63. data/examples/block/01-basic-blocks.mmd +13 -0
  64. data/examples/block/01-basic-blocks.svg +44 -0
  65. data/examples/block/02-block-shapes.mmd +13 -0
  66. data/examples/block/02-block-shapes.svg +47 -0
  67. data/examples/block/README.adoc +85 -0
  68. data/examples/c4/01-context-diagram.mmd +10 -0
  69. data/examples/c4/01-context-diagram.svg +45 -0
  70. data/examples/c4/02-container-diagram.mmd +24 -0
  71. data/examples/c4/02-container-diagram.svg +105 -0
  72. data/examples/c4/README.adoc +92 -0
  73. data/examples/class_diagram/01-basic-classes.mmd +61 -0
  74. data/examples/class_diagram/01-basic-classes.svg +117 -0
  75. data/examples/class_diagram/02-relationships.mmd +61 -0
  76. data/examples/class_diagram/02-relationships.svg +129 -0
  77. data/examples/class_diagram/README.adoc +93 -0
  78. data/examples/er_diagram/01-basic-entities.mmd +64 -0
  79. data/examples/er_diagram/01-basic-entities.svg +5 -0
  80. data/examples/er_diagram/02-cardinality.mmd +57 -0
  81. data/examples/er_diagram/02-cardinality.svg +125 -0
  82. data/examples/er_diagram/README.adoc +88 -0
  83. data/examples/error/01-basic-error.mmd +1 -0
  84. data/examples/error/01-basic-error.svg +13 -0
  85. data/examples/error/02-error-display.mmd +1 -0
  86. data/examples/error/02-error-display.svg +13 -0
  87. data/examples/error/README.adoc +71 -0
  88. data/examples/error_message_example.svg +13 -0
  89. data/examples/flowchart/00-original.mmd +13 -0
  90. data/examples/flowchart/00-original.svg +5 -0
  91. data/examples/flowchart/01-basic-flow.mmd +7 -0
  92. data/examples/flowchart/01-basic-flow.svg +52 -0
  93. data/examples/flowchart/01-basic-flow.yml +13 -0
  94. data/examples/flowchart/02*.svg +87 -0
  95. data/examples/flowchart/02-node-shapes.mmd +9 -0
  96. data/examples/flowchart/02-node-shapes.svg +33 -0
  97. data/examples/flowchart/03-edge-types.mmd +7 -0
  98. data/examples/flowchart/03-edge-types.svg +53 -0
  99. data/examples/flowchart/04-subgraphs.mmd +9 -0
  100. data/examples/flowchart/04-subgraphs.svg +33 -0
  101. data/examples/flowchart/05-styling.mmd +9 -0
  102. data/examples/flowchart/05-styling.svg +33 -0
  103. data/examples/flowchart/06-complex-flow.mmd +8 -0
  104. data/examples/flowchart/06-complex-flow.svg +59 -0
  105. data/examples/flowchart/README.adoc +167 -0
  106. data/examples/gantt/01-simple-timeline.* +14 -0
  107. data/examples/gantt/01-simple-timeline.mmd +6 -0
  108. data/examples/gantt/01-simple-timeline.svg +26 -0
  109. data/examples/gantt/02-task-dependencies.mmd +6 -0
  110. data/examples/gantt/02-task-dependencies.svg +26 -0
  111. data/examples/gantt/README.adoc +86 -0
  112. data/examples/git_graph/01-linear-history.mmd +12 -0
  113. data/examples/git_graph/01-linear-history.svg +26 -0
  114. data/examples/git_graph/02-branching.mmd +12 -0
  115. data/examples/git_graph/02-branching.svg +26 -0
  116. data/examples/git_graph/README.adoc +73 -0
  117. data/examples/info/02-showinfo.mmd +1 -0
  118. data/examples/info/02-showinfo.svg +10 -0
  119. data/examples/info/README.adoc +58 -0
  120. data/examples/info_showinfo_example.svg +10 -0
  121. data/examples/kanban/01-simple-board.mmd +8 -0
  122. data/examples/kanban/01-simple-board.svg +43 -0
  123. data/examples/kanban/02-workflow.mmd +8 -0
  124. data/examples/kanban/02-workflow.svg +43 -0
  125. data/examples/kanban/README.adoc +79 -0
  126. data/examples/mindmap/01-simple-tree.mmd +19 -0
  127. data/examples/mindmap/01-simple-tree.svg +61 -0
  128. data/examples/mindmap/02-knowledge-map.mmd +19 -0
  129. data/examples/mindmap/02-knowledge-map.svg +61 -0
  130. data/examples/mindmap/README.adoc +77 -0
  131. data/examples/packet/01-basic-packet.* +17 -0
  132. data/examples/packet/01-basic-packet.mmd +4 -0
  133. data/examples/packet/01-basic-packet.svg +82 -0
  134. data/examples/packet/README.adoc +58 -0
  135. data/examples/pie/01-simple-chart.mmd +5 -0
  136. data/examples/pie/01-simple-chart.svg +17 -0
  137. data/examples/pie/02-labeled-slices.mmd +6 -0
  138. data/examples/pie/02-labeled-slices.svg +19 -0
  139. data/examples/pie/README.adoc +75 -0
  140. data/examples/quadrant/01-basic-quadrant.mmd +13 -0
  141. data/examples/quadrant/01-basic-quadrant.svg +33 -0
  142. data/examples/quadrant/02-positioned-items.mmd +14 -0
  143. data/examples/quadrant/02-positioned-items.svg +35 -0
  144. data/examples/quadrant/README.adoc +84 -0
  145. data/examples/radar/01-simple-radar.* +5 -0
  146. data/examples/radar/01-simple-radar.mmd +3 -0
  147. data/examples/radar/01-simple-radar.svg +25 -0
  148. data/examples/radar/02-multiple-curves.mmd +4 -0
  149. data/examples/radar/02-multiple-curves.svg +43 -0
  150. data/examples/radar/README.adoc +75 -0
  151. data/examples/requirement/01-basic-requirements.mmd +23 -0
  152. data/examples/requirement/01-basic-requirements.svg +49 -0
  153. data/examples/requirement/02-risk-levels.mmd +23 -0
  154. data/examples/requirement/02-risk-levels.svg +49 -0
  155. data/examples/requirement/README.adoc +85 -0
  156. data/examples/sankey/01-simple-flow.mmd +7 -0
  157. data/examples/sankey/01-simple-flow.svg +34 -0
  158. data/examples/sankey/02-multi-stage.mmd +11 -0
  159. data/examples/sankey/02-multi-stage.svg +44 -0
  160. data/examples/sankey/README.adoc +74 -0
  161. data/examples/sequence/01-basic-sequence.mmd +27 -0
  162. data/examples/sequence/01-basic-sequence.svg +5 -0
  163. data/examples/sequence/02-activations.mmd +17 -0
  164. data/examples/sequence/02-activations.svg +78 -0
  165. data/examples/sequence/README.adoc +86 -0
  166. data/examples/state_diagram/01-simple-states.mmd +29 -0
  167. data/examples/state_diagram/01-simple-states.svg +5 -0
  168. data/examples/state_diagram/02-composite.mmd +19 -0
  169. data/examples/state_diagram/02-composite.svg +81 -0
  170. data/examples/state_diagram/README.adoc +90 -0
  171. data/examples/timeline/01-simple-timeline.mmd +11 -0
  172. data/examples/timeline/01-simple-timeline.svg +36 -0
  173. data/examples/timeline/02-periods.mmd +15 -0
  174. data/examples/timeline/02-periods.svg +47 -0
  175. data/examples/timeline/README.adoc +78 -0
  176. data/examples/treemap/01-basic-treemap.mmd +12 -0
  177. data/examples/treemap/01-basic-treemap.svg +59 -0
  178. data/examples/treemap/README.adoc +59 -0
  179. data/examples/user_journey/01-simple-journey.mmd +23 -0
  180. data/examples/user_journey/01-simple-journey.svg +5 -0
  181. data/examples/user_journey/02-multi-actor.mmd +18 -0
  182. data/examples/user_journey/02-multi-actor.svg +129 -0
  183. data/examples/user_journey/README.adoc +81 -0
  184. data/examples/xychart/01-line-chart.mmd +5 -0
  185. data/examples/xychart/01-line-chart.svg +43 -0
  186. data/examples/xychart/02-bar-chart.mmd +7 -0
  187. data/examples/xychart/02-bar-chart.svg +48 -0
  188. data/examples/xychart/README.adoc +80 -0
  189. data/exe/sirena +7 -0
  190. data/lib/sirena/cli.rb +138 -0
  191. data/lib/sirena/commands/batch.rb +117 -0
  192. data/lib/sirena/commands/render.rb +80 -0
  193. data/lib/sirena/commands/types.rb +29 -0
  194. data/lib/sirena/commands/version.rb +24 -0
  195. data/lib/sirena/diagram/architecture.rb +46 -0
  196. data/lib/sirena/diagram/base.rb +61 -0
  197. data/lib/sirena/diagram/block.rb +81 -0
  198. data/lib/sirena/diagram/c4.rb +328 -0
  199. data/lib/sirena/diagram/class_diagram.rb +385 -0
  200. data/lib/sirena/diagram/er_diagram.rb +238 -0
  201. data/lib/sirena/diagram/error.rb +38 -0
  202. data/lib/sirena/diagram/flowchart.rb +160 -0
  203. data/lib/sirena/diagram/gantt.rb +71 -0
  204. data/lib/sirena/diagram/git_graph.rb +36 -0
  205. data/lib/sirena/diagram/info.rb +38 -0
  206. data/lib/sirena/diagram/kanban.rb +178 -0
  207. data/lib/sirena/diagram/mindmap.rb +54 -0
  208. data/lib/sirena/diagram/packet.rb +79 -0
  209. data/lib/sirena/diagram/pie.rb +115 -0
  210. data/lib/sirena/diagram/quadrant.rb +138 -0
  211. data/lib/sirena/diagram/radar.rb +52 -0
  212. data/lib/sirena/diagram/requirement.rb +133 -0
  213. data/lib/sirena/diagram/sankey.rb +217 -0
  214. data/lib/sirena/diagram/sequence.rb +242 -0
  215. data/lib/sirena/diagram/state_diagram.rb +237 -0
  216. data/lib/sirena/diagram/timeline.rb +171 -0
  217. data/lib/sirena/diagram/treemap.rb +84 -0
  218. data/lib/sirena/diagram/user_journey.rb +149 -0
  219. data/lib/sirena/diagram/xy_chart.rb +76 -0
  220. data/lib/sirena/diagram.rb +8 -0
  221. data/lib/sirena/diagram_registry.rb +101 -0
  222. data/lib/sirena/engine.rb +292 -0
  223. data/lib/sirena/parser/architecture.rb +41 -0
  224. data/lib/sirena/parser/base.rb +41 -0
  225. data/lib/sirena/parser/block.rb +72 -0
  226. data/lib/sirena/parser/c4.rb +53 -0
  227. data/lib/sirena/parser/class_diagram.rb +63 -0
  228. data/lib/sirena/parser/er_diagram.rb +40 -0
  229. data/lib/sirena/parser/error.rb +49 -0
  230. data/lib/sirena/parser/flowchart.rb +71 -0
  231. data/lib/sirena/parser/gantt.rb +60 -0
  232. data/lib/sirena/parser/git_graph.rb +95 -0
  233. data/lib/sirena/parser/grammars/architecture.rb +145 -0
  234. data/lib/sirena/parser/grammars/block.rb +190 -0
  235. data/lib/sirena/parser/grammars/c4.rb +226 -0
  236. data/lib/sirena/parser/grammars/class_diagram.rb +284 -0
  237. data/lib/sirena/parser/grammars/common.rb +84 -0
  238. data/lib/sirena/parser/grammars/er_diagram.rb +114 -0
  239. data/lib/sirena/parser/grammars/error.rb +40 -0
  240. data/lib/sirena/parser/grammars/flowchart.rb +298 -0
  241. data/lib/sirena/parser/grammars/gantt.rb +252 -0
  242. data/lib/sirena/parser/grammars/git_graph.rb +167 -0
  243. data/lib/sirena/parser/grammars/info.rb +58 -0
  244. data/lib/sirena/parser/grammars/kanban.rb +83 -0
  245. data/lib/sirena/parser/grammars/mindmap.rb +115 -0
  246. data/lib/sirena/parser/grammars/packet.rb +73 -0
  247. data/lib/sirena/parser/grammars/pie.rb +128 -0
  248. data/lib/sirena/parser/grammars/quadrant.rb +199 -0
  249. data/lib/sirena/parser/grammars/radar.rb +150 -0
  250. data/lib/sirena/parser/grammars/requirement.rb +188 -0
  251. data/lib/sirena/parser/grammars/sankey.rb +104 -0
  252. data/lib/sirena/parser/grammars/sequence.rb +247 -0
  253. data/lib/sirena/parser/grammars/state_diagram.rb +172 -0
  254. data/lib/sirena/parser/grammars/timeline.rb +142 -0
  255. data/lib/sirena/parser/grammars/treemap.rb +120 -0
  256. data/lib/sirena/parser/grammars/xy_chart.rb +120 -0
  257. data/lib/sirena/parser/info.rb +49 -0
  258. data/lib/sirena/parser/kanban.rb +97 -0
  259. data/lib/sirena/parser/mindmap.rb +106 -0
  260. data/lib/sirena/parser/packet.rb +76 -0
  261. data/lib/sirena/parser/pie.rb +49 -0
  262. data/lib/sirena/parser/quadrant.rb +57 -0
  263. data/lib/sirena/parser/radar.rb +104 -0
  264. data/lib/sirena/parser/requirement.rb +70 -0
  265. data/lib/sirena/parser/sankey.rb +64 -0
  266. data/lib/sirena/parser/sequence.rb +51 -0
  267. data/lib/sirena/parser/state_diagram.rb +69 -0
  268. data/lib/sirena/parser/timeline.rb +57 -0
  269. data/lib/sirena/parser/transforms/architecture.rb +97 -0
  270. data/lib/sirena/parser/transforms/block.rb +254 -0
  271. data/lib/sirena/parser/transforms/c4.rb +347 -0
  272. data/lib/sirena/parser/transforms/class_diagram.rb +352 -0
  273. data/lib/sirena/parser/transforms/er_diagram.rb +169 -0
  274. data/lib/sirena/parser/transforms/error.rb +58 -0
  275. data/lib/sirena/parser/transforms/flowchart.rb +293 -0
  276. data/lib/sirena/parser/transforms/gantt.rb +215 -0
  277. data/lib/sirena/parser/transforms/git_graph.rb +160 -0
  278. data/lib/sirena/parser/transforms/info.rb +58 -0
  279. data/lib/sirena/parser/transforms/kanban.rb +176 -0
  280. data/lib/sirena/parser/transforms/mindmap.rb +227 -0
  281. data/lib/sirena/parser/transforms/packet.rb +63 -0
  282. data/lib/sirena/parser/transforms/pie.rb +143 -0
  283. data/lib/sirena/parser/transforms/quadrant.rb +177 -0
  284. data/lib/sirena/parser/transforms/radar.rb +126 -0
  285. data/lib/sirena/parser/transforms/requirement.rb +272 -0
  286. data/lib/sirena/parser/transforms/sankey.rb +122 -0
  287. data/lib/sirena/parser/transforms/sequence.rb +342 -0
  288. data/lib/sirena/parser/transforms/state_diagram.rb +292 -0
  289. data/lib/sirena/parser/transforms/timeline.rb +177 -0
  290. data/lib/sirena/parser/transforms/treemap.rb +81 -0
  291. data/lib/sirena/parser/transforms/xy_chart.rb +132 -0
  292. data/lib/sirena/parser/treemap.rb +98 -0
  293. data/lib/sirena/parser/user_journey.rb +120 -0
  294. data/lib/sirena/parser/xy_chart.rb +114 -0
  295. data/lib/sirena/parser.rb +8 -0
  296. data/lib/sirena/renderer/architecture.rb +251 -0
  297. data/lib/sirena/renderer/base.rb +251 -0
  298. data/lib/sirena/renderer/block.rb +286 -0
  299. data/lib/sirena/renderer/c4.rb +490 -0
  300. data/lib/sirena/renderer/class_diagram.rb +499 -0
  301. data/lib/sirena/renderer/er_diagram.rb +417 -0
  302. data/lib/sirena/renderer/error.rb +131 -0
  303. data/lib/sirena/renderer/flowchart.rb +301 -0
  304. data/lib/sirena/renderer/gantt.rb +331 -0
  305. data/lib/sirena/renderer/git_graph.rb +368 -0
  306. data/lib/sirena/renderer/info.rb +93 -0
  307. data/lib/sirena/renderer/kanban.rb +295 -0
  308. data/lib/sirena/renderer/mindmap.rb +396 -0
  309. data/lib/sirena/renderer/packet.rb +239 -0
  310. data/lib/sirena/renderer/pie.rb +235 -0
  311. data/lib/sirena/renderer/quadrant.rb +292 -0
  312. data/lib/sirena/renderer/radar.rb +323 -0
  313. data/lib/sirena/renderer/requirement.rb +371 -0
  314. data/lib/sirena/renderer/sankey.rb +255 -0
  315. data/lib/sirena/renderer/sequence.rb +424 -0
  316. data/lib/sirena/renderer/state_diagram.rb +328 -0
  317. data/lib/sirena/renderer/timeline.rb +304 -0
  318. data/lib/sirena/renderer/treemap.rb +152 -0
  319. data/lib/sirena/renderer/user_journey.rb +331 -0
  320. data/lib/sirena/renderer/xy_chart.rb +452 -0
  321. data/lib/sirena/renderer.rb +8 -0
  322. data/lib/sirena/svg/circle.rb +41 -0
  323. data/lib/sirena/svg/document.rb +103 -0
  324. data/lib/sirena/svg/element.rb +65 -0
  325. data/lib/sirena/svg/ellipse.rb +33 -0
  326. data/lib/sirena/svg/group.rb +71 -0
  327. data/lib/sirena/svg/line.rb +49 -0
  328. data/lib/sirena/svg/path.rb +76 -0
  329. data/lib/sirena/svg/polygon.rb +43 -0
  330. data/lib/sirena/svg/polyline.rb +35 -0
  331. data/lib/sirena/svg/rect.rb +57 -0
  332. data/lib/sirena/svg/style.rb +44 -0
  333. data/lib/sirena/svg/text.rb +72 -0
  334. data/lib/sirena/svg.rb +19 -0
  335. data/lib/sirena/text_measurement.rb +71 -0
  336. data/lib/sirena/theme/builtin/dark.yml +70 -0
  337. data/lib/sirena/theme/builtin/default.yml +80 -0
  338. data/lib/sirena/theme/builtin/high_contrast.yml +70 -0
  339. data/lib/sirena/theme/builtin/light.yml +70 -0
  340. data/lib/sirena/theme/color_palette.rb +48 -0
  341. data/lib/sirena/theme/effect_styles.rb +28 -0
  342. data/lib/sirena/theme/registry.rb +41 -0
  343. data/lib/sirena/theme/shape_styles.rb +28 -0
  344. data/lib/sirena/theme/spacing_config.rb +24 -0
  345. data/lib/sirena/theme/typography.rb +30 -0
  346. data/lib/sirena/theme.rb +69 -0
  347. data/lib/sirena/transform/architecture.rb +273 -0
  348. data/lib/sirena/transform/base.rb +199 -0
  349. data/lib/sirena/transform/block.rb +215 -0
  350. data/lib/sirena/transform/c4.rb +288 -0
  351. data/lib/sirena/transform/class_diagram.rb +296 -0
  352. data/lib/sirena/transform/er_diagram.rb +204 -0
  353. data/lib/sirena/transform/error.rb +39 -0
  354. data/lib/sirena/transform/flowchart.rb +161 -0
  355. data/lib/sirena/transform/gantt.rb +253 -0
  356. data/lib/sirena/transform/git_graph.rb +283 -0
  357. data/lib/sirena/transform/info.rb +39 -0
  358. data/lib/sirena/transform/kanban.rb +180 -0
  359. data/lib/sirena/transform/mindmap.rb +251 -0
  360. data/lib/sirena/transform/packet.rb +185 -0
  361. data/lib/sirena/transform/pie.rb +62 -0
  362. data/lib/sirena/transform/quadrant.rb +167 -0
  363. data/lib/sirena/transform/radar.rb +227 -0
  364. data/lib/sirena/transform/requirement.rb +233 -0
  365. data/lib/sirena/transform/sankey.rb +212 -0
  366. data/lib/sirena/transform/sequence.rb +143 -0
  367. data/lib/sirena/transform/state_diagram.rb +228 -0
  368. data/lib/sirena/transform/timeline.rb +139 -0
  369. data/lib/sirena/transform/treemap.rb +120 -0
  370. data/lib/sirena/transform/user_journey.rb +207 -0
  371. data/lib/sirena/transform/xy_chart.rb +273 -0
  372. data/lib/sirena/transform.rb +8 -0
  373. data/lib/sirena/version.rb +5 -0
  374. data/lib/sirena.rb +328 -0
  375. data/lib/tasks/benchmark.rake +532 -0
  376. data/lib/tasks/examples.rake +468 -0
  377. data/lib/tasks/generate_mermaid_fixtures.rake +363 -0
  378. data/lib/tasks/mermaid_fixtures.rake +46 -0
  379. data/scripts/extract_mermaid_tests.rb +493 -0
  380. data/scripts/rename_to_sirena.rb +73 -0
  381. data/sirena.gemspec +47 -0
  382. 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