caixanegra 0.1.2 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: df3d38ccee4981c27d6acb8e72bc4c40c00537c6c370cdc17581de1dc179ac3f
4
- data.tar.gz: 4bad76dfbfb9834d19389a28f92a7b3a7a1208c9cdf230a43fc4f9faf6322fb4
3
+ metadata.gz: ece5f658699dfddfd51744c4f78b12b207e9e7108359e9d6d4f39366a494987e
4
+ data.tar.gz: 0e0fca0b8abdcc03cfd9851563105300d4a6033206917df7fe7d3d49a3cd00d7
5
5
  SHA512:
6
- metadata.gz: 464c5ebbc06fa24c1f6f9261dd9e4241dff7bbfc8ccca0f067de5f289e5bda3a4e658475c8b15fa72971574748927016e5e23eed6020a0eda94580e7e85128ed
7
- data.tar.gz: 55fcf0df7d3aac4d6e4458728cab6f3cfe874014903d47b5e4de1976290d8728cccdd28a687a748e43b5338bb6090b94596bb56fb43437ebbf203934c525717a
6
+ metadata.gz: 4db14867bd4861f76e4ffe49be5f8c35094b6f16e045bb6b2023b72067780e06faac4ea6f0afdd483b4f1dec847d6888c28060abc4dbf443aab10505782b64dd
7
+ data.tar.gz: df495aa1270c312c87f52b194a93a4c3022db83203b71a24256321e3af5f33489cd02b9b01f6c645402299195f4890623253b78fc6f081bee5f157d17aaf22b4
data/README.md CHANGED
@@ -16,8 +16,8 @@ or
16
16
  bundler add caixanegra
17
17
  ```
18
18
  # Getting Started
19
- **caixanegra** implements a self-contained designer, which only requires Redis to be able to receive and report back flow descriptors.
20
- To get started, the only thing you need is to point to your Redis instance and point which classes on your codebase should represent units.
19
+ **caixanegra** implements a self-contained flow designer and executor.
20
+ To get started, the only thing you need is to point to your [transient store](https://github.com/sergiorribeiro/caixanegra/wiki/The-Transient-Store) implementation and point out which classes on your codebase should represent units.
21
21
 
22
22
  First, mount the engine. Add the line below to `routes.rb` file:
23
23
 
@@ -29,19 +29,17 @@ Then, let's create a `caixanegra.rb` initializer (or any name you prefer)
29
29
 
30
30
  ```ruby
31
31
  Caixanegra.setup do |config|
32
- config.units = {
33
- awesome_unit: Caixanegra::Units::AU,
34
- another_awesome_unit: Some::Other::Namespace::AAU,
35
- you_can_also: {
36
- scope_units: Caixanegra::Units::ScopedUnit,
37
- },
38
- }
39
- config.redis = Redis.new
32
+ config.units = [
33
+ Caixanegra::Units::AwesomeUnit,
34
+ Some::Other::Namespace::AnotherAwesomeUnit,
35
+ Caixanegra::Units::SuperUnit,
36
+ ]
37
+ config.transient_store = GreatTransientStore.new
40
38
  end
41
39
  ```
42
40
 
43
41
  With the designer configured, you can use `Caixanegra::Manager` to handle the previously stored definition (or an empty one).
44
- This will give you the UID that **caixanegra** designer will understand.
42
+ This will give you the UID that **caixanegra** designer will understand, using transient store.
45
43
 
46
44
  ```ruby
47
45
  my_flow = somewhere.get_flow # get from your own persistence or transport solution
@@ -54,15 +52,13 @@ You can then safely navigate to the designer.
54
52
  link_to "Some flow", "/caixanegra/design/#{@uid}?unit_scope=optional_scope,another_optional_scope", target: :_blank
55
53
  ```
56
54
 
57
- Saved changes will update the flow definition on Redis, and you must then persist them. You can get the flow definition for any specified **caixanegra** handled UID:
55
+ Saved changes will update the flow definition through the transient store, and you must then persist them. You can get the flow definition for any specified **caixanegra** handled UID, given that the transient store is able to correctly sort them out:
58
56
 
59
57
  ```ruby
60
58
  updated_flow = Caixanegra::Manager.get(my_uid)
61
59
  persist_flow(updated_flow) # your own persistence or transport solution. It's a JSON
