marty 8.5.0 → 9.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintignore +1 -0
  3. data/.eslintrc.js +26 -0
  4. data/.gitignore +3 -0
  5. data/.gitlab-ci.yml +7 -0
  6. data/.prettierignore +14 -0
  7. data/.rubocop_todo.yml +1 -1
  8. data/Dockerfile.dummy +3 -0
  9. data/Makefile +1 -0
  10. data/app/assets/javascripts/marty/cable.js +7 -3
  11. data/app/assets/javascripts/marty/extjs/extensions/datetime_field/component.js +401 -0
  12. data/app/assets/javascripts/marty/extjs/extensions/datetime_field/field.js +140 -0
  13. data/app/assets/javascripts/marty/extjs/extensions/marty.js +845 -781
  14. data/app/assets/stylesheets/marty/codemirror/codemirror.css +215 -77
  15. data/app/assets/stylesheets/marty/codemirror/delorean.css +2 -2
  16. data/app/assets/stylesheets/marty/dark_mode.css +13 -3
  17. data/app/components/marty/auth_app/client/auth_app.js +107 -102
  18. data/app/components/marty/base_rule_view/client/base_rule_view.js +10 -8
  19. data/app/components/marty/data_grid_view/client/data_grid_edit.js +534 -519
  20. data/app/components/marty/form/client/form.js +3 -3
  21. data/app/components/marty/grid/client/grid.js +110 -87
  22. data/app/components/marty/import_view/client/import_view.js +18 -18
  23. data/app/components/marty/live_search_grid_panel/client/live_search_grid_panel.js +14 -13
  24. data/app/components/marty/main_auth_app/client/main_auth_app.js +42 -42
  25. data/app/components/marty/mcfly_grid_panel/client/mcfly_grid_panel.js +27 -18
  26. data/app/components/marty/new_posting_form/client/new_posting_form.js +3 -3
  27. data/app/components/marty/panel/client/panel.js +3 -3
  28. data/app/components/marty/posting_grid/client/posting_grid.js +24 -18
  29. data/app/components/marty/promise_view/client/promise_view.css +12 -12
  30. data/app/components/marty/promise_view/client/promise_view.js +46 -38
  31. data/app/components/marty/report_form/client/report_form.js +30 -28
  32. data/app/components/marty/report_select/client/report_select.js +28 -23
  33. data/app/components/marty/reporting/client/reporting.js +3 -3
  34. data/app/components/marty/script_form/client/script_form.js +29 -23
  35. data/app/components/marty/script_tester/client/script_tester.js +4 -5
  36. data/app/components/marty/scripting/client/scripting.js +40 -36
  37. data/app/components/marty/simple_app/client/simple_app.js +33 -24
  38. data/app/components/marty/simple_app/client/statusbar_ext.js +1 -1
  39. data/app/controllers/marty/rpc_controller.rb +3 -0
  40. data/app/models/marty/promise.rb +10 -2
  41. data/app/services/marty/data_grid/constraint.rb +2 -1
  42. data/app/services/marty/jobs/schedule.rb +2 -2
  43. data/app/services/marty/promises/delorean/create.rb +9 -2
  44. data/app/services/marty/promises/ruby/create.rb +7 -2
  45. data/config/initializers/delayed_job_config.rb +1 -0
  46. data/delorean/blame_report.dl +50 -58
  47. data/delorean/enum_report.dl +2 -3
  48. data/delorean/{marty_fields.dl → fields.dl} +16 -0
  49. data/delorean/styles.dl +216 -0
  50. data/delorean/table_report.dl +4 -4
  51. data/lib/marty/monkey.rb +17 -0
  52. data/lib/marty/promise_job.rb +9 -0
  53. data/lib/marty/promise_ruby_job.rb +8 -0
  54. data/lib/marty/version.rb +1 -1
  55. data/make-lint.mk +19 -0
  56. data/package.json +16 -0
  57. data/prettier.config.js +6 -0
  58. data/spec/controllers/diagnostic/controller_spec.rb +0 -1
  59. data/spec/controllers/rpc_controller_spec.rb +21 -7
  60. data/spec/dummy/delorean/data_report.dl +4 -4
  61. data/spec/dummy/delorean/fields.dl +1 -0
  62. data/spec/features/data_grid_spec.rb +37 -1
  63. data/spec/job_helper.rb +6 -0
  64. data/spec/lib/data_blame_spec.rb +4 -4
  65. data/spec/lib/data_importer_spec.rb +6 -4
  66. data/spec/models/promise_spec.rb +31 -0
  67. data/spec/spec_helper.rb +8 -0
  68. data/spec/support/download_helper.rb +53 -49
  69. data/spec/support/json_helper.rb +11 -0
  70. data/spec/support/shared_connection_db_helpers.rb +1 -0
  71. data/spec/support/suite.rb +20 -14
  72. data/yarn.lock +967 -0
  73. metadata +16 -4
  74. data/spec/dummy/delorean/marty_fields.dl +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6fd0c089c5ad585e2ee595e562468d11ba1bcb93561faf4af44b7efd657eead
