mxrb 0.1.3 → 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 (181) hide show
  1. checksums.yaml +4 -4
  2. data/bin/mxrb +455 -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/oql-sql.md +41 -0
  17. data/docs/pt-BR/project-structure.md +197 -2
  18. data/docs/pt-BR/runtime-ruby.md +108 -0
  19. data/docs/pt-BR/runtime-schema-migrations.md +40 -0
  20. data/docs/pt-BR/validation-matrix.md +42 -5
  21. data/lib/mxrb/cli/help.rb +426 -0
  22. data/lib/mxrb/cli/release.rb +137 -0
  23. data/lib/mxrb/compiler/compatibility_analyzer.rb +1 -1
  24. data/lib/mxrb/compiler/data_grid_bundle_compiler.rb +8 -1
  25. data/lib/mxrb/compiler/domain_document_compiler.rb +3 -0
  26. data/lib/mxrb/compiler/gallery_bundle_compiler.rb +11 -1
  27. data/lib/mxrb/compiler/generic_widget_bundle_compiler.rb +76 -19
  28. data/lib/mxrb/compiler/legacy_custom_widget_compiler.rb +159 -0
  29. data/lib/mxrb/compiler/legacy_data_grid_compiler.rb +43 -7
  30. data/lib/mxrb/compiler/legacy_page_builder.rb +1248 -23
  31. data/lib/mxrb/compiler/page_bundle_compiler.rb +13 -4
  32. data/lib/mxrb/compiler/web_bundle_builder.rb +5 -1
  33. data/lib/mxrb/compiler/web_shell_materializer.rb +42 -2
  34. data/lib/mxrb/compiler/widget_package_extractor.rb +3 -0
  35. data/lib/mxrb/domain_diagram/lifecycle.rb +272 -0
  36. data/lib/mxrb/domain_diagram.rb +409 -0
  37. data/lib/mxrb/dsl/builder.rb +43 -9
  38. data/lib/mxrb/environment.rb +226 -0
  39. data/lib/mxrb/exporter.rb +89 -15
  40. data/lib/mxrb/http/server.rb +107 -0
  41. data/lib/mxrb/initializer.rb +80 -9
  42. data/lib/mxrb/integrity/validator.rb +14 -0
  43. data/lib/mxrb/io/bson_codec.rb +42 -1
  44. data/lib/mxrb/io/mpr_file.rb +146 -3
  45. data/lib/mxrb/model/attribute.rb +3 -0
  46. data/lib/mxrb/model/entity.rb +28 -2
  47. data/lib/mxrb/model/microflow.rb +3 -1
  48. data/lib/mxrb/model/page.rb +126 -15
  49. data/lib/mxrb/model/project.rb +3 -0
  50. data/lib/mxrb/modeler/catalog.rb +178 -0
  51. data/lib/mxrb/oql/reverse_translator.rb +394 -0
  52. data/lib/mxrb/oql/server.rb +4 -8
  53. data/lib/mxrb/oql.rb +23 -1
  54. data/lib/mxrb/project_lifecycle.rb +5 -2
  55. data/lib/mxrb/ruby_app/exporter.rb +1749 -0
  56. data/lib/mxrb/ruby_app/preset.rb +506 -0
  57. data/lib/mxrb/ruby_app/session_manager.rb +122 -0
  58. data/lib/mxrb/ruby_app.rb +1361 -0
  59. data/lib/mxrb/runtime/access_control.rb +408 -0
  60. data/lib/mxrb/runtime/native.rb +1015 -93
  61. data/lib/mxrb/runtime/scheduler.rb +396 -0
  62. data/lib/mxrb/runtime/schema_migrator.rb +598 -0
  63. data/lib/mxrb/runtime/shared_store.rb +335 -0
  64. data/lib/mxrb/runtime/sqlite_store.rb +729 -0
  65. data/lib/mxrb/scaffold/generator.rb +11 -3
  66. data/lib/mxrb/uml/activity_diagram.rb +118 -0
  67. data/lib/mxrb/uml/class_diagram.rb +118 -0
  68. data/lib/mxrb/uml/sequence_diagram.rb +112 -0
  69. data/lib/mxrb/uml/server.rb +146 -0
  70. data/lib/mxrb/uml/support.rb +35 -0
  71. data/lib/mxrb/version.rb +1 -1
  72. data/lib/mxrb/web_ui/assets/abnfDiagram-N423BO3Z-C4JD3cEC.js +1 -0
  73. data/lib/mxrb/web_ui/assets/arc-3Z53kgFp.js +1 -0
  74. data/lib/mxrb/web_ui/assets/architecture-TIHT7OUA-BStBTLea.js +1 -0
  75. data/lib/mxrb/web_ui/assets/architectureDiagram-T3A2C74G-DBwqnM4J.js +36 -0
  76. data/lib/mxrb/web_ui/assets/array-BifhSqXX.js +1 -0
  77. data/lib/mxrb/web_ui/assets/blockDiagram-VBNYF7ZC-DLtuHt-j.js +132 -0
  78. data/lib/mxrb/web_ui/assets/c4Diagram-5PPSVZJV-ConSk_pe.js +10 -0
  79. data/lib/mxrb/web_ui/assets/channel-BhPvD7Dt.js +1 -0
  80. data/lib/mxrb/web_ui/assets/chunk-2GRJ4B5K-D2HpWsf_.js +1 -0
  81. data/lib/mxrb/web_ui/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
  82. data/lib/mxrb/web_ui/assets/chunk-4I5QYGJK-CiIdNwaF.js +1 -0
  83. data/lib/mxrb/web_ui/assets/chunk-5RXB4S5H-Bu_AaEkE.js +231 -0
  84. data/lib/mxrb/web_ui/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
  85. data/lib/mxrb/web_ui/assets/chunk-6Q2QTUOP-wQkNmt_X.js +88 -0
  86. data/lib/mxrb/web_ui/assets/chunk-7BUUIJ7U-Bb538aSH.js +1 -0
  87. data/lib/mxrb/web_ui/assets/chunk-GF5L2VYU-G8MmiBzU.js +206 -0
  88. data/lib/mxrb/web_ui/assets/chunk-I66GZJ75-CulcrLPh.js +127 -0
  89. data/lib/mxrb/web_ui/assets/chunk-JQJVKLGR-ukjzcdlL.js +156 -0
  90. data/lib/mxrb/web_ui/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
  91. data/lib/mxrb/web_ui/assets/chunk-KBJHAD2P-Wm35lYov.js +1 -0
  92. data/lib/mxrb/web_ui/assets/chunk-KEIR6QF5-BICK3FdT.js +161 -0
  93. data/lib/mxrb/web_ui/assets/chunk-NSK5VX7P-D2nYrCY4.js +2 -0
  94. data/lib/mxrb/web_ui/assets/chunk-QR6OTTB3-BrdIzg4Q.js +62 -0
  95. data/lib/mxrb/web_ui/assets/chunk-RYQCIY6F-CCfZMcW6.js +1 -0
  96. data/lib/mxrb/web_ui/assets/chunk-UBXNYLIW-BPEVYsFK.js +1 -0
  97. data/lib/mxrb/web_ui/assets/chunk-W5SLKNZC-2E6OPhA4.js +1 -0
  98. data/lib/mxrb/web_ui/assets/chunk-WRU74C26-CBIvOyWp.js +70 -0
  99. data/lib/mxrb/web_ui/assets/chunk-XXDRQBXY-pH58XAyl.js +1 -0
  100. data/lib/mxrb/web_ui/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
  101. data/lib/mxrb/web_ui/assets/classDiagram-JCYQIIEL-xiqo4D89.js +1 -0
  102. data/lib/mxrb/web_ui/assets/classDiagram-v2-OCEON4UE-xiqo4D89.js +1 -0
  103. data/lib/mxrb/web_ui/assets/cose-bilkent-JH36ORCC-t_flPpii.js +1 -0
  104. data/lib/mxrb/web_ui/assets/cynefin-VYW2F7L2-BIqktTEv.js +1 -0
  105. data/lib/mxrb/web_ui/assets/cynefinDiagram-MW4NZA55-C2d--lut.js +62 -0
  106. data/lib/mxrb/web_ui/assets/cytoscape.esm-B-NFISlW.js +321 -0
  107. data/lib/mxrb/web_ui/assets/dagre-Buvkdvvj.js +1 -0
  108. data/lib/mxrb/web_ui/assets/dagre-VZM6K2ZE-CSz2_tb5.js +4 -0
  109. data/lib/mxrb/web_ui/assets/defaultLocale-BFoDCU3G.js +1 -0
  110. data/lib/mxrb/web_ui/assets/diagram-7IWD3JNH-DCt7u7V_.js +30 -0
  111. data/lib/mxrb/web_ui/assets/diagram-B4RE2ZJO-DqZBqkMu.js +3 -0
  112. data/lib/mxrb/web_ui/assets/diagram-LBJQPF4R-ClIYbIsI.js +24 -0
  113. data/lib/mxrb/web_ui/assets/diagram-Q27KOJAE-DFxhvthJ.js +24 -0
  114. data/lib/mxrb/web_ui/assets/diagram-UB23O5K3-BSUTXptv.js +41 -0
  115. data/lib/mxrb/web_ui/assets/dist-D2qOEbeJ.js +1 -0
  116. data/lib/mxrb/web_ui/assets/domain-mGOZ1KCG.css +1 -0
  117. data/lib/mxrb/web_ui/assets/domain-qsjXhy4v.js +1 -0
  118. data/lib/mxrb/web_ui/assets/ebnfDiagram-BXEA7PRR-B5reICEi.js +1 -0
  119. data/lib/mxrb/web_ui/assets/erDiagram-JOGREHBK-Co5Mxn2p.js +85 -0
  120. data/lib/mxrb/web_ui/assets/eventmodeling-45OFAUF4-C1ylhSks.js +1 -0
  121. data/lib/mxrb/web_ui/assets/flowDiagram-UKHOOZJN-CHXaJIaS.js +1 -0
  122. data/lib/mxrb/web_ui/assets/ganttDiagram-PKOTCBZU-DPPPaWaz.js +292 -0
  123. data/lib/mxrb/web_ui/assets/gitGraph-TEB2WS4Q-qS77ywA9.js +1 -0
  124. data/lib/mxrb/web_ui/assets/gitGraphDiagram-DS77QQ5N-DpAv6-BT.js +106 -0
  125. data/lib/mxrb/web_ui/assets/graphlib-DS17s2tU.js +1 -0
  126. data/lib/mxrb/web_ui/assets/info-DKCQHKI2-C9VD1Y4e.js +1 -0
  127. data/lib/mxrb/web_ui/assets/infoDiagram-6WML65LV-Bk1kbcCy.js +2 -0
  128. data/lib/mxrb/web_ui/assets/init-C-OQMol4.js +1 -0
  129. data/lib/mxrb/web_ui/assets/ishikawaDiagram-WSZJBQD7-0gcISBFR.js +70 -0
  130. data/lib/mxrb/web_ui/assets/journeyDiagram-NVQOT4AX-Dt0RQnlh.js +139 -0
  131. data/lib/mxrb/web_ui/assets/jsx-runtime-DV0a5kSb.js +9 -0
  132. data/lib/mxrb/web_ui/assets/kanban-definition-27J2QSJJ-DIPQM7w9.js +89 -0
  133. data/lib/mxrb/web_ui/assets/katex-CXMH3UgJ.js +257 -0
  134. data/lib/mxrb/web_ui/assets/line-C0rYD-tL.js +1 -0
  135. data/lib/mxrb/web_ui/assets/linear-BPPxWorj.js +1 -0
  136. data/lib/mxrb/web_ui/assets/map-BaFkSB1l.js +1 -0
  137. data/lib/mxrb/web_ui/assets/mermaid-parser.core-B_UPzTxa.js +7 -0
  138. data/lib/mxrb/web_ui/assets/mindmap-definition-FAOFIHXS-CdJRSMuk.js +96 -0
  139. data/lib/mxrb/web_ui/assets/modeler-BNqHthwk.css +1 -0
  140. data/lib/mxrb/web_ui/assets/modeler-CQmqflbg.js +1 -0
  141. data/lib/mxrb/web_ui/assets/ordinal-BDEzSJ7C.js +1 -0
  142. data/lib/mxrb/web_ui/assets/packet-7NZHBO7P-CwQ4NSBs.js +1 -0
  143. data/lib/mxrb/web_ui/assets/path-fybaL0A-.js +1 -0
  144. data/lib/mxrb/web_ui/assets/pegDiagram-VL7TDLO6-D40S7ppx.js +1 -0
  145. data/lib/mxrb/web_ui/assets/pie-RZYD4A2V-CJroHMKh.js +1 -0
  146. data/lib/mxrb/web_ui/assets/pieDiagram-7S7Q4E2Y-QB54__BV.js +39 -0
  147. data/lib/mxrb/web_ui/assets/quadrantDiagram-CIZ2JOQS-DBhDS6ag.js +7 -0
  148. data/lib/mxrb/web_ui/assets/radar-I7S5WNFK-DdIUwd2d.js +1 -0
  149. data/lib/mxrb/web_ui/assets/railroad-3IZDKUUU-B1Lw6Xyq.js +1 -0
  150. data/lib/mxrb/web_ui/assets/railroad-abnf-AHOZXSZD-CQgQoeak.js +1 -0
  151. data/lib/mxrb/web_ui/assets/railroad-ebnf-EBAXGLYW-D7qEQ2x8.js +1 -0
  152. data/lib/mxrb/web_ui/assets/railroad-peg-LSFZ7HO6-C005Vxbb.js +1 -0
  153. data/lib/mxrb/web_ui/assets/railroadDiagram-AXF67PYL-BBP2qk7M.js +1 -0
  154. data/lib/mxrb/web_ui/assets/requirementDiagram-LRYGKXZP-D2cLsAoI.js +84 -0
  155. data/lib/mxrb/web_ui/assets/rough.esm-Dy-Kn_BL.js +1 -0
  156. data/lib/mxrb/web_ui/assets/sankeyDiagram-W5VNT64P-Bpi3FiP2.js +40 -0
  157. data/lib/mxrb/web_ui/assets/sequenceDiagram-SI44F4Z6-DCRcDlVe.js +162 -0
  158. data/lib/mxrb/web_ui/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +1 -0
  159. data/lib/mxrb/web_ui/assets/src-CvxfUak2.js +1 -0
  160. data/lib/mxrb/web_ui/assets/stateDiagram-OKZ733FA-C2GCEoxl.js +1 -0
  161. data/lib/mxrb/web_ui/assets/stateDiagram-v2-UEYNNEHI-D2gWgedh.js +1 -0
  162. data/lib/mxrb/web_ui/assets/swimlanes-SLNWSIFB-CRI9irEW.js +2 -0
  163. data/lib/mxrb/web_ui/assets/swimlanesDiagram-ULZ7WXOC-C5Wb_-hZ.js +8 -0
  164. data/lib/mxrb/web_ui/assets/theme-_mxc7BdY.css +1 -0
  165. data/lib/mxrb/web_ui/assets/timeline-definition-Z64GVDOM-B1-mNg-h.js +120 -0
  166. data/lib/mxrb/web_ui/assets/treeView-QDETBFTQ-7CTvIwKq.js +1 -0
  167. data/lib/mxrb/web_ui/assets/treemap-6X3UGDF4-CtL22pq5.js +1 -0
  168. data/lib/mxrb/web_ui/assets/uml-C3KAFGTq.js +51 -0
  169. data/lib/mxrb/web_ui/assets/uml-Dn-qd01X.css +1 -0
  170. data/lib/mxrb/web_ui/assets/vennDiagram-T6HMQDX7-BzVnUEAy.js +34 -0
  171. data/lib/mxrb/web_ui/assets/wardley-OPB4EBWU-JNi-QNag.js +1 -0
  172. data/lib/mxrb/web_ui/assets/wardleyDiagram-T6FBY63Y-BUwmAQ-B.js +78 -0
  173. data/lib/mxrb/web_ui/assets/xychartDiagram-ELKLHX3M-DEQS8sKp.js +7 -0
  174. data/lib/mxrb/web_ui/domain.html +15 -0
  175. data/lib/mxrb/web_ui/modeler.html +16 -0
  176. data/lib/mxrb/web_ui/uml.html +31 -0
  177. data/lib/mxrb/web_ui.rb +89 -0
  178. data/lib/mxrb/widget_certification.rb +208 -0
  179. data/lib/mxrb/writer.rb +200 -24
  180. data/lib/mxrb.rb +26 -1
  181. metadata +156 -5
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+ require 'json'
5
+ require 'tmpdir'
6
+ require 'zip'
7
+
8
+ module Mxrb
9
+ # Fail-closed evidence gate for core and pluggable widgets used by an MPR.
10
+ # rubocop:disable Metrics
11
+ class WidgetCertification
12
+ CORE_WIDGET_TYPES = %w[
13
+ Forms$DivContainer Forms$LayoutGrid Forms$LayoutGridRow Forms$LayoutGridColumn Forms$Table
14
+ Forms$DynamicText Forms$Title Forms$ActionButton Forms$DataView Forms$ListView Forms$TextBox
15
+ Forms$TextArea Forms$DatePicker Forms$CheckBox Forms$RadioButtonGroup Forms$FileManager
16
+ Forms$GroupBox Forms$SnippetCallWidget Forms$Label Forms$TabControl Forms$StaticImageViewer
17
+ Forms$ScrollContainer Forms$Placeholder Forms$SidebarToggleButton Forms$Header
18
+ Forms$NavigationTree Forms$MenuBar Forms$SimpleMenuBar
19
+ ].freeze
20
+ FAILURE_MARKERS = [
21
+ 'Could not render widget',
22
+ 'An error occurred, please contact your system administrator.',
23
+ 'Executing runtime operation failed for security reasons',
24
+ 'Out of context', 'out of context', 'must be placed inside',
25
+ 'should be placed within', 'failed to load'
26
+ ].freeze
27
+
28
+ def initialize(path, browser_report: nil, build: true, mendix_home: nil)
29
+ @path = File.expand_path(path)
30
+ @browser_report = browser_report && File.expand_path(browser_report)
31
+ @build = build
32
+ @mendix_home = mendix_home
33
+ end
34
+
35
+ def run
36
+ started = monotonic
37
+ source = Compiler::SourceModel.read(@path)
38
+ inventory = widget_inventory(source)
39
+ compilation = compile_pages(source)
40
+ package_inventory = packages(inventory.fetch(:custom).map { _1.fetch(:id) })
41
+ native_build = @build ? build_web(source.version) : { status: 'not_run' }
42
+ browser = browser_evidence(inventory)
43
+ failures = failures(compilation, package_inventory, native_build, browser)
44
+ {
45
+ path: @path, mendix_version: source.version,
46
+ status: failures.empty? ? 'pass' : 'fail', failures:,
47
+ inventory:, packages: package_inventory, compilation:, native_build:, browser:,
48
+ elapsed_seconds: (monotonic - started).round(3)
49
+ }
50
+ rescue StandardError => e
51
+ {
52
+ path: @path, status: 'fail', failures: [e.message],
53
+ error: { class: e.class.name, message: e.message },
54
+ elapsed_seconds: (monotonic - started).round(3)
55
+ }
56
+ end
57
+
58
+ private
59
+
60
+ def widget_inventory(source)
61
+ found = web_units(source).flat_map do |unit|
62
+ nested_widgets(unit.document).map do |widget|
63
+ custom = widget['$Type'] == 'CustomWidgets$CustomWidget'
64
+ {
65
+ page: "#{unit.module_name}.#{unit.document['Name']}", name: widget['Name'].to_s,
66
+ type: widget['$Type'].to_s, id: (custom ? custom_widget_id(source, widget) : nil)
67
+ }
68
+ end
69
+ end
70
+ core = found.reject { _1[:id] }.group_by { _1.fetch(:type) }.map do |type, entries|
71
+ { type:, occurrences: entries.length, pages: entries.map { _1.fetch(:page) }.uniq.sort }
72
+ end
73
+ core.sort_by! { _1.fetch(:type) }
74
+ custom = found.select { _1[:id] }.group_by { _1.fetch(:id) }.map do |id, entries|
75
+ { id:, occurrences: entries.length, pages: entries.map { _1.fetch(:page) }.uniq.sort }
76
+ end
77
+ custom.sort_by! { _1.fetch(:id) }
78
+ { total: found.length, core:, custom: }
79
+ end
80
+
81
+ def nested_widgets(value, found = [])
82
+ case value
83
+ when Hash
84
+ type = value['$Type'].to_s
85
+ found << value if CORE_WIDGET_TYPES.include?(type) || type == 'CustomWidgets$CustomWidget'
86
+ value.each_value { nested_widgets(_1, found) }
87
+ when Array then value.each { nested_widgets(_1, found) }
88
+ end
89
+ found
90
+ end
91
+
92
+ def custom_widget_id(source, widget)
93
+ direct = widget.dig('Type', 'WidgetId').to_s
94
+ return direct unless direct.empty?
95
+
96
+ pointer = IO::BsonCodec.extract_id(widget.dig('Object', 'TypePointer'))
97
+ source.document_index.values.find do |document|
98
+ IO::BsonCodec.extract_id(document.dig('ObjectType', '$ID')) == pointer
99
+ end&.fetch('WidgetId', '').to_s
100
+ end
101
+
102
+ def compile_pages(source)
103
+ pages = web_units(source).map do |unit|
104
+ bundle = if unit.document['$Type'] == 'Forms$Layout'
105
+ Compiler::PageBundleCompiler.new(source).compile_layout(unit)
106
+ else
107
+ Compiler::PageBundleCompiler.new(source).compile(unit)
108
+ end
109
+ {
110
+ name: bundle.qualified_name, bytes: bundle.source.bytesize,
111
+ unsupported: bundle.unsupported_widgets,
112
+ unsupported_custom: bundle.unsupported_custom_widgets
113
+ }
114
+ end
115
+ { status: pages.all? { _1[:unsupported].empty? && _1[:unsupported_custom].empty? } ? 'pass' : 'fail', pages: }
116
+ end
117
+
118
+ def packages(widget_ids)
119
+ paths = Dir.glob(File.join(File.dirname(@path), 'widgets', '*.mpk')).sort
120
+ entries = paths.map do |path|
121
+ modules = Zip::File.open(path) { _1.map(&:name).grep(/\.mjs\z/) }
122
+ ids = widget_ids.select { modules.include?("#{_1.tr('.', '/')}.mjs") }
123
+ next if ids.empty?
124
+
125
+ { file: File.basename(path), sha256: Digest::SHA256.file(path).hexdigest, widget_ids: ids.sort }
126
+ rescue Zip::Error => e
127
+ { file: File.basename(path), error: e.message, widget_ids: [] }
128
+ end.compact
129
+ covered = entries.flat_map { _1.fetch(:widget_ids) }.uniq
130
+ {
131
+ status: (widget_ids - covered).empty? ? 'pass' : 'fail', entries:,
132
+ missing_widget_ids: (widget_ids - covered).sort
133
+ }
134
+ end
135
+
136
+ def build_web(version)
137
+ root = @mendix_home || File.join(Dir.home, '.local', 'share', 'mendix', version)
138
+ started = monotonic
139
+ Dir.mktmpdir('mxrb-widget-certification-') do |directory|
140
+ deployment = File.join(directory, 'deployment')
141
+ stages = Compiler::DeploymentMaterializer.new(
142
+ @path, deployment:, mendix_home: root
143
+ ).materialize.stages.length
144
+ web = Compiler::WebBundleBuilder.new(
145
+ @path, deployment:, mendix_home: root
146
+ ).build
147
+ return {
148
+ status: 'pass', stages:, files: web.files, bytes: web.bytes,
149
+ elapsed_ms: ((monotonic - started) * 1000).round(1)
150
+ }
151
+ end
152
+ end
153
+
154
+ def browser_evidence(inventory)
155
+ required_ids = inventory.fetch(:custom).map { _1.fetch(:id) }
156
+ required_types = inventory.fetch(:core).map { _1.fetch(:type) }
157
+ return { status: 'missing', missing_widget_ids: required_ids, missing_widget_types: required_types } unless
158
+ @browser_report && File.file?(@browser_report)
159
+
160
+ report = JSON.parse(File.read(@browser_report))
161
+ declared = Array(report.dig('certification', 'widget_ids')).map(&:to_s).uniq
162
+ declared_types = Array(report.dig('certification', 'widget_types')).map(&:to_s).uniq
163
+ visible_failures = browser_failure_markers(report)
164
+ missing = required_ids - declared
165
+ missing_types = required_types - declared_types
166
+ status = if report['passed'] == true && visible_failures.empty? && missing.empty? && missing_types.empty?
167
+ 'pass'
168
+ else
169
+ 'fail'
170
+ end
171
+ {
172
+ status:, passed: report['passed'] == true, scenario: report['scenario'],
173
+ widget_ids: declared.sort, missing_widget_ids: missing.sort,
174
+ widget_types: declared_types.sort, missing_widget_types: missing_types.sort,
175
+ visible_failures:, console_errors: Array(report['console_errors']),
176
+ report: @browser_report
177
+ }
178
+ rescue JSON::ParserError => e
179
+ { status: 'fail', error: "invalid browser report: #{e.message}" }
180
+ end
181
+
182
+ def browser_failure_markers(report)
183
+ elements = Array(report['pages']).flat_map { Array(_1.dig('snapshot', 'elements')) } +
184
+ Array(report.dig('failure_snapshot', 'elements'))
185
+ elements.filter_map do |element|
186
+ text = element['text'].to_s
187
+ text if FAILURE_MARKERS.any? { text.include?(_1) }
188
+ end.uniq
189
+ end
190
+
191
+ def failures(compilation, package_inventory, native_build, browser)
192
+ [].tap do |result|
193
+ result << 'page compiler reported unsupported widgets' unless compilation[:status] == 'pass'
194
+ result << 'one or more pluggable widget modules are missing' unless package_inventory[:status] == 'pass'
195
+ result << 'native web bundle did not pass' unless native_build[:status] == 'pass'
196
+ result << 'passing browser evidence does not cover every widget contract' unless browser[:status] == 'pass'
197
+ end
198
+ end
199
+
200
+ def web_units(source)
201
+ source.units_of('Forms$Page').select { source.web_page?(_1) } +
202
+ source.units_of('Forms$Layout').select { source.web_layout?(_1) }
203
+ end
204
+
205
+ def monotonic = Process.clock_gettime(Process::CLOCK_MONOTONIC)
206
+ end
207
+ # rubocop:enable Metrics
208
+ end
data/lib/mxrb/writer.rb CHANGED
@@ -47,7 +47,9 @@ module Mxrb
47
47
  mpr = IO::MprFile.open(@path)