62
60
  ```
63
61
 
64
- **NOTE:** There's currently no managed way to set callbacks on save actions from the designer. Working on it
65
-
66
62
  Please, refer to [the wiki](https://github.com/sergiorribeiro/caixanegra/wiki) to get to know more about [units](https://github.com/sergiorribeiro/caixanegra/wiki/Anatomy-of-a-unit).
67
63
 
68
64
  # License
@@ -3,6 +3,7 @@ window.Caixanegra.Designer = {
3
3
  #drawValues;
4
4
  title;
5
5
  class;
6
+ color;
6
7
  type;
7
8
  exits;
8
9
  mappings;
@@ -20,6 +21,7 @@ window.Caixanegra.Designer = {
20
21
  this.class = params.class || "unspecified";
21
22
  this.exits = params.exits || [];
22
23
  this.mappings = params.mappings || {};
24
+ this.color = params.color;
23
25
  this.#drawValues = {};
24
26
  this.MARGIN = 10;
25
27
  this.SNAP = 10;
@@ -36,7 +38,9 @@ window.Caixanegra.Designer = {
36
38
 
37
39
  this.size.x = this.UNIT_WIDTH;
38
40
  let hCursor = this.#drawValues.position.y;
39
- this.#drawValues.unitBgColor = Caixanegra.Designer.typeColor(this.type);
41
+ const typeColor = Caixanegra.Designer.typeColor(this);
42
+ this.#drawValues.unitBgColor = typeColor.background;
43
+ this.#drawValues.unitFgColor = typeColor.foreground;
40
44
  this.#drawValues.text = {};
41
45
  this.#drawValues.exitRectangles = [];
42
46
 
@@ -83,25 +87,25 @@ window.Caixanegra.Designer = {
83
87
  }
84
88
 
85
89
  update(context) {
86
- if (context.mouse.down &&
90
+ if (context.mouse.down &&
87
91
  context.mouse.down.button === 1 &&
88
- context.mouse.down.cursorAt &&
92
+ context.mouse.down.cursorAt &&
89
93
  context.mouse.down.cursorAt.object.oid === this.oid) {
90
94
  this.#beingDragged = true;
91
95
  } else {
92
96
  this.#beingDragged = false;
93
97
  }
94
98
 
95
- if (context.mouse.move &&
99
+ if (context.mouse.move &&
96
100
  context.mouse.move.button === 1 &&
97
- context.mouse.down &&
98
- context.mouse.down.cursorAt &&
101
+ context.mouse.down &&
102
+ context.mouse.down.cursorAt &&
99
103
  context.mouse.down.cursorAt.object.oid === this.oid) {
100
104
  const intersection = new Sabertooth.Vector2(
101
105
  context.mouse.down.cursorAt.intersection.x,
102
106
  context.mouse.down.cursorAt.intersection.y
103
107
  );
104
-
108
+
105
109
  if (this.connectingExit === null) {
106
110
  for (let eidx = 0; eidx < this.#drawValues.exits.length; eidx++) {
107
111
  if (this.#drawValues.exits[eidx].rectangle.intersectionPoint(intersection)) {
@@ -180,7 +184,7 @@ window.Caixanegra.Designer = {
180
184
  case "extra":
181
185
  ctx.beginPath();
182
186
  ctx.roundRect(this.#drawValues.position.x, this.#drawValues.position.y, this.size.x, this.size.y, 5);
183
- ctx.fillStyle = this.#drawValues.unitBgColor;
187
+ ctx.fillStyle = `rgb(${this.#drawValues.unitBgColor.r}, ${this.#drawValues.unitBgColor.g}, ${this.#drawValues.unitBgColor.b})`;
184
188
  ctx.fill();
185
189
 
186
190
  ctx.beginPath();
@@ -202,18 +206,20 @@ window.Caixanegra.Designer = {
202
206
  );
203
207
  ctx.globalCompositeOperation = gco;
204
208
 
209
+ const rgbString = `${this.#drawValues.unitFgColor.r}, ${this.#drawValues.unitFgColor.g}, ${this.#drawValues.unitFgColor.b}`;
210
+
205
211
  ctx.textBaseline = "top";
206
212
  ctx.textAlign = "center";
207
- ctx.fillStyle = "#000";
213
+ ctx.fillStyle = `rgb(${rgbString})`;
208
214
  ctx.font = "25px Helvetica";
209
215
  ctx.fillText(this.title, this.#drawValues.center.x, this.#drawValues.text.title.y, this.UNIT_WIDTH - this.MARGIN * 2);
210
216
 
211
- ctx.fillStyle = "rgba(0,0,0,0.5)";
217
+ ctx.fillStyle = `rgba(${rgbString},0.5)`;
212
218
  ctx.font = "18px Helvetica";
213
219
  ctx.fillText(this.class, this.#drawValues.center.x, this.#drawValues.text.class.y, this.UNIT_WIDTH - this.MARGIN * 2);
214
220
 
215
221
  ctx.strokeStyle = "#FFF";
216
- ctx.fillStyle = "rgba(0,0,0,0.8)";
222
+ ctx.fillStyle = `rgba(${rgbString},0.8)`;
217
223
  ctx.font = "bold 14px monospace";
218
224
  ctx.textAlign = "right";
219
225
  ctx.lineWidth = 2;
@@ -269,7 +275,7 @@ window.Caixanegra.Designer = {
269
275
  selectedUnit;
270
276
  flowId;
271
277
  unitScope;
272
-
278
+
273
279
  constructor(drawingSurface) {
274
280
  this.flowId = this.#extractFlowId();
275
281
  this.unitScope = this.#extractUnitScope();
@@ -522,33 +528,64 @@ window.Caixanegra.Designer = {
522
528
  this.#catalog = response;
523
529
  const unitMenu = document.querySelector("#unitMenu");
524
530
  unitMenu.innerHTML = "";
525
- this.#catalog.forEach((unitData) => {
526
- const item = document.createElement("div");
527
- const content = document.createElement("div");
528
- const colorCode = document.createElement("div");
529
- const header = document.createElement("div");
530
- const name = document.createElement("span");
531
- const type = document.createElement("span");
532
- const description = document.createElement("div");
533
- description.classList.add("description");
534
- item.classList.add("unit");
535
- header.classList.add("header");
536
- content.classList.add("content");
537
- colorCode.classList.add("color-code");
538
- name.classList.add("name");
539
- type.classList.add("type");
540
-
541
- name.innerHTML = unitData.title;
542
- type.innerHTML = unitData.type;
543
- description.innerHTML = unitData.description;
544
- colorCode.style.backgroundColor = Caixanegra.Designer.typeColor(unitData.type);
545
-
546
- header.append(name, type);
547
- content.append(header, description);
548
- item.append(colorCode, content);
549
- item.addEventListener("click", this.createUnit.bind(this, unitData));
550
- unitMenu.appendChild(item);
531
+
532
+ const scopes = [];
533
+
534
+ this.#catalog.forEach(unit => {
535
+ if (unit.hasOwnProperty("scope") && Array.isArray(unit.scope)) {
536
+ scopes.push(...unit.scope);
537
+ } else {
538
+ scopes.push("_unscoped");
539
+ }
551
540
  });
541
+
542
+ [...new Set(scopes)].sort().forEach(scope => {
543
+ const scopeWrapper = document.createElement("div");
544
+ const scopeTitle = document.createElement("div");
545
+ const scopeUnits = document.createElement("div");
546
+
547
+ scopeWrapper.classList.add("unit-wrapper");
548
+ scopeTitle.classList.add("title");
549
+
550
+ scopeTitle.innerHTML = scope === "_unscoped" ? "unscoped" : scope.replace(/_/g, " ");
551
+ scopeWrapper.appendChild(scopeTitle);
552
+ scopeWrapper.appendChild(scopeUnits);
553
+
554
+ const filteredUnits = this.#catalog.filter(unit => {
555
+ return (unit.scope === null && scope === "_unscoped") || (unit.scope || []).includes(scope)
556
+ });
557
+
558
+ filteredUnits.forEach((unitData) => {
559
+ const item = document.createElement("div");
560
+ const content = document.createElement("div");
561
+ const colorCode = document.createElement("div");
562
+ const header = document.createElement("div");
563
+ const name = document.createElement("span");
564
+ const type = document.createElement("span");
565
+ const description = document.createElement("div");
566
+ description.classList.add("description");
567
+ item.classList.add("unit");
568
+ header.classList.add("header");
569
+ content.classList.add("content");
570
+ colorCode.classList.add("color-code");
571
+ name.classList.add("name");
572
+ type.classList.add("type");
573
+
574
+ name.innerHTML = unitData.title;
575
+ type.innerHTML = unitData.type;
576
+ description.innerHTML = unitData.description;
577
+ const typeColor = Caixanegra.Designer.typeColor(unitData).background;
578
+ colorCode.style.backgroundColor = `rgb(${typeColor.r}, ${typeColor.g}, ${typeColor.b})`;
579
+
580
+ header.append(name, type);
581
+ content.append(header, description);
582
+ item.append(colorCode, content);
583
+ item.addEventListener("click", this.createUnit.bind(this, unitData));
584
+ scopeUnits.appendChild(item);
585
+ });
586
+ unitMenu.appendChild(scopeWrapper);
587
+ });
588
+
552
589
  this.#loadedComponents.catalog = true;
553
590
  this.#reveal();
554
591
  });
@@ -611,7 +648,7 @@ window.Caixanegra.Designer = {
611
648
 
612
649
  this.messenger.querySelector(".message").innerHTML = message;
613
650
  }
614
-
651
+
615
652
  createUnit(params) {
616
653
  this.#sequence++;
617
654
  const newUnit = new Caixanegra.Designer.Unit({
@@ -619,13 +656,14 @@ window.Caixanegra.Designer = {
619
656
  type: params.type,
620
657
  title: params.title,
621
658
  class: params.class,
659
+ color: params.color,
622
660
  zIndex: this.#sequence
623
661
  });
624
662
 
625
663
  if (params.oid) {
626
664
  newUnit.oid = params.oid;
627
665
  newUnit.position = new Sabertooth.Vector2(params.position.x, params.position.y);
628
- newUnit.title = params.title;
666
+ newUnit.title = params.title;
629
667
  }
630
668
 
631
669
  if (params.exits && params.exits.length > 0) {
@@ -657,7 +695,7 @@ window.Caixanegra.Designer = {
657
695
  this.#units.push(newUnit);
658
696
  this.gre.addObject(newUnit);
659
697
  }
660
-
698
+
661
699
  removeUnit(oid) {
662
700
  const killIndex = this.#units.findIndex((unit) => {return unit.oid === oid});
663
701
  this.#units.splice(killIndex, 1);
@@ -766,7 +804,7 @@ window.Caixanegra.Designer = {
766
804
  const endObject = moments.end.cursorAt?.object;
767
805
 
768
806
  if (startObject && endObject &&
769
- startObject?.connectingExit &&
807
+ startObject?.connectingExit &&
770
808
  startObject.oid !== endObject.oid) {
771
809
  startObject.connectingExit.reference.target = moments.end.cursorAt.object;
772
810
  startObject.connectingExit = null;
@@ -872,7 +910,7 @@ window.Caixanegra.Designer = {
872
910
  header.classList.add("hit-header");
873
911
  header.innerHTML = `hit #${idx + 1}`;
874
912
  hitWrapper.append(header);
875
-
913
+
876
914
  let section = this.#buildStepHitFold("in", hit.in);
877
915
  if (section !== null) { hitWrapper.append(section); }
878
916
 
@@ -892,7 +930,8 @@ window.Caixanegra.Designer = {
892
930
  const pane = this.unitDetailPane;
893
931
  const debugPane = this.unitDebugHitsPane;
894
932
  const dynamicContent = pane.querySelector("#dynamicContent");
895
- pane.querySelector(".color-code").style.backgroundColor = Caixanegra.Designer.typeColor(object.type);
933
+ const typeColor = Caixanegra.Designer.typeColor(object).background;
934
+ pane.querySelector(".color-code").style.backgroundColor = `rgb(${typeColor.r}, ${typeColor.g}, ${typeColor.b})`;
896
935
  pane.classList.add("-open");
897
936
 
898
937
  if (object.debugHits.length > 0) {
@@ -902,7 +941,7 @@ window.Caixanegra.Designer = {
902
941
  debugPane.querySelector("#debugData").innerHTML = "";
903
942
  debugPane.classList.remove("-open");
904
943
  }
905
-
944
+
906
945
  pane.querySelector("#unitDetailTitle").value = object.title;
907
946
  pane.querySelector("#unitDetailClass .name").innerHTML = matrix.class;
908
947
  pane.querySelector("#unitDetailDescription").innerHTML = matrix.description;
@@ -1082,7 +1121,7 @@ window.Caixanegra.Designer = {
1082
1121
  });
1083
1122
  typeSelector.addEventListener("change", this.#unitInputTypeChanged.bind(this));
1084
1123
  typeSelectorHeader.append(name, typeSelector);
1085
-
1124
+
1086
1125
  wrapper.appendChild(typeSelectorHeader);
1087
1126
  wrapper.appendChild(fieldDescription);
1088
1127
 
@@ -1114,12 +1153,48 @@ window.Caixanegra.Designer = {
1114
1153
  valueInput.addEventListener("change", this.#unitInputValueChanged.bind(this));
1115
1154
  }
1116
1155
  break;
1156
+ case "boolean":
1157
+ {
1158
+ const trueInput = document.createElement("input");
1159
+ const falseInput = document.createElement("input");
1160
+ const trueInputLabel = document.createElement("label");
1161
+ const falseInputLabel = document.createElement("label");
1162
+ const trueRadioGroup = document.createElement("div");
1163
+ const falseRadioGroup = document.createElement("div");
1164
+ trueRadioGroup.classList.add("radio-group");
1165
+ falseRadioGroup.classList.add("radio-group");
1166
+ trueInput.setAttribute("type", "radio");
1167
+ falseInput.setAttribute("type", "radio");
1168
+ trueInput.setAttribute("value", 1);
1169
+ trueInputLabel.innerHTML = "True";
1170
+ falseInput.setAttribute("value", 0);
1171
+ falseInputLabel.innerHTML = "False";
1172
+ trueInput.setAttribute("name", `b${input}`);
1173
+ falseInput.setAttribute("name", `b${input}`);
1174
+ const value = unit.mappings[input]?.value || matrix.default || "0"
1175
+ if (value === "1") { trueInput.setAttribute("checked", true); }
1176
+ if (value === "0") { falseInput.setAttribute("checked", true); }
1177
+ trueInput.addEventListener("change", this.#unitInputValueChanged.bind(this));
1178
+ falseInput.addEventListener("change", this.#unitInputValueChanged.bind(this));
1179
+
1180
+ trueRadioGroup.appendChild(trueInput);
1181
+ trueRadioGroup.appendChild(trueInputLabel);
1182
+ falseRadioGroup.appendChild(falseInput);
1183
+ falseRadioGroup.appendChild(falseInputLabel);
1184
+
1185
+ valueWrapper.appendChild(trueRadioGroup);
1186
+ valueWrapper.appendChild(falseRadioGroup);
1187
+ }
1188
+ break;
1117
1189
  }
1118
1190
 
1119
1191
  if (unit.mappings[input]?.type !== "user") {
1120
1192
  valueWrapper.classList.add("-disabled");
1121
1193
  }
1122
- valueWrapper.appendChild(valueInput);
1194
+
1195
+ if(valueInput !== null) {
1196
+ valueWrapper.appendChild(valueInput);
1197
+ }
1123
1198
 
1124
1199
  wrapper.append(valueWrapper);
1125
1200
  } else {
@@ -1182,12 +1257,12 @@ window.Caixanegra.Designer = {
1182
1257
  const exitMappings = {};
1183
1258
 
1184
1259
  Array.from(exits).forEach((exit) => {
1185
- exitMappings[exit.dataset.exit] =
1260
+ exitMappings[exit.dataset.exit] =
1186
1261
  Array.from(exit.querySelectorAll(".exit-mapping")).map((mapping) => {
1187
1262
  const useValue = mapping.querySelector("input.use").value;
1188
1263
  const asValue = mapping.querySelector("input.as").value;
1189
1264
  return { use: useValue, as: asValue };
1190
- });
1265
+ });
1191
1266
  });
1192
1267
 
1193
1268
  for(let idx = 0; idx < unit.exits.length; idx++) {
@@ -1232,9 +1307,9 @@ window.Caixanegra.Designer = {
1232
1307
  #engineUpdate(ev) {
1233
1308
  const context = ev.detail;
1234
1309
 
1235
- if (context.mouse.move &&
1310
+ if (context.mouse.move &&
1236
1311
  [1, 2].includes(context.mouse.move.button) &&
1237
- context.mouse.down &&
1312
+ context.mouse.down &&
1238
1313
  context.mouse.down.cursorAt === null) {
1239
1314
  const offset = {
1240
1315
  x: context.mouse.move.internal_x - context.mouse.down.internal_x,
@@ -1243,7 +1318,7 @@ window.Caixanegra.Designer = {
1243
1318
 
1244
1319
  this.gre.worldCenter.x = context.mouse.down.referential.x + offset.x;
1245
1320
  this.gre.worldCenter.y = context.mouse.down.referential.y + offset.y;
1246
-
1321
+
1247
1322
  for (let oidx = 0; oidx < context.objects.length; oidx++) {
1248
1323
  context.objects[oidx].initialize(this.gre.engineContext());
1249
1324
  }
@@ -1258,23 +1333,51 @@ window.Caixanegra.Designer = {
1258
1333
  }
1259
1334
  },
1260
1335
 
1261
- typeColor: (type) => {
1262
- switch (type) {
1263
- case "starter":
1264
- return "#65CCA9";
1265
- case "terminator":
1266
- return "#E44";
1267
- case "blackbox":
1268
- return "#EEE";
1269
- case "passthrough":
1270
- return "#c4c66a";
1271
- case "fork":
1272
- return "#ffcc5c"
1273
- case "feeder":
1274
- return "#5a92d8";
1275
- default:
1276
- return "#FFF";
1336
+ typeColor: (unit) => {
1337
+ let baseColor = unit.color || undefined;
1338
+
1339
+ if (baseColor === undefined) {
1340
+ switch (unit.type) {
1341
+ case "starter":
1342
+ baseColor = "#65CCA9";
1343
+ break;
1344
+ case "terminator":
1345
+ baseColor = "#E44";
1346
+ break;
1347
+ case "blackbox":
1348
+ baseColor = "#EEE";
1349
+ break;
1350
+ case "passthrough":
1351
+ baseColor = "#C4C66A";
1352
+ break;
1353
+ case "fork":
1354
+ baseColor = "#FFCC5C"
1355
+ break;
1356
+ case "feeder":
1357
+ baseColor = "#5A92D8";
1358
+ break;
1359
+ default:
1360
+ baseColor = "#F00";
1361
+ break;
1362
+ }
1363
+ }
1364
+
1365
+ hex = baseColor.replace("#", "");
1366
+
1367
+ if (hex.length === 3) {
1368
+ hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
1277
1369
  }
1370
+
1371
+ const r = parseInt(hex.substring(0, 2), 16);
1372
+ const g = parseInt(hex.substring(2, 4), 16);
1373
+ const b = parseInt(hex.substring(4, 6), 16);
1374
+
1375
+ const luminance = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
1376
+
1377
+ return {
1378
+ background: { r, g, b },
1379
+ foreground: luminance > 0.5 ? { r: 0, g: 0, b: 0 } : { r: 255, g: 255, b: 255 }
1380
+ };
1278
1381
  }
1279
1382
  }
1280
1383
 
@@ -119,6 +119,15 @@ $accent-contrast-color: darken($accent-color, 20%);
119
119
  transform: translateY(0%);
120
120
  }
121
121
 
122
+ .unit-wrapper {
123
+ .title {
124
+ font-family: Verdana, Geneva, Tahoma, sans-serif;
125
+ font-size: 0.7em;
126
+ color: rgba(0,0,0,.7);
127
+ margin-bottom: 5px;
128
+ }
129
+ }
130
+
122
131
  .unit {
123
132
  display: flex;
124
133
  flex-direction: row;
@@ -588,6 +597,9 @@ $accent-contrast-color: darken($accent-color, 20%);
588
597
 
589
598
  .unit-input {
590
599
  .unit-input-value {
600
+ display: flex;
601
+ justify-content: center;
602
+
591
603
  &.-disabled {
592
604
  display: none;
593
605
 
@@ -595,6 +607,18 @@ $accent-contrast-color: darken($accent-color, 20%);
595
607
  pointer-events: none;
596
608
  }
597
609
  }
610
+
611
+ .radio-group {
612
+ display: flex;
613
+
614
+ &:first-child {
615
+ margin-right: 20px;
616
+ }
617
+
618
+ input, label {
619
+ display: inline;
620
+ }
621
+ }
598
622
  }
599
623
  }
600
624
 
@@ -17,13 +17,15 @@ module Caixanegra
17
17
  def units
18
18
  @units ||= ::Caixanegra::UnitHelper.scoped_units(unit_scope).map do |k, v|
19
19
  base = {
20
- title: v.name,
20
+ scope: v.scope,
21
+ title: v.unit_name,
21
22
  type: v.type,
22
23
  description: v.description,
23
24
  class: k,
24
25
  exits: v.exits&.map { |e| { name: e } },
25
26
  inputs: v.inputs,
26
- assignments: v.assignments
27
+ assignments: v.assignments,
28
+ color: v.color
27
29
  }
28
30
 
29
31
  base[:set] = v.set if base[:set]
@@ -73,11 +73,15 @@ module Caixanegra
73
73
 
74
74
  input_value.presence || @inputs[id][:default]
75
75
  rescue StandardError
76
- raise(UnitIOException.new, "Unable to fetch input '#{id}'")
76
+ raise(UnitIOException.new(self), "Unable to fetch input '#{id}'")
77
77
  end
78
78
 
79
- def name
80
- self.class.name
79
+ def scope
80
+ self.class.scope
81
+ end
82
+
83
+ def unit_name
84
+ self.class.unit_name
81
85
  end
82
86
 
83
87
  def description
@@ -96,6 +100,10 @@ module Caixanegra
96
100
  self.class.set || []
97
101
  end
98
102
 
103
+ def color
104
+ self.class.color
105
+ end
106
+
99
107
  private
100
108
 
101
109
  def set_mapping_defaults
@@ -108,12 +116,25 @@ module Caixanegra
108
116
  end
109
117
 
110
118
  class << self
111
- attr_reader :name, :description, :inputs, :exits, :assignments, :type
119
+ attr_reader(
120
+ :unit_name,
121
+ :description,
122
+ :inputs,
123
+ :exits,
124
+ :assignments,
125
+ :type,
126
+ :scope,
127
+ :color
128
+ )
112
129
 
113
130
  @type = :passthrough
114
131
 
132
+ def configure_scope(value)
133
+ @scope = value
134
+ end
135
+
115
136
  def configure_name(value)
116
- @name = value
137
+ @unit_name = value
117
138
  end
118
139
 
119
140
  def configure_description(value)
@@ -135,6 +156,10 @@ module Caixanegra
135
156
  def configure_assignments(assignments)
136
157
  @assignments = assignments
137
158
  end
159
+
160
+ def configure_color(color)
161
+ @color = color
162
+ end
138
163
  end
139
164
  end
140
165
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Caixanegra
4
4
  self.mattr_accessor :units
5
- self.mattr_accessor :redis
5
+ self.mattr_accessor :transient_store
6
6
  self.units = []
7
7
 
8
8
  def self.setup(&block)
@@ -1,7 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Caixanegra
4
- class UnitScopedException < StandardError; end
5
- class UnitIOException < StandardError; end
4
+ class UnitScopedException < StandardError
5
+ attr_reader :unit
6
+
7
+ def initialize(exception, unit)
8
+ @unit = unit
9
+ super(exception.message)
10
+ end
11
+ end
12
+
13
+ class UnitIOException < StandardError
14
+ attr_reader :unit
15
+
16
+ def initialize(unit)
17
+ @unit = unit
18
+ super
19
+ end
20
+ end
21
+
6
22
  class ContinuityException < StandardError; end
7
23
  end
@@ -29,9 +29,9 @@ module Caixanegra
29
29
  log_console_entry 'Started'
30
30
  set_start_unit
31
31
  format_result(flow_through)
32
- rescue Caixanegra::UnitScopedException => e
32
+ rescue Caixanegra::UnitScopedException, Caixanegra::UnitIOException => e
33
33
  log_step_exception(e)
34
- log_console_entry("Unit error: #{e.message}")
34
+ log_console_entry("Unit '#{e.unit.oid}' error: #{e.message}")
35
35
  format_result(e.message)
36
36
  rescue => e
37
37
  log_console_entry("Error: #{e.message}")
@@ -107,7 +107,7 @@ module Caixanegra
107
107
  @storage.merge! @step_unit.current_storage
108
108
  result
109
109
  rescue => e
110
- exception = Caixanegra::UnitScopedException.new(e)
110
+ exception = Caixanegra::UnitScopedException.new(e, @step_unit)
111
111
  exception.set_backtrace(e.backtrace)
112
112
  raise exception
113
113
  end
@@ -148,9 +148,11 @@ module Caixanegra
148
148
 
149
149
  exit_name = result[:exit_through]
150
150
  metadata = unit_metadata(@step_unit.oid)
151
- log_console_entry "Next unit found through '#{exit_name}': '#{@step_unit.oid}'"
152
151
  exit_metadata = metadata[:exits].find { |ex| ex[:name] == exit_name.to_s }
153
- unit(exit_metadata[:target], map_carry_over(result))
152
+ next_unit = unit(exit_metadata[:target], map_carry_over(result))
153
+ log_console_entry "Next unit found through '#{exit_name}': '#{next_unit.oid}'"
154
+
155
+ next_unit
154
156
  end
155
157
 
156
158
  def process_feeders
@@ -4,28 +4,20 @@ module Caixanegra
4
4
  class Manager
5
5
  class << self
6
6
  def handler(flow_definition = {})
7
- uid = SecureRandom.uuid.gsub('-', '')
7
+ transient_store = Caixanegra.transient_store
8
+ uid = transient_store.new_uid
8
9
 
9
- Caixanegra.redis.multi do |pipeline|
10
- pipeline.hset(:caixanegra, uid, JSON.dump(flow_definition))
11
- pipeline.expire(:caixanegra, 1.hour)
12
- end
10
+ transient_store.hold(uid, flow_definition)
13
11
 
14
12
  uid
15
13
  end
16
14
 
17
- def destroy
18
- Caixanegra.redis.hdel(:caixanegra, key)
19
- end
20
-
21
15
  def get(uid)
22
- value = Caixanegra.redis.hget(:caixanegra, uid)
23
-
24
- JSON.parse(value) if value.present?
16
+ Caixanegra.transient_store.get(uid)
25
17
  end
26
18
 
27
19
  def set(uid, flow_definition)
28
- Caixanegra.redis.hset(:caixanegra, uid, JSON.dump(flow_definition))
20
+ Caixanegra.transient_store.hold(uid, definition: flow_definition)
29
21
  end
30
22
  end
31
23
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Caixanegra
4
+ class TransientStore
5
+ def get(uid)
6
+ raise NotImplementedError, "Transient Store needs to implement 'get(uid)'"
7
+ end
8
+
9
+ def hold(uid, definition: {})
10
+ raise NotImplementedError, "Transient Store needs to implement 'hold(uid, definition: {})'"
11
+ end
12
+
13
+ def new_uid
14
+ raise NotImplementedError, "Transient Store needs to implement 'new_uid'"
15
+ end
16
+ end
17
+ end
@@ -2,30 +2,26 @@ module Caixanegra
2
2
  class UnitHelper
3
3
  class << self
4
4
  def scoped_units(scope)
5
- all_units = {}
6
- all_units = all_units.merge(Caixanegra.units.reject { |_, v| v.is_a? Hash })
5
+ units = {}
7
6
 
8
- (scope || "").split(",").each do |current_scope|
9
- all_units = all_units.merge(Caixanegra.units[current_scope.to_sym])
7
+ scopes = (scope || "").split(",").map(&:to_sym)
8
+ Caixanegra.units.each do |unit|
9
+ if unit.scope.nil? || unit.scope.any? { |checking_scope| scopes.include?(checking_scope) }
10
+ units[unit.name.demodulize.underscore.to_sym] = unit
11
+ end
10
12
  end
11
13
 
12
- all_units
14
+ units
13
15
  end
14
16
 
15
17
  def all_units
16
- all_units = {}
18
+ units = {}
17
19
 
18
- Caixanegra.units.each do |k, v|
19
- if v.is_a? Hash
20
- v.each do |ik, iv|
21
- all_units[ik] = iv
22
- end
23
- else
24
- all_units[k] = v
25
- end
20
+ Caixanegra.units.each do |unit|
21
+ units[unit.name.demodulize.underscore.to_sym] = unit
26
22
  end
27
23
 
28
- all_units
24
+ units
29
25
  end
30
26
  end
31
27
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Caixanegra
4
- VERSION = '0.1.2'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/caixanegra.rb CHANGED
@@ -6,5 +6,6 @@ require 'caixanegra/engine'
6
6
  require 'caixanegra/version'
7
7
  require 'caixanegra/executor'
8
8
  require 'caixanegra/manager'
9
+ require 'caixanegra/transient_store'
9
10
 
10
11
  module Caixanegra ; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: caixanegra
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - sergiorribeiro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-14 00:00:00.000000000 Z
11
+ date: 2023-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -61,7 +61,6 @@ files:
61
61
  - app/controllers/caixanegra/api_controller.rb
62
62
  - app/controllers/caixanegra/application_controller.rb
63
63
  - app/controllers/caixanegra/designer_controller.rb
64
- - app/helpers/caixanegra/application_helper.rb
65
64
  - app/models/caixanegra/unit.rb
66
65
  - app/views/caixanegra/designer/index.html.erb
67
66
  - app/views/layouts/caixanegra/application.html.erb
@@ -72,6 +71,7 @@ files:
72
71
  - lib/caixanegra/exceptions.rb
73
72
  - lib/caixanegra/executor.rb
74
73
  - lib/caixanegra/manager.rb
74
+ - lib/caixanegra/transient_store.rb
75
75
  - lib/caixanegra/unit_helper.rb
76
76
  - lib/caixanegra/version.rb
77
77
  - lib/tasks/caixanegra_tasks.rake
@@ -1,4 +0,0 @@
1
- module Caixanegra
2
- module ApplicationHelper
3
- end
4
- end