mxrb 0.1.2 → 0.1.4

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 (191) hide show
  1. checksums.yaml +4 -4
  2. data/bin/mxrb +458 -10
  3. data/docs/de-DE/README.md +1 -0
  4. data/docs/de-DE/oql-sql.md +42 -0
  5. data/docs/de-DE/project-structure.md +201 -2
  6. data/docs/de-DE/runtime-ruby.md +104 -0
  7. data/docs/de-DE/runtime-schema-migrations.md +44 -0
  8. data/docs/de-DE/validation-matrix.md +42 -4
  9. data/docs/en-US/README.md +1 -0
  10. data/docs/en-US/oql-sql.md +41 -0
  11. data/docs/en-US/project-structure.md +192 -2
  12. data/docs/en-US/runtime-ruby.md +100 -0
  13. data/docs/en-US/runtime-schema-migrations.md +41 -0
  14. data/docs/en-US/validation-matrix.md +39 -4
  15. data/docs/pt-BR/README.md +1 -0
  16. data/docs/pt-BR/entity-dsl.md +8 -0
  17. data/docs/pt-BR/oql-sql.md +41 -0
  18. data/docs/pt-BR/project-structure.md +197 -2
  19. data/docs/pt-BR/runtime-ruby.md +108 -0
  20. data/docs/pt-BR/runtime-schema-migrations.md +40 -0
  21. data/docs/pt-BR/validation-matrix.md +42 -5
  22. data/docs/pt-BR/writing.md +28 -0
  23. data/lib/mxrb/cli/help.rb +426 -0
  24. data/lib/mxrb/cli/release.rb +137 -0
  25. data/lib/mxrb/compiler/compatibility_analyzer.rb +1 -1
  26. data/lib/mxrb/compiler/data_grid_bundle_compiler.rb +8 -1
  27. data/lib/mxrb/compiler/domain_document_compiler.rb +3 -0
  28. data/lib/mxrb/compiler/gallery_bundle_compiler.rb +11 -1
  29. data/lib/mxrb/compiler/generic_widget_bundle_compiler.rb +76 -19
  30. data/lib/mxrb/compiler/legacy_custom_widget_compiler.rb +159 -0
  31. data/lib/mxrb/compiler/legacy_data_grid_compiler.rb +43 -7
  32. data/lib/mxrb/compiler/legacy_page_builder.rb +1248 -23
  33. data/lib/mxrb/compiler/packager.rb +27 -13
  34. data/lib/mxrb/compiler/page_bundle_compiler.rb +13 -4
  35. data/lib/mxrb/compiler/portable_packager.rb +16 -11
  36. data/lib/mxrb/compiler/web_bundle_builder.rb +5 -1
  37. data/lib/mxrb/compiler/web_shell_materializer.rb +42 -2
  38. data/lib/mxrb/compiler/widget_package_extractor.rb +3 -0
  39. data/lib/mxrb/domain_diagram/lifecycle.rb +272 -0
  40. data/lib/mxrb/domain_diagram.rb +409 -0
  41. data/lib/mxrb/dsl/builder.rb +62 -11
  42. data/lib/mxrb/environment.rb +226 -0
  43. data/lib/mxrb/exporter.rb +265 -45
  44. data/lib/mxrb/http/server.rb +107 -0
  45. data/lib/mxrb/initializer.rb +80 -9
  46. data/lib/mxrb/integrity/validator.rb +32 -7
  47. data/lib/mxrb/io/bson_codec.rb +42 -1
  48. data/lib/mxrb/io/mpr_file.rb +146 -3
  49. data/lib/mxrb/model/attribute.rb +3 -0
  50. data/lib/mxrb/model/entity.rb +44 -2
  51. data/lib/mxrb/model/microflow.rb +3 -1
  52. data/lib/mxrb/model/module.rb +71 -0
  53. data/lib/mxrb/model/page.rb +126 -15
  54. data/lib/mxrb/model/project.rb +3 -0
  55. data/lib/mxrb/modeler/catalog.rb +178 -0
  56. data/lib/mxrb/official_marketplace/module_package_importer.rb +23 -12
  57. data/lib/mxrb/official_marketplace/widget_package_installer.rb +15 -11
  58. data/lib/mxrb/official_marketplace.rb +9 -3
  59. data/lib/mxrb/oql/reverse_translator.rb +394 -0
  60. data/lib/mxrb/oql/server.rb +4 -8
  61. data/lib/mxrb/oql.rb +32 -4
  62. data/lib/mxrb/progress.rb +242 -0
  63. data/lib/mxrb/project_lifecycle.rb +5 -2
  64. data/lib/mxrb/ruby_app/exporter.rb +1749 -0
  65. data/lib/mxrb/ruby_app/preset.rb +506 -0
  66. data/lib/mxrb/ruby_app/session_manager.rb +122 -0
  67. data/lib/mxrb/ruby_app.rb +1361 -0
  68. data/lib/mxrb/runtime/access_control.rb +408 -0
  69. data/lib/mxrb/runtime/native.rb +1015 -93
  70. data/lib/mxrb/runtime/scheduler.rb +396 -0
  71. data/lib/mxrb/runtime/schema_migrator.rb +598 -0
  72. data/lib/mxrb/runtime/shared_store.rb +335 -0
  73. data/lib/mxrb/runtime/sqlite_store.rb +729 -0
  74. data/lib/mxrb/scaffold/generator.rb +11 -3
  75. data/lib/mxrb/team_server.rb +10 -7
  76. data/lib/mxrb/uml/activity_diagram.rb +118 -0
  77. data/lib/mxrb/uml/class_diagram.rb +118 -0
  78. data/lib/mxrb/uml/sequence_diagram.rb +112 -0
  79. data/lib/mxrb/uml/server.rb +146 -0
  80. data/lib/mxrb/uml/support.rb +35 -0
  81. data/lib/mxrb/version.rb +1 -1
  82. data/lib/mxrb/web_ui/assets/abnfDiagram-N423BO3Z-C4JD3cEC.js +1 -0
  83. data/lib/mxrb/web_ui/assets/arc-3Z53kgFp.js +1 -0
  84. data/lib/mxrb/web_ui/assets/architecture-TIHT7OUA-BStBTLea.js +1 -0
  85. data/lib/mxrb/web_ui/assets/architectureDiagram-T3A2C74G-DBwqnM4J.js +36 -0
  86. data/lib/mxrb/web_ui/assets/array-BifhSqXX.js +1 -0
  87. data/lib/mxrb/web_ui/assets/blockDiagram-VBNYF7ZC-DLtuHt-j.js +132 -0
  88. data/lib/mxrb/web_ui/assets/c4Diagram-5PPSVZJV-ConSk_pe.js +10 -0
  89. data/lib/mxrb/web_ui/assets/channel-BhPvD7Dt.js +1 -0
  90. data/lib/mxrb/web_ui/assets/chunk-2GRJ4B5K-D2HpWsf_.js +1 -0
  91. data/lib/mxrb/web_ui/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  92. data/lib/mxrb/web_ui/assets/chunk-4I5QYGJK-CiIdNwaF.js +1 -0
  93. data/lib/mxrb/web_ui/assets/chunk-5RXB4S5H-Bu_AaEkE.js +231 -0
  94. data/lib/mxrb/web_ui/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  95. data/lib/mxrb/web_ui/assets/chunk-6Q2QTUOP-wQkNmt_X.js +88 -0
  96. data/lib/mxrb/web_ui/assets/chunk-7BUUIJ7U-Bb538aSH.js +1 -0
  97. data/lib/mxrb/web_ui/assets/chunk-GF5L2VYU-G8MmiBzU.js +206 -0
  98. data/lib/mxrb/web_ui/assets/chunk-I66GZJ75-CulcrLPh.js +127 -0
  99. data/lib/mxrb/web_ui/assets/chunk-JQJVKLGR-ukjzcdlL.js +156 -0
  100. data/lib/mxrb/web_ui/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  101. data/lib/mxrb/web_ui/assets/chunk-KBJHAD2P-Wm35lYov.js +1 -0
  102. data/lib/mxrb/web_ui/assets/chunk-KEIR6QF5-BICK3FdT.js +161 -0
  103. data/lib/mxrb/web_ui/assets/chunk-NSK5VX7P-D2nYrCY4.js +2 -0
  104. data/lib/mxrb/web_ui/assets/chunk-QR6OTTB3-BrdIzg4Q.js +62 -0
  105. data/lib/mxrb/web_ui/assets/chunk-RYQCIY6F-CCfZMcW6.js +1 -0
  106. data/lib/mxrb/web_ui/assets/chunk-UBXNYLIW-BPEVYsFK.js +1 -0
  107. data/lib/mxrb/web_ui/assets/chunk-W5SLKNZC-2E6OPhA4.js +1 -0
  108. data/lib/mxrb/web_ui/assets/chunk-WRU74C26-CBIvOyWp.js +70 -0
  109. data/lib/mxrb/web_ui/assets/chunk-XXDRQBXY-pH58XAyl.js +1 -0
  110. data/lib/mxrb/web_ui/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  111. data/lib/mxrb/web_ui/assets/classDiagram-JCYQIIEL-xiqo4D89.js +1 -0
  112. data/lib/mxrb/web_ui/assets/classDiagram-v2-OCEON4UE-xiqo4D89.js +1 -0
  113. data/lib/mxrb/web_ui/assets/cose-bilkent-JH36ORCC-t_flPpii.js +1 -0
  114. data/lib/mxrb/web_ui/assets/cynefin-VYW2F7L2-BIqktTEv.js +1 -0
  115. data/lib/mxrb/web_ui/assets/cynefinDiagram-MW4NZA55-C2d--lut.js +62 -0
  116. data/lib/mxrb/web_ui/assets/cytoscape.esm-B-NFISlW.js +321 -0
  117. data/lib/mxrb/web_ui/assets/dagre-Buvkdvvj.js +1 -0
  118. data/lib/mxrb/web_ui/assets/dagre-VZM6K2ZE-CSz2_tb5.js +4 -0
  119. data/lib/mxrb/web_ui/assets/defaultLocale-BFoDCU3G.js +1 -0
  120. data/lib/mxrb/web_ui/assets/diagram-7IWD3JNH-DCt7u7V_.js +30 -0
  121. data/lib/mxrb/web_ui/assets/diagram-B4RE2ZJO-DqZBqkMu.js +3 -0
  122. data/lib/mxrb/web_ui/assets/diagram-LBJQPF4R-ClIYbIsI.js +24 -0
  123. data/lib/mxrb/web_ui/assets/diagram-Q27KOJAE-DFxhvthJ.js +24 -0
  124. data/lib/mxrb/web_ui/assets/diagram-UB23O5K3-BSUTXptv.js +41 -0
  125. data/lib/mxrb/web_ui/assets/dist-D2qOEbeJ.js +1 -0
  126. data/lib/mxrb/web_ui/assets/domain-mGOZ1KCG.css +1 -0
  127. data/lib/mxrb/web_ui/assets/domain-qsjXhy4v.js +1 -0
  128. data/lib/mxrb/web_ui/assets/ebnfDiagram-BXEA7PRR-B5reICEi.js +1 -0
  129. data/lib/mxrb/web_ui/assets/erDiagram-JOGREHBK-Co5Mxn2p.js +85 -0
  130. data/lib/mxrb/web_ui/assets/eventmodeling-45OFAUF4-C1ylhSks.js +1 -0
  131. data/lib/mxrb/web_ui/assets/flowDiagram-UKHOOZJN-CHXaJIaS.js +1 -0
  132. data/lib/mxrb/web_ui/assets/ganttDiagram-PKOTCBZU-DPPPaWaz.js +292 -0
  133. data/lib/mxrb/web_ui/assets/gitGraph-TEB2WS4Q-qS77ywA9.js +1 -0
  134. data/lib/mxrb/web_ui/assets/gitGraphDiagram-DS77QQ5N-DpAv6-BT.js +106 -0
  135. data/lib/mxrb/web_ui/assets/graphlib-DS17s2tU.js +1 -0
  136. data/lib/mxrb/web_ui/assets/info-DKCQHKI2-C9VD1Y4e.js +1 -0
  137. data/lib/mxrb/web_ui/assets/infoDiagram-6WML65LV-Bk1kbcCy.js +2 -0
  138. data/lib/mxrb/web_ui/assets/init-C-OQMol4.js +1 -0
  139. data/lib/mxrb/web_ui/assets/ishikawaDiagram-WSZJBQD7-0gcISBFR.js +70 -0
  140. data/lib/mxrb/web_ui/assets/journeyDiagram-NVQOT4AX-Dt0RQnlh.js +139 -0
  141. data/lib/mxrb/web_ui/assets/jsx-runtime-DV0a5kSb.js +9 -0
  142. data/lib/mxrb/web_ui/assets/kanban-definition-27J2QSJJ-DIPQM7w9.js +89 -0
  143. data/lib/mxrb/web_ui/assets/katex-CXMH3UgJ.js +257 -0
  144. data/lib/mxrb/web_ui/assets/line-C0rYD-tL.js +1 -0
  145. data/lib/mxrb/web_ui/assets/linear-BPPxWorj.js +1 -0
  146. data/lib/mxrb/web_ui/assets/map-BaFkSB1l.js +1 -0
  147. data/lib/mxrb/web_ui/assets/mermaid-parser.core-B_UPzTxa.js +7 -0
  148. data/lib/mxrb/web_ui/assets/mindmap-definition-FAOFIHXS-CdJRSMuk.js +96 -0
  149. data/lib/mxrb/web_ui/assets/modeler-BNqHthwk.css +1 -0
  150. data/lib/mxrb/web_ui/assets/modeler-CQmqflbg.js +1 -0
  151. data/lib/mxrb/web_ui/assets/ordinal-BDEzSJ7C.js +1 -0
  152. data/lib/mxrb/web_ui/assets/packet-7NZHBO7P-CwQ4NSBs.js +1 -0
  153. data/lib/mxrb/web_ui/assets/path-fybaL0A-.js +1 -0
  154. data/lib/mxrb/web_ui/assets/pegDiagram-VL7TDLO6-D40S7ppx.js +1 -0
  155. data/lib/mxrb/web_ui/assets/pie-RZYD4A2V-CJroHMKh.js +1 -0
  156. data/lib/mxrb/web_ui/assets/pieDiagram-7S7Q4E2Y-QB54__BV.js +39 -0
  157. data/lib/mxrb/web_ui/assets/quadrantDiagram-CIZ2JOQS-DBhDS6ag.js +7 -0
  158. data/lib/mxrb/web_ui/assets/radar-I7S5WNFK-DdIUwd2d.js +1 -0
  159. data/lib/mxrb/web_ui/assets/railroad-3IZDKUUU-B1Lw6Xyq.js +1 -0
  160. data/lib/mxrb/web_ui/assets/railroad-abnf-AHOZXSZD-CQgQoeak.js +1 -0
  161. data/lib/mxrb/web_ui/assets/railroad-ebnf-EBAXGLYW-D7qEQ2x8.js +1 -0
  162. data/lib/mxrb/web_ui/assets/railroad-peg-LSFZ7HO6-C005Vxbb.js +1 -0
  163. data/lib/mxrb/web_ui/assets/railroadDiagram-AXF67PYL-BBP2qk7M.js +1 -0
  164. data/lib/mxrb/web_ui/assets/requirementDiagram-LRYGKXZP-D2cLsAoI.js +84 -0
  165. data/lib/mxrb/web_ui/assets/rough.esm-Dy-Kn_BL.js +1 -0
  166. data/lib/mxrb/web_ui/assets/sankeyDiagram-W5VNT64P-Bpi3FiP2.js +40 -0
  167. data/lib/mxrb/web_ui/assets/sequenceDiagram-SI44F4Z6-DCRcDlVe.js +162 -0
  168. data/lib/mxrb/web_ui/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +1 -0
  169. data/lib/mxrb/web_ui/assets/src-CvxfUak2.js +1 -0
  170. data/lib/mxrb/web_ui/assets/stateDiagram-OKZ733FA-C2GCEoxl.js +1 -0
  171. data/lib/mxrb/web_ui/assets/stateDiagram-v2-UEYNNEHI-D2gWgedh.js +1 -0
  172. data/lib/mxrb/web_ui/assets/swimlanes-SLNWSIFB-CRI9irEW.js +2 -0
  173. data/lib/mxrb/web_ui/assets/swimlanesDiagram-ULZ7WXOC-C5Wb_-hZ.js +8 -0
  174. data/lib/mxrb/web_ui/assets/theme-_mxc7BdY.css +1 -0
  175. data/lib/mxrb/web_ui/assets/timeline-definition-Z64GVDOM-B1-mNg-h.js +120 -0
  176. data/lib/mxrb/web_ui/assets/treeView-QDETBFTQ-7CTvIwKq.js +1 -0
  177. data/lib/mxrb/web_ui/assets/treemap-6X3UGDF4-CtL22pq5.js +1 -0
  178. data/lib/mxrb/web_ui/assets/uml-C3KAFGTq.js +51 -0
  179. data/lib/mxrb/web_ui/assets/uml-Dn-qd01X.css +1 -0
  180. data/lib/mxrb/web_ui/assets/vennDiagram-T6HMQDX7-BzVnUEAy.js +34 -0
  181. data/lib/mxrb/web_ui/assets/wardley-OPB4EBWU-JNi-QNag.js +1 -0
  182. data/lib/mxrb/web_ui/assets/wardleyDiagram-T6FBY63Y-BUwmAQ-B.js +78 -0
  183. data/lib/mxrb/web_ui/assets/xychartDiagram-ELKLHX3M-DEQS8sKp.js +7 -0
  184. data/lib/mxrb/web_ui/domain.html +15 -0
  185. data/lib/mxrb/web_ui/modeler.html +16 -0
  186. data/lib/mxrb/web_ui/uml.html +31 -0
  187. data/lib/mxrb/web_ui.rb +89 -0
  188. data/lib/mxrb/widget_certification.rb +208 -0
  189. data/lib/mxrb/writer.rb +278 -41
  190. data/lib/mxrb.rb +38 -8
  191. metadata +157 -5
