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,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "unit"
4
+
5
+ module Mxrb
6
+ module Model
7
+ # $Type: Microflows$Microflow
8
+ # ContainmentName: "Documents"
9
+ class Microflow < Unit
10
+ attr_reader :name, :documentation, :return_variable_name,
11
+ :allow_concurrent_execution, :mark_as_used, :excluded,
12
+ :allowed_module_roles, :parameters, :return_type,
13
+ :objects, :flows
14
+
15
+ def decode(doc)
16
+ @name = doc["Name"] || doc["name"]
17
+ @documentation = doc["Documentation"] || doc["documentation"] || ""
18
+ @return_variable_name = doc["ReturnVariableName"] || "ReturnValue"
19
+ @allow_concurrent_execution = doc["AllowConcurrentExecution"] != false
20
+ @mark_as_used = doc["MarkAsUsed"] == true
21
+ @excluded = doc["Excluded"] == true
22
+ @allowed_module_roles = parse_array(doc["AllowedModuleRoles"])
23
+ @parameters = extract_parameters(doc)
24
+ return_doc = doc["MicroflowReturnType"] || doc["ReturnType"]
25
+ @return_type = return_doc.is_a?(Hash) ? (return_doc["$Type"] || return_doc["Type"]) : return_doc
26
+
27
+ obj_col = doc["ObjectCollection"] || {}
28
+ @objects = parse_array(obj_col["Objects"] || obj_col["objects"] || obj_col)
29
+ # Mendix stores all sequence flows (including flows inside looped
30
+ # activities) at document level. Older MXRB-generated documents used
31
+ # ObjectCollection/Flows, so keep that as a compatibility fallback.
32
+ @flows = parse_array(
33
+ doc["Flows"] || doc["flows"] ||
34
+ obj_col["Flows"] || obj_col["flows"] || []
35
+ )
36
+ end
37
+
38
+ def to_bson
39
+ {
40
+ "$ID" => @id,
41
+ "$Type" => "Microflows$Microflow",
42
+ "Name" => @name,
43
+ "Documentation" => @documentation || "",
44
+ "ReturnVariableName" => @return_variable_name || "ReturnValue",
45
+ "AllowConcurrentExecution" => @allow_concurrent_execution != false,
46
+ "MarkAsUsed" => @mark_as_used || false,
47
+ "Excluded" => @excluded || false,
48
+ "AllowedModuleRoles" => IO::BsonCodec.build_array(@allowed_module_roles || [], marker: 1),
49
+ "MicroflowParameterCollection" => serialize_parameters,
50
+ "MicroflowReturnType" => @return_type || default_void_return,
51
+ "ObjectCollection" => serialize_object_collection,
52
+ "Flows" => IO::BsonCodec.build_array(@flows || []),
53
+ }
54
+ end
55
+
56
+ def inspect
57
+ "#<Mxrb::Microflow name=#{@name.inspect} objects=#{@objects.size} flows=#{@flows.size}>"
58
+ end
59
+
60
+ private
61
+
62
+ def extract_parameters(doc)
63
+ pc = doc["MicroflowParameterCollection"] || doc["Parameters"] || {}
64
+ if pc.is_a?(Hash) && (pc["Parameters"] || !pc.empty?)
65
+ list = parse_array(pc["Parameters"] || [])
66
+ return list unless list.empty?
67
+ end
68
+ # MXRB writer embeds parameters inside ObjectCollection.Objects
69
+ obj_col = doc["ObjectCollection"] || {}
70
+ all_objects = parse_array(obj_col["Objects"] || obj_col["objects"] || obj_col)
71
+ all_objects.select { |o| o.is_a?(Hash) && (o["$Type"] || o["\$Type"]) == "Microflows$MicroflowParameter" }
72
+ end
73
+
74
+ def serialize_parameters
75
+ {
76
+ "$ID" => SecureRandom.uuid,
77
+ "$Type" => "Microflows$MicroflowParameterCollection",
78
+ "Parameters" => IO::BsonCodec.build_array(@parameters || []),
79
+ }
80
+ end
81
+
82
+ def default_void_return
83
+ {
84
+ "$ID" => SecureRandom.uuid,
85
+ "$Type" => "Microflows$MicroflowReturnType",
86
+ "Type" => nil,
87
+ "AllowedModuleRoles" => IO::BsonCodec.build_array([], marker: 1),
88
+ }
89
+ end
90
+
91
+ def serialize_object_collection
92
+ {
93
+ "$ID" => SecureRandom.uuid,
94
+ "$Type" => "Microflows$MicroflowObjectCollection",
95
+ "Objects" => IO::BsonCodec.build_array(@objects || []),
96
+ }
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "unit"
4
+
5
+ module Mxrb
6
+ module Model
7
+ # $Type: Projects$Module
8
+ # ContainmentName: "Modules"
9
+ class Module < Unit
10
+ attr_reader :name, :sort_index, :from_app_store,
11
+ :app_store_guid, :app_store_version, :export_level
12
+
13
+ def decode(doc)
14
+ @name = doc["Name"]
15
+ @sort_index = doc["SortIndex"] || doc["NewSortIndex"]
16
+ @from_app_store = doc["FromAppStore"] == true
17
+ @app_store_guid = doc["AppStoreGuid"]
18
+ @app_store_version = doc["AppStoreVersion"]
19
+ @export_level = doc["ExportLevel"] || "Hidden"
20
+ end
21
+
22
+ def to_bson
23
+ {
24
+ "$ID" => @id,
25
+ "$Type" => "Projects$Module",
26
+ "Name" => @name,
27
+ "SortIndex" => @sort_index || 0,
28
+ "FromAppStore" => @from_app_store || false,
29
+ "ExportLevel" => @export_level || "Hidden",
30
+ }
31
+ end
32
+
33
+ # ── Children (resolved from Unit table by ContainerID) ────────────
34
+
35
+ def domain_model
36
+ @domain_model ||= begin
37
+ raws = @mpr.units_by_containment("DomainModel").select { _1["ContainerID"] == @id }
38
+ raws.empty? ? nil : DomainModel.new(raws.first, @mpr)
39
+ end
40
+ end
41
+
42
+ def entities = domain_model&.entities || []
43
+ def associations = domain_model&.associations || []
44
+
45
+ def pages
46
+ @pages ||= document_units
47
+ .select { |u| %w[Pages$Page Forms$Page].include?(u[:type]) }
48
+ .map { Page.new(_1[:raw], @mpr) }
49
+ end
50
+
51
+ def microflows
52
+ @microflows ||= document_units
53
+ .select { |u| u[:type] == "Microflows$Microflow" }
54
+ .map { Microflow.new(_1[:raw], @mpr) }
55
+ end
56
+
57
+ def nanoflows
58
+ @nanoflows ||= document_units
59
+ .select { |u| u[:type] == "Microflows$Nanoflow" }
60
+ .map { Microflow.new(_1[:raw], @mpr) }
61
+ end
62
+
63
+ def menus
64
+ @menus ||= document_units
65
+ .select { |u| u[:type] == "Menus$MenuDocument" }
66
+ .map { Menu.new(_1[:raw], @mpr) }
67
+ end
68
+
69
+ def enumerations
70
+ @enumerations ||= document_units
71
+ .select { |u| u[:type] == "Enumerations$Enumeration" }
72
+ .map { unit_to_doc(_1) }
73
+ end
74
+
75
+ def constants
76
+ @constants ||= document_units
77
+ .select { |u| u[:type] == "Constants$Constant" }
78
+ .map { unit_to_doc(_1) }
79
+ end
80
+
81
+ def scheduled_events
82
+ @scheduled_events ||= document_units
83
+ .select { |u| u[:type] == "ScheduledEvents$ScheduledEvent" }
84
+ .map { unit_to_doc(_1) }
85
+ end
86
+
87
+ def module_roles
88
+ @module_roles ||= begin
89
+ raw = @mpr.children_of(@id).find { _1["ContainmentName"] == "ModuleSecurity" }
90
+ if raw
91
+ doc = @mpr.parse_contents(raw)
92
+ parse_array(doc["ModuleRoles"]).map do |role|
93
+ { name: role["Name"], description: role["Description"].to_s }
94
+ end
95
+ else
96
+ []
97
+ end
98
+ end
99
+ end
100
+
101
+ def inspect
102
+ "#<Mxrb::Module name=#{@name.inspect} entities=#{entities.size} " \
103
+ "pages=#{pages.size} microflows=#{microflows.size}>"
104
+ end
105
+
106
+ private
107
+
108
+ def unit_to_doc(unit_hash)
109
+ @mpr.parse_contents(unit_hash[:raw])
110
+ end
111
+
112
+ # Documents live in ContainmentName = "Documents" recursively under this module.
113
+ # We do a simple two-pass: direct Documents children + Documents inside Folders.
114
+ def document_units
115
+ @document_units ||= begin
116
+ collect_documents(@id).map do |raw|
117
+ doc = @mpr.parse_contents(raw)
118
+ type = doc["$Type"]
119
+ { raw: raw, type: type }
120
+ end
121
+ end
122
+ end
123
+
124
+ def children_with_containment(name)
125
+ @mpr.units_by_containment(name).select { _1["ContainerID"] == @id }
126
+ end
127
+
128
+ def collect_documents(parent_id)
129
+ @mpr.children_of(parent_id).flat_map do |raw|
130
+ case raw["ContainmentName"]
131
+ when "Documents"
132
+ [raw]
133
+ when "Folders"
134
+ collect_documents(raw.fetch("UnitID"))
135
+ else
136
+ []
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mxrb
4
+ module Model
5
+ # Typed reader for modern and legacy native Mendix navigation documents.
6
+ class Navigation
7
+ LEGACY_PROFILES = {
8
+ 'DesktopProfile' => 'Desktop',
9
+ 'TabletProfile' => 'Tablet',
10
+ 'PhoneProfile' => 'Phone',
11
+ 'OfflinePhoneProfile' => 'OfflinePhone',
12
+ 'HybridPhoneProfile6' => 'HybridPhone',
13
+ 'HybridTabletProfile6' => 'HybridTablet'
14
+ }.freeze
15
+
16
+ attr_reader :raw_document
17
+
18
+ def initialize(raw_document)
19
+ @raw_document = raw_document || {}
20
+ end
21
+
22
+ def profiles
23
+ @profiles ||= begin
24
+ modern = items(@raw_document['Profiles'])
25
+ documents = modern.empty? ? legacy_profile_documents : modern
26
+ documents.map { NavigationProfile.new(_1) }.freeze
27
+ end
28
+ end
29
+
30
+ def empty? = @raw_document.empty?
31
+ def to_h = { profiles: profiles.map(&:to_h) }
32
+
33
+ private
34
+
35
+ def items(value) = IO::BsonCodec.parse_array(value).fetch(:items)
36
+
37
+ def legacy_profile_documents
38
+ LEGACY_PROFILES.filter_map do |key, name|
39
+ value = @raw_document[key]
40
+ value.merge('Name' => value['Name'] || name) if value.is_a?(Hash)
41
+ end
42
+ end
43
+ end
44
+
45
+ # Typed view of one navigation profile, including recursive menu items.
46
+ class NavigationProfile
47
+ attr_reader :raw_document
48
+
49
+ def initialize(raw_document)
50
+ @raw_document = raw_document
51
+ end
52
+
53
+ def name = @raw_document['Name'].to_s
54
+ def kind = @raw_document['Kind'].to_s
55
+ def offline? = kind.match?(/offline/i)
56
+ def app_icon = @raw_document['AppIcon']
57
+ def app_title = text_translations(@raw_document['AppTitle'])
58
+ def home_page = reference(@raw_document.dig('HomePage', 'Page'))
59
+ def home_microflow = reference(@raw_document.dig('HomePage', 'Microflow'))
60
+ def sign_in_page = reference(@raw_document.dig('LoginPageSettings', 'Form'))
61
+
62
+ def role_homes
63
+ items(@raw_document['HomeItems'] || @raw_document['RoleBasedHomePages']).map do |home|
64
+ {
65
+ role: reference(home['UserRole']),
66
+ page: reference(home['Page']),
67
+ microflow: reference(home['Microflow'])
68
+ }.compact
69
+ end
70
+ end
71
+
72
+ def menu_items
73
+ collection = @raw_document['Menu'] || @raw_document['MenuItemCollection']
74
+ items(collection&.fetch('Items', nil)).map { navigation_item(_1) }
75
+ end
76
+
77
+ def to_h
78
+ base_profile.merge(
79
+ offline: offline?,
80
+ app_icon:,
81
+ app_title:,
82
+ items: menu_items
83
+ )
84
+ end
85
+
86
+ private
87
+
88
+ def base_profile
89
+ {
90
+ name:,
91
+ kind:,
92
+ home_page:,
93
+ home_microflow:,
94
+ sign_in_page:,
95
+ role_homes:
96
+ }
97
+ end
98
+
99
+ def items(value) = IO::BsonCodec.parse_array(value).fetch(:items)
100
+
101
+ def reference(value)
102
+ result = IO::BsonCodec.extract_id(value)
103
+ result.to_s.empty? ? nil : result
104
+ end
105
+
106
+ def text_translations(text)
107
+ return {} unless text.is_a?(Hash)
108
+
109
+ translations = items(text['Translations'] || text['Items']).to_h do |translation|
110
+ [translation['LanguageCode'].to_s, translation['Text'].to_s]
111
+ end
112
+ translations.reject { |_locale, value| value.empty? }
113
+ end
114
+
115
+ def navigation_item(item)
116
+ action = item['Action'].is_a?(Hash) ? item['Action'] : {}
117
+ {
118
+ caption: text_translations(item['Caption']),
119
+ page: reference(action.dig('FormSettings', 'Form')),
120
+ microflow: reference(action.dig('MicroflowSettings', 'Microflow')),
121
+ icon: item.dig('Icon', 'Code'),
122
+ items: items(item['Items']).map { navigation_item(_1) }
123
+ }.compact
124
+ end
125
+ end
126
+ end
127
+ end