caixanegra 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c291dfc1c33d2c17e87093eff1bceff3c061ef8c7230a561ace088cd9e23f279
4
- data.tar.gz: e812c3f4c1d202902f926115162c0b9b46885b35af56db52db3aa94ab8e53ac8
3
+ metadata.gz: ece5f658699dfddfd51744c4f78b12b207e9e7108359e9d6d4f39366a494987e
4
+ data.tar.gz: 0e0fca0b8abdcc03cfd9851563105300d4a6033206917df7fe7d3d49a3cd00d7
5
5
  SHA512:
6
- metadata.gz: c6276bd14d36c832729b8a717a15ad5a8c6b9e6223343d6169fa1c153dc0389d0d0ef73ed61b0eb4f19a9dfc1936683b59b80e994b740df4cc670958fbc21833
7
- data.tar.gz: 70059940599beff0f5cf09bbb73defa53fc31e336166a15a0262902f194862cd318b5bb8fb7bcb22a7934960411c7dce14f997816a0430fc943ba6d0dceddb88
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();
@@ -524,7 +530,7 @@ window.Caixanegra.Designer = {
524
530
  unitMenu.innerHTML = "";
525
531
 
526
532
  const scopes = [];
527
-
533
+
528
534
  this.#catalog.forEach(unit => {
529
535
  if (unit.hasOwnProperty("scope") && Array.isArray(unit.scope)) {
530
536
  scopes.push(...unit.scope);
@@ -532,7 +538,7 @@ window.Caixanegra.Designer = {
532
538
  scopes.push("_unscoped");
533
539
  }
534
540
  });