4
- data.tar.gz: 706faaa4fd4c52cdf947546fbe5362b6862335ff085c68a8a78b52c257e35380
3
+ metadata.gz: 7fd7c45cf6825476a774ee84fe6b3f9bff603e022747ecc5079cda8a037a869b
4
+ data.tar.gz: 48fdb90df6cb49e720fc4446975f8b55205eaa560a23af60a49b1d02769127cf
5
5
  SHA512:
6
- metadata.gz: 5350b886990d9c4efffa480d3727efed7f357208d966394ae637f7cfae110203046a768379d1b373f931cf8bcb85d982f97ef1e5c7e1240e5ddde3e2f4038324
7
- data.tar.gz: b4ac412e00641a7e5138036f9af8eac4c5c68fb48fd76c05296c195825a6eb3755659adf7287f92c70d0a058c645a7dacec675c2a590376e6ed2e786053cf6e0
6
+ metadata.gz: 81813549a4614fa1191c466a5506ec2d36796e21e6d45dec4eb8bf054e0614a01c1c2bf1a0539e2afdc980c81e22a32bcdce2749d27634bf65c6f491e058978d
7
+ data.tar.gz: be097e58e928782e89761d4980218e417c2df6c5fa9a28233c4449feed3df1a5492a244ce7211cb604471e7188e08d5b8e923921948b7614dcc6a873cab60af6
@@ -0,0 +1 @@
1
+ app/assets/javascripts/marty/codemirror/*
@@ -0,0 +1,26 @@
1
+ module.exports = {
2
+ env: {
3
+ browser: true,
4
+ es6: true,
5
+ },
6
+ extends: ["eslint:recommended", "prettier"],
7
+ globals: {
8
+ RailsApp: "writable",
9
+ ActionCable: "readonly",
10
+ Ext: "readonly",
11
+ CodeMirror: "readonly"
12
+ },
13
+ parserOptions: {
14
+ ecmaVersion: 6,
15
+ },
16
+ plugins: ["prettier"],
17
+ rules: {
18
+ "no-var": ["error"],
19
+ "prefer-const": ["error"],
20
+ "linebreak-style": ["error", "unix"],
21
+ "quotes": [2, "double", { "avoidEscape": true }],
22
+ "no-unused-vars": ["error", { "args": "after-used", "argsIgnorePattern": "^_" }],
23
+ "object-shorthand": ["error", "always"],
24
+ "no-constant-condition": ["error", { "checkLoops": false }]
25
+ }
26
+ };
data/.gitignore CHANGED
@@ -39,3 +39,6 @@ spec/dummy/.sass-cache
39
39
 
40
40
  # Ignore Gemfile.lock
41
41
  Gemfile.lock
42
+
43
+ # JS stuff
44
+ /node_modules
@@ -14,6 +14,7 @@ before_script:
14
14
  interruptible: true
15
15
  # Run only when there is an MR
16
16
  only:
17
+ - master
17
18
  - merge_requests
18
19
 
19
20
  stage: test
@@ -30,6 +31,12 @@ rubocop:
30
31
  script:
31
32
  - bundle exec rubocop
32
33
 
34
+ eslint-prettier:
35
+ extends: .base-test
36
+ script:
37
+ - yarn
38
+ - make lint-js
39
+
33
40
  rspec (controllers):
34
41
  extends: .base-test
35
42
  script:
@@ -0,0 +1,14 @@
1
+ # Files ignored by Prettier.
2
+ .gitignore
3
+ .dockerignore
4
+ .editorconfig
5
+ .prettierignore
6
+ .browserslistrc
7
+ .env.local*
8
+ .ssh-docker/*
9
+ yarn.lock
10
+ Makefile
11
+ Dockerfile
12
+ docker-compose.yml
13
+ docker-entrypoint.sh
14
+ app/assets/javascripts/marty/codemirror/**/*
@@ -1024,5 +1024,5 @@ Style/WordArray:
1024
1024
  # Cop supports --auto-correct.