48
48
  mpr.transaction do
49
49
  apply(mpr, native_units)
50
+ write_native_compatibility(mpr, native_units)
50
51
  mpr.write_architecture_definition(@definition)
52
+ mpr.write_ruby_app_sources(ruby_app_source_files) if @definition[:ruby_app_sources_path]
51
53
  end
52
54
  progress.advance(detail: "model transaction")
53
55
  materialize_project_assets(asset_manifest)
@@ -62,6 +64,22 @@ module Mxrb
62
64
 
63
65
  private
64
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
+
65
83
  def materialize_design_system
66
84
  design_system = @definition[:design_system]
67
85
  return unless design_system
@@ -352,7 +370,8 @@ module Mxrb
352
370
  if existing
353
371
  current = mpr.parse_contents(existing)
354
372
  preserved = current.merge(doc).merge(
355
- '$ID' => current['$ID'] || existing.fetch('UnitID'), '$Type' => doc.fetch('$Type')
373
+ '$ID' => current['$ID'] || existing.fetch('UnitID'),
374
+ '$Type' => doc['$Type'] || doc[:'$Type'] || document.fetch(:type)
356
375
  )
357
376
  mpr.update_unit(existing.fetch('UnitID'), preserved)
358
377
  next
@@ -431,26 +450,59 @@ module Mxrb
431
450
  def upsert_native_unit(mpr, container_id, unit)