data/lib/mxrb/writer.rb CHANGED
@@ -31,24 +31,55 @@ module Mxrb
31
31
  def initialize(path, definition)
32
32
  @path = File.expand_path(path)
33
33
  @definition = definition
34
+ @progress = Progress::NullTask.instance
34
35
  end
35
36
 
36
37
  def write!
37
- create_project! unless File.exist?(@path)
38
- mpr = IO::MprFile.open(@path)
39
- mpr.transaction do
40
- apply(mpr)
41
- mpr.write_architecture_definition(@definition)
42
- end
43
- materialize_project_assets
44
- materialize_design_system
38
+ mpr = nil
39
+ native_units = prepared_native_units
40
+ asset_manifest = project_asset_manifest
41
+ total = 4 + native_units.count { _1["containment"] != "Modules" } +
42
+ @definition.fetch(:modules).size + Array(asset_manifest&.fetch("files", [])).size
43
+ Progress.with("Generating #{File.basename(@path)}", total:) do |progress|
44
+ @progress = progress
45
+ create_project! unless File.exist?(@path)
46
+ progress.advance(detail: "project container")
47
+ mpr = IO::MprFile.open(@path)
48
+ mpr.transaction do
49
+ apply(mpr, native_units)
50
+ write_native_compatibility(mpr, native_units)
51
+ mpr.write_architecture_definition(@definition)
52
+ mpr.write_ruby_app_sources(ruby_app_source_files) if @definition[:ruby_app_sources_path]
53
+ end
54
+ progress.advance(detail: "model transaction")
55
+ materialize_project_assets(asset_manifest)
56
+ materialize_design_system
57
+ progress.advance(detail: "design system")
58
+ end
45
59
  self
