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
@@ -0,0 +1,242 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'io/console'
4
+ require 'singleton'
5
+
6
+ module Mxrb
7
+ # Terminal progress rendering shared by every long-running MXRB operation.
8
+ # It is enabled automatically only for an interactive terminal, writes to
9
+ # stderr, and therefore never contaminates command output or JSON on stdout.
10
+ module Progress
11
+ THREAD_KEY = :mxrb_progress_task
12
+ FALSE_VALUES = %w[0 false no off].freeze
13
+
14
+ # No-op object returned when progress rendering is disabled.
15
+ class NullTask
16
+ include Singleton
17
+
18
+ def start = self
19
+ def update(**) = self
20
+ def advance(*, **) = self
21
+ def add_total(*) = self
22
+ def finish(*) = self
23
+ def fail(*) = self
24
+ def enabled? = false
25
+ end
26
+
27
+ # Thread-safe terminal renderer for a single operation.
28
+ class Task # rubocop:disable Metrics/ClassLength
29
+ SPINNER = %w[| / - \\].freeze
30
+ BAR_WIDTH = 28
31
+ REFRESH_INTERVAL = 0.08
32
+
33
+ attr_reader :label, :total, :current
34
+
35
+ def initialize(label, total: nil, io: $stderr)
36
+ @label = label.to_s
37
+ @total = normalize_total(total)
38
+ @io = io
39
+ @current = 0
40
+ @detail = nil
41
+ @started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
42
+ @last_rendered_at = 0.0
43
+ @spinner_index = 0
44
+ @mutex = Mutex.new
45
+ @finished = false
46
+ end
47
+
48
+ def enabled? = true
49
+
50
+ def start
51
+ render(force: true)
52
+ start_spinner unless determinate?
53
+ self
54
+ end
55
+
56
+ def update(current: nil, total: nil, detail: nil, force: false)
57
+ became_determinate = false
58
+ @mutex.synchronize do
59
+ became_determinate = @total.nil? && !total.nil?
60
+ @total = normalize_total(total) unless total.nil?
61
+ @current = [[Integer(current), 0].max, @total || Float::INFINITY].min unless current.nil?
62
+ @detail = detail.to_s unless detail.nil?
63
+ end
64
+ stop_spinner if became_determinate
65
+ render(force:)
66
+ self
67
+ end
68
+
69
+ def advance(amount = 1, detail: nil, force: false)
70
+ @mutex.synchronize do
71
+ @current += amount
72
+ @current = [@current, @total].min if @total
73
+ @detail = detail.to_s unless detail.nil?
74
+ end
75
+ render(force:)
76
+ self
77
+ end
78
+
79
+ def add_total(amount)
80
+ @mutex.synchronize { @total = (@total || 0) + Integer(amount) }
81
+ render(force: true)
82
+ self
83
+ end
84
+
85
+ def finish(detail = nil)
86
+ stop_spinner
87
+ @mutex.synchronize do
88
+ return self if @finished
89
+
90
+ @detail = detail.to_s if detail
91
+ @current = @total if @total
92
+ @finished = true
93
+ end
94
+ render(force: true, final: true)
95
+ self
96
+ end
97
+
98
+ def fail(message = nil)
99
+ stop_spinner
100
+ @mutex.synchronize do
101
+ return self if @finished
102
+
103
+ @detail = message.to_s unless message.to_s.empty?
104
+ @finished = true
105
+ end
106
+ render(force: true, final: true, failed: true)
107
+ self
108
+ end
109
+
110
+ private
111
+
112
+ def normalize_total(value)
113
+ return nil if value.nil?
114
+
115
+ [Integer(value), 1].max
116
+ end
117
+
118
+ def determinate? = !@total.nil?
119
+
120
+ def start_spinner
121
+ return unless dynamic_terminal?
122
+
123
+ @spinner_thread = Thread.new do
124
+ loop do
125
+ sleep REFRESH_INTERVAL
126
+ break if @mutex.synchronize { @finished }
127
+
128
+ render(force: true)
129
+ end
130
+ end
131
+ end
132
+
133
+ def stop_spinner
134
+ thread = @spinner_thread
135
+ return unless thread
136
+
137
+ @mutex.synchronize { @finished = true }
138
+ thread.join
139
+ @spinner_thread = nil
140
+ @mutex.synchronize { @finished = false }
141
+ end
142
+
143
+ def render(force: false, final: false, failed: false) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
144
+ line = nil
145
+ @mutex.synchronize do
146
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
147
+ return if !force && !final && now - @last_rendered_at < REFRESH_INTERVAL
148
+
149
+ @last_rendered_at = now
150
+ line = rendered_line(now, failed:)
151
+ end
152
+ prefix = dynamic_terminal? ? "\r\e[2K" : ''
153
+ suffix = final || !dynamic_terminal? ? "\n" : ''
154
+ @io.write("#{prefix}#{truncate(line)}#{suffix}")
155
+ @io.flush if @io.respond_to?(:flush)
156
+ rescue IOError, Errno::EPIPE
157
+ nil
158
+ end
159
+
160
+ def rendered_line(now, failed:)
161
+ detail = @detail.to_s.empty? ? '' : " - #{@detail}"
162
+ elapsed = format('%.1fs', now - @started_at)
163
+ return "[mxrb] [FAILED] #{@label}#{detail} (#{elapsed})" if failed
164
+
165
+ determinate_line(detail, elapsed) || spinner_line(detail, elapsed)
166
+ end
167
+
168
+ def determinate_line(detail, elapsed)
169
+ return unless determinate?
170
+
171
+ ratio = [@current.fdiv(@total), 1.0].min
172
+ filled = (ratio * BAR_WIDTH).round
173
+ bar = '#' * filled + '-' * (BAR_WIDTH - filled)
174
+ "[mxrb] [#{bar}] #{format('%3d%%', ratio * 100)} #{@label}#{detail} (#{elapsed})"
175
+ end
176
+
177
+ def spinner_line(detail, elapsed)
178
+ frame = SPINNER[@spinner_index % SPINNER.length]
179
+ @spinner_index += 1
180
+ "[mxrb] [#{frame}] #{@label}#{detail} (#{elapsed})"
181
+ end
182
+
183
+ def dynamic_terminal?
184
+ @io.respond_to?(:tty?) && @io.tty?
185
+ end
186
+
187
+ def truncate(line)
188
+ width = @io.respond_to?(:winsize) ? @io.winsize.last : 100
189
+ width = 100 unless width.to_i.positive?
190
+ return line if line.length <= width
191
+
192
+ "#{line[0, width - 3]}..."
193
+ rescue IOError, Errno::ENOTTY
194
+ line
195
+ end
196
+ end # rubocop:enable Metrics/ClassLength
197
+
198
+ class << self
199
+ attr_writer :io
200
+
201
+ def configure(enabled: nil, io: nil)
202
+ @enabled = enabled unless enabled.nil?
203
+ @io = io if io
204
+ end
205
+
206
+ def reset!
207
+ @enabled = nil
208
+ @io = nil
209
+ end
210
+
211
+ def enabled?
212
+ return @enabled unless @enabled.nil?
213
+ return false if FALSE_VALUES.include?(ENV.fetch('MXRB_PROGRESS', '').downcase)
214
+
215
+ output.respond_to?(:tty?) && output.tty?
216
+ end
217
+
218
+ def with(label, total: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
219
+ active = Thread.current[THREAD_KEY]
220
+ return yield(active) if active
221
+ return yield(NullTask.instance) unless enabled?
222
+
223
+ task = Task.new(label, total:, io: output).start
224
+ Thread.current[THREAD_KEY] = task
225
+ result = yield(task)
226
+ task.finish
227
+ result
228
+ rescue StandardError => e
229
+ task&.fail(e.message)
230
+ raise
231
+ ensure
232
+ Thread.current[THREAD_KEY] = nil if defined?(task) && task
233
+ end
234
+
235
+ def current = Thread.current[THREAD_KEY] || NullTask.instance
236
+
237
+ private
238
+
239
+ def output = @io || $stderr
240
+ end
241
+ end
242
+ end
@@ -11,6 +11,7 @@ module Mxrb
11
11
  # Inspects, previews upgrades, and compares generated definitions with an MPR.
12
12
  class ProjectLifecycle
13
13
  VERSION = /\A\d+\.\d+\.\d+\z/
14
+ VERSION_DECLARATION = /(?<prefix>mendix_version(?:\s+|\s*=\s*))["'](?<version>[^"']+)["']/
14
15
 
15
16
  def initialize(root = Dir.pwd)
16
17
  @root = File.expand_path(root)
@@ -36,7 +37,9 @@ module Mxrb
36
37
  raise ArgumentError, 'project.rb has no mendix_version declaration' unless current
37
38
 
38
39
  if apply
39
- updated = source.sub(/mendix_version\s+["'][^"']+["']/, %(mendix_version "#{target}"))
40
+ updated = source.sub(VERSION_DECLARATION) do
41
+ "#{Regexp.last_match(:prefix)}\"#{target}\""
42
+ end
40
43
  transaction = Scaffold::Transaction.new
41
44
  transaction.write(@project_file, updated)
42
45
  transaction.commit
@@ -70,7 +73,7 @@ module Mxrb
70
73
  def declared_version
71
74
  return unless File.file?(@project_file)
72
75
 
73
- File.read(@project_file)[/mendix_version\s+["']([^"']+)["']/, 1]
76
+ File.read(@project_file).match(VERSION_DECLARATION)&.[](:version)
74
77
  end
75
78
  end
76
79
  end