mxrb 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 (237) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.de-DE.md +108 -0
  4. data/README.md +172 -0
  5. data/README.pt-BR.md +109 -0
  6. data/bin/mxrb +1613 -0
  7. data/docs/README.md +10 -0
  8. data/docs/de-DE/README.md +24 -0
  9. data/docs/de-DE/architectural-patterns.md +26 -0
  10. data/docs/de-DE/architectural-standard.md +48 -0
  11. data/docs/de-DE/architecture.md +76 -0
  12. data/docs/de-DE/compiler.md +162 -0
  13. data/docs/de-DE/conventions.md +19 -0
  14. data/docs/de-DE/design-system.md +52 -0
  15. data/docs/de-DE/entity-dsl.md +53 -0
  16. data/docs/de-DE/native-runtime-quality-report.md +74 -0
  17. data/docs/de-DE/oql-sql.md +211 -0
  18. data/docs/de-DE/platform-api-integration.md +36 -0
  19. data/docs/de-DE/platform-operations.md +243 -0
  20. data/docs/de-DE/project-structure.md +32 -0
  21. data/docs/de-DE/ruby-first-roadmap.md +116 -0
  22. data/docs/de-DE/scaffolds.md +59 -0
  23. data/docs/de-DE/semantic-refactoring.md +56 -0
  24. data/docs/de-DE/team-server.md +33 -0
  25. data/docs/de-DE/validation-matrix.md +64 -0
  26. data/docs/de-DE/vetclinic-acceptance.md +24 -0
  27. data/docs/de-DE/writing.md +138 -0
  28. data/docs/en-US/README.md +24 -0
  29. data/docs/en-US/architectural-patterns.md +151 -0
  30. data/docs/en-US/architectural-standard.md +89 -0
  31. data/docs/en-US/architecture.md +170 -0
  32. data/docs/en-US/compiler.md +157 -0
  33. data/docs/en-US/conventions.md +82 -0
  34. data/docs/en-US/design-system.md +163 -0
  35. data/docs/en-US/entity-dsl.md +53 -0
  36. data/docs/en-US/native-runtime-quality-report.md +73 -0
  37. data/docs/en-US/oql-sql.md +201 -0
  38. data/docs/en-US/platform-api-integration.md +36 -0
  39. data/docs/en-US/platform-operations.md +244 -0
  40. data/docs/en-US/project-structure.md +49 -0
  41. data/docs/en-US/ruby-first-roadmap.md +186 -0
  42. data/docs/en-US/scaffolds.md +57 -0
  43. data/docs/en-US/semantic-refactoring.md +198 -0
  44. data/docs/en-US/team-server.md +32 -0
  45. data/docs/en-US/validation-matrix.md +257 -0
  46. data/docs/en-US/vetclinic-acceptance.md +23 -0
  47. data/docs/en-US/writing.md +655 -0
  48. data/docs/pt-BR/README.md +24 -0
  49. data/docs/pt-BR/architectural-patterns.md +25 -0
  50. data/docs/pt-BR/architectural-standard.md +533 -0
  51. data/docs/pt-BR/architecture.md +300 -0
  52. data/docs/pt-BR/compiler.md +162 -0
  53. data/docs/pt-BR/conventions.md +19 -0
  54. data/docs/pt-BR/design-system.md +65 -0
  55. data/docs/pt-BR/entity-dsl.md +116 -0
  56. data/docs/pt-BR/native-runtime-quality-report.md +73 -0
  57. data/docs/pt-BR/oql-sql.md +208 -0
  58. data/docs/pt-BR/platform-api-integration.md +36 -0
  59. data/docs/pt-BR/platform-operations.md +260 -0
  60. data/docs/pt-BR/project-structure.md +32 -0
  61. data/docs/pt-BR/ruby-first-roadmap.md +239 -0
  62. data/docs/pt-BR/scaffolds.md +104 -0
  63. data/docs/pt-BR/semantic-refactoring.md +55 -0
  64. data/docs/pt-BR/team-server.md +59 -0
  65. data/docs/pt-BR/validation-matrix.md +86 -0
  66. data/docs/pt-BR/vetclinic-acceptance.md +50 -0
  67. data/docs/pt-BR/writing.md +177 -0
  68. data/examples/sudoku_evaluation.rb +14 -0
  69. data/examples/sudoku_functional_test.rb +28 -0
  70. data/lib/mxrb/architecture/graph.rb +124 -0
  71. data/lib/mxrb/architecture/validator.rb +98 -0
  72. data/lib/mxrb/benchmark.rb +33 -0
  73. data/lib/mxrb/compare.rb +390 -0
  74. data/lib/mxrb/compiler/adapter.rb +96 -0
  75. data/lib/mxrb/compiler/artifact_document_compiler.rb +102 -0
  76. data/lib/mxrb/compiler/artifact_materializer.rb +53 -0
  77. data/lib/mxrb/compiler/client_model_materializer.rb +42 -0
  78. data/lib/mxrb/compiler/code_action_document_compiler.rb +64 -0
  79. data/lib/mxrb/compiler/code_action_materializer.rb +35 -0
  80. data/lib/mxrb/compiler/code_action_type_compiler.rb +32 -0
  81. data/lib/mxrb/compiler/combo_box_bundle_compiler.rb +314 -0
  82. data/lib/mxrb/compiler/compatibility_analyzer.rb +169 -0
  83. data/lib/mxrb/compiler/constants_materializer.rb +111 -0
  84. data/lib/mxrb/compiler/data_grid_bundle_compiler.rb +290 -0
  85. data/lib/mxrb/compiler/database_connector_action_compiler.rb +280 -0
  86. data/lib/mxrb/compiler/deployment_asset_copier.rb +60 -0
  87. data/lib/mxrb/compiler/deployment_bootstrapper.rb +208 -0
  88. data/lib/mxrb/compiler/deployment_materializer.rb +43 -0
  89. data/lib/mxrb/compiler/deployment_metadata.rb +64 -0
  90. data/lib/mxrb/compiler/domain_document_compiler.rb +153 -0
  91. data/lib/mxrb/compiler/domain_model_materializer.rb +35 -0
  92. data/lib/mxrb/compiler/domain_security_compiler.rb +82 -0
  93. data/lib/mxrb/compiler/gallery_bundle_compiler.rb +190 -0
  94. data/lib/mxrb/compiler/generic_widget_bundle_compiler.rb +281 -0
  95. data/lib/mxrb/compiler/image_bundle_compiler.rb +216 -0
  96. data/lib/mxrb/compiler/java_proxy_generator.rb +509 -0
  97. data/lib/mxrb/compiler/legacy_data_grid_compiler.rb +267 -0
  98. data/lib/mxrb/compiler/legacy_page_builder.rb +287 -0
  99. data/lib/mxrb/compiler/mda.rb +99 -0
  100. data/lib/mxrb/compiler/microflow_document_compiler.rb +85 -0
  101. data/lib/mxrb/compiler/microflow_materializer.rb +48 -0
  102. data/lib/mxrb/compiler/microflow_node_compiler.rb +250 -0
  103. data/lib/mxrb/compiler/model_package.rb +116 -0
  104. data/lib/mxrb/compiler/model_values.rb +67 -0
  105. data/lib/mxrb/compiler/nanoflow_program_compiler.rb +483 -0
  106. data/lib/mxrb/compiler/navigation_document_compiler.rb +93 -0
  107. data/lib/mxrb/compiler/packager.rb +100 -0
  108. data/lib/mxrb/compiler/page_bundle_builder.rb +56 -0
  109. data/lib/mxrb/compiler/page_bundle_compiler.rb +2004 -0
  110. data/lib/mxrb/compiler/page_document_compiler.rb +77 -0
  111. data/lib/mxrb/compiler/portable_packager.rb +356 -0
  112. data/lib/mxrb/compiler/project_jar_archive.rb +84 -0
  113. data/lib/mxrb/compiler/project_jar_builder.rb +121 -0
  114. data/lib/mxrb/compiler/project_materializer.rb +139 -0
  115. data/lib/mxrb/compiler/project_model_orderer.rb +52 -0
  116. data/lib/mxrb/compiler/runtime_data_types.rb +31 -0
  117. data/lib/mxrb/compiler/runtime_model_schema.rb +90 -0
  118. data/lib/mxrb/compiler/schemas/runtime-10.24.0.73019.json +1052 -0
  119. data/lib/mxrb/compiler/schemas/runtime-11.json +1215 -0
  120. data/lib/mxrb/compiler/schemas/runtime-6.10.8.json +1148 -0
  121. data/lib/mxrb/compiler/schemas/runtime-7.17.0.json +1144 -0
  122. data/lib/mxrb/compiler/schemas/runtime-7.5.0.json +1215 -0
  123. data/lib/mxrb/compiler/schemas/runtime-9.6.1.29396.json +868 -0
  124. data/lib/mxrb/compiler/schemas/system-model-10.24.0.73019.b64 +1135 -0
  125. data/lib/mxrb/compiler/schemas/system-model-11.12.1.b64 +815 -0
  126. data/lib/mxrb/compiler/schemas/system-model-6.10.8.b64 +497 -0
  127. data/lib/mxrb/compiler/schemas/system-model-7.17.0.b64 +519 -0
  128. data/lib/mxrb/compiler/schemas/system-model-7.5.0.b64 +504 -0
  129. data/lib/mxrb/compiler/schemas/system-model-9.6.1.29396.b64 +816 -0
  130. data/lib/mxrb/compiler/security_materializer.rb +104 -0
  131. data/lib/mxrb/compiler/settings_document_compiler.rb +45 -0
  132. data/lib/mxrb/compiler/settings_materializer.rb +25 -0
  133. data/lib/mxrb/compiler/source_model.rb +128 -0
  134. data/lib/mxrb/compiler/system_model_seed.rb +88 -0
  135. data/lib/mxrb/compiler/system_queue_materializer.rb +54 -0
  136. data/lib/mxrb/compiler/system_text_materializer.rb +49 -0
  137. data/lib/mxrb/compiler/translation_materializer.rb +95 -0
  138. data/lib/mxrb/compiler/web_bundle_builder.rb +155 -0
  139. data/lib/mxrb/compiler/web_list_data_source.rb +139 -0
  140. data/lib/mxrb/compiler/web_operation_compiler.rb +614 -0
  141. data/lib/mxrb/compiler/web_shell_materializer.rb +194 -0
  142. data/lib/mxrb/compiler/widget_package_extractor.rb +63 -0
  143. data/lib/mxrb/doctor.rb +116 -0
  144. data/lib/mxrb/dsl/builder.rb +1636 -0
  145. data/lib/mxrb/errors.rb +18 -0
  146. data/lib/mxrb/evaluation.rb +131 -0
  147. data/lib/mxrb/exporter.rb +1716 -0
  148. data/lib/mxrb/frontend/migrator.rb +775 -0
  149. data/lib/mxrb/functional.rb +307 -0
  150. data/lib/mxrb/github/annotator.rb +161 -0
  151. data/lib/mxrb/initializer.rb +257 -0
  152. data/lib/mxrb/integrity/validator.rb +136 -0
  153. data/lib/mxrb/io/bson_codec.rb +145 -0
  154. data/lib/mxrb/io/mpr_file.rb +558 -0
  155. data/lib/mxrb/io/mxunit_codec.rb +42 -0
  156. data/lib/mxrb/marketplace.rb +380 -0
  157. data/lib/mxrb/model/association.rb +98 -0
  158. data/lib/mxrb/model/attribute.rb +100 -0
  159. data/lib/mxrb/model/connector.rb +17 -0
  160. data/lib/mxrb/model/design_materializer.rb +133 -0
  161. data/lib/mxrb/model/design_migration.rb +126 -0
  162. data/lib/mxrb/model/design_system.rb +111 -0
  163. data/lib/mxrb/model/domain_model.rb +51 -0
  164. data/lib/mxrb/model/entity.rb +180 -0
  165. data/lib/mxrb/model/menu.rb +38 -0
  166. data/lib/mxrb/model/microflow.rb +100 -0
  167. data/lib/mxrb/model/module.rb +142 -0
  168. data/lib/mxrb/model/navigation.rb +127 -0
  169. data/lib/mxrb/model/page.rb +410 -0
  170. data/lib/mxrb/model/project.rb +187 -0
  171. data/lib/mxrb/model/unit.rb +48 -0
  172. data/lib/mxrb/module_initializer.rb +75 -0
  173. data/lib/mxrb/official_marketplace/content_api.rb +292 -0
  174. data/lib/mxrb/official_marketplace/dependency_resolver.rb +329 -0
  175. data/lib/mxrb/official_marketplace/lifecycle.rb +367 -0
  176. data/lib/mxrb/official_marketplace/module_package_importer.rb +318 -0
  177. data/lib/mxrb/official_marketplace/widget_package_installer.rb +414 -0
  178. data/lib/mxrb/official_marketplace.rb +720 -0
  179. data/lib/mxrb/oql/analyzer.rb +155 -0
  180. data/lib/mxrb/oql/index_advisor.rb +85 -0
  181. data/lib/mxrb/oql/plan_analyzer.rb +185 -0
  182. data/lib/mxrb/oql/server.rb +132 -0
  183. data/lib/mxrb/oql/sql_server_plan_analyzer.rb +190 -0
  184. data/lib/mxrb/oql/sql_server_workload_analyzer.rb +80 -0
  185. data/lib/mxrb/oql/workload_analyzer.rb +175 -0
  186. data/lib/mxrb/oql/workload_baseline.rb +60 -0
  187. data/lib/mxrb/oql.rb +424 -0
  188. data/lib/mxrb/project_lifecycle.rb +76 -0
  189. data/lib/mxrb/protocols/plan.rb +86 -0
  190. data/lib/mxrb/protocols.rb +123 -0
  191. data/lib/mxrb/runtime/database_workspace.rb +677 -0
  192. data/lib/mxrb/runtime/docker_executor.rb +41 -0
  193. data/lib/mxrb/runtime/docker_workspace.rb +29 -0
  194. data/lib/mxrb/runtime/executor.rb +169 -0
  195. data/lib/mxrb/runtime/java_locator.rb +26 -0
  196. data/lib/mxrb/runtime/native.rb +474 -0
  197. data/lib/mxrb/runtime/sql_server_database.rb +146 -0
  198. data/lib/mxrb/runtime/toolchain.rb +82 -0
  199. data/lib/mxrb/scaffold/cli.rb +113 -0
  200. data/lib/mxrb/scaffold/generator.rb +246 -0
  201. data/lib/mxrb/scaffold/help.rb +80 -0
  202. data/lib/mxrb/scaffold/page_templates.rb +54 -0
  203. data/lib/mxrb/scaffold/recipes.rb +198 -0
  204. data/lib/mxrb/scaffold/registry.rb +82 -0
  205. data/lib/mxrb/scaffold/templates.rb +723 -0
  206. data/lib/mxrb/scaffold/transaction.rb +84 -0
  207. data/lib/mxrb/schema/tables.rb +55 -0
  208. data/lib/mxrb/semantic/analyzer.rb +473 -0
  209. data/lib/mxrb/semantic/batch_plan.rb +68 -0
  210. data/lib/mxrb/semantic/domain_mutator.rb +419 -0
  211. data/lib/mxrb/semantic/embedder.rb +51 -0
  212. data/lib/mxrb/semantic/extractor.rb +290 -0
  213. data/lib/mxrb/semantic/index.rb +623 -0
  214. data/lib/mxrb/semantic/inliner.rb +216 -0
  215. data/lib/mxrb/semantic/mover.rb +153 -0
  216. data/lib/mxrb/semantic/onnx_embedder.rb +34 -0
  217. data/lib/mxrb/semantic/remover.rb +70 -0
  218. data/lib/mxrb/semantic/renamer.rb +211 -0
  219. data/lib/mxrb/semantic/tfidf_embedder.rb +46 -0
  220. data/lib/mxrb/semantic/vec_store.rb +75 -0
  221. data/lib/mxrb/team_server.rb +390 -0
  222. data/lib/mxrb/templates/project/10.24.0.73019.json +32 -0
  223. data/lib/mxrb/templates/project/11.12.1.json +32 -0
  224. data/lib/mxrb/templates/project/6.10.8.json +50 -0
  225. data/lib/mxrb/templates/project/7.17.0.json +50 -0
  226. data/lib/mxrb/templates/project/7.5.0.json +50 -0
  227. data/lib/mxrb/templates/project/9.6.1.29396.json +50 -0
  228. data/lib/mxrb/version.rb +5 -0
  229. data/lib/mxrb/widget_package.rb +338 -0
  230. data/lib/mxrb/widget_synchronizer.rb +31 -0
  231. data/lib/mxrb/writer.rb +3775 -0
  232. data/lib/mxrb.rb +188 -0
  233. data/marketplace/catalog.json +10 -0
  234. data/marketplace/modules/shared-kernel/domain/README.md +4 -0
  235. data/marketplace/modules/shared-kernel/module.rb +5 -0
  236. data/marketplace/modules/shared-kernel/mxrb-module.json +9 -0
  237. metadata +447 -0
@@ -0,0 +1,198 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mxrb
4
+ module Scaffold
5
+ # Command recipes; filesystem mechanics remain in Generator.
6
+ # rubocop:disable Metrics/ModuleLength
7
+ module Recipes
8
+ SIMPLE = {
9
+ entity: %w[domain entities], enumeration: %w[domain enumerations],
10
+ use_case: %w[application use_cases], validation: %w[application validations],
11
+ query: %w[application queries], constant: %w[domain constants]
12
+ }.freeze
13
+ INFRASTRUCTURE = {
14
+ integration: 'integrations', published_rest: 'endpoints',
15
+ consumed_rest: 'integrations', java_action: 'actions'
16
+ }.freeze
17
+
18
+ private
19
+
20
+ def scaffold_entity = scaffold_simple(:entity)
21
+ def scaffold_enumeration = scaffold_simple(:enumeration)
22
+ def scaffold_use_case = scaffold_simple(:use_case)
23
+ def scaffold_validation = scaffold_simple(:validation)
24
+ def scaffold_query = scaffold_simple(:query)
25
+
26
+ def scaffold_constant
27
+ module_name, artifact_name = qualified_name
28
+ create_connected_module_file(
29
+ module_name, SIMPLE.fetch(:constant), artifact_name, :constant
30
+ )
31
+ end
32
+
33
+ def scaffold_page
34
+ module_name, artifact_name = qualified_name
35
+ chain = page_chain
36
+ template = selected_page_template
37
+ return scaffold_basic_page(module_name, artifact_name) unless chain || template
38
+
39
+ scaffold_templated_page(module_name, artifact_name, template || 'form-vertical', chain)
40
+ end
41
+
42
+ def scaffold_templated_page(module_name, artifact_name, template, chain)
43
+ ensure_presentation(module_name)
44
+ page_support_specs(artifact_name, chain, template).each do |directories, name, source|
45
+ create_connected_module_file(module_name, directories, name, source)
46
+ end
47
+ create_page_template(module_name, artifact_name, template:, chain:)
48
+ create_page_navigation(module_name, artifact_name)
49
+ end
50
+
51
+ def scaffold_basic_page(module_name, artifact_name)
52
+ ensure_presentation(module_name)
53
+ create_connected_module_file(module_name, %w[presentation pages], artifact_name, :page)
54
+ end
55
+
56
+ def page_support_specs(name, chain, template)
57
+ specs = PageTemplates.fetch(template).data_backed ? page_chain_base_specs(name) : []
58
+ specs << page_chain_microflow_spec(name) if chain&.end_with?('microflow')
59
+ specs << page_chain_nanoflow_spec(name, chain) if chain&.include?('nanoflow')
60
+ specs
61
+ end
62
+
63
+ def page_chain_base_specs(name)
64
+ [
65
+ [%w[domain entities], name, :page_chain_entity],
66
+ [%w[application use_cases], "ACT_Load#{name}", :page_chain_loader]
67
+ ]
68
+ end
69
+
70
+ def page_chain_microflow_spec(name)
71
+ [%w[application use_cases], "ACT_Refresh#{name}", :page_chain_action]
72
+ end
73
+
74
+ def page_chain_nanoflow_spec(name, chain)
75
+ template = chain.end_with?('microflow') ? :page_chain_client_server : :page_chain_nanoflow
76
+ [%w[presentation client_actions], "NAN_Refresh#{name}", template]
77
+ end
78
+
79
+ def scaffold_nanoflow
80
+ module_name, artifact_name = qualified_name
81
+ ensure_presentation(module_name)
82
+ create_connected_module_file(
83
+ module_name, %w[presentation client_actions], artifact_name, :nanoflow
84
+ )
85
+ end
86
+
87
+ def scaffold_repository
88
+ module_name, artifact_name = qualified_name
89
+ ensure_infrastructure(module_name)
90
+ create_connected_module_file(
91
+ module_name, %w[application repositories], artifact_name, :repository
92
+ )
93
+ create_connected_module_file(
94
+ module_name, %w[infrastructure repositories], artifact_name,
95
+ :repository_implementation, suffix: '_implementation'
96
+ )
97
+ end
98
+
99
+ def scaffold_scheduled_event
100
+ module_name, artifact_name = qualified_name
101
+ create_connected_module_file(
102
+ module_name, %w[application jobs], artifact_name, :scheduled_event
103
+ )
104
+ end
105
+
106
+ def scaffold_integration = scaffold_infrastructure_artifact(:integration)
107
+ def scaffold_published_rest = scaffold_infrastructure_artifact(:published_rest)
108
+ def scaffold_consumed_rest = scaffold_infrastructure_artifact(:consumed_rest)
109
+ def scaffold_java_action = scaffold_infrastructure_artifact(:java_action)
110
+
111
+ def scaffold_presentation
112
+ module_name = module_only
113
+ aggregator = module_path(module_name, 'presentation', 'presentation.rb')
114
+ keep_files = %w[pages snippets client_actions].map do |directory|
115
+ module_path(module_name, 'presentation', directory, '.keep')
116
+ end
117
+ initialized = @transaction.content(aggregator) && keep_files.all? { @transaction.content(_1) }
118
+ abort "#{aggregator}: presentation already initialized" if initialized
119
+
120
+ ensure_presentation(module_name)
121
+ end
122
+
123
+ def scaffold_security
124
+ module_name = module_only
125
+ ensure_module(module_name)
126
+ path = module_path(module_name, 'security', 'security.rb')
127
+ @transaction.create(path, Templates.render(:security, module_name:, name: nil))
128
+ connect_module_aggregator(module_name, 'security', 'security.rb')
129
+ connect_project_security(module_name)
130
+ end
131
+
132
+ def scaffold_functional_test
133
+ module_name, artifact_name = qualified_name
134
+ ensure_project
135
+ create_project_file(
136
+ %w[functional_tests], artifact_name,
137
+ Templates.render(:functional_test, module_name:, name: artifact_name)
138
+ )
139
+ end
140
+
141
+ def scaffold_evaluation
142
+ ensure_project
143
+ artifact_name = identifier(@name, label: 'evaluation')
144
+ create_project_file(
145
+ %w[evaluations], artifact_name,
146
+ Templates.render(:evaluation, module_name: nil, name: artifact_name)
147
+ )
148
+ end
149
+
150
+ def scaffold_design
151
+ ensure_project
152
+ abort 'Usage: mxrb design init' unless @name.empty?
153
+
154
+ design_assets.each do |parts, template|
155
+ ensure_file(project_path(*parts), Templates.render(template, module_name: nil, name: nil))
156
+ end
157
+ connect_project_file('app', 'design_system', 'design_system.rb')
158
+ end
159
+
160
+ def design_assets
161
+ {
162
+ %w[app design_system design_system.rb] => :design_system,
163
+ %w[theme web custom-variables.scss] => :theme_custom_variables,
164
+ %w[theme web main.scss] => :theme_main,
165
+ %w[theme web exclusion-variables.scss] => :theme_exclusion_variables,
166
+ %w[theme web settings.json] => :theme_settings,
167
+ %w[theme-cache web theme.compiled.css] => :theme_compiled
168
+ }
169
+ end
170
+
171
+ def scaffold_ci
172
+ ensure_project
173
+ raise ArgumentError, 'ci provider must be github' unless @name == 'github'
174
+
175
+ path = project_path('.github', 'workflows', 'mxrb.yml')
176
+ @transaction.create(path, Templates.render(:github_workflow, module_name: nil, name: nil))
177
+ end
178
+
179
+ def scaffold_simple(kind)
180
+ module_name, artifact_name = qualified_name
181
+ create_connected_module_file(module_name, SIMPLE.fetch(kind), artifact_name, kind)
182
+ end
183
+
184
+ def scaffold_infrastructure_artifact(kind)
185
+ module_name, artifact_name = qualified_name
186
+ ensure_infrastructure(module_name)
187
+ create_connected_module_file(
188
+ module_name, ['infrastructure', INFRASTRUCTURE.fetch(kind)], artifact_name, kind
189
+ )
190
+ end
191
+
192
+ def ensure_file(path, content)
193
+ @transaction.create(path, content) unless @transaction.content(path)
194
+ end
195
+ end
196
+ # rubocop:enable Metrics/ModuleLength
197
+ end
198
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+ require 'json'
5
+
6
+ module Mxrb
7
+ module Scaffold
8
+ Removal = Data.define(:key, :files)
9
+
10
+ # Records generated files so scaffold removal never relies on guessing paths.
11
+ class Registry
12
+ RELATIVE_PATH = File.join('.mxrb', 'scaffolds.json')
13
+
14
+ def self.stage(transaction, root:, key:, files:) # rubocop:disable Metrics/MethodLength
15
+ path = File.join(root, RELATIVE_PATH)
16
+ payload = transaction.content(path) ? JSON.parse(transaction.content(path)) : { 'scaffolds' => {} }
17
+ payload['scaffolds'][key] = {
18
+ 'files' => files.map do |file|
19
+ { 'path' => file.delete_prefix("#{root}/"),
20
+ 'sha256' => Digest::SHA256.hexdigest(transaction.content(file)) }
21
+ end
22
+ }
23
+ transaction.write(path, JSON.pretty_generate(payload) << "\n")
24
+ path
25
+ rescue JSON::ParserError => e
26
+ raise ArgumentError, "invalid scaffold registry: #{e.message}"
27
+ end
28
+
29
+ def initialize(root)
30
+ @root = File.expand_path(root)
31
+ @path = File.join(@root, RELATIVE_PATH)
32
+ end
33
+
34
+ def entries
35
+ return {} unless File.file?(@path)
36
+
37
+ JSON.parse(File.read(@path)).fetch('scaffolds', {})
38
+ rescue JSON::ParserError => e
39
+ raise ArgumentError, "invalid scaffold registry: #{e.message}"
40
+ end
41
+
42
+ def remove(key)
43
+ registry = entries
44
+ entry = registry.fetch(key) { raise ArgumentError, "scaffold not registered: #{key}" }
45
+ paths = entry.fetch('files').map { safe_path(_1.fetch('path')) }
46
+ verify_unchanged!(entry.fetch('files'), paths)
47
+ paths.each { FileUtils.rm_f(_1) }
48
+ registry.delete(key)
49
+ write('scaffolds' => registry)
50
+ Removal.new(key, paths.freeze)
51
+ end
52
+
53
+ private
54
+
55
+ def safe_path(relative)
56
+ path = File.expand_path(File.join(@root, relative))
57
+ prefix = "#{@root}#{File::SEPARATOR}"
58
+ raise ArgumentError, "unsafe scaffold path: #{relative}" unless path.start_with?(prefix)
59
+
60
+ path
61
+ end
62
+
63
+ def verify_unchanged!(files, paths)
64
+ files.zip(paths).each do |entry, path|
65
+ actual = File.file?(path) ? Digest::SHA256.file(path).hexdigest : nil
66
+ next if actual == entry['sha256']
67
+
68
+ raise ArgumentError, "refusing to remove changed scaffold file: #{path}"
69
+ end
70
+ end
71
+
72
+ def write(payload)
73
+ FileUtils.mkdir_p(File.dirname(@path))
74
+ temporary = "#{@path}.tmp-#{Process.pid}"
75
+ File.write(temporary, JSON.pretty_generate(payload) << "\n")
76
+ File.rename(temporary, @path)
77
+ ensure
78
+ FileUtils.rm_f(temporary) if defined?(temporary) && temporary && File.exist?(temporary)
79
+ end
80
+ end
81
+ end
82
+ end