46
60
  ensure
61
+ @progress = Progress::NullTask.instance
47
62
  mpr&.close
48
63
  end
49
64
 
50
65
  private
51
66
 
67
+ def ruby_app_source_files
68
+ path = @definition.fetch(:ruby_app_sources_path)
69
+ manifest = JSON.parse(File.read(path))
70
+ manifest.fetch("files").map do |file|
71
+ relative = safe_asset_path(file.fetch("path"))
72
+ contents = Base64.strict_decode64(file.fetch("contents"))
73
+ checksum = Digest::SHA256.hexdigest(contents)
74
+ raise SerializationError, "Ruby source checksum mismatch: #{relative}" \
75
+ unless checksum == file.fetch("sha256")
76
+
77
+ { path: relative, contents:, sha256: checksum, mode: file.fetch("mode", 0o644) }
78
+ end
79
+ rescue JSON::ParserError, KeyError, ArgumentError => e
80
+ raise SerializationError, "invalid Ruby source manifest: #{e.message}"
81
+ end
82
+
52
83
  def materialize_design_system
53
84
  design_system = @definition[:design_system]
54
85
  return unless design_system
@@ -56,11 +87,17 @@ module Mxrb
56
87
  Model::DesignMaterializer.new(File.dirname(@path), design_system).materialize!