1025
1025
  # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
1026
1026
  # URISchemes: http, https
1027
- Metrics/LineLength:
1027
+ Layout/LineLength:
1028
1028
  Max: 100
@@ -49,6 +49,9 @@ RUN export VERSION="node_${NODE_VER}" DISTRO="$(lsb_release -s -c)" && \
49
49
  apt-get -o Acquire::Check-Valid-Until=false update && \
50
50
  apt-get install -qq -y --no-install-recommends nodejs
51
51
 
52
+ # Install Yarn
53
+ RUN curl -o- -L https://yarnpkg.com/install.sh | bash
54
+
52
55
  # Install chrome for integration tests
53
56
  RUN curl -L -o google-chrome.deb https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \
54
57
  && dpkg -i google-chrome.deb || true \
data/Makefile CHANGED
@@ -1,2 +1,3 @@
1
1
  include make-app.mk
2
+ include make-lint.mk
2
3
 
@@ -5,8 +5,12 @@
5
5
  this.RailsApp || (this.RailsApp = {});
6
6
 
7
7
  if (window.location.port === "") {
8
- RailsApp.cable = ActionCable.createConsumer(`ws://${window.location.hostname}/cable`);
8
+ RailsApp.cable = ActionCable.createConsumer(
9
+ `ws://${window.location.hostname}/cable`
10
+ );
9
11
  } else {
10
- RailsApp.cable = ActionCable.createConsumer(`ws://${window.location.hostname}:${window.location.port}/cable`);
12
+ RailsApp.cable = ActionCable.createConsumer(
13
+ `ws://${window.location.hostname}:${window.location.port}/cable`
14
+ );
11
15
  }