535
-
541
+
536
542
  [...new Set(scopes)].sort().forEach(scope => {
537
543
  const scopeWrapper = document.createElement("div");
538
544
  const scopeTitle = document.createElement("div");
@@ -564,12 +570,13 @@ window.Caixanegra.Designer = {
564
570
  colorCode.classList.add("color-code");
565
571
  name.classList.add("name");
566
572
  type.classList.add("type");
567
-
573
+
568
574
  name.innerHTML = unitData.title;
569
575
  type.innerHTML = unitData.type;
570
576
  description.innerHTML = unitData.description;
571
- colorCode.style.backgroundColor = Caixanegra.Designer.typeColor(unitData.type);
572
-
577
+ const typeColor = Caixanegra.Designer.typeColor(unitData).background;
578
+ colorCode.style.backgroundColor = `rgb(${typeColor.r}, ${typeColor.g}, ${typeColor.b})`;
579
+
573
580
  header.append(name, type);
574
581
  content.append(header, description);
575
582
  item.append(colorCode, content);
@@ -641,7 +648,7 @@ window.Caixanegra.Designer = {
641
648
 
642
649
  this.messenger.querySelector(".message").innerHTML = message;
643
650
  }
644
-
651
+
645
652
  createUnit(params) {
646
653
  this.#sequence++;
647
654
  const newUnit = new Caixanegra.Designer.Unit({
@@ -649,13 +656,14 @@ window.Caixanegra.Designer = {
649
656
  type: params.type,
650
657
  title: params.title,
651
658
  class: params.class,
659
+ color: params.color,
652
660
  zIndex: this.#sequence
653
661
  });
654
662
 
655
663
  if (params.oid) {
656
664
  newUnit.oid = params.oid;
657
665
  newUnit.position = new Sabertooth.Vector2(params.position.x, params.position.y);
658
- newUnit.title = params.title;
666
+ newUnit.title = params.title;
659
667
  }
660
668
 
661
669
  if (params.exits && params.exits.length > 0) {
@@ -687,7 +695,7 @@ window.Caixanegra.Designer = {
687
695
  this.#units.push(newUnit);
688
696
  this.gre.addObject(newUnit);
689
697
  }
690
-
698
+
691
699
  removeUnit(oid) {
692
700
  const killIndex = this.#units.findIndex((unit) => {return unit.oid === oid});
693
701
  this.#units.splice(killIndex, 1);
@@ -796,7 +804,7 @@ window.Caixanegra.Designer = {
796
804
  const endObject = moments.end.cursorAt?.object;
797
805
 
798
806
  if (startObject && endObject &&
799
- startObject?.connectingExit &&
807
+ startObject?.connectingExit &&
800
808
  startObject.oid !== endObject.oid) {
801
809
  startObject.connectingExit.reference.target = moments.end.cursorAt.object;
802
810
  startObject.connectingExit = null;
@@ -902,7 +910,7 @@ window.Caixanegra.Designer = {
902
910
  header.classList.add("hit-header");
903
911
  header.innerHTML = `hit #${idx + 1}`;
904
912
  hitWrapper.append(header);
905
-
913
+
906
914
  let section = this.#buildStepHitFold("in", hit.in);
907
915
  if (section !== null) { hitWrapper.append(section); }
908
916
 
@@ -922,7 +930,8 @@ window.Caixanegra.Designer = {
922
930
  const pane = this.unitDetailPane;
923
931
  const debugPane = this.unitDebugHitsPane;
924
932
  const dynamicContent = pane.querySelector("#dynamicContent");
925
- 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})`;
926
935
  pane.classList.add("-open");
927
936
 
928
937
  if (object.debugHits.length > 0) {
@@ -932,7 +941,7 @@ window.Caixanegra.Designer = {
932
941
  debugPane.querySelector("#debugData").innerHTML = "";
933
942
  debugPane.classList.remove("-open");
934
943
  }
935
-
944
+
936
945
  pane.querySelector("#unitDetailTitle").value = object.title;
937
946
  pane.querySelector("#unitDetailClass .name").innerHTML = matrix.class;
938
947
  pane.querySelector("#unitDetailDescription").innerHTML = matrix.description;
@@ -1112,7 +1121,7 @@ window.Caixanegra.Designer = {
1112
1121
  });
1113
1122
  typeSelector.addEventListener("change", this.#unitInputTypeChanged.bind(this));
1114
1123
  typeSelectorHeader.append(name, typeSelector);
1115
-
1124
+
1116
1125
  wrapper.appendChild(typeSelectorHeader);
1117
1126
  wrapper.appendChild(fieldDescription);
1118
1127
 
@@ -1144,12 +1153,48 @@ window.Caixanegra.Designer = {
1144
1153
  valueInput.addEventListener("change", this.#unitInputValueChanged.bind(this));
1145
1154
  }
1146
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;
1147
1189
  }
1148
1190
 
1149
1191
  if (unit.mappings[input]?.type !== "user") {
1150
1192
  valueWrapper.classList.add("-disabled");
1151
1193
  }
1152
- valueWrapper.appendChild(valueInput);
1194
+
1195
+ if(valueInput !== null) {
1196
+ valueWrapper.appendChild(valueInput);
1197
+ }
1153
1198
 
1154
1199
  wrapper.append(valueWrapper);
1155
1200
  } else {
@@ -1212,12 +1257,12 @@ window.Caixanegra.Designer = {
1212
1257
  const exitMappings = {};
1213
1258
 
1214
1259
  Array.from(exits).forEach((exit) => {
1215
- exitMappings[exit.dataset.exit] =
1260
+ exitMappings[exit.dataset.exit] =
1216
1261
  Array.from(exit.querySelectorAll(".exit-mapping")).map((mapping) => {
1217
1262
  const useValue = mapping.querySelector("input.use").value;
1218
1263
  const asValue = mapping.querySelector("input.as").value;
1219
1264
  return { use: useValue, as: asValue };
1220
- });
1265
+ });
1221
1266
  });
1222
1267
 
1223
1268
  for(let idx = 0; idx < unit.exits.length; idx++) {
@@ -1262,9 +1307,9 @@ window.Caixanegra.Designer = {
1262
1307
  #engineUpdate(ev) {
1263
1308
  const context = ev.detail;
1264
1309
 
1265
- if (context.mouse.move &&
1310
+ if (context.mouse.move &&
1266
1311
  [1, 2].includes(context.mouse.move.button) &&
1267
- context.mouse.down &&
1312
+ context.mouse.down &&
1268
1313
  context.mouse.down.cursorAt === null) {
1269
1314
  const offset = {
1270
1315
  x: context.mouse.move.internal_x - context.mouse.down.internal_x,
@@ -1273,7 +1318,7 @@ window.Caixanegra.Designer = {
1273
1318
 
1274
1319
  this.gre.worldCenter.x = context.mouse.down.referential.x + offset.x;
1275
1320
  this.gre.worldCenter.y = context.mouse.down.referential.y + offset.y;
1276
-
1321
+
1277
1322
  for (let oidx = 0; oidx < context.objects.length; oidx++) {
1278
1323
  context.objects[oidx].initialize(this.gre.engineContext());
1279
1324
  }
@@ -1288,23 +1333,51 @@ window.Caixanegra.Designer = {
1288
1333
  }
1289
1334
  },
1290
1335
 
1291
- typeColor: (type) => {
1292
- switch (type) {
1293
- case "starter":
1294
- return "#65CCA9";
1295
- case "terminator":
1296
- return "#E44";
1297
- case "blackbox":
1298
- return "#EEE";
1299
- case "passthrough":
1300
- return "#c4c66a";
1301
- case "fork":
1302
- return "#ffcc5c"
1303
- case "feeder":
1304
- return "#5a92d8";
1305
- default:
1306
- 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];
1307
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
+ };
1308
1381
  }
1309
1382
  }
1310
1383
 
@@ -597,6 +597,9 @@ $accent-contrast-color: darken($accent-color, 20%);
597
597
 
598
598
  .unit-input {
599
599
  .unit-input-value {
600
+ display: flex;
601
+ justify-content: center;
602
+
600
603
  &.-disabled {
601
604
  display: none;
602
605
 
@@ -604,6 +607,18 @@ $accent-contrast-color: darken($accent-color, 20%);
604
607
  pointer-events: none;
605
608
  }
606
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
+ }
607
622
  }
608
623
  }
609
624
 
@@ -24,7 +24,8 @@ module Caixanegra
24
24
  class: k,
25
25
  exits: v.exits&.map { |e| { name: e } },
26
26
  inputs: v.inputs,
27
- assignments: v.assignments
27
+ assignments: v.assignments,
28
+ color: v.color
28
29
  }
29
30
 
30
31
  base[:set] = v.set if base[:set]
@@ -73,7 +73,7 @@ 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
79
  def scope
@@ -100,6 +100,10 @@ module Caixanegra
100
100
  self.class.set || []
101
101
  end
102
102
 
103
+ def color
104
+ self.class.color
105
+ end
106
+
103
107
  private
104
108
 
105
109
  def set_mapping_defaults
@@ -112,7 +116,16 @@ module Caixanegra
112
116
  end
113
117
 
114
118
  class << self
115
- attr_reader :unit_name, :description, :inputs, :exits, :assignments, :type, :scope
119
+ attr_reader(
120
+ :unit_name,
121
+ :description,
122
+ :inputs,
123
+ :exits,
124
+ :assignments,
125
+ :type,
126
+ :scope,
127
+ :color
128
+ )
116
129
 
117
130
  @type = :passthrough
118
131
 
@@ -143,6 +156,10 @@ module Caixanegra
143
156
  def configure_assignments(assignments)
144
157
  @assignments = assignments
145
158
  end
159
+
160
+ def configure_color(color)
161
+ @color = color
162
+ end
146
163
  end
147
164
  end
148
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
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Caixanegra
4
- VERSION = '0.2.0'
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.2.0
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-27 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
@@ -71,6 +71,7 @@ files:
71
71
  - lib/caixanegra/exceptions.rb
72
72
  - lib/caixanegra/executor.rb
73
73
  - lib/caixanegra/manager.rb
74
+ - lib/caixanegra/transient_store.rb
74
75
  - lib/caixanegra/unit_helper.rb
75
76
  - lib/caixanegra/version.rb
76
77
  - lib/tasks/caixanegra_tasks.rake