57
88
  end
58
89
 
59
- def materialize_project_assets
90
+ def project_asset_manifest
60
91
  assets = @definition[:project_assets]
61
92
  return unless assets
62
93
 
63
- manifest = JSON.parse(File.read(assets.fetch(:manifest)))
94
+ JSON.parse(File.read(assets.fetch(:manifest)))
95
+ end
96
+
97
+ def materialize_project_assets(manifest)
98
+ assets = @definition[:project_assets]
99
+ return unless assets && manifest
100
+
64
101
  source_root = File.realpath(assets.fetch(:root))
65
102
  target_root = File.dirname(@path)
66
103
  manifest.fetch("files").each do |entry|
@@ -79,6 +116,7 @@ module Mxrb
79
116
  FileUtils.mv(temporary, target)
80
117
  ensure
81
118
  FileUtils.rm_f(temporary) if temporary
119
+ @progress.advance(detail: "asset #{relative}") if defined?(relative) && relative
82
120
  end
83
121
  end
84
122
 
@@ -161,7 +199,7 @@ module Mxrb
161
199
  JSON.parse(File.read(path))["format_version"]
162
200
  end
163
201
 
164
- def apply(mpr)
202
+ def apply(mpr, native_units)
165
203
  root = mpr.root_unit
166
204
  root_id = root.fetch("UnitID")
167
205
  root_doc = mpr.parse_contents(root)
@@ -171,10 +209,6 @@ module Mxrb
171
209
  "IsSystemProject" => false
172
210
  }
173
211
  mpr.update_unit(root_id, root_doc)
174
- native_units = load_native_units(@definition[:native_units_path])
175
- native_units = apply_native_unit_overrides(
176
- native_units, @definition.fetch(:native_unit_overrides, [])
177
- )
178
212
  apply_native_project_units(mpr, root_id, native_units)
179
213
  apply_default_project_units(mpr, root_id)
180
214
  ensure_project_documents(mpr, root_id)
@@ -194,9 +228,18 @@ module Mxrb
194
228
  write_module_security(mpr, module_id, mod) if mod.key?(:module_roles)
195
229
  write_domain_model(mpr, module_id, mod)
196
230
  write_documents(mpr, module_id, mod)
231
+ @progress.advance(detail: "module #{mod.fetch(:name)}")
197
232
  end
198
233
  write_project_security(mpr, root_id, @definition[:security]) if @definition[:security]
199
234
  write_project_navigation(mpr, root_id, @definition[:navigation]) if @definition[:navigation]
235
+ @progress.advance(detail: "project security and navigation")
236
+ end
237
+
238
+ def prepared_native_units
239
+ apply_native_unit_overrides(
240
+ load_native_units(@definition[:native_units_path]),
241
+ @definition.fetch(:native_unit_overrides, [])
242
+ )
200
243
  end
201
244
 
202
245
  def load_native_units(path)
@@ -323,8 +366,21 @@ module Mxrb
323
366
  mod.fetch(:native_documents, []).each do |document|
324
367
  doc = document.fetch(:doc)
325
368
  doc = legacy_layout_doc(doc) if doc['$Type'] == 'Forms$Layout' && legacy_layout?
369
+ existing = mpr.unit(document[:unit_id]) if document[:unit_id]
370
+ if existing
371
+ current = mpr.parse_contents(existing)
372
+ preserved = current.merge(doc).merge(
373
+ '$ID' => current['$ID'] || existing.fetch('UnitID'),
374
+ '$Type' => doc['$Type'] || doc[:'$Type'] || document.fetch(:type)
375
+ )
376
+ mpr.update_unit(existing.fetch('UnitID'), preserved)
377
+ next
378
+ end
379
+
380
+ requested_container = document[:container_id]
381
+ target_container = requested_container && mpr.unit(requested_container) ? requested_container : module_id
326
382
  upsert_native_unit(
327
- mpr, module_id,
383
+ mpr, target_container,
328
384
  'containment' => document.fetch(:containment), 'doc' => doc
329
385
  )
330
386
  end
@@ -365,7 +421,10 @@ module Mxrb
365
421
 
366
422
  def apply_native_unit_tree(mpr, target_root_id, units)
367
423
  if units.any? { _1["unit_id"].to_s.empty? || _1["container_id"].to_s.empty? }
368
- units.each { upsert_native_unit(mpr, target_root_id, _1) }
424
+ units.each do |unit|
425
+ upsert_native_unit(mpr, target_root_id, unit)
426
+ @progress.advance(detail: "native unit #{unit['name']}")
427
+ end
369
428
  return
370
429
  end
371
430
 
@@ -382,6 +441,7 @@ module Mxrb
382
441
  target_container = mapped_containers.fetch(unit.fetch("container_id"))
383
442
  actual_id = upsert_native_unit(mpr, target_container, unit)
384
443
  mapped_containers[unit.fetch("unit_id")] = actual_id
444
+ @progress.advance(detail: "native unit #{unit['name']}")
385
445
  end
386
446
  pending = blocked
387
447
  end
@@ -390,26 +450,59 @@ module Mxrb
390
450
  def upsert_native_unit(mpr, container_id, unit)
391
451
  doc = unit.fetch("doc")
392
452
  name = doc["Name"] || doc["name"]
