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,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parslet"
4
+
5
+ module Sirena
6
+ module Parser
7
+ module Transforms
8
+ # Transform for Radar diagrams
9
+ class Radar < Parslet::Transform
10
+ # Transform axis definition
11
+ rule(id: simple(:id), label: simple(:label)) do
12
+ { id: id.to_s, label: label.to_s }
13
+ end
14
+
15
+ rule(id: simple(:id)) do
16
+ { id: id.to_s, label: id.to_s }
17
+ end
18
+
19
+ # Transform positional values (simple list)
20
+ rule(value: simple(:v)) do
21
+ v.to_s.to_f
22
+ end
23
+
24
+ # Transform named values (axis: value pairs)
25
+ rule(axis: simple(:axis), value: simple(:v)) do
26
+ { axis: axis.to_s, value: v.to_s.to_f }
27
+ end
28
+
29
+ # Transform curve definition
30
+ rule(id: simple(:id), label: simple(:label), values: subtree(:vals)) do
31
+ {
32
+ type: :curve,
33
+ id: id.to_s,
34
+ label: label.to_s,
35
+ values: vals
36
+ }
37
+ end
38
+
39
+ rule(id: simple(:id), values: subtree(:vals)) do
40
+ {
41
+ type: :curve,
42
+ id: id.to_s,
43
+ label: id.to_s,
44
+ values: vals
45
+ }
46
+ end
47
+
48
+ # Transform title
49
+ rule(title: simple(:title)) do
50
+ { type: :title, title: title.to_s }
51
+ end
52
+
53
+ # Transform accessibility
54
+ rule(acc_title: simple(:acc_title)) do
55
+ { type: :acc_title, acc_title: acc_title.to_s }
56
+ end
57
+
58
+ rule(acc_descr: simple(:acc_descr)) do
59
+ { type: :acc_descr, acc_descr: acc_descr.to_s }
60
+ end
61
+
62
+ # Transform axes definition
63
+ rule(axes: subtree(:axes)) do
64
+ {
65
+ type: :axes,
66
+ axes: Array(axes)
67
+ }
68
+ end
69
+
70
+ # Transform options
71
+ rule(ticks: simple(:ticks)) do
72
+ { type: :option, key: :ticks, value: ticks.to_s.to_i }
73
+ end
74
+
75
+ rule(show_legend: simple(:show_legend)) do
76
+ { type: :option, key: :show_legend, value: show_legend.to_s == "true" }
77
+ end
78
+
79
+ rule(graticule: simple(:graticule)) do
80
+ { type: :option, key: :graticule, value: graticule.to_s }
81
+ end
82
+
83
+ rule(min: simple(:min)) do
84
+ { type: :option, key: :min, value: min.to_s.to_f }
85
+ end
86
+
87
+ rule(max: simple(:max)) do
88
+ { type: :option, key: :max, value: max.to_s.to_f }
89
+ end
90
+
91
+ # Transform the entire diagram
92
+ rule(statements: subtree(:statements)) do
93
+ result = {
94
+ title: nil,
95
+ acc_title: nil,
96
+ acc_descr: nil,
97
+ axes: [],
98
+ curves: [],
99
+ options: {}
100
+ }
101
+
102
+ Array(statements).each do |stmt|
103
+ next unless stmt.is_a?(Hash)
104
+
105
+ case stmt[:type]
106
+ when :title
107
+ result[:title] = stmt[:title]
108
+ when :acc_title
109
+ result[:acc_title] = stmt[:acc_title]
110
+ when :acc_descr
111
+ result[:acc_descr] = stmt[:acc_descr]
112
+ when :axes
113
+ result[:axes] = stmt[:axes]
114
+ when :curve
115
+ result[:curves] << stmt
116
+ when :option
117
+ result[:options][stmt[:key]] = stmt[:value]
118
+ end
119
+ end
120
+
121
+ result
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,272 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'parslet'
4
+ require_relative '../../diagram/requirement'
5
+
6
+ module Sirena
7
+ module Parser
8
+ module Transforms
9
+ # Transform for converting requirement diagram parse trees to diagram models.
10
+ #
11
+ # Handles transformation of requirements, elements, relationships,
12
+ # styling directives, and class definitions from Parslet parse trees
13
+ # into RequirementDiagram objects.
14
+ class Requirement < Parslet::Transform
15
+ # Requirement type mapping (for shorthand to full type)
16
+ REQUIREMENT_TYPE_MAP = {
17
+ 'functionalRequirement' => 'functionalRequirement',
18
+ 'interfaceRequirement' => 'interfaceRequirement',
19
+ 'performanceRequirement' => 'performanceRequirement',
20
+ 'physicalRequirement' => 'physicalRequirement',
21
+ 'designConstraint' => 'designConstraint',
22
+ 'requirement' => 'requirement'
23
+ }.freeze
24
+
25
+ # Process parsed diagram
26
+ def self.apply(tree, diagram = nil)
27
+ diagram ||= Diagram::RequirementDiagram.new
28
+
29
+ # The tree is an array of statement hashes
30
+ statements = tree.is_a?(Array) ? tree : [tree]
31
+
32
+ # Filter out header
33
+ statements = statements.reject { |s| s.is_a?(Hash) && s[:header] }
34
+
35
+ process_statements(diagram, statements)
36
+
37
+ diagram
38
+ end
39
+
40
+ def self.process_statements(diagram, statements)
41
+ statements.each do |stmt|
42
+ next unless stmt.is_a?(Hash)
43
+
44
+ if stmt[:req_type]
45
+ # Requirement statement
46
+ requirement = create_requirement(stmt)
47
+ diagram.add_requirement(requirement)
48
+ elsif stmt[:elem_keyword]
49
+ # Element statement
50
+ element = create_element(stmt)
51
+ diagram.add_element(element)
52
+ elsif stmt[:rel_source] && stmt[:rel_target]
53
+ # Relationship statement
54
+ relationship = create_relationship(stmt)
55
+ diagram.add_relationship(relationship)
56
+ elsif stmt[:style_keyword]
57
+ # Style statement
58
+ style = create_style(stmt)
59
+ diagram.add_style(style)
60
+ elsif stmt[:classdef_keyword]
61
+ # Class definition
62
+ klass = create_class_definition(stmt)
63
+ diagram.add_class(klass)
64
+ elsif stmt[:class_keyword]
65
+ # Class assignment
66
+ assignment = create_class_assignment(stmt)
67
+ diagram.add_class_assignment(assignment)
68
+ end
69
+ end
70
+ end
71
+
72
+ def self.create_requirement(stmt)
73
+ Diagram::Requirement.new.tap do |req|
74
+ req.name = stmt[:req_name].to_s
75
+ req.type = stmt[:req_type].to_s
76
+
77
+ # Process properties
78
+ if stmt[:req_properties]
79
+ props = stmt[:req_properties]
80
+ props = [props] unless props.is_a?(Array)
81
+
82
+ props.each do |prop|
83
+ next unless prop.is_a?(Hash)
84
+
85
+ key = prop[:key][:prop_key].to_s if prop[:key]
86
+ value = prop[:value].to_s.strip if prop[:value]
87
+
88
+ case key
89
+ when 'id'
90
+ req.id = value
91
+ when 'text'
92
+ req.text = value
93
+ when 'risk'
94
+ req.risk = value
95
+ when 'verifymethod'
96
+ req.verifymethod = value
97
+ end
98
+ end
99
+ end
100
+
101
+ # Process class shorthand
102
+ if stmt[:req_classes]
103
+ classes = extract_class_names(stmt[:req_classes])
104
+ classes.each { |c| req.add_class(c) }
105
+ end
106
+ end
107
+ end
108
+
109
+ def self.create_element(stmt)
110
+ Diagram::RequirementElement.new.tap do |elem|
111
+ elem.name = stmt[:elem_name].to_s
112
+
113
+ # Process properties
114
+ if stmt[:elem_properties]
115
+ props = stmt[:elem_properties]
116
+ props = [props] unless props.is_a?(Array)
117
+
118
+ props.each do |prop|
119
+ next unless prop.is_a?(Hash)
120
+
121
+ key = prop[:key][:prop_key].to_s if prop[:key]
122
+ value = prop[:value].to_s.strip if prop[:value]
123
+
124
+ case key
125
+ when 'type'
126
+ elem.type = value
127
+ when 'docref'
128
+ elem.docref = value
129
+ end
130
+ end
131
+ end
132
+
133
+ # Process class shorthand
134
+ if stmt[:elem_classes]
135
+ classes = extract_class_names(stmt[:elem_classes])
136
+ classes.each { |c| elem.add_class(c) }
137
+ end
138
+ end
139
+ end
140
+
141
+ def self.create_relationship(stmt)
142
+ Diagram::RequirementRelationship.new.tap do |rel|
143
+ rel.source = stmt[:rel_source].to_s
144
+ rel.target = stmt[:rel_target].to_s
145
+
146
+ if stmt[:rel_type] && stmt[:rel_type][:type]
147
+ rel.type = stmt[:rel_type][:type].to_s
148
+ end
149
+ end
150
+ end
151
+
152
+ def self.create_style(stmt)
153
+ Diagram::RequirementStyle.new.tap do |style|
154
+ # Process targets
155
+ if stmt[:style_targets]
156
+ targets = stmt[:style_targets]
157
+ targets = [targets] unless targets.is_a?(Array)
158
+
159
+ targets.each do |target|
160
+ style.add_target(target.to_s)
161
+ end
162
+ end
163
+
164
+ # Process properties
165
+ if stmt[:style_props]
166
+ props = stmt[:style_props]
167
+ props = [props] unless props.is_a?(Array)
168
+
169
+ props.each do |prop|
170
+ prop_str = prop.to_s.strip
171
+ # Split by comma if it contains multiple properties
172
+ prop_parts = prop_str.split(',')
173
+
174
+ prop_parts.each do |part|
175
+ part = part.strip
176
+ if part.start_with?('fill:')
177
+ style.fill = part.sub('fill:', '').strip
178
+ elsif part.start_with?('stroke:')
179
+ style.stroke = part.sub('stroke:', '').strip
180
+ elsif part.start_with?('stroke-width:')
181
+ style.stroke_width = part.sub('stroke-width:', '').strip
182
+ else
183
+ style.add_property(part) unless part.empty?
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ def self.create_class_definition(stmt)
192
+ Diagram::RequirementClass.new.tap do |klass|
193
+ klass.name = stmt[:class_name].to_s
194
+
195
+ # Process properties
196
+ if stmt[:class_props]
197
+ props = stmt[:class_props]
198
+ props = [props] unless props.is_a?(Array)
199
+
200
+ props.each do |prop|
201
+ prop_str = prop.to_s.strip
202
+ # Split by comma if it contains multiple properties
203
+ prop_parts = prop_str.split(',')
204
+
205
+ prop_parts.each do |part|
206
+ part = part.strip
207
+ if part.start_with?('fill:')
208
+ klass.fill = part.sub('fill:', '').strip
209
+ elsif part.start_with?('stroke:')
210
+ klass.stroke = part.sub('stroke:', '').strip
211
+ elsif part.start_with?('stroke-width:')
212
+ klass.stroke_width = part.sub('stroke-width:', '').strip
213
+ else
214
+ klass.add_property(part) unless part.empty?
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
220
+ end
221
+
222
+ def self.create_class_assignment(stmt)
223
+ Diagram::RequirementClassAssignment.new.tap do |assignment|
224
+ # Process targets
225
+ if stmt[:class_targets]
226
+ targets = stmt[:class_targets]
227
+ targets = [targets] unless targets.is_a?(Array)
228
+
229
+ targets.each do |target|
230
+ assignment.add_target(target.to_s)
231
+ end
232
+ end
233
+
234
+ # Process class names
235
+ if stmt[:class_names]
236
+ names = stmt[:class_names]
237
+ names = [names] unless names.is_a?(Array)
238
+
239
+ names.each do |name|
240
+ assignment.add_class(name.to_s)
241
+ end
242
+ end
243
+ end
244
+ end
245
+
246
+ def self.extract_class_names(class_data)
247
+ # Extract class names from shorthand syntax
248
+ classes = []
249
+
250
+ if class_data.is_a?(String)
251
+ classes = class_data.split(',').map(&:strip)
252
+ elsif class_data.is_a?(Array)
253
+ class_data.each do |item|
254
+ if item.is_a?(String)
255
+ classes << item.strip
256
+ elsif item.is_a?(Hash) && item[:string]
257
+ classes << item[:string].to_s.strip
258
+ else
259
+ classes << item.to_s.strip
260
+ end
261
+ end
262
+ elsif class_data.is_a?(Hash)
263
+ # Could be a single identifier or complex structure
264
+ classes << class_data.to_s.strip
265
+ end
266
+
267
+ classes
268
+ end
269
+ end
270
+ end
271
+ end
272
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../diagram/sankey"
4
+
5
+ module Sirena
6
+ module Parser
7
+ module Transforms
8
+ # Transform for converting Parslet parse tree to Sankey diagram model.
9
+ #
10
+ # Converts the parse tree output from Grammars::Sankey into a
11
+ # fully-formed Diagram::SankeyDiagram object with nodes and flows.
12
+ class Sankey
13
+ # Transform parse tree into Sankey diagram.
14
+ #
15
+ # @param tree [Array, Hash] Parslet parse tree
16
+ # @return [Diagram::SankeyDiagram] the sankey diagram model
17
+ def apply(tree)
18
+ diagram = Diagram::SankeyDiagram.new
19
+
20
+ # Tree structure: array with header and statements
21
+ if tree.is_a?(Array)
22
+ tree.each do |item|
23
+ next unless item.is_a?(Hash)
24
+
25
+ process_item(diagram, item)
26
+ end
27
+ elsif tree.is_a?(Hash)
28
+ process_item(diagram, tree)
29
+
30
+ if tree[:statements]
31
+ process_statements(diagram, tree[:statements])
32
+ end
33
+ end
34
+
35
+ # Auto-discover nodes from flows if not explicitly declared
36
+ ensure_nodes_from_flows(diagram)
37
+
38
+ diagram
39
+ end
40
+
41
+ private
42
+
43
+ def process_item(diagram, item)
44
+ return unless item.is_a?(Hash)
45
+
46
+ process_header(diagram, item) if item.key?(:header)
47
+ process_node_declaration(diagram, item) if item.key?(:node_declaration)
48
+ process_flow_entry(diagram, item) if item.key?(:flow_entry)
49
+ end
50
+
51
+ def process_statements(diagram, statements)
52
+ Array(statements).each do |stmt|
53
+ process_item(diagram, stmt) if stmt.is_a?(Hash)
54
+ end
55
+ end
56
+
57
+ def process_header(diagram, item)
58
+ # Header is just the 'sankey-beta' keyword, nothing to extract
59
+ end
60
+
61
+ def process_node_declaration(diagram, item)
62
+ node_data = item[:node_declaration]
63
+ return unless node_data
64
+
65
+ node_id = extract_text(node_data[:node_id])
66
+ node_label = extract_text(node_data[:node_label])
67
+
68
+ # Check if node already exists
69
+ existing_node = diagram.nodes.find { |n| n.id == node_id }
70
+ if existing_node
71
+ # Update label if provided
72
+ existing_node.label = node_label unless node_label.empty?
73
+ else
74
+ # Create new node
75
+ node = Diagram::SankeyNode.new(node_id, node_label)
76
+ diagram.nodes << node
77
+ end
78
+ end
79
+
80
+ def process_flow_entry(diagram, item)
81
+ flow_data = item[:flow_entry]
82
+ return unless flow_data
83
+
84
+ source = extract_text(flow_data[:source])
85
+ target = extract_text(flow_data[:target])
86
+ value_str = extract_text(flow_data[:value])
87
+ value = value_str.to_f
88
+
89
+ flow = Diagram::SankeyFlow.new(source, target, value)
90
+ diagram.flows << flow
91
+ end
92
+
93
+ # Ensure all nodes referenced in flows exist in the nodes collection
94
+ def ensure_nodes_from_flows(diagram)
95
+ node_ids_from_flows = diagram.flows.flat_map do |flow|
96
+ [flow.source, flow.target]
97
+ end.uniq
98
+
99
+ node_ids_from_flows.each do |node_id|
100
+ # Check if node already exists
101
+ unless diagram.nodes.any? { |n| n.id == node_id }
102
+ # Create node with id as label
103
+ node = Diagram::SankeyNode.new(node_id, node_id)
104
+ diagram.nodes << node
105
+ end
106
+ end
107
+ end
108
+
109
+ def extract_text(value)
110
+ case value
111
+ when Hash
112
+ value.values.first.to_s
113
+ when String
114
+ value
115
+ else
116
+ value.to_s
117
+ end.strip
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end