432
451
  doc = unit.fetch("doc")
433
452
  name = doc["Name"] || doc["name"]
434
- existing = if name.to_s.empty?
435
- mpr.children_of(container_id).find { mpr.parse_contents(_1)["$Type"] == doc["$Type"] }
436
- else
437
- mpr.children_of(container_id).find do |raw|
438
- existing_doc = mpr.parse_contents(raw)
439
- (existing_doc["Name"] || existing_doc["name"]) == name && existing_doc["$Type"] == doc["$Type"]
440
- end
441
- 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
442
467
  if existing
443
468
  current = mpr.parse_contents(existing)
444
469
  preserved = {
445
470
  "$ID" => current["$ID"] || existing.fetch("UnitID"),
446
471
  "$Type" => doc["$Type"]
447
472
  }.merge(current).merge(doc)
473
+ preserved["$ID"] = current["$ID"] || existing.fetch("UnitID") unless existing.equal?(stable)
448
474
  mpr.update_unit(existing.fetch("UnitID"), preserved)
449
475
  existing.fetch("UnitID")
450
476
  else
451
477
  containment = unit.fetch("containment")
452
- 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"] }
453
504
  end
505
+ mpr.write_legacy_unit_identity_mismatches(mismatches)
454
506
  end
455
507
 
456
508
  def write_project_security(mpr, root_id, security)