393
- existing = if name.to_s.empty?
394
- mpr.children_of(container_id).find { mpr.parse_contents(_1)["$Type"] == doc["$Type"] }
395
- else
396
- mpr.children_of(container_id).find do |raw|
397
- existing_doc = mpr.parse_contents(raw)
398
- (existing_doc["Name"] || existing_doc["name"]) == name && existing_doc["$Type"] == doc["$Type"]
399
- end
400
- end
453
+ requested_id = unit["unit_id"].to_s
454
+ requested_id = nil if requested_id.empty?
455
+ stable = mpr.unit(requested_id) if requested_id
456
+ existing = stable if stable && mpr.parse_contents(stable)["$Type"] == doc["$Type"]
457
+ candidates = mpr.children_of(container_id)
458
+ existing ||= if name.to_s.empty?
459
+ candidates.find { mpr.parse_contents(_1)["$Type"] == doc["$Type"] }
460
+ else
461
+ candidates.find do |raw|
462
+ existing_doc = mpr.parse_contents(raw)
463
+ (existing_doc["Name"] || existing_doc["name"]) == name &&
464
+ existing_doc["$Type"] == doc["$Type"]
465
+ end
466
+ end
401
467
  if existing
402
468
  current = mpr.parse_contents(existing)
403
469
  preserved = {
404
470
  "$ID" => current["$ID"] || existing.fetch("UnitID"),
405
471
  "$Type" => doc["$Type"]
406
472
  }.merge(current).merge(doc)
473
+ preserved["$ID"] = current["$ID"] || existing.fetch("UnitID") unless existing.equal?(stable)
407
474
  mpr.update_unit(existing.fetch("UnitID"), preserved)
408
475
  existing.fetch("UnitID")
409
476
  else
410
477
  containment = unit.fetch("containment")
411
- mpr.insert_unit(container_uuid: container_id, containment_name: containment, contents_doc: doc)
478
+ inserted_id = stable ? SecureRandom.uuid : requested_id
479
+ inserted_doc = stable ? doc.merge("$ID" => inserted_id) : doc
480
+ mpr.insert_unit(
481
+ container_uuid: container_id,
482
+ containment_name: containment,
483
+ contents_doc: inserted_doc,
484
+ unit_uuid: inserted_id
485
+ )
486
+ end
487
+ end
488
+
489
+ def write_native_compatibility(mpr, native_units)
490
+ mismatches = native_units.filter_map do |unit|
491
+ unit_id = unit["unit_id"].to_s
492
+ doc = unit.fetch("doc")
493
+ content_id = IO::BsonCodec.extract_id(doc["$ID"]).to_s
494
+ next if unit_id.empty? || content_id.empty? || unit_id == content_id
495
+
496
+ raw = mpr.unit(unit_id)
497
+ next unless raw
498
+
499
+ written = mpr.parse_contents(raw)
500
+ written_id = IO::BsonCodec.extract_id(written["$ID"]).to_s
501
+ next unless written_id == content_id && written["$Type"] == doc["$Type"]
502
+
503
+ { unit_id: unit_id, content_id: content_id, type: doc["$Type"] }
412
504
  end
505
+ mpr.write_legacy_unit_identity_mismatches(mismatches)
413
506
  end
414
507
 
415
508
  def write_project_security(mpr, root_id, security)
@@ -858,6 +951,7 @@ module Mxrb
858
951
  doc.delete("__mxrb_preserve_native_body")
859
952
  doc.delete("__mxrb_deep_structure_declared")
860
953
  doc.delete("__mxrb_allow_concurrent_execution_declared")
954
+ doc.delete("__mxrb_apply_entity_access_declared")
861
955
  doc.delete("__mxrb_mark_as_used_declared")
862
956
  doc.delete("__mxrb_excluded_declared")
863
957
  strip_nested_internal_keys(doc)
@@ -894,15 +988,61 @@ module Mxrb
894
988
  def configure_pluggable_widget!(widget, options)
895
989
  properties = custom_widget_properties(widget)
896
990
  options.fetch(:properties, {}).each do |key, configured|
897
- value = properties.dig(key.to_s, "Value")
898
- next unless value
991
+ property = properties[key.to_s]
992
+ configure_custom_widget_value!(property, configured) if property
993
+ end
994
+ end
995
+
996
+ def configure_custom_widget_value!(property, configured)
997
+ value = property.fetch('Value')
998
+ unless configured.is_a?(Hash)
999
+ value['PrimitiveValue'] = configured.to_s unless configured.nil?
1000
+ return value
1001
+ end
1002
+ return value.merge!(stringify_keys(configured)) unless semantic_widget_value?(configured)
1003
+
1004
+ configured = configured.to_h { |key, item| [key.to_sym, item] }
1005
+ value['PrimitiveValue'] = configured[:primitive].to_s if configured.key?(:primitive)
1006
+ value['Expression'] = configured[:expression].to_s if configured.key?(:expression)
1007
+ value['Selection'] = configured[:selection].to_s if configured.key?(:selection)
1008
+ value['TextTemplate'] = client_template_doc(configured[:text]) if configured.key?(:text)
1009
+ value['AttributeRef'] = attribute_ref_doc(configured[:attribute]) if configured.key?(:attribute)
1010
+ value['EntityRef'] = indirect_entity_ref_doc(configured[:association]) if configured.key?(:association)
1011
+ configure_widget_data_source!(value, configured[:data_source]) if configured.key?(:data_source)
1012
+ configure_widget_children!(value, configured[:widgets]) if configured.key?(:widgets)
1013
+ configure_widget_objects!(property, value, configured[:objects]) if configured.key?(:objects)
1014
+ value
1015
+ end
1016
+
1017
+ def semantic_widget_value?(value)
1018
+ value.is_a?(Hash) && (value.keys.map(&:to_sym) & %i[
1019
+ primitive expression selection text attribute association data_source widgets objects
1020
+ ]).any?
1021
+ end
899
1022
 
900
- if configured.is_a?(Hash)
901
- value.merge!(stringify_keys(configured))
902
- elsif !configured.nil?
903
- value["PrimitiveValue"] = configured.to_s
1023
+ def configure_widget_data_source!(value, configured)
1024
+ options = configured.is_a?(Hash) ? configured : { entity: configured }
1025
+ value['DataSource'] = custom_xpath_source_doc(
1026
+ options.fetch(:entity), xpath: options.fetch(:xpath, '')
1027
+ )
1028
+ end
1029
+
1030
+ def configure_widget_children!(value, widgets)
1031
+ children = (widgets.is_a?(Array) ? widgets : [widgets]).compact.map { widget_doc(_1) }
1032
+ value['Widgets'] = IO::BsonCodec.build_array(children, marker: 2)
1033
+ end
1034
+
1035
+ def configure_widget_objects!(property, value, configurations)
1036
+ object_type = property.dig('ValueType', 'ObjectType')
1037
+ objects = Array(configurations).map do |configuration|
1038
+ object = custom_widget_object_doc(object_type)
1039
+ nested = widget_object_properties(object_type, object)
1040
+ configuration.each do |key, configured|
1041
+ configure_custom_widget_value!(nested.fetch(key.to_s), configured)
904
1042
  end
1043
+ object
905
1044
  end
