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 +4 -4
- data/README.md +10 -14
- data/app/assets/javascripts/caixanegra/designer.js +171 -68
- data/app/assets/stylesheets/caixanegra/designer.scss +24 -0
- data/app/controllers/caixanegra/api/designer/units_controller.rb +4 -2
- data/app/models/caixanegra/unit.rb +30 -5
- data/lib/caixanegra/engine.rb +1 -1
- data/lib/caixanegra/exceptions.rb +18 -2
- data/lib/caixanegra/executor.rb +7 -5
- data/lib/caixanegra/manager.rb +5 -13
- data/lib/caixanegra/transient_store.rb +17 -0
- data/lib/caixanegra/unit_helper.rb +11 -15
- data/lib/caixanegra/version.rb +1 -1
- data/lib/caixanegra.rb +1 -0
- metadata +3 -3
- data/app/helpers/caixanegra/application_helper.rb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ece5f658699dfddfd51744c4f78b12b207e9e7108359e9d6d4f39366a494987e
|
4
|
+
data.tar.gz: 0e0fca0b8abdcc03cfd9851563105300d4a6033206917df7fe7d3d49a3cd00d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
20
|
-
To get started, the only thing you need is to point to your
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
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
|
-
|
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 =
|
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 =
|
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 =
|
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
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
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
|
-
|
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
|
-
|
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: (
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
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
|
-
|
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
|
80
|
-
self.class.
|
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
|
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
|
-
@
|
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
|
data/lib/caixanegra/engine.rb
CHANGED
@@ -1,7 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Caixanegra
|
4
|
-
class UnitScopedException < StandardError
|
5
|
-
|
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
|
data/lib/caixanegra/executor.rb
CHANGED
@@ -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
|
data/lib/caixanegra/manager.rb
CHANGED
@@ -4,28 +4,20 @@ module Caixanegra
|
|
4
4
|
class Manager
|
5
5
|
class << self
|
6
6
|
def handler(flow_definition = {})
|
7
|
-
|
7
|
+
transient_store = Caixanegra.transient_store
|
8
|
+
uid = transient_store.new_uid
|
8
9
|
|
9
|
-
|
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
|
-
|
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.
|
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
|
-
|
6
|
-
all_units = all_units.merge(Caixanegra.units.reject { |_, v| v.is_a? Hash })
|
5
|
+
units = {}
|
7
6
|
|
8
|
-
(scope || "").split(",").
|
9
|
-
|
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
|
-
|
14
|
+
units
|
13
15
|
end
|
14
16
|
|
15
17
|
def all_units
|
16
|
-
|
18
|
+
units = {}
|
17
19
|
|
18
|
-
Caixanegra.units.each do |
|
19
|
-
|
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
|
-
|
24
|
+
units
|
29
25
|
end
|
30
26
|
end
|
31
27
|
end
|
data/lib/caixanegra/version.rb
CHANGED
data/lib/caixanegra.rb
CHANGED
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.
|
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-
|
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
|