caixanegra 0.1.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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