1045
+ value['Objects'] = IO::BsonCodec.build_array(objects, marker: 2)
906
1046
  end
907
1047
 
908
1048
  def stringify_keys(value)
@@ -946,6 +1086,15 @@ module Mxrb
946
1086
  if options[:entity] && properties["datasource"]
947
1087
  properties["datasource"]["Value"]["DataSource"] = custom_xpath_source_doc(options[:entity])
948
1088
  end
1089
+ if options[:selection] && properties['itemSelection']
1090
+ properties.dig('itemSelection', 'Value')['Selection'] = options[:selection].to_s
1091
+ end
1092
+ if properties['filtersPlaceholder'] && options[:filters]
1093
+ configured_filters = options[:filters].is_a?(Array) ? options[:filters] : [options[:filters]]
1094
+ filters = configured_filters.map { widget_doc(_1) }
1095
+ properties.dig('filtersPlaceholder', 'Value')['Widgets'] =
1096
+ IO::BsonCodec.build_array(filters, marker: 2)
1097
+ end
949
1098
  return unless properties["columns"] && options[:columns]
950
1099
 
951
1100
  column_type = properties["columns"].dig("ValueType", "ObjectType")
@@ -1011,16 +1160,20 @@ module Mxrb
1011
1160
  end
1012
1161
 
1013
1162
  def custom_widget_properties(widget)
1014
- types = array_items(widget.dig("Type", "ObjectType", "PropertyTypes")).to_h do |type|
1163
+ widget_object_properties(widget.dig('Type', 'ObjectType'), widget['Object'])
1164
+ end
1165
+
1166
+ def widget_object_properties(object_type, object)
1167
+ types = array_items(object_type&.fetch('PropertyTypes', nil)).to_h do |type|
1015
1168
  [IO::BsonCodec.extract_id(type["$ID"]), type]
1016
1169
  end
1017
- array_items(widget.dig("Object", "Properties")).to_h do |property|
1170
+ array_items(object&.fetch('Properties', nil)).to_h do |property|
1018
1171
  type = types[IO::BsonCodec.extract_id(property["TypePointer"])]
1019
1172
  [type && type["PropertyKey"], property.merge("ValueType" => type && type["ValueType"])]
1020
1173
  end.compact
1021
1174
  end
1022
1175
 
1023
- def custom_xpath_source_doc(entity)
1176
+ def custom_xpath_source_doc(entity, xpath: '')
1024
1177
  {
1025
1178
  "$ID" => SecureRandom.uuid, "$Type" => "CustomWidgets$CustomWidgetXPathSource",
1026
1179
  "EntityRef" => {
@@ -1032,19 +1185,24 @@ module Mxrb
1032
1185
  "$ID" => SecureRandom.uuid, "$Type" => "Forms$GridSortBar",
1033
1186
  "SortItems" => IO::BsonCodec.build_array([], marker: 2)
1034
1187
  },
1035
- "SourceVariable" => nil, "XPathConstraint" => ""
1188
+ "SourceVariable" => nil, "XPathConstraint" => xpath.to_s
1036
1189
  }
1037
1190
  end
1038
1191
 
1039
1192
  def data_grid2_column_doc(object_type, column, entity: nil)
1040
1193
  attribute = column[:attribute]
1041
1194
  attribute = "#{entity}.#{attribute}" if entity && attribute && !attribute.include?('.')
1195
+ filters = column[:filter].is_a?(Array) ? column[:filter] : [column[:filter]].compact
1042
1196
  overrides = {
1043
1197
  "showContentAs" => { "PrimitiveValue" => "attribute" },
1044
1198
  "attribute" => { "AttributeRef" => attribute_ref_doc(attribute) },
1045
1199
  "header" => { "TextTemplate" => client_template_doc(column[:caption] || column[:name]) },
1046
1200
  "tooltip" => { "TextTemplate" => empty_client_template_doc },
1047
- "filter" => { "TextTemplate" => empty_client_template_doc }
1201
+ "filter" => {
1202
+ "Widgets" => IO::BsonCodec.build_array(
1203
+ filters.map { widget_doc(_1) }, marker: 2
1204
+ )
1205
+ }
1048
1206
  }
1049
1207
  custom_widget_object_doc(object_type, overrides)
1050
1208
  end
@@ -1090,6 +1248,7 @@ module Mxrb
1090
1248
  def preserve_flow_metadata(merged, existing, generated)
1091
1249
  {
1092
1250
  "AllowConcurrentExecution" => "__mxrb_allow_concurrent_execution_declared",
1251
+ "ApplyEntityAccess" => "__mxrb_apply_entity_access_declared",
1093
1252
  "MarkAsUsed" => "__mxrb_mark_as_used_declared",
1094
1253
  "Excluded" => "__mxrb_excluded_declared"
1095
1254
  }.each do |field, declaration|
@@ -1360,7 +1519,7 @@ module Mxrb
1360
1519
  previous&.dig(rules_key) || IO::BsonCodec.build_array([])
1361
1520
  end
1362
1521
  doc = (previous || {}).merge(
1363
- "$ID" => id, "$Type" => "DomainModels$EntityImpl",
1522
+ "$ID" => id, "$Type" => previous&.fetch("$Type", nil) || "DomainModels$EntityImpl",
1364
1523
  "Name" => entity.fetch(:name), "Documentation" => entity.fetch(:documentation, ""),
1365
1524
  "GUID" => previous&.dig("GUID") || SecureRandom.uuid,
1366
1525
  "Location" => previous&.dig("Location") || "#{(index % 4) * 220};#{(index / 4) * 160}",
@@ -1368,6 +1527,7 @@ module Mxrb
1368
1527
  "IsRemote" => previous&.fetch("IsRemote", false) || false,
1369
1528
  "RemoteSource" => previous&.fetch("RemoteSource", "") || ""
1370
1529
  )
1530
+ apply_oql_view!(doc, entity.fetch(:oql_view, nil), previous)
1371
1531
  doc[attrs_key] = IO::BsonCodec.build_array(attrs)
1372
1532
  doc[rules_key] = access_rules
