mat_views 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/app/assets/images/mat_views/android-chrome-192x192.png +0 -0
  4. data/app/assets/images/mat_views/android-chrome-512x512.png +0 -0
  5. data/app/assets/images/mat_views/apple-touch-icon.png +0 -0
  6. data/app/assets/images/mat_views/favicon-16x16.png +0 -0
  7. data/app/assets/images/mat_views/favicon-32x32.png +0 -0
  8. data/app/assets/images/mat_views/favicon-48x48.png +0 -0
  9. data/app/assets/images/mat_views/favicon.ico +0 -0
  10. data/app/assets/images/mat_views/favicon.svg +18 -0
  11. data/app/assets/images/mat_views/logo.svg +18 -0
  12. data/app/assets/images/mat_views/mask-icon.svg +5 -0
  13. data/app/assets/stylesheets/mat_views/application.css +323 -12
  14. data/app/controllers/mat_views/admin/application_controller.rb +135 -0
  15. data/app/controllers/mat_views/admin/dashboard_controller.rb +32 -0
  16. data/app/controllers/mat_views/admin/mat_view_definitions_controller.rb +248 -0
  17. data/app/controllers/mat_views/admin/preferences_controller.rb +91 -0
  18. data/app/controllers/mat_views/admin/runs_controller.rb +74 -0
  19. data/app/helpers/mat_views/admin/ui_helper.rb +385 -0
  20. data/app/javascript/mat_views/application.js +8 -0
  21. data/app/javascript/mat_views/controllers/application.js +10 -0
  22. data/app/javascript/mat_views/controllers/details_controller.js +122 -0
  23. data/app/javascript/mat_views/controllers/drawer_controller.js +252 -0
  24. data/app/javascript/mat_views/controllers/filter_controller.js +90 -0
  25. data/app/javascript/mat_views/controllers/flash_controller.js +13 -0
  26. data/app/javascript/mat_views/controllers/index.js +10 -0
  27. data/app/javascript/mat_views/controllers/mv_confirm_controller.js +281 -0
  28. data/app/javascript/mat_views/controllers/submitter_controller.js +15 -0
  29. data/app/javascript/mat_views/controllers/tabs_controller.js +67 -0
  30. data/app/javascript/mat_views/controllers/timezone_controller.js +16 -0
  31. data/app/javascript/mat_views/controllers/tooltip_controller.js +328 -0
  32. data/app/javascript/mat_views/controllers/turbo_frame_lifecycle_controller.js +49 -0
  33. data/app/jobs/mat_views/application_job.rb +2 -2
  34. data/app/jobs/mat_views/create_view_job.rb +9 -8
  35. data/app/jobs/mat_views/delete_view_job.rb +8 -8
  36. data/app/jobs/mat_views/refresh_view_job.rb +8 -9
  37. data/app/models/concerns/mat_views_i18n.rb +139 -0
  38. data/app/models/mat_views/application_record.rb +1 -0
  39. data/app/models/mat_views/mat_view_definition.rb +12 -7
  40. data/app/models/mat_views/mat_view_run.rb +11 -13
  41. data/app/views/layouts/mat_views/_footer.html.erb +41 -0
  42. data/app/views/layouts/mat_views/_header.html.erb +25 -0
  43. data/app/views/layouts/mat_views/admin.html.erb +47 -0
  44. data/app/views/layouts/mat_views/turbo_frame.html.erb +3 -0
  45. data/app/views/mat_views/admin/dashboard/index.html.erb +33 -0
  46. data/app/views/mat_views/admin/mat_view_definitions/_definition_actions.html.erb +94 -0
  47. data/app/views/mat_views/admin/mat_view_definitions/_table.html.erb +48 -0
  48. data/app/views/mat_views/admin/mat_view_definitions/empty.html.erb +1 -0
  49. data/app/views/mat_views/admin/mat_view_definitions/form.html.erb +79 -0
  50. data/app/views/mat_views/admin/mat_view_definitions/index.html.erb +10 -0
  51. data/app/views/mat_views/admin/mat_view_definitions/show.html.erb +40 -0
  52. data/app/views/mat_views/admin/preferences/show.html.erb +50 -0
  53. data/app/views/mat_views/admin/runs/_table.html.erb +61 -0
  54. data/app/views/mat_views/admin/runs/index.html.erb +38 -0
  55. data/app/views/mat_views/admin/runs/show.html.erb +64 -0
  56. data/app/views/mat_views/admin/ui/_card.html.erb +15 -0
  57. data/app/views/mat_views/admin/ui/_details.html.erb +10 -0
  58. data/app/views/mat_views/admin/ui/_flash.html.erb +6 -0
  59. data/app/views/mat_views/admin/ui/_table.html.erb +8 -0
  60. data/config/importmap.rb +9 -0
  61. data/config/locales/en-AU-ocker.yml +187 -0
  62. data/config/locales/en-AU.yml +187 -0
  63. data/config/locales/en-BB.yml +187 -0
  64. data/config/locales/en-BD.yml +187 -0
  65. data/config/locales/en-BE.yml +187 -0
  66. data/config/locales/en-BORK.yml +187 -0
  67. data/config/locales/en-BS.yml +187 -0
  68. data/config/locales/en-BZ.yml +187 -0
  69. data/config/locales/en-CA.yml +187 -0
  70. data/config/locales/en-CM.yml +187 -0
  71. data/config/locales/en-CY.yml +187 -0
  72. data/config/locales/en-EG.yml +187 -0
  73. data/config/locales/en-FJ.yml +187 -0
  74. data/config/locales/en-GB.yml +187 -0
  75. data/config/locales/en-GH.yml +187 -0
  76. data/config/locales/en-GI.yml +187 -0
  77. data/config/locales/en-GM.yml +187 -0
  78. data/config/locales/en-GY.yml +187 -0
  79. data/config/locales/en-HK.yml +187 -0
  80. data/config/locales/en-IE.yml +187 -0
  81. data/config/locales/en-IN.yml +187 -0
  82. data/config/locales/en-JM.yml +187 -0
  83. data/config/locales/en-KE.yml +187 -0
  84. data/config/locales/en-LK.yml +187 -0
  85. data/config/locales/en-LOL.yml +187 -0
  86. data/config/locales/en-LR.yml +187 -0
  87. data/config/locales/en-MS.yml +187 -0
  88. data/config/locales/en-MT.yml +187 -0
  89. data/config/locales/en-MW.yml +187 -0
  90. data/config/locales/en-MY.yml +187 -0
  91. data/config/locales/en-NG.yml +187 -0
  92. data/config/locales/en-NP.yml +187 -0
  93. data/config/locales/en-NZ.yml +187 -0
  94. data/config/locales/en-PG.yml +187 -0
  95. data/config/locales/en-PH.yml +187 -0
  96. data/config/locales/en-PK.yml +187 -0
  97. data/config/locales/en-RW.yml +187 -0
  98. data/config/locales/en-SCOT.yml +187 -0
  99. data/config/locales/en-SG.yml +187 -0
  100. data/config/locales/en-SHAKESPEARE.yml +187 -0
  101. data/config/locales/en-SL.yml +187 -0
  102. data/config/locales/en-SS.yml +187 -0
  103. data/config/locales/en-TH.yml +187 -0
  104. data/config/locales/en-TT.yml +187 -0
  105. data/config/locales/en-TZ.yml +187 -0
  106. data/config/locales/en-UG.yml +187 -0
  107. data/config/locales/en-US-pirate.yml +187 -0
  108. data/config/locales/en-US.yml +187 -0
  109. data/config/locales/en-YODA.yml +187 -0
  110. data/config/locales/en-ZA.yml +187 -0
  111. data/config/locales/en-ZW.yml +187 -0
  112. data/config/locales/en.yml +187 -0
  113. data/config/routes.rb +27 -3
  114. data/lib/generators/mat_views/install/templates/create_mat_view_definitions.rb +7 -7
  115. data/lib/generators/mat_views/install/templates/create_mat_view_runs.rb +5 -5
  116. data/lib/mat_views/admin/auth_bridge.rb +93 -0
  117. data/lib/mat_views/admin/default_auth.rb +61 -0
  118. data/lib/mat_views/configuration.rb +9 -0
  119. data/lib/mat_views/engine.rb +50 -2
  120. data/lib/mat_views/helpers/ui_test_ids.rb +43 -0
  121. data/lib/mat_views/services/base_service.rb +46 -38
  122. data/lib/mat_views/services/check_matview_exists.rb +76 -0
  123. data/lib/mat_views/services/concurrent_refresh.rb +9 -6
  124. data/lib/mat_views/services/create_view.rb +15 -15
  125. data/lib/mat_views/services/delete_view.rb +8 -11
  126. data/lib/mat_views/services/regular_refresh.rb +6 -5
  127. data/lib/mat_views/services/swap_refresh.rb +11 -9
  128. data/lib/mat_views/version.rb +1 -1
  129. data/lib/mat_views.rb +10 -4
  130. data/lib/tasks/helpers.rb +13 -13
  131. data/lib/tasks/mat_views_tasks.rake +15 -15
  132. metadata +130 -5
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Copyright Codevedas Inc. 2025-present
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import { Controller } from "@hotwired/stimulus";
8
+
9
+ export default class extends Controller {
10
+ static targets = ["link", "panel"];
11
+
12
+ connect() {
13
+ const qs = new URLSearchParams(window.location.search);
14
+ const name = qs.get("tab") || this.linkTargets[0]?.dataset?.name;
15
+ setTimeout(() => {
16
+ if (name) this.showByName(name, false);
17
+ else if (this.panelTargets[0])
18
+ this._ensureFrameLoaded(this.panelTargets[0]);
19
+ });
20
+ }
21
+
22
+ show(e) {
23
+ e.preventDefault();
24
+ const name = e.currentTarget.dataset.name;
25
+ this.showByName(name, true);
26
+ }
27
+
28
+ showByName(name, pushState) {
29
+ this.linkTargets.forEach((a) => {
30
+ const on = a.dataset.name === name;
31
+ a.classList.toggle("mv-tab--on", on);
32
+ a.setAttribute("aria-selected", on ? "true" : "false");
33
+ });
34
+
35
+ this.panelTargets.forEach((p) => {
36
+ p.hidden = p.dataset.name !== name;
37
+ });
38
+
39
+ const active = this.panelTargets.find((p) => p.dataset.name === name);
40
+ if (active) this._ensureFrameLoaded(active);
41
+
42
+ if (pushState) {
43
+ const url = new URL(window.location);
44
+ url.searchParams.set("tab", name);
45
+
46
+ if (name == "definitions") {
47
+ url.searchParams.delete("mat_view_definition_id");
48
+ url.searchParams.delete("operation");
49
+ url.searchParams.delete("status");
50
+ }
51
+
52
+ history.replaceState({}, "", url);
53
+ }
54
+ }
55
+
56
+ _ensureFrameLoaded(panel) {
57
+ const frame = panel.querySelector("turbo-frame");
58
+
59
+ if (!frame) return;
60
+ if (!frame.getAttribute("src")) {
61
+ const src = frame.dataset.src;
62
+ if (src) frame.setAttribute("src", src);
63
+ }
64
+
65
+ frame.reload();
66
+ }
67
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Copyright Codevedas Inc. 2025-present
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import { Controller } from "@hotwired/stimulus";
8
+
9
+ export default class extends Controller {
10
+ connect() {
11
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
12
+ if (document.cookie.indexOf(`browser_tz=${tz}`) === -1) {
13
+ document.cookie = `browser_tz=${tz}; path=/`;
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Copyright Codevedas Inc. 2025-present
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import { Controller } from "@hotwired/stimulus";
8
+
9
+ /**
10
+ * Usage:
11
+ * <button
12
+ * data-controller="tooltip"
13
+ * data-tooltip-text-value="Save"
14
+ * data-tooltip-placement="bottom"
15
+ * >
16
+ * Save
17
+ * </button>
18
+ */
19
+ export default class extends Controller {
20
+ static values = {
21
+ text: String,
22
+ placement: { type: String, default: "top" },
23
+ delay: { type: Number, default: 120 },
24
+ hideDelay: { type: Number, default: 80 },
25
+ disabled: { type: Boolean, default: false },
26
+ gap: { type: Number, default: 8 },
27
+ margin: { type: Number, default: 8 },
28
+ };
29
+
30
+ // ──────────────────────────────────────────────
31
+ // Lifecycle
32
+ // ──────────────────────────────────────────────
33
+ connect() {
34
+ this.tooltipEl = this._ensureTooltipEl();
35
+ this.contentEl = this.tooltipEl.querySelector(".mv-tooltip__content");
36
+ this.element.classList.add("mv-cursor-pointer");
37
+ this._bindHandlers();
38
+ this._addListeners();
39
+ }
40
+
41
+ disconnect() {
42
+ this._clearAllTimers();
43
+ this._removeListeners();
44
+ this._restoreTitle();
45
+ this._clearDescribedBy();
46
+ }
47
+
48
+ // ──────────────────────────────────────────────
49
+ // Public actions
50
+ // ──────────────────────────────────────────────
51
+ show() {
52
+ if (this.disabledValue) return;
53
+ this._clearTimer("_hideTimer");
54
+ this._schedule("_showTimer", () => this._actuallyShow(), this.delayValue);
55
+ }
56
+
57
+ hide() {
58
+ this._clearTimer("_showTimer");
59
+ this._schedule(
60
+ "_hideTimer",
61
+ () => this._actuallyHide(),
62
+ this.hideDelayValue,
63
+ );
64
+ }
65
+
66
+ toggle() {
67
+ this._isVisible() ? this.hide() : this.show();
68
+ }
69
+
70
+ forceHide() {
71
+ this._clearAllTimers();
72
+ this._actuallyHide(true);
73
+ }
74
+
75
+ // ──────────────────────────────────────────────
76
+ // Internals: show/hide
77
+ // ──────────────────────────────────────────────
78
+ _actuallyShow() {
79
+ const text = this._resolveText();
80
+ if (!text) return;
81
+
82
+ // Remove native title to avoid default browser tooltip
83
+ this._saveAndRemoveTitle();
84
+
85
+ // Fill content
86
+ this.contentEl.textContent = text;
87
+
88
+ // Prep, place, show
89
+ this._prepareForMeasure();
90
+ const rect = this.element.getBoundingClientRect();
91
+ const ttRect = this.tooltipEl.getBoundingClientRect();
92
+ const placement = this._resolvePlacement();
93
+ const { x, y } = this._computePosition(
94
+ rect,
95
+ ttRect,
96
+ placement,
97
+ this.gapValue,
98
+ this.marginValue,
99
+ );
100
+
101
+ this._applyPlacementClass(placement);
102
+ this._setTransform(x, y);
103
+ this._setVisible(true);
104
+ this._setDescribedBy();
105
+ }
106
+
107
+ _actuallyHide(immediate = false) {
108
+ this._setVisible(false);
109
+
110
+ if (immediate) {
111
+ this._offscreen();
112
+ } else {
113
+ // Allow CSS transition to finish before moving offscreen (if not reopened)
114
+ setTimeout(() => {
115
+ if (!this._isVisible()) this._offscreen();
116
+ }, 150);
117
+ }
118
+
119
+ this._restoreTitle();
120
+ this._clearDescribedBy();
121
+ }
122
+
123
+ // ──────────────────────────────────────────────
124
+ // Internals: listeners and handlers
125
+ // ──────────────────────────────────────────────
126
+ _bindHandlers() {
127
+ this._onEnter = this.show.bind(this);
128
+ this._onLeave = this.hide.bind(this);
129
+ this._onFocus = this.show.bind(this);
130
+ this._onBlur = this.hide.bind(this);
131
+ this._onKey = (e) => {
132
+ if (e.key === "Escape") this.forceHide();
133
+ };
134
+ this._onTouch = (e) => {
135
+ e.preventDefault();
136
+ this.show();
137
+ this._clearTimer("_touchTimer");
138
+ this._touchTimer = setTimeout(() => this.hide(), 1500);
139
+ };
140
+ }
141
+
142
+ _addListeners() {
143
+ const el = this.element;
144
+ el.addEventListener("mouseenter", this._onEnter);
145
+ el.addEventListener("mouseleave", this._onLeave);
146
+ el.addEventListener("focus", this._onFocus);
147
+ el.addEventListener("blur", this._onBlur);
148
+ el.addEventListener("keydown", this._onKey);
149
+ // preventDefault() requires passive: false
150
+ el.addEventListener("touchstart", this._onTouch, { passive: false });
151
+ }
152
+
153
+ _removeListeners() {
154
+ const el = this.element;
155
+ if (!el) return;
156
+ el.removeEventListener("mouseenter", this._onEnter);
157
+ el.removeEventListener("mouseleave", this._onLeave);
158
+ el.removeEventListener("focus", this._onFocus);
159
+ el.removeEventListener("blur", this._onBlur);
160
+ el.removeEventListener("keydown", this._onKey);
161
+ el.removeEventListener("touchstart", this._onTouch, { passive: false });
162
+ }
163
+
164
+ // ──────────────────────────────────────────────
165
+ // Internals: DOM helpers
166
+ // ──────────────────────────────────────────────
167
+ _ensureTooltipEl() {
168
+ let el = document.getElementById("mv-tooltip");
169
+ if (el) return el;
170
+
171
+ el = document.createElement("div");
172
+ el.id = "mv-tooltip";
173
+ el.setAttribute("role", "tooltip");
174
+ el.className = "mv-tooltip";
175
+ Object.assign(el.style, {
176
+ position: "fixed",
177
+ top: "0",
178
+ left: "0",
179
+ pointerEvents: "none",
180
+ opacity: "0",
181
+ transform: "translate(-9999px,-9999px)",
182
+ });
183
+ el.innerHTML =
184
+ '<div class="mv-tooltip__content"></div><div class="mv-tooltip__arrow" data-arrow></div>';
185
+ document.body.appendChild(el);
186
+ return el;
187
+ }
188
+
189
+ _prepareForMeasure() {
190
+ this.tooltipEl.style.opacity = "0";
191
+ this._offscreen();
192
+ this._removeAllPlacementClasses();
193
+ }
194
+
195
+ _applyPlacementClass(placement) {
196
+ this._removeAllPlacementClasses();
197
+ this.tooltipEl.classList.add(`mv-tooltip--${placement}`);
198
+ }
199
+
200
+ _removeAllPlacementClasses() {
201
+ this.tooltipEl.classList.remove(
202
+ "mv-tooltip--top",
203
+ "mv-tooltip--right",
204
+ "mv-tooltip--bottom",
205
+ "mv-tooltip--left",
206
+ );
207
+ }
208
+
209
+ _setTransform(x, y) {
210
+ this.tooltipEl.style.transform = `translate(${x}px, ${y}px)`;
211
+ }
212
+
213
+ _offscreen() {
214
+ this.tooltipEl.style.transform = "translate(-9999px,-9999px)";
215
+ }
216
+
217
+ _setVisible(visible) {
218
+ if (visible) {
219
+ this.tooltipEl.style.opacity = "1";
220
+ this.tooltipEl.setAttribute("data-show", "true");
221
+ } else {
222
+ this.tooltipEl.removeAttribute("data-show");
223
+ this.tooltipEl.style.opacity = "0";
224
+ }
225
+ }
226
+
227
+ _setDescribedBy() {
228
+ this.element.setAttribute("aria-describedby", "mv-tooltip");
229
+ }
230
+
231
+ _clearDescribedBy() {
232
+ this.element.removeAttribute("aria-describedby");
233
+ }
234
+
235
+ // ──────────────────────────────────────────────
236
+ // Internals: data/text/title
237
+ // ──────────────────────────────────────────────
238
+ _resolveText() {
239
+ return (
240
+ this.textValue ||
241
+ this.element.getAttribute("aria-label") ||
242
+ this.element.getAttribute("title")
243
+ );
244
+ }
245
+
246
+ _resolvePlacement() {
247
+ return (
248
+ this.element.getAttribute("data-tooltip-placement") || this.placementValue
249
+ );
250
+ }
251
+
252
+ _saveAndRemoveTitle() {
253
+ if (this.element.hasAttribute("title")) {
254
+ this._savedTitle = this.element.getAttribute("title");
255
+ this.element.removeAttribute("title");
256
+ }
257
+ }
258
+
259
+ _restoreTitle() {
260
+ if (this._savedTitle) {
261
+ this.element.setAttribute("title", this._savedTitle);
262
+ this._savedTitle = null;
263
+ }
264
+ }
265
+
266
+ // ──────────────────────────────────────────────
267
+ // Internals: timers & state helpers
268
+ // ──────────────────────────────────────────────
269
+ _schedule(name, fn, ms) {
270
+ this._clearTimer(name);
271
+ this[name] = setTimeout(fn, ms);
272
+ }
273
+
274
+ _clearTimer(name) {
275
+ if (this[name]) {
276
+ clearTimeout(this[name]);
277
+ this[name] = null;
278
+ }
279
+ }
280
+
281
+ _clearAllTimers() {
282
+ this._clearTimer("_showTimer");
283
+ this._clearTimer("_hideTimer");
284
+ this._clearTimer("_touchTimer");
285
+ }
286
+
287
+ _isVisible() {
288
+ return this.tooltipEl?.getAttribute("data-show") === "true";
289
+ }
290
+
291
+ // ──────────────────────────────────────────────
292
+ // Geometry
293
+ // ──────────────────────────────────────────────
294
+ _computePosition(targetRect, ttRect, placement, gap, margin) {
295
+ const coords = this._coordsFor(placement, targetRect, ttRect, gap);
296
+ const x = this._clampX(coords.x, ttRect.width, margin);
297
+ const y = this._clampY(coords.y, ttRect.height, margin);
298
+ return { x: Math.round(x), y: Math.round(y) };
299
+ }
300
+
301
+ _coordsFor(placement, r, tt, gap) {
302
+ switch (placement) {
303
+ case "top":
304
+ return {
305
+ x: r.left + (r.width - tt.width) / 2,
306
+ y: r.top - tt.height - gap,
307
+ };
308
+ case "bottom":
309
+ return { x: r.left + (r.width - tt.width) / 2, y: r.bottom + gap };
310
+ case "left":
311
+ return {
312
+ x: r.left - tt.width - gap,
313
+ y: r.top + (r.height - tt.height) / 2,
314
+ };
315
+ case "right":
316
+ default:
317
+ return { x: r.right + gap, y: r.top + (r.height - tt.height) / 2 };
318
+ }
319
+ }
320
+
321
+ _clampX(x, width, margin) {
322
+ return Math.max(margin, Math.min(x, window.innerWidth - width - margin));
323
+ }
324
+
325
+ _clampY(y, height, margin) {
326
+ return Math.max(margin, Math.min(y, window.innerHeight - height - margin));
327
+ }
328
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Copyright Codevedas Inc. 2025-present
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import { Controller } from "@hotwired/stimulus";
8
+
9
+ export default class extends Controller {
10
+ connect() {
11
+ this.elements = document.querySelectorAll("turbo-frame");
12
+ this.elements.forEach((el) => {
13
+ el.addEventListener("turbo:before-fetch-request", this._addBusyAttribute);
14
+ el.addEventListener("turbo:frame-load", this._removeBusyAttribute);
15
+ el.addEventListener(
16
+ "turbo:fetch-request-error",
17
+ this._removeBusyAttribute,
18
+ );
19
+ });
20
+ }
21
+
22
+ disconnect() {
23
+ this.elements.forEach((el) => {
24
+ el.removeEventListener(
25
+ "turbo:before-fetch-request",
26
+ this._addBusyAttribute,
27
+ );
28
+ el.removeEventListener("turbo:frame-load", this._removeBusyAttribute);
29
+ el.removeEventListener(
30
+ "turbo:fetch-request-error",
31
+ this._removeBusyAttribute,
32
+ );
33
+ });
34
+ }
35
+
36
+ _addBusyAttribute = (event) => {
37
+ document.body.setAttribute("aria-busy", "true");
38
+ document.body.setAttribute("busy", "");
39
+ event.target.setAttribute("aria-busy", "true");
40
+ event.target.setAttribute("busy", "");
41
+ };
42
+
43
+ _removeBusyAttribute = (event) => {
44
+ document.body.removeAttribute("aria-busy");
45
+ document.body.removeAttribute("busy");
46
+ event.target.removeAttribute("aria-busy");
47
+ event.target.removeAttribute("busy");
48
+ };
49
+ }
@@ -8,7 +8,7 @@
8
8
  ##
9
9
  # Top-level namespace for the mat_views engine.
10
10
  #
11
- # All classes, modules, and services for materialized view management
11
+ # All classes, modules, and services for materialised view management
12
12
  # are defined under this namespace.
13
13
  #
14
14
  # @example Accessing a job
@@ -29,7 +29,7 @@ module MatViews
29
29
  #
30
30
  # @example Defining a custom job
31
31
  # class MyCustomJob < MatViews::ApplicationJob
32
- # def perform(definition_id)
32
+ # def perform(mat_view_definition_id)
33
33
  # # custom logic here
34
34
  # end
35
35
  # end
@@ -9,7 +9,7 @@
9
9
  # Top-level namespace for the mat_views engine.
10
10
  module MatViews
11
11
  ##
12
- # ActiveJob that handles *creation* of PostgreSQL materialized views for a
12
+ # ActiveJob that handles *creation* of PostgreSQL materialised views for a
13
13
  # given {MatViews::MatViewDefinition}.
14
14
  #
15
15
  # The job:
@@ -17,7 +17,7 @@ module MatViews
17
17
  # 2. Looks up the target {MatViews::MatViewDefinition}.
18
18
  # 3. Starts a {MatViews::MatViewRun} row to track lifecycle/timing, with `operation: :create`.
19
19
  # 4. Executes {MatViews::Services::CreateView}.
20
- # 5. Finalizes the run with success/failure, duration, and payload meta.
20
+ # 5. Finalizes the run with success/failure, duration, and meta.
21
21
  #
22
22
  # @see MatViews::Services::CreateView
23
23
  # @see MatViews::MatViewDefinition
@@ -40,28 +40,29 @@ module MatViews
40
40
  queue_as { MatViews.configuration.job_queue || :default }
41
41
 
42
42
  ##
43
- # Perform the create job for the given materialized view definition.
43
+ # Perform the create job for the given materialised view definition.
44
44
  #
45
45
  # @api public
46
46
  #
47
- # @param definition_id [Integer, String] ID of {MatViews::MatViewDefinition}.
47
+ # @param mat_view_definition_id [Integer, String] ID of {MatViews::MatViewDefinition}.
48
48
  # @param force_arg [Boolean, Hash, nil] Optional flag or hash (`{ force: true }`)
49
49
  # @param row_count_strategy_arg [:Symbol, String] One of: `:estimated`, `:exact`, `:none` or `nil`.
50
50
  #
51
51
  # @return [Hash] Serialized {MatViews::ServiceResponse#to_h}:
52
52
  # - `:status` [Symbol]
53
- # - `:payload` [Hash]
54
53
  # - `:error` [String, nil]
55
54
  # - `:duration_ms` [Integer]
56
55
  # - `:meta` [Hash]
57
56
  #
58
57
  # @raise [StandardError] Re-raised on unexpected failure after marking the run failed.
59
58
  #
60
- def perform(definition_id, force_arg = nil, row_count_strategy_arg = nil)
61
- definition = MatViews::MatViewDefinition.find(definition_id)
59
+ def perform(mat_view_definition_id, force_arg = nil, row_count_strategy_arg = nil)
60
+ definition = MatViews::MatViewDefinition.find(mat_view_definition_id)
62
61
 
63
62
  record_run(definition, :create) do
64
- MatViews::Services::CreateView.new(definition, force: force?(force_arg), row_count_strategy: normalize_strategy(row_count_strategy_arg)).run
63
+ MatViews::Services::CreateView.new(definition,
64
+ force: force?(force_arg),
65
+ row_count_strategy: normalize_strategy(row_count_strategy_arg)).call
65
66
  end
66
67
  end
67
68
 
@@ -9,7 +9,7 @@
9
9
  # Top-level namespace for the mat_views engine.
10
10
  module MatViews
11
11
  ##
12
- # ActiveJob that handles *deletion* of PostgreSQL materialized views via
12
+ # ActiveJob that handles *deletion* of PostgreSQL materialised views via
13
13
  # {MatViews::Services::DeleteView}.
14
14
  #
15
15
  # This job mirrors {MatViews::CreateViewJob} and {MatViews::RefreshViewJob}:
@@ -39,28 +39,28 @@ module MatViews
39
39
  queue_as { MatViews.configuration.job_queue || :default }
40
40
 
41
41
  ##
42
- # Perform the job for the given materialized view definition.
42
+ # Perform the job for the given materialised view definition.
43
43
  #
44
44
  # @api public
45
45
  #
46
- # @param definition_id [Integer, String] ID of {MatViews::MatViewDefinition}.
46
+ # @param mat_view_definition_id [Integer, String] ID of {MatViews::MatViewDefinition}.
47
47
  # @param cascade_arg [Boolean, String, Integer, Hash, nil] Cascade option.
48
48
  # @param row_count_strategy_arg [:Symbol, String] One of: `:estimated`, `:exact`, `:none` or `nil`.
49
49
  #
50
50
  # @return [Hash] Serialized {MatViews::ServiceResponse#to_h}:
51
51
  # - `:status` [Symbol]
52
- # - `:payload` [Hash]
53
52
  # - `:error` [String, nil]
54
53
  # - `:duration_ms` [Integer]
55
54
  # - `:meta` [Hash]
56
55
  #
57
56
  # @raise [StandardError] Re-raised on unexpected failure after marking the run failed.
58
57
  #
59
- def perform(definition_id, cascade_arg = nil, row_count_strategy_arg = nil)
60
- definition = MatViews::MatViewDefinition.find(definition_id)
58
+ def perform(mat_view_definition_id, cascade_arg = nil, row_count_strategy_arg = nil)
59
+ definition = MatViews::MatViewDefinition.find(mat_view_definition_id)
61
60
  record_run(definition, :drop) do
62
- MatViews::Services::DeleteView.new(definition, cascade: cascade?(cascade_arg),
63
- row_count_strategy: normalize_strategy(row_count_strategy_arg)).run
61
+ MatViews::Services::DeleteView.new(definition,
62
+ cascade: cascade?(cascade_arg),
63
+ row_count_strategy: normalize_strategy(row_count_strategy_arg)).call
64
64
  end
65
65
  end
66
66
 
@@ -23,9 +23,9 @@ module MatViews
23
23
  # - otherwise → {MatViews::Services::RegularRefresh}
24
24
  #
25
25
  # Row count reporting can be controlled via `row_count_strategy`:
26
- # - `:estimated` (default) fast, approximate via reltuples
27
- # - `:exact` accurate `COUNT(*)`
28
- # - `nil` skip counting
26
+ # - `:estimated` (default) - fast, approximate via reltuples
27
+ # - `:exact` - accurate `COUNT(*)`
28
+ # - `nil` - skip counting
29
29
  #
30
30
  # @see MatViews::MatViewDefinition
31
31
  # @see MatViews::MatViewRun
@@ -48,26 +48,25 @@ module MatViews
48
48
  queue_as { MatViews.configuration.job_queue || :default }
49
49
 
50
50
  ##
51
- # Perform the refresh job for the given materialized view definition.
51
+ # Perform the refresh job for the given materialised view definition.
52
52
  #
53
53
  # @api public
54
54
  #
55
- # @param definition_id [Integer, String] ID of {MatViews::MatViewDefinition}.
55
+ # @param mat_view_definition_id [Integer, String] ID of {MatViews::MatViewDefinition}.
56
56
  # @param row_count_strategy_arg [:Symbol, String] One of: `:estimated`, `:exact`, `:none` or `nil`.
57
57
  #
58
58
  # @return [Hash] Serialized {MatViews::ServiceResponse#to_h}:
59
59
  # - `:status` [Symbol]
60
- # - `:payload` [Hash]
61
60
  # - `:error` [String, nil]
62
61
  # - `:duration_ms` [Integer]
63
62
  # - `:meta` [Hash]
64
63
  #
65
64
  # @raise [StandardError] Re-raised on unexpected failure after marking the run failed.
66
65
  #
67
- def perform(definition_id, row_count_strategy_arg = nil)
68
- definition = MatViews::MatViewDefinition.find(definition_id)
66
+ def perform(mat_view_definition_id, row_count_strategy_arg = nil)
67
+ definition = MatViews::MatViewDefinition.find(mat_view_definition_id)
69
68
  record_run(definition, :refresh) do
70
- service(definition).new(definition, row_count_strategy: normalize_strategy(row_count_strategy_arg)).run
69
+ service(definition).new(definition, row_count_strategy: normalize_strategy(row_count_strategy_arg)).call
71
70
  end
72
71
  end
73
72