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 +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
|