1373
1533
  doc[validation_key] = validation_rules_doc(
@@ -1397,8 +1557,27 @@ module Mxrb
1397
1557
  doc
1398
1558
  end
1399
1559
 
1560
+ def apply_oql_view!(doc, view, previous)
1561
+ return unless view
1562
+
1563
+ if view[:source]
1564
+ source_key = native_existing_key(previous, 'source', 'Source') || 'Source'
1565
+ current_source = previous&.dig(source_key)
1566
+ source = (current_source.is_a?(Hash) ? current_source : {}).merge(
1567
+ '$ID' => current_source&.fetch('$ID', nil) || SecureRandom.uuid,
1568
+ '$Type' => 'DomainModels$OqlViewEntitySource',
1569
+ 'SourceDocument' => view.fetch(:source)
1570
+ )
1571
+ doc[source_key] = source
1572
+ end
1573
+ return unless view[:query]
1574
+
1575
+ query_key = native_existing_key(previous, 'oqlQuery', 'OqlQuery', 'OQLQuery') || 'OqlQuery'
1576
+ doc[query_key] = view.fetch(:query)
1577
+ end
1578
+
1400
1579
  def attribute_doc(attr, previous)
1401
- storage_type = Model::Attribute::TYPE_MAP.fetch(attr.fetch(:type))
1580
+ storage_type = Model::Attribute::TYPE_MAP.fetch(attr.fetch(:type).to_sym)
1402
1581
  type_key = native_existing_key(previous, "type", "Type", "newType", "NewType") || "NewType"
1403
1582
  value_key = native_existing_key(previous, "value", "Value") || "Value"
1404
1583
  name_key = native_key(previous, "name", "Name")
@@ -1410,9 +1589,11 @@ module Mxrb
1410
1589
  { "$ID" => SecureRandom.uuid, "$Type" => storage_type }
1411
1590
  end
1412
1591
  type_doc = type_doc.merge("Enumeration" => attr[:enumeration].to_s) if attr[:enumeration]
1413
- if attr[:length]
1592
+ string_type = storage_type == Model::Attribute::TYPE_MAP[:string]
1593
+ if attr[:length] || (string_type && !previous_type&.key?('length') && !previous_type&.key?('Length'))
1414
1594
  length_key = native_key(previous_type, 'length', 'Length')
1415
- type_doc = type_doc.merge(length_key => Integer(attr[:length]))
1595
+ length = attr[:length] || Model::Attribute::DEFAULT_STRING_LENGTH
1596
+ type_doc = type_doc.merge(length_key => Integer(length))
1416
1597
  end
1417
1598
  if attr.key?(:localize_date)
1418
1599
  localize_key = native_key(previous_type, 'localizeDate', 'LocalizeDate')
@@ -2198,6 +2379,48 @@ module Mxrb
2198
2379
  }
2199
2380
  end
2200
2381
 
2382
+ # A project created outside Studio Pro may not have the pluggable-widget
2383
+ # package available yet. These standard-shaped fallback fields preserve
2384
+ # the concise Ruby projection until Studio Pro can hydrate the widget.
2385
+ def configure_fallback_data_grid!(doc, options)
2386
+ entity = options[:entity].to_s
2387
+ columns = Array(options[:columns]).map do |column|
2388
+ attribute = column[:attribute].to_s
2389
+ attribute = "#{entity}.#{attribute}" if !entity.empty? && !attribute.empty? && !attribute.include?('.')
2390
+ {
2391
+ '$ID' => SecureRandom.uuid, '$Type' => 'Forms$DataGridColumn',
2392
+ 'Name' => column[:name].to_s,
2393
+ 'AttributeRef' => attribute_ref_doc(attribute),
2394
+ 'Caption' => text_doc(column[:caption] || column[:name])
2395
+ }
2396
+ end
2397
+ toolbar = Array(options.dig(:toolbar, :buttons)).map do |button|
2398
+ kind = { new: 'New', delete: 'Delete', search: 'Search', export: 'ExportToExcel' }
2399
+ .fetch(button[:type].to_sym)
2400
+ {
2401
+ '$ID' => SecureRandom.uuid, '$Type' => "Forms$Grid#{kind}Button",
2402
+ 'Caption' => text_doc(button[:caption].to_s)
2403
+ }
2404
+ end
2405
+ doc['DataSource'] = {
2406
+ '$ID' => SecureRandom.uuid, '$Type' => 'Forms$DatabaseSource', 'Entity' => entity
2407
+ }
2408
+ doc['Columns'] = IO::BsonCodec.build_array(columns)
2409
+ doc['ToolBar'] = {
2410
+ '$ID' => SecureRandom.uuid, '$Type' => 'Forms$GridControlBar',
2411
+ 'Buttons' => IO::BsonCodec.build_array(toolbar)
2412
+ }
2413
+ doc
2414
+ end
2415
+
2416
+ def configure_fallback_combo_box!(doc, options)
2417
+ doc['AttributePath'] = options[:attribute].to_s if options[:attribute]
2418
+ doc['LabelText'] = text_doc(options[:caption]) if options.key?(:caption)
2419
+ doc['SelectorType'] = options[:__kind].to_sym == :reference_selector ? 'Reference' : 'Enumeration'
2420
+ doc['DisplayAttribute'] = options[:display_attribute].to_s if options[:display_attribute]
2421
+ doc
2422
+ end
2423
+
2201
2424
  def combo_box_descriptor