12
- }).call(this);
16
+ }.call(this));
@@ -0,0 +1,401 @@
1
+ /*
2
+ * File: DateTimePicker.js
3
+ *
4
+ * This file requires use of the Ext JS library, under independent license.
5
+ * This is part of the UX for DateTimeField developed by Guilherme Portela
6
+ */
7
+
8
+ Ext.define("Ext.ux.DateTimePicker", {
9
+ extend: "Ext.picker.Date",
10
+ alias: "widget.datetimepicker",
11
+ requires: [
12
+ "Ext.picker.Date",
13
+ "Ext.slider.Single",
14
+ "Ext.form.field.Time",
15
+ "Ext.form.Label"
16
+ ],
17
+ // <locale>
18
+ /**
19
+ * @cfg {String} todayText
20
+ * The default text that will be displayed in the calendar to pick the curent date.
21
+ */
22
+ todayText: "Current Date",
23
+ // </locale>
24
+ // <locale>
25
+ /**
26
+ * @cfg {String} hourText
27
+ * The default text displayed above the hour slider
28
+ */
29
+ hourText: "Hour",
30
+ // </locale>
31
+ // <locale>
32
+ /**
33
+ * @cfg {String} minuteText
34
+ * The default text displayed above the minute slider
35
+ */
36
+ minuteText: "Minutes",
37
+ // </locale>
38
+
39
+ /**
40
+ * @cfg {Object} hourSliderConfig
41
+ * A config object that will be applied to the hour slider. Any of the config options available for
42
+ * {@link Ext.slider.Single} can be specified here.
43
+ */
44
+
45
+ /**
46
+ * @cfg {Object} minuteSliderConfig
47
+ * A config object that will be applied to the minute slider. Any of the config options available for
48
+ * {@link Ext.slider.Single} can be specified here.
49
+ */
50
+
51
+ /**
52
+ * @cfg {Object} timePickerConfig
53
+ * A config object that will be applied to the time picker. Any of the config options available for
54
+ * {@link Ext.panel.Panel} can be specified here.
55
+ */
56
+
57
+ initEvents() {
58
+ const me = this,
59
+ eDate = Ext.Date,
60
+ day = eDate.DAY;
61
+
62
+ Ext.apply(me.keyNavConfig, {
63
+ up: (e) => {
64
+ if (e.ctrlKey) {
65
+ if (e.shiftKey) {
66
+ me.minuteSlider.setValue(me.minuteSlider.getValue() + 1);
67
+ } else {
68
+ me.showNextYear();
69
+ }
70
+ } else {
71
+ if (e.shiftKey) {
72
+ me.hourSlider.setValue(me.hourSlider.getValue() + 1);
73
+ } else {
74
+ me.update(eDate.add(me.activeDate, day, -7));
75
+ }
76
+ }
77
+ },
78
+
79
+ down: (e) => {
80
+ if (e.ctrlKey) {
81
+ if (e.shiftKey) {
82
+ me.minuteSlider.setValue(me.minuteSlider.getValue() - 1);
83
+ } else {
84
+ me.showPrevYear();
85
+ }
86
+ } else {
87
+ if (e.shiftKey) {
88
+ me.hourSlider.setValue(me.hourSlider.getValue() - 1);
89
+ } else {
90
+ me.update(eDate.add(me.activeDate, day, 7));
91
+ }
92
+ }
93
+ }
94
+ });
95
+ me.callParent();
96
+ },
97
+
98
+ initComponent() {
99
+ const me = this;
100
+
101
+ if (typeof me.value === "string") {
102
+ me.value = Ext.Date.parse(me.value, me.format);
103
+ } else if (!me.value) {
104
+ me.value = new Date();
105
+ }
106
+
107
+ const dtAux = me.value;
108
+
109
+ dtAux.setSeconds(0);
110
+
111
+ me.timeFormat = me.format.indexOf("h") !== -1 ? "h" : "H";
112
+ me.hourSlider = new Ext.slider.Single(
113
+ Ext.Object.merge(
114
+ {
115
+ fieldLabel: me.hourText,
116
+ labelAlign: "top",
117
+ labelSeparator: " ",
118
+ padding: "0 0 10 17",
119
+ focusable: false,
120
+ value: 0,
121
+ minValue: 0,
122
+ maxValue: 23,
123
+ vertical: true,
124
+ tipText: (thumb) => {
125
+ const value = thumb.value;
126
+
127
+ if (me.timeFormat === "H") {
128
+ return value || "0";
129
+ } else {
130
+ return value && value - 12 <= 0 ? value : Math.abs(value - 12);
131
+ }
132
+ }
133
+ },
134
+ me.hourSliderConfig
135
+ )
136
+ );
137
+
138
+ me.minuteSlider = new Ext.slider.Single(
139
+ Ext.Object.merge(
140
+ {
141
+ fieldLabel: me.minuteText,
142
+ labelAlign: "top",
143
+ labelSeparator: " ",
144
+ padding: "0 10 10 0",
145
+ focusable: false,
146
+ value: 0,
147
+ increment: 1,
148
+ minValue: 0,
149
+ maxValue: 59,
150
+ vertical: true
151
+ },
152
+ me.minuteSliderConfig
153
+ )
154
+ );
155
+
156
+ me.timePicker = new Ext.panel.Panel(
157
+ Ext.Object.merge(
158
+ {
159
+ layout: {
160
+ type: "hbox",
161
+ align: "stretch"
162
+ },
163
+ border: false,
164
+ defaults: {
165
+ flex: 1
166
+ },
167
+ width: 130,
168
+ floating: true,
169
+ dockedItems: [
170
+ {
171
+ xtype: "toolbar",
172
+ dock: "top",
173
+ ui: "footer",
174
+ items: [
175
+ "->",
176
+ {
177
+ xtype: "label",
178
+ text: me.timeFormat == "h" ? "12:00 AM" : "00:00"
179
+ },
180
+ "->"
181
+ ]
182
+ }
183
+ ],
184
+ items: [me.hourSlider, me.minuteSlider],
185
+ onMouseDown: (e) => {
186
+ e.preventDefault();
187
+ }
188
+ },
189
+ me.timePickerConfig
190
+ )
191
+ );
192
+
193
+ me.callParent();
194
+ me.ownerCt = me.up("[floating]");
195
+ me.timePicker.ownerCt = me.ownerCt;
196
+ me.registerWithOwnerCt();
197
+ me.timePicker.registerWithOwnerCt();
198
+ me.setValue(new Date(dtAux));
199
+ me.hourSlider.addListener("change", me.changeTimeValue, me);
200
+ me.minuteSlider.addListener("change", me.changeTimeValue, me);
201
+ },
202
+
203
+ handleTabClick(e) {
204
+ this.handleDateClick(e, this.activeCell.firstChild, true);
205
+ },
206
+
207
+ getSelectedDate(date) {
208
+ const me = this,
209
+ t = Ext.Date.clearTime(date, true).getTime(),
210
+ cells = me.cells,
211
+ cls = me.selectedCls,
212
+ cellItems = cells.elements,
213
+ cLen = cellItems.length;
214
+
215
+ let cell, c;
216
+
217
+ cells.removeCls(cls);
218
+
219
+ for (c = 0; c < cLen; c++) {
220
+ cell = cellItems[c].firstChild;
221
+ if (cell.dateValue === t) {
222
+ return cell;
223
+ }
224
+ }
225
+ return null;
226
+ },
227
+
228
+ changeTimeValue(slider) {
229
+ const me = this,
230
+ label = me.timePicker.down("label"),
231
+ minutePrefix = me.minuteSlider.getValue() < 10 ? "0" : "";
232
+
233
+ let hourDisplay = me.hourSlider.getValue(),
234
+ pickerValue,
235
+ timeSufix,
236
+ auxValue;
237
+
238
+ if (me.timeFormat == "h") {
239
+ timeSufix = me.hourSlider.getValue() < 12 ? " AM" : " PM";
240
+ hourDisplay =
241
+ me.hourSlider.getValue() < 13 ? hourDisplay : hourDisplay - 12;
242
+ hourDisplay = hourDisplay || "12";
243
+ }
244
+
245
+ const hourPrefix = hourDisplay < 10 ? "0" : "";
246
+
247
+ label.setText(
248
+ hourPrefix +
249
+ hourDisplay +
250
+ ":" +
251
+ minutePrefix +
252
+ me.minuteSlider.getValue() +
253
+ (timeSufix || "")
254
+ );
255
+
256
+ if (me.pickerField && (pickerValue = me.pickerField.getValue())) {
257
+ auxValue = new Date(
258
+ pickerValue[slider == me.hourSlider ? "setHours" : "setMinutes"](
259
+ slider.getValue()
260
+ )
261
+ );
262
+ me.pickerField.setValue(auxValue);
263
+ me.pickerField.fireEvent("select", me.pickerField, auxValue);
264
+ }
265
+ },
266
+
267
+ afterShow(animateTarget, callback, scope) {
268
+ const me = this;
269
+
270
+ me.callParent([animateTarget, callback, scope]);
271
+ me.timePicker.show();
272
+
273
+ // this is a workaround for the classic theme, where the time
274
+ // panel would have a transparent background with the classic theme.
275
+ const timePickerToolbarEl = me.timePicker.down("toolbar").getEl();
276
+ const backgroundColor = timePickerToolbarEl.getStyle("background-color");
277
+ if (backgroundColor == "transparent") {
278
+ timePickerToolbarEl.setStyle(
279
+ "background-color",
280
+ timePickerToolbarEl.getStyle("border-color")
281
+ );
282
+ }
283
+ },
284
+
285
+ afterSetPosition(x, y) {
286
+ this.callParent([x, y]);
287
+ this.alignTimePicker();
288
+ },
289
+
290
+ alignTimePicker() {
291
+ const me = this,
292
+ el = me.el,
293
+ alignTo = me.getTimePickerSide(),
294
+ xPos = alignTo == "tl" ? -1 * me.timePicker.getWidth() - 5 : 5;
295
+
296
+ me.timePicker.setHeight(el.getHeight());
297
+ me.timePicker.showBy(me, alignTo, [xPos, 0]);
298
+ },
299
+
300
+ onHide() {
301
+ const me = this;
302
+ me.timePicker.hide();
303
+ me.callParent();
304
+ },
305
+
306
+ beforeDestroy() {
307
+ const me = this;
308
+
309
+ if (me.rendered) {
310
+ Ext.destroy(me.timePicker, me.minuteSlider, me.hourSlider);
311
+ }
312
+ me.callParent();
313
+ },
314
+
315
+ getTimePickerSide() {
316
+ const el = this.el,
317
+ body = Ext.getBody(),
318
+ bodyWidth = body.getViewSize().width;
319
+
320
+ return bodyWidth < el.getX() + el.getWidth() + 140 ? "tl" : "tr";
321
+ },
322
+
323
+ setValue(value) {
324
+ value = value || new Date();
325
+
326
+ value.setSeconds(0);
327
+ this.value = new Date(value);
328
+ return this.update(this.value);
329
+ },
330
+
331
+ selectToday() {
332
+ const me = this,
333
+ btn = me.todayBtn,
334
+ handler = me.handler,
335
+ auxDate = new Date();
336
+
337
+ if (btn && !btn.disabled) {
338
+ me.setValue(new Date(auxDate.setSeconds(0)));
339
+ me.fireEvent("select", me, me.value);
340
+ if (handler) {
341
+ handler.call(me.scope || me, me, me.value);
342
+ }
343
+ me.onSelect();
344
+ }
345
+ return me;
346
+ },
347
+
348
+ handleDateClick(e, t, /*private*/ blockStopEvent) {
349
+ const me = this,
350
+ handler = me.handler,
351
+ hourSet = me.timePicker.items.items[0].getValue(),
352
+ minuteSet = me.timePicker.items.items[1].getValue(),
353
+ auxDate = new Date(t.dateValue);
354
+
355
+ if (blockStopEvent !== true) {
356
+ e.stopEvent();
357
+ }
358
+
359
+ if (
360
+ !me.disabled &&
361
+ t.dateValue &&
362
+ !Ext.fly(t.parentNode).hasCls(me.disabledCellCls)
363
+ ) {
364
+ me.doCancelFocus = me.focusOnSelect === false;
365
+ auxDate.setHours(hourSet, minuteSet, 0);
366
+ me.setValue(new Date(auxDate));
367
+ delete me.doCancelFocus;
368
+ me.fireEvent("select", me, me.value);
369
+ if (handler) {
370
+ handler.call(me.scope || me, me, me.value);
371
+ }
372
+ me.onSelect();
373
+ }
374
+ },
375
+
376
+ selectedUpdate(date) {
377
+ const me = this,
378
+ dateOnly = Ext.Date.clearTime(date, true);
379
+
380
+ this.callParent([dateOnly]);
381
+ me.updateSliders();
382
+ },
383
+
384
+ fullUpdate(date) {
385
+ const me = this,
386
+ dateOnly = Ext.Date.clearTime(date, true);
387
+
388
+ this.callParent([dateOnly]);
389
+ me.updateSliders();
390
+ },
391
+
392
+ updateSliders() {
393
+ const me = this,
394
+ currentDate = (me.pickerField && me.pickerField.getValue()) || new Date();
395
+
396
+ if (me.timePicker.rendered) {
397
+ me.hourSlider.setValue(currentDate.getHours());
398
+ me.minuteSlider.setValue(currentDate.getMinutes());
399
+ }
400
+ }
401
+ });