@@ -899,6 +951,7 @@ module Mxrb
899
951
  doc.delete("__mxrb_preserve_native_body")
900
952
  doc.delete("__mxrb_deep_structure_declared")
901
953
  doc.delete("__mxrb_allow_concurrent_execution_declared")
954
+ doc.delete("__mxrb_apply_entity_access_declared")
902
955
  doc.delete("__mxrb_mark_as_used_declared")
903
956
  doc.delete("__mxrb_excluded_declared")
904
957
  strip_nested_internal_keys(doc)
@@ -935,15 +988,61 @@ module Mxrb
935
988
  def configure_pluggable_widget!(widget, options)
936
989
  properties = custom_widget_properties(widget)
937
990
  options.fetch(:properties, {}).each do |key, configured|
938
- value = properties.dig(key.to_s, "Value")
939
- 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
1022
+
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
940
1034
 
941
- if configured.is_a?(Hash)
942
- value.merge!(stringify_keys(configured))
943
- elsif !configured.nil?
944
- value["PrimitiveValue"] = configured.to_s
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)
945
1042
  end
1043
+ object
946
1044
  end
1045
+ value['Objects'] = IO::BsonCodec.build_array(objects, marker: 2)
947
1046
  end
948
1047
 
949
1048
  def stringify_keys(value)