2202
2425
  {
2203
2426
  id: "com.mendix.widget.web.combobox.Combobox", name: "Combo box",
@@ -2244,6 +2467,15 @@ module Mxrb
2244
2467
  configure_data_grid2!(doc, options) if descriptor.fetch(:id) == data_grid2_descriptor[:id]
2245
2468
  configure_combo_box!(doc, options) if descriptor.fetch(:id) == combo_box_descriptor[:id]
2246
2469
  configure_pluggable_widget!(doc, options) if widget.fetch(:type) == :pluggable_widget
2470
+ if definition.nil? && descriptor.fetch(:id) == data_grid2_descriptor[:id]
2471
+ configure_fallback_data_grid!(doc, options)
2472
+ end
2473
+ if definition.nil? && descriptor.fetch(:id) == combo_box_descriptor[:id]
2474
+ configure_fallback_combo_box!(doc, options)
2475
+ end
2476
+ widget.fetch(:events, []).each do |event|
2477
+ doc[event_property(event.fetch(:event))] = client_action_doc(event)
2478
+ end
2247
2479
  doc
2248
2480
  end
2249
2481
 
@@ -2404,6 +2636,8 @@ module Mxrb
2404
2636
  case event.fetch(:kind).to_sym
2405
2637
  when :action
2406
2638
  native_action_doc(event.fetch(:handler))
2639
+ when :page
2640
+ form_action_doc(event.fetch(:handler))
2407
2641
  when :nanoflow
2408
2642
  {
2409
2643
  "$ID" => SecureRandom.uuid,
@@ -2523,6 +2757,7 @@ module Mxrb
2523
2757
  body_declared = !flow[:body].nil? || !flow[:return_expression].nil?
2524
2758
  return_var_name = flow[:return_variable_name] || "ReturnValue"
2525
2759
  allow_concurrent = flow[:allow_concurrent_execution]
2760
+ apply_entity_access = flow[:apply_entity_access]
2526
2761
  mark_as_used = flow[:mark_as_used]
2527
2762
  excluded = flow[:excluded]
2528
2763
 
@@ -2540,6 +2775,7 @@ module Mxrb
2540
2775
  "Name" => flow_name, "Documentation" => flow.fetch(:documentation, ""),
2541
2776
  "ReturnVariableName" => return_var_name,
2542
2777
  "AllowConcurrentExecution" => allow_concurrent.nil? ? true : allow_concurrent,
2778
+ "ApplyEntityAccess" => apply_entity_access.nil? ? false : apply_entity_access,
2543
2779
  "MarkAsUsed" => mark_as_used.nil? ? false : mark_as_used,
2544
2780
  "Excluded" => excluded.nil? ? false : excluded,
2545
2781
  "AllowedModuleRoles" => IO::BsonCodec.build_array(Array(flow[:allowed_roles]), marker: 1),
@@ -2547,6 +2783,7 @@ module Mxrb
2547
2783
  "__mxrb_body_declared" => body_declared,
2548
2784
  "__mxrb_preserve_native_body" => flow[:preserve_native_body] == true,
2549
2785
  "__mxrb_allow_concurrent_execution_declared" => !allow_concurrent.nil?,
2786
+ "__mxrb_apply_entity_access_declared" => !apply_entity_access.nil?,
2550
2787
  "__mxrb_mark_as_used_declared" => !mark_as_used.nil?,
2551
2788
  "__mxrb_excluded_declared" => !excluded.nil?,
2552
2789
  "MicroflowReturnType" => microflow_data_type_doc(flow[:return_type], module_name),
data/lib/mxrb.rb CHANGED
@@ -2,9 +2,12 @@
2
2
 
3
3
  require_relative "mxrb/version"
4
4
  require_relative "mxrb/errors"
5
+ require_relative "mxrb/http/server"
6
+ require_relative "mxrb/progress"
5
7
  require_relative "mxrb/doctor"
6
8
  require_relative "mxrb/benchmark"
7
9
  require_relative "mxrb/project_lifecycle"
10
+ require_relative "mxrb/environment"
8
11
  require_relative "mxrb/scaffold/templates"
9
12
  require_relative "mxrb/scaffold/page_templates"
10
13
  require_relative "mxrb/initializer"
@@ -15,8 +18,11 @@ require_relative "mxrb/scaffold/recipes"
15
18
  require_relative "mxrb/scaffold/generator"
16
19
  require_relative "mxrb/scaffold/help"
17
20
  require_relative "mxrb/scaffold/cli"
21
+ require_relative "mxrb/cli/help"
22
+ require_relative "mxrb/cli/release"
18
23
  require_relative "mxrb/io/bson_codec"
19
24
  require_relative "mxrb/widget_package"
25
+ require_relative "mxrb/widget_certification"
20
26
  require_relative "mxrb/frontend/migrator"
21
27
  require_relative "mxrb/io/mxunit_codec"
22
28
  require_relative "mxrb/io/mpr_file"
@@ -35,6 +41,13 @@ require_relative "mxrb/model/design_migration"
35
41
  require_relative "mxrb/model/module"
36
42
  require_relative "mxrb/model/connector"
37
43
  require_relative "mxrb/model/project"
44
+ require_relative "mxrb/modeler/catalog"
45
+ require_relative "mxrb/domain_diagram"
46
+ require_relative "mxrb/domain_diagram/lifecycle"
47
+ require_relative "mxrb/uml/class_diagram"
48
+ require_relative "mxrb/uml/activity_diagram"
49
+ require_relative "mxrb/uml/sequence_diagram"
50
+ require_relative "mxrb/uml/server"
38
51
  require_relative "mxrb/protocols"
39
52
  require_relative "mxrb/oql"
40
53
  require_relative "mxrb/oql/plan_analyzer"
@@ -115,6 +128,7 @@ require_relative "mxrb/compiler/image_bundle_compiler"
115
128
  require_relative "mxrb/compiler/generic_widget_bundle_compiler"
116
129
  require_relative "mxrb/compiler/combo_box_bundle_compiler"
117
130
  require_relative "mxrb/compiler/legacy_data_grid_compiler"
131
+ require_relative "mxrb/compiler/legacy_custom_widget_compiler"
118
132
  require_relative "mxrb/compiler/legacy_page_builder"
119
133
  require_relative "mxrb/compiler/page_bundle_compiler"
120
134
  require_relative "mxrb/compiler/compatibility_analyzer"
@@ -125,6 +139,14 @@ require_relative "mxrb/widget_synchronizer"
125
139
  require_relative "mxrb/runtime/docker_workspace"
126
140
  require_relative "mxrb/runtime/executor"
127
141
  require_relative "mxrb/runtime/native"
142
+ require_relative "mxrb/runtime/schema_migrator"
143
+ require_relative "mxrb/runtime/sqlite_store"
144
+ require_relative "mxrb/runtime/access_control"
145
+ require_relative "mxrb/runtime/shared_store"
146
+ require_relative "mxrb/runtime/scheduler"
147
+ require_relative "mxrb/ruby_app"
148
+ require_relative "mxrb/ruby_app/preset"
149
+ require_relative "mxrb/ruby_app/exporter"
128
150
  require_relative "mxrb/runtime/docker_executor"
129
151
  require_relative "mxrb/runtime/clipboard"
130
152
  require_relative "mxrb/runtime/database_workspace"
@@ -139,14 +161,18 @@ module Mxrb
139
161
  #
140
162
  # Mxrb.open("MyApp.mpr") { |p| puts p.modules.map(&:name) }
141
163
  #
142
- def self.open(path, readonly: true, &block)
143
- project = Model::Project.open(path, readonly: readonly)
144
- return project unless block
164
+ def self.open(path, readonly: true, &block) # rubocop:disable Metrics/MethodLength
165
+ return Model::Project.open(path, readonly: readonly) unless block
145
166
 
146
- begin
147
- block.call(project)
148
- ensure
149
- project.close
167
+ Progress.with("Loading #{File.basename(path)}") do |progress|
168
+ progress.update(detail: "opening model")
169
+ project = Model::Project.open(path, readonly: readonly)
170
+ begin
171
+ progress.update(detail: "processing model")
172
+ block.call(project)
173
+ ensure
174
+ project.close
175
+ end
150
176
  end
151
177
  end
152
178
 
@@ -164,9 +190,13 @@ module Mxrb
164
190
  #
165
191
  def self.define(path, &block)
166
192
  output = ENV.fetch("MXRB_OUTPUT_PATH", path)
167
- Dsl::Builder.new(output).tap { _1.instance_eval(&block) }.build!
193
+ builder = Dsl::Builder.new(output).tap { _1.instance_eval(&block) }.build!
194
+ @last_defined_path = builder.path
195
+ builder
168
196
  end
169
197
 
198
+ def self.last_defined_path = @last_defined_path
199
+
170
200
  def self.validate(path)
171
201
  Integrity::Validator.new(path).validate
172
202
  end