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,363 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'json'
5
+
6
+ namespace :mermaid do
7
+ desc 'Generate SVG fixtures from mermaid-js for comparison and testing'
8
+ task :generate_fixtures, [:diagram_type] do |_t, args|
9
+ diagram_type = args[:diagram_type] || 'all'
10
+
11
+ puts "Generating Mermaid.js fixtures for: #{diagram_type}"
12
+ puts "=" * 60
13
+
14
+ # Check if mermaid-cli (mmdc) is installed
15
+ unless system('which mmdc > /dev/null 2>&1')
16
+ puts "\n❌ ERROR: mermaid-cli not found!"
17
+ puts "\nPlease install mermaid-cli:"
18
+ puts " npm install -g @mermaid-js/mermaid-cli"
19
+ puts "\nOr using yarn:"
20
+ puts " yarn global add @mermaid-js/mermaid-cli"
21
+ exit 1
22
+ end
23
+
24
+ # Get mermaid-cli version
25
+ mmdc_version = `mmdc --version`.strip
26
+ puts "✅ Found mermaid-cli: #{mmdc_version}"
27
+ puts
28
+
29
+ generator = MermaidFixtureGenerator.new
30
+
31
+ if diagram_type == 'all'
32
+ generator.generate_all_fixtures
33
+ else
34
+ generator.generate_fixtures_for_type(diagram_type)
35
+ end
36
+ end
37
+
38
+ desc 'Compare Sirena output with Mermaid.js reference fixtures'
39
+ task :compare, [:diagram_type] do |_t, args|
40
+ diagram_type = args[:diagram_type] || 'all'
41
+
42
+ puts "Comparing Sirena vs Mermaid.js output for: #{diagram_type}"
43
+ puts "=" * 60
44
+ puts
45
+
46
+ comparator = MermaidOutputComparator.new
47
+
48
+ if diagram_type == 'all'
49
+ comparator.compare_all_types
50
+ else
51
+ comparator.compare_type(diagram_type)
52
+ end
53
+ end
54
+
55
+ desc 'Validate all Sirena parsers against Mermaid test suite'
56
+ task :validate do
57
+ puts "Validating Sirena parsers against Mermaid test suite"
58
+ puts "=" * 60
59
+ puts
60
+
61
+ validator = MermaidTestValidator.new
62
+ results = validator.validate_all
63
+
64
+ puts "\n" + "=" * 60
65
+ puts "VALIDATION SUMMARY"
66
+ puts "=" * 60
67
+
68
+ results.each do |type, result|
69
+ status = result[:passing] == result[:total] ? '✅' : '⚠️ '
70
+ puts format("%-20s %s %3d/%3d passing (%.1f%%)",
71
+ type, status, result[:passing], result[:total],
72
+ (result[:passing].to_f / result[:total] * 100))
73
+ end
74
+
75
+ total_passing = results.values.sum { |r| r[:passing] }
76
+ total_tests = results.values.sum { |r| r[:total] }
77
+ overall_pct = (total_passing.to_f / total_tests * 100)
78
+
79
+ puts "\n%-20s %3d/%3d passing (%.1f%%)" %
80
+ ["OVERALL", total_passing, total_tests, overall_pct]
81
+ end
82
+ end
83
+
84
+ # Fixture generator class
85
+ class MermaidFixtureGenerator
86
+ FIXTURE_DIR = 'spec/fixtures_mermaid'
87
+
88
+ # Supported diagram types in Sirena
89
+ DIAGRAM_TYPES = %w[
90
+ flowchart sequence class er state user_journey
91
+ gantt pie timeline quadrant gitgraph
92
+ mindmap radar c4 architecture requirement
93
+ block xychart sankey info error packet treemap kanban
94
+ ].freeze
95
+
96
+ def initialize
97
+ FileUtils.mkdir_p(FIXTURE_DIR)
98
+ end
99
+
100
+ def generate_all_fixtures
101
+ DIAGRAM_TYPES.each do |type|
102
+ generate_fixtures_for_type(type)
103
+ end
104
+ end
105
+
106
+ def generate_fixtures_for_type(diagram_type)
107
+ puts "\n🔄 Generating fixtures for: #{diagram_type}"
108
+ puts "-" * 60
109
+
110
+ # Find all mermaid files for this type
111
+ mmd_files = Dir.glob("spec/mermaid/#{diagram_type}/*.mmd")
112
+
113
+ if mmd_files.empty?
114
+ puts "⚠️ No .mmd files found for #{diagram_type}"
115
+ return
116
+ end
117
+
118
+ # Create output directories
119
+ output_dir = File.join(FIXTURE_DIR, diagram_type)
120
+ correct_dir = File.join(output_dir, 'correct')
121
+ expected_errors_dir = File.join(output_dir, 'expected_errors')
122
+ FileUtils.mkdir_p([correct_dir, expected_errors_dir])
123
+
124
+ stats = {
125
+ correct_success: 0,
126
+ correct_unexpected_fail: 0,
127
+ error_expected_fail: 0,
128
+ error_unexpected_success: 0
129
+ }
130
+ unexpected_failures = []
131
+ unexpected_successes = []
132
+
133
+ mmd_files.each_with_index do |mmd_file, idx|
134
+ basename = File.basename(mmd_file, '.mmd')
135
+ error_marker = mmd_file.sub('.mmd', '.error')
136
+ has_error_marker = File.exist?(error_marker)
137
+
138
+ # Determine output location
139
+ target_dir = has_error_marker ? expected_errors_dir : correct_dir
140
+ output_file = File.join(target_dir, "#{basename}.svg")
141
+
142
+ # Generate SVG using mermaid-cli
143
+ success, error_msg = generate_svg_with_error(mmd_file, output_file)
144
+
145
+ # Evaluate result based on expectations
146
+ if has_error_marker
147
+ # File is EXPECTED to fail
148
+ if !success
149
+ # Failed as expected - this is CORRECT
150
+ stats[:error_expected_fail] += 1
151
+ print 'E' # E = Expected error (pass)
152
+ else
153
+ # Succeeded but should have failed - UNEXPECTED
154
+ stats[:error_unexpected_success] += 1
155
+ unexpected_successes << { file: basename, note: 'Should have failed but succeeded' }
156
+ print 'U' # U = Unexpected success (fail)
157
+ end
158
+ else
159
+ # File is EXPECTED to succeed
160
+ if success
161
+ # Succeeded as expected - this is CORRECT
162
+ stats[:correct_success] += 1
163
+ print '.' # . = Correct success (pass)
164
+ else
165
+ # Failed but should have succeeded - UNEXPECTED
166
+ stats[:correct_unexpected_fail] += 1
167
+ unexpected_failures << { file: basename, error: error_msg }
168
+ print 'F' # F = Unexpected failure (fail)
169
+ end
170
+ end
171
+
172
+ # Progress indicator every 50 files
173
+ if (idx + 1) % 50 == 0
174
+ puts " [#{idx + 1}/#{mmd_files.length}]"
175
+ end
176
+ end
177
+
178
+ puts if mmd_files.length % 50 != 0
179
+
180
+ # Print summary
181
+ puts "\n" + "=" * 60
182
+ puts "RESULTS SUMMARY"
183
+ puts "=" * 60
184
+ puts "✅ Correct behaviors: #{stats[:correct_success] + stats[:error_expected_fail]}"
185
+ puts " - Generated fixtures: #{stats[:correct_success]}"
186
+ puts " - Failed as expected: #{stats[:error_expected_fail]}"
187
+
188
+ total_unexpected = stats[:correct_unexpected_fail] + stats[:error_unexpected_success]
189
+ if total_unexpected > 0
190
+ puts "\n⚠️ Unexpected behaviors: #{total_unexpected}"
191
+ puts " - Should succeed but failed: #{stats[:correct_unexpected_fail]}"
192
+ puts " - Should fail but succeeded: #{stats[:error_unexpected_success]}"
193
+
194
+ if stats[:correct_unexpected_fail] > 0
195
+ puts "\n Unexpected failures (first 10):"
196
+ unexpected_failures.first(10).each do |f|
197
+ puts " #{f[:file]}: #{f[:error]}"
198
+ end
199
+ end
200
+
201
+ if stats[:error_unexpected_success] > 0
202
+ puts "\n Unexpected successes (first 10):"
203
+ unexpected_successes.first(10).each do |f|
204
+ puts " #{f[:file]}: #{f[:note]}"
205
+ end
206
+ end
207
+
208
+ # Save detailed logs
209
+ if stats[:correct_unexpected_fail] > 0
210
+ fail_log = File.join(output_dir, '_unexpected_failures.log')
211
+ File.write(fail_log, unexpected_failures.map { |f|
212
+ "#{f[:file]}: #{f[:error]}"
213
+ }.join("\n"))
214
+ puts "\n📝 Failure log: #{fail_log}"
215
+ end
216
+
217
+ if stats[:error_unexpected_success] > 0
218
+ success_log = File.join(output_dir, '_unexpected_successes.log')
219
+ File.write(success_log, unexpected_successes.map { |f|
220
+ "#{f[:file]}: #{f[:note]}"
221
+ }.join("\n"))
222
+ puts "📝 Success log: #{success_log}"
223
+ end
224
+ end
225
+
226
+ # Calculate pass rate
227
+ total = mmd_files.length
228
+ passing = stats[:correct_success] + stats[:error_expected_fail]
229
+ pass_rate = (passing.to_f / total * 100).round(1)
230
+ puts "\n🎯 Pass rate: #{passing}/#{total} (#{pass_rate}%)"
231
+ end
232
+
233
+ private
234
+
235
+ def generate_svg_with_error(input_file, output_file)
236
+ # Use mermaid-cli to generate SVG, capture stderr for errors
237
+ require 'open3'
238
+
239
+ cmd = "mmdc -i '#{input_file}' -o '#{output_file}' -b transparent"
240
+ stdout, stderr, status = Open3.capture3(cmd)
241
+
242
+ if status.success?
243
+ [true, nil]
244
+ else
245
+ # Extract meaningful error message
246
+ error_msg = (stderr + stdout).lines
247
+ .reject { |l| l.include?('Generating single mermaid chart') }
248
+ .first&.strip || 'Unknown error'
249
+ [false, error_msg]
250
+ end
251
+ end
252
+ end
253
+
254
+ # Output comparator class
255
+ class MermaidOutputComparator
256
+ def compare_all_types
257
+ MermaidFixtureGenerator::DIAGRAM_TYPES.each do |type|
258
+ compare_type(type)
259
+ end
260
+ end
261
+
262
+ def compare_type(diagram_type)
263
+ puts "\n📊 Comparing: #{diagram_type}"
264
+ puts "-" * 60
265
+
266
+ reference_dir = "spec/fixtures_mermaid/#{diagram_type}"
267
+
268
+ unless Dir.exist?(reference_dir)
269
+ puts "⚠️ No reference fixtures found. Run:"
270
+ puts " rake mermaid:generate_fixtures[#{diagram_type}]"
271
+ return
272
+ end
273
+
274
+ require 'sirena'
275
+
276
+ mmd_files = Dir.glob("spec/mermaid/#{diagram_type}/*.mmd")
277
+ matches = 0
278
+ differences = 0
279
+
280
+ mmd_files.each do |mmd_file|
281
+ basename = File.basename(mmd_file, '.mmd')
282
+ reference_svg = File.join(reference_dir, "#{basename}.svg")
283
+
284
+ next unless File.exist?(reference_svg)
285
+
286
+ # Generate with Sirena
287
+ source = File.read(mmd_file)
288
+ begin
289
+ sirena_svg = Sirena.render(source)
290
+
291
+ # Basic comparison (structure, not exact match)
292
+ if similar_structure?(sirena_svg, File.read(reference_svg))
293
+ matches += 1
294
+ else
295
+ differences += 1
296
+ end
297
+ rescue => e
298
+ differences += 1
299
+ end
300
+ end
301
+
302
+ total = matches + differences
303
+ if total > 0
304
+ pct = (matches.to_f / total * 100)
305
+ puts "✅ #{matches}/#{total} similar (#{pct.round(1)}%)"
306
+ puts "⚠️ #{differences} differences" if differences > 0
307
+ else
308
+ puts "⚠️ No comparisons performed"
309
+ end
310
+ end
311
+
312
+ private
313
+
314
+ def similar_structure?(svg1, svg2)
315
+ # Basic structural comparison
316
+ # Check for similar element counts
317
+ svg1_elements = svg1.scan(/<\w+/).length
318
+ svg2_elements = svg2.scan(/<\w+/).length
319
+
320
+ # Allow 20% variation
321
+ (svg1_elements - svg2_elements).abs < (svg2_elements * 0.2)
322
+ end
323
+ end
324
+
325
+ # Test validator class
326
+ class MermaidTestValidator
327
+ def validate_all
328
+ require 'sirena'
329
+
330
+ results = {}
331
+
332
+ MermaidFixtureGenerator::DIAGRAM_TYPES.each do |type|
333
+ results[type] = validate_type(type)
334
+ end
335
+
336
+ results
337
+ end
338
+
339
+ private
340
+
341
+ def validate_type(diagram_type)
342
+ mmd_files = Dir.glob("spec/mermaid/#{diagram_type}/*.mmd")
343
+
344
+ passing = 0
345
+ failing = 0
346
+
347
+ mmd_files.each do |file|
348
+ source = File.read(file)
349
+ begin
350
+ Sirena.render(source)
351
+ passing += 1
352
+ rescue => e
353
+ failing += 1
354
+ end
355
+ end
356
+
357
+ {
358
+ total: mmd_files.length,
359
+ passing: passing,
360
+ failing: failing
361
+ }
362
+ end
363
+ end
@@ -0,0 +1,46 @@
1
+ namespace :fixtures do
2
+ desc "Generate reference SVGs using mermaid-js CLI"
3
+ task :generate_from_mermaidjs do
4
+ require "fileutils"
5
+
6
+ examples_dir = File.expand_path("../../examples", __dir__)
7
+ fixtures_dir = File.expand_path("../../spec/fixtures", __dir__)
8
+
9
+ diagram_mappings = {
10
+ "flowchart_example.mmd" => "flowchart",
11
+ "sequence_example.mmd" => "sequence",
12
+ "class_diagram_example.mmd" => "class_diagram",
13
+ "state_diagram_example.mmd" => "state_diagram",
14
+ "er_diagram_example.mmd" => "er_diagram",
15
+ "user_journey_example.mmd" => "user_journey"
16
+ }
17
+
18
+ puts "Generating reference SVGs using mermaid-js CLI..."
19
+
20
+ diagram_mappings.each do |input_file, diagram_type|
21
+ input_path = File.join(examples_dir, input_file)
22
+ output_dir = File.join(fixtures_dir, diagram_type)
23
+ output_path = File.join(output_dir, "expected.svg")
24
+
25
+ unless File.exist?(input_path)
26
+ puts " Warning: Input file not found: #{input_path}"
27
+ next
28
+ end
29
+
30
+ FileUtils.mkdir_p(output_dir)
31
+
32
+ command = "mmdc -i #{input_path} -o #{output_path}"
33
+ puts " Generating #{diagram_type}/expected.svg..."
34
+
35
+ system(command)
36
+
37
+ if $?.success?
38
+ puts " ✓ Generated #{output_path}"
39
+ else
40
+ puts " ✗ Failed to generate #{output_path}"
41
+ end
42
+ end
43
+
44
+ puts "\nDone! Generated reference SVGs for testing."
45
+ end
46
+ end