@@ -987,6 +1086,15 @@ module Mxrb
987
1086
  if options[:entity] && properties["datasource"]
988
1087
  properties["datasource"]["Value"]["DataSource"] = custom_xpath_source_doc(options[:entity])
989
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
990
1098
  return unless properties["columns"] && options[:columns]
991
1099
 
992
1100
  column_type = properties["columns"].dig("ValueType", "ObjectType")
@@ -1052,16 +1160,20 @@ module Mxrb
1052
1160
  end
1053
1161
 
1054
1162
  def custom_widget_properties(widget)
1055
- 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|
1056
1168
  [IO::BsonCodec.extract_id(type["$ID"]), type]
1057
1169
  end
1058
- array_items(widget.dig("Object", "Properties")).to_h do |property|
1170
+ array_items(object&.fetch('Properties', nil)).to_h do |property|
1059
1171
  type = types[IO::BsonCodec.extract_id(property["TypePointer"])]
1060
1172
  [type && type["PropertyKey"], property.merge("ValueType" => type && type["ValueType"])]
1061
1173
  end.compact
1062
1174
  end
1063
1175
 
1064
- def custom_xpath_source_doc(entity)
1176
+ def custom_xpath_source_doc(entity, xpath: '')
1065
1177
  {
1066
1178
  "$ID" => SecureRandom.uuid, "$Type" => "CustomWidgets$CustomWidgetXPathSource",
1067
1179
  "EntityRef" => {
@@ -1073,19 +1185,24 @@ module Mxrb
1073
1185
  "$ID" => SecureRandom.uuid, "$Type" => "Forms$GridSortBar",
1074
1186
  "SortItems" => IO::BsonCodec.build_array([], marker: 2)
1075
1187
  },
1076
- "SourceVariable" => nil, "XPathConstraint" => ""
1188
+ "SourceVariable" => nil, "XPathConstraint" => xpath.to_s
1077
1189
  }
1078
1190
  end
1079
1191
 
1080
1192
  def data_grid2_column_doc(object_type, column, entity: nil)
1081
1193
  attribute = column[:attribute]
1082
1194
  attribute = "#{entity}.#{attribute}" if entity && attribute && !attribute.include?('.')
1195
+ filters = column[:filter].is_a?(Array) ? column[:filter] : [column[:filter]].compact
1083
1196
  overrides = {
1084
1197
  "showContentAs" => { "PrimitiveValue" => "attribute" },
1085
1198
  "attribute" => { "AttributeRef" => attribute_ref_doc(attribute) },
1086
1199
  "header" => { "TextTemplate" => client_template_doc(column[:caption] || column[:name]) },
1087
1200
  "tooltip" => { "TextTemplate" => empty_client_template_doc },
1088
- "filter" => { "TextTemplate" => empty_client_template_doc }
1201
+ "filter" => {
1202
+ "Widgets" => IO::BsonCodec.build_array(
1203
+ filters.map { widget_doc(_1) }, marker: 2
1204
+ )
1205
+ }
1089
1206
  }
1090
1207
  custom_widget_object_doc(object_type, overrides)
1091
1208
  end
@@ -1131,6 +1248,7 @@ module Mxrb
1131
1248
  def preserve_flow_metadata(merged, existing, generated)
1132
1249
  {
1133
1250
  "AllowConcurrentExecution" => "__mxrb_allow_concurrent_execution_declared",
1251
+ "ApplyEntityAccess" => "__mxrb_apply_entity_access_declared",
1134
1252
  "MarkAsUsed" => "__mxrb_mark_as_used_declared",
1135
1253
  "Excluded" => "__mxrb_excluded_declared"
1136
1254
  }.each do |field, declaration|
@@ -1459,7 +1577,7 @@ module Mxrb
1459
1577
  end
1460
1578
 
1461
1579
  def attribute_doc(attr, previous)
1462
- storage_type = Model::Attribute::TYPE_MAP.fetch(attr.fetch(:type))
1580
+ storage_type = Model::Attribute::TYPE_MAP.fetch(attr.fetch(:type).to_sym)
1463
1581
  type_key = native_existing_key(previous, "type", "Type", "newType", "NewType") || "NewType"
1464
1582
  value_key = native_existing_key(previous, "value", "Value") || "Value"
1465
1583
  name_key = native_key(previous, "name", "Name")
@@ -1471,9 +1589,11 @@ module Mxrb
1471
1589
  { "$ID" => SecureRandom.uuid, "$Type" => storage_type }
1472
1590
  end
1473
1591
  type_doc = type_doc.merge("Enumeration" => attr[:enumeration].to_s) if attr[:enumeration]
1474
- 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'))
1475
1594
  length_key = native_key(previous_type, 'length', 'Length')
1476
- 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))
1477
1597
  end
1478
1598
  if attr.key?(:localize_date)
1479
1599
  localize_key = native_key(previous_type, 'localizeDate', 'LocalizeDate')
@@ -2259,6 +2379,48 @@ module Mxrb
2259
2379
  }
2260
2380
  end
2261
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
+
2262
2424
  def combo_box_descriptor
2263
2425
  {
2264
2426
  id: "com.mendix.widget.web.combobox.Combobox", name: "Combo box",
@@ -2305,6 +2467,15 @@ module Mxrb
2305
2467
  configure_data_grid2!(doc, options) if descriptor.fetch(:id) == data_grid2_descriptor[:id]
2306
2468
  configure_combo_box!(doc, options) if descriptor.fetch(:id) == combo_box_descriptor[:id]
2307
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
2308
2479
  doc
2309
2480
  end
2310
2481
 
@@ -2465,6 +2636,8 @@ module Mxrb
2465
2636
  case event.fetch(:kind).to_sym
2466
2637
  when :action
2467
2638
  native_action_doc(event.fetch(:handler))
2639
+ when :page
2640
+ form_action_doc(event.fetch(:handler))
2468
2641
  when :nanoflow
2469
2642
  {
2470
2643
  "$ID" => SecureRandom.uuid,
@@ -2584,6 +2757,7 @@ module Mxrb
2584
2757
  body_declared = !flow[:body].nil? || !flow[:return_expression].nil?
2585
2758
  return_var_name = flow[:return_variable_name] || "ReturnValue"
2586
2759
  allow_concurrent = flow[:allow_concurrent_execution]
2760
+ apply_entity_access = flow[:apply_entity_access]
2587
2761
  mark_as_used = flow[:mark_as_used]
2588
2762
  excluded = flow[:excluded]
2589
2763
 
@@ -2601,6 +2775,7 @@ module Mxrb
2601
2775
  "Name" => flow_name, "Documentation" => flow.fetch(:documentation, ""),
2602
2776
  "ReturnVariableName" => return_var_name,
2603
2777
  "AllowConcurrentExecution" => allow_concurrent.nil? ? true : allow_concurrent,
2778
+ "ApplyEntityAccess" => apply_entity_access.nil? ? false : apply_entity_access,
2604
2779
  "MarkAsUsed" => mark_as_used.nil? ? false : mark_as_used,
2605
2780
  "Excluded" => excluded.nil? ? false : excluded,
2606
2781
  "AllowedModuleRoles" => IO::BsonCodec.build_array(Array(flow[:allowed_roles]), marker: 1),
@@ -2608,6 +2783,7 @@ module Mxrb
2608
2783
  "__mxrb_body_declared" => body_declared,
2609
2784
  "__mxrb_preserve_native_body" => flow[:preserve_native_body] == true,
2610
2785
  "__mxrb_allow_concurrent_execution_declared" => !allow_concurrent.nil?,
2786
+ "__mxrb_apply_entity_access_declared" => !apply_entity_access.nil?,
2611
2787
  "__mxrb_mark_as_used_declared" => !mark_as_used.nil?,
2612
2788
  "__mxrb_excluded_declared" => !excluded.nil?,
2613
2789
  "MicroflowReturnType" => microflow_data_type_doc(flow[:return_type], module_name),
data/lib/mxrb.rb CHANGED
@@ -2,10 +2,12 @@
2
2
 
3
3
  require_relative "mxrb/version"
4
4
  require_relative "mxrb/errors"
5
+ require_relative "mxrb/http/server"
5
6
  require_relative "mxrb/progress"
6
7
  require_relative "mxrb/doctor"
7
8
  require_relative "mxrb/benchmark"
8
9
  require_relative "mxrb/project_lifecycle"
10
+ require_relative "mxrb/environment"
9
11
  require_relative "mxrb/scaffold/templates"
10
12
  require_relative "mxrb/scaffold/page_templates"
11
13
  require_relative "mxrb/initializer"
@@ -16,8 +18,11 @@ require_relative "mxrb/scaffold/recipes"
16
18
  require_relative "mxrb/scaffold/generator"
17
19
  require_relative "mxrb/scaffold/help"
18
20
  require_relative "mxrb/scaffold/cli"
21
+ require_relative "mxrb/cli/help"
22
+ require_relative "mxrb/cli/release"
19
23
  require_relative "mxrb/io/bson_codec"
20
24
  require_relative "mxrb/widget_package"
25
+ require_relative "mxrb/widget_certification"
21
26
  require_relative "mxrb/frontend/migrator"
22
27
  require_relative "mxrb/io/mxunit_codec"
23
28
  require_relative "mxrb/io/mpr_file"
@@ -36,6 +41,13 @@ require_relative "mxrb/model/design_migration"
36
41
  require_relative "mxrb/model/module"
37
42
  require_relative "mxrb/model/connector"
38
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"
39
51
  require_relative "mxrb/protocols"
40
52
  require_relative "mxrb/oql"
41
53
  require_relative "mxrb/oql/plan_analyzer"
@@ -116,6 +128,7 @@ require_relative "mxrb/compiler/image_bundle_compiler"
116
128
  require_relative "mxrb/compiler/generic_widget_bundle_compiler"
117
129
  require_relative "mxrb/compiler/combo_box_bundle_compiler"
118
130
  require_relative "mxrb/compiler/legacy_data_grid_compiler"
131
+ require_relative "mxrb/compiler/legacy_custom_widget_compiler"
119
132
  require_relative "mxrb/compiler/legacy_page_builder"
120
133
  require_relative "mxrb/compiler/page_bundle_compiler"
121
134
  require_relative "mxrb/compiler/compatibility_analyzer"
@@ -126,6 +139,14 @@ require_relative "mxrb/widget_synchronizer"
126
139
  require_relative "mxrb/runtime/docker_workspace"
127
140
  require_relative "mxrb/runtime/executor"
128
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"
129
150
  require_relative "mxrb/runtime/docker_executor"
130
151
  require_relative "mxrb/runtime/clipboard"
131
152
  require_relative "mxrb/runtime/database_workspace"
@@ -169,9 +190,13 @@ module Mxrb
169
190
  #
170
191
  def self.define(path, &block)
171
192
  output = ENV.fetch("MXRB_OUTPUT_PATH", path)
172
- 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
173
196
  end
174
197
 
198
+ def self.last_defined_path = @last_defined_path
199
+
175
200
  def self.validate(path)
176
201
  Integrity::Validator.new(path).validate
177
202
  end