The Boring Tool
Formaatreferentie
Veldkeys, tokens, opgeslagen JSON en generate-semantiek voor TBT custom documents.
Deze pagina beschrijft de twee custom documenttypes die TheBoringTool (TBT) registreert voor Mendix Studio Pro 11.x:
| Documenttype-id | Leesbare naam | Typisch gebruik |
|---|---|---|
TheBoringTool.ValidationExpression | TBT Validation Expression | Herbruikbare validatieconditie + feedbackbericht voor Validate… en DTO… |
TheBoringTool.AttributePreset | TBT Attribute Preset | Herbruikbare attribuutdefinitie voor de Attributes-picker |
Inhoud wordt opgeslagen als JSON in een Studio Pro custom blob-document. Je bewerkt het via de TBT-formuliereditor (niet als ruwe JSON). De editor debounce’t wijzigingen en schrijft een genormaliseerde JSON-blob via de model-API.
Bron voor deze pagina: schemas, Svelte-editors, generate/DTO-emitters en catalogus-merge op de branch development.
1. TBT Validation Expression
1.1 Wat het is en waar het leeft
Een TBT Validation Expression is een herbruikbare validatie-template: een Mendix-achtige booleaanse expression, optionele parameters (met optionele operators), en een message-template. Bij genereren inline’t TBT de template in een Exclusive Split (expression-conditie) op een attribuut-validatiemicroflow / DTO-attribuut-nanoflow, en substitueert het bericht voor Validation Feedback.
- Documenttype-id:
TheBoringTool.ValidationExpression - Leesbare naam in Studio Pro:
TBT Validation Expression - Hint voor nieuwe documentnaam:
ValidationExpression - App Explorer: elke module / map. TBT laadt alle documenten van dit type in de app; er is geen geseede library-map voor validation expressions.
- Aanmaken / bewerken: voeg een document van type TBT Validation Expression toe en bewerk de velden in de tab-editor. Er is geen Save-knop in het formulier — wijzigingen worden automatisch opgeslagen (debounced). Gebruikers bewerken geen ruwe JSON.
1.2 Editorvelden en opgeslagen keys
Sectiekoppen in de editor: General, Params, Expression.
| Editorlabel | Opgeslagen key | Type | Toegestane waarden | Verplicht | Default | Opmerkingen |
|---|---|---|---|---|---|---|
| (impliciet) | schemaVersion | getalliteraal 1 | altijd 1 | ja (wordt altijd geschreven) | 1 | Ongeldige / ontbrekende versie wordt bij parse naar 1 gezet |
| Label | label | string | willekeurig | optioneel | "" | Weergavenaam in de catalogus; indien leeg wordt de Studio Pro-documentnaam als presetlabel gebruikt |
| Documentation | documentation | string | willekeurig | optioneel | "" | Samengevouwen getoond in Validate/DTO-presetbeschrijvingen |
| Applies to | appliesTo | ValueType[] | String, Integer, Long, Decimal, DateTime, Boolean | optioneel | [] | Checkboxes. Onbekende types worden genegeerd; duplicaten verwijderd. HashedString-attributen matchen wanneer String is aangevinkt |
| Server-only | serverOnly | boolean | true / false | optioneel | false | Zie §1.3 |
| Default for attribute names | defaultForAttributes | string[] | hints voor attribuutnamen | optioneel | [] | Chiplijst. Getrimd; lege waarden weg; case-insensitive dedupe (eerste casing wint) |
| Param *N* name | params[].name | string | willekeurig (gebruikt als $Name / {Name}-token) | optioneel per param | "" | Exacte naam in tokens; langere namen eerst gesubstitueerd |
| Param *N* type | params[].type | ValueType | zelfde als Applies to | optioneel | String indien ongeldig | |
| Required (op param) | params[].required | boolean | optioneel | false | Gebruikt door Validate/DTO-UI-validatie van gebruikswaarden | |
| Param *N* default | params[].default | string | number | boolean | null | getypeerd; leeg wist; letterlijk null → null | optioneel | weggelaten | Ongeldige getypeerde input wordt weggelaten |
| Operator | params[].operator | { options: string[]; default: string } | optioneel | weggelaten | Operator aanzetten seed’t options: [">=", ">"], default: ">=" | |
| Param *N* operator options | params[].operator.options | string[] | kommagescheiden in het formulier | verplicht als Operator aan | Getrimd; lege / duplicaten weg. Lege lijst → operator verwijderd bij normalisatie | |
| Param *N* default operator | params[].operator.default | string | moet in options zitten | verplicht als Operator aan | eerste optie indien ongeldig | |
| Expression | expression | string | Mendix expression-template | optioneel | "" | Placeholders in §1.3 |
| Message | message | string | feedbacktemplate | optioneel | "" | Placeholders in §1.3 |
Ruwe JSON wordt nooit ter bewerking getoond; alleen het formulier hierboven.
1.3 Tokens, expressionsyntax en semantiek
Expression-placeholders
| Token | Betekenis |
|---|---|
$Value | Vervangen door het attribuutpad, bijv. $Order/Age |
$ParamName | Vervangen door een getypeerde literal uit de gebruikswaarde (of param-default). Naam moet exact params[].name zijn. Langere namen winnen (dus $MaxValue wordt niet opgegeten door $Max) |
{ParamName.operator} | Vervangen door de gekozen operatorstring (gebruik → param-default → eerste optie) |
Het geïnline’de resultaat moet een geldige Mendix microflow/nanoflow boolean expression zijn (Exclusive Split expression-conditie). TBT compile-checkt dit niet verder dan stringsubstitutie.
Lege waarden: gegenereerde flows voeren de expressie ongewijzigd uit, ook als het attribuut leeg is. Schrijf $Value = empty or ..., tenzij de regel lege waarden juist moet afkeuren (bijvoorbeeld een "niet leeg"-regel).
Literal-opmaak bij substitutie van `$ParamName`:
| Paramtype | Leeg gebruik | Niet-leeg gebruik |
|---|---|---|
Integer / Long / Decimal | 0 | getrimde tekst as-is (bijv. 42) |
Boolean | false | true alleen als getrimde lower-case true is, anders false |
DateTime | empty | als waarde met [ begint, as-is; anders gequote Mendix-string (' → '') |
String (default) | '' | gequote Mendix-string |
Message-placeholders
| Token | Betekenis |
|---|---|
{AttributeName} | Attribuutnaam bij genereren / UI-voorinvulling |
{ParamName} | Param-gebruikswaarde (weergave = getrimde string); langere namen eerst |
{1} | Ongewijzigd gelaten voor Validation Feedback-argumentkoppeling (attribuutwaarde) |
`serverOnly`
- `false` (default): beschikbaar voor zowel Validate… (microflow) als DTO… attribuut-nanoflow-paden.
- `true`: blijft opgenomen in de Validate…-microflow-presetlijst (
includeServerOnly: true). Op het DTO-nano-pad worden expressions metserverOnly: trueuitgesloten (includeServerOnly: false). Bijgehouden als overgeslagen document-ids bij nano-copy.
Matching op `appliesTo`
Een preset wordt aangeboden voor een attribuut wanneer appliesTo het attribuuttype bevat, of het attribuut HashedString is en appliesTo String bevat.
Auto-select via `defaultForAttributes`
Wanneer Validate/DTO een attribuut toevoegt dat nog geen rules heeft, zoekt TBT expression-presets waarvan defaultForAttributes de attribuutnaam bevat (case-insensitive exact). Bij meerdere matches wint die met de kleinste label via localeCompare.
1.4 Wat generate in het model schrijft
Validate… (attribuut-validatiemicroflow) voor een expression-rule:
- Exclusive Split-caption = preset
label(of documentnaam). - Expression split condition = resultaat van
inlineValidationExpression(...)(bijv.$Person/Age >= 18). - Split-documentation =
TBT.ExpressionUsage:+ JSON{ documentId, params }voor round-trip reload. - Op de
false-tak: aanroep om de validation context ongeldig te maken + Validation Feedback op het attribuut. - Feedbacktemplatetekst = het (reeds gesubstitueerde) message uit de dialoog; argument
{1}wordt gebonden aan$Entity/Attribute.
DTO… (attribuut-nanoflow): dezelfde inlining + TBT.ExpressionUsage:-documentation + gesubstitueerd message op de nanoflow exclusive split — maar alleen als de expression niet serverOnly is.
Expression-rules gebruiken nooit Mendix Rules; ze gebruiken altijd een expression exclusive split.
1.5 Fouten, waarschuwingen en normalisatie
| Situatie | Gebruikerszichtbare / gelogde tekst |
|---|---|
| Persist-fout in editor | Could not save validation expression. (+ details) |
| Document ontbreekt bij laden | Document was not found. |
| Onleesbare / non-JSON-inhoud | Document contents could not be read. Expected JSON matching the IR schema. |
| Handmatig bewerkte split komt niet meer overeen met preset | Tooltip: Condition no longer matches the preset; Save will rewrite it |
| Verplichte param leeg in Validate-UI | parameter {name} is required for {preset label} (formulering uit generate-editor) |
Normalisatie bij parse / save (stil):
- Non-object JSON → leeg document (
schemaVersion: 1, lege strings/arrays,serverOnly: false). - Onbekende keys worden weggelaten.
- Ongeldige
appliesTo-entries weg; ongeldige param-objecten weg; ongeldig param-type→String. - Ongeldige param-
defaultvoor het type → weggelaten. - Operator zonder options → weggelaten; default-operator wordt in
optionsgedwongen. defaultForAttributes: trimmen, lege weg, case-insensitive dedupe.
1.6 Voorbeelden
Voorbeeld A — eenvoudig (geen params)
Editor
| Veld | Waarde |
|---|---|
| Label | Not blank |
| Documentation | Trimmed string must not be empty |
| Applies to | String |
| Server-only | uit |
| Default for attribute names | (geen) |
| Expression | trim($Value) != '' |
| Message | {AttributeName} must not be blank |
Opgeslagen JSON
{
"schemaVersion": 1,
"label": "Not blank",
"documentation": "Trimmed string must not be empty",
"appliesTo": ["String"],
"serverOnly": false,
"defaultForAttributes": [],
"params": [],
"expression": "trim($Value) != ''",
"message": "{AttributeName} must not be blank"
}Voor attribuut Name op entity Person inline’t generate naar trim($Person/Name) != '' en message Name must not be blank.
Voorbeeld B — realistisch met params + operators
Editor
| Veld | Waarde |
|---|---|
| Label | Range |
| Documentation | Inclusive/exclusive bounds via operators |
| Applies to | Integer, Long, Decimal |
| Server-only | uit |
| Default for attribute names | Age |
| Param 1 name / type / required / default | Lower / Integer / ja / 0 |
| Param 1 Operator | aan; options >=, >; default >= |
| Param 2 name / type / required / default | Upper / Integer / ja / 100 |
| Param 2 Operator | aan; options <=, <; default <= |
| Expression | $Value = empty or ($Value {Lower.operator} $Lower and $Value {Upper.operator} $Upper) |
| Message | {AttributeName} must be between {Lower} and {Upper} |
Opgeslagen JSON
{
"schemaVersion": 1,
"label": "Range",
"documentation": "Inclusive/exclusive bounds via operators",
"appliesTo": ["Integer", "Long", "Decimal"],
"serverOnly": false,
"defaultForAttributes": ["Age"],
"params": [
{
"name": "Lower",
"type": "Integer",
"required": true,
"default": 0,
"operator": { "options": [">=", ">"], "default": ">=" }
},
{
"name": "Upper",
"type": "Integer",
"required": true,
"default": 100,
"operator": { "options": ["<=", "<"], "default": "<=" }
}
],
"expression": "$Value = empty or ($Value {Lower.operator} $Lower and $Value {Upper.operator} $Upper)",
"message": "{AttributeName} must be between {Lower} and {Upper}"
}Met gebruik Lower=18/>=, Upper=65/<= op $Order/Age → $Order/Age = empty or ($Order/Age >= 18 and $Order/Age <= 65), message Age must be between 18 and 65.
Voorbeeld C — server-only
Zelfde als voorbeeld A maar Server-only aangevinkt → "serverOnly": true. Aangeboden in Validate…; weggelaten uit DTO…-nanoflow-expression-presets.
2. TBT Attribute Preset
2.1 Wat het is en waar het leeft
Een TBT Attribute Preset beschrijft één attribuut om op een domain entity te maken (type, length, default, required, optionele calculated microflow, en catalogus-zoekmetadata).
- Documenttype-id:
TheBoringTool.AttributePreset - Leesbare naam:
TBT Attribute Preset - Hint voor nieuwe documentnaam:
AttributePreset - Geseede map: module
TheBoringTool→ mapAttributePresets(aangemaakt/gevuld bij registratie van de extensie indien ontbrekend) - Override-key: opgeslagen veld
name— exact, case-sensitive. App-module-presets met dezelfdenameoverschrijven TheBoringTool-standaardpresets - Aanmaken / bewerken: formuliereditor (auto-persist). Ruwe JSON wordt niet door de gebruiker bewerkt. Je kunt presets ook onder elke module toevoegen; de picker merget de hele catalogus
2.2 Editorvelden en opgeslagen keys
Secties: Attribute, Catalog.
| Editorlabel | Opgeslagen key | Type | Toegestane waarden | Verplicht | Default | Opmerkingen |
|---|---|---|---|---|---|---|
| (impliciet) | schemaVersion | 1 | altijd 1 | ja | 1 | Bij parse naar 1 gezet |
| Name | name | string | Mendix-attribuutnaam; tevens cataloguskey | zou niet-leeg moeten zijn om in de catalogus te verschijnen | "" | Getrimd bij parse. Lege name → overgeslagen in catalogus-merge |
| Type | type | enum | String, Integer, Long, Decimal, DateTime, Boolean | ja | String indien ongeldig | |
| Length | length | number | geheel getal ≥ 0; alleen String; 0 = unlimited | optioneel | weggelaten | Verborgen voor non-String; verwijderd als type ≠ String |
| Default value | defaultValue | string | Mendix StoredValue-wireformaat | optioneel | weggelaten | Verborgen bij Calculated; verwijderd als calculated |
| Required | required | boolean | optioneel | weggelaten / false | Verborgen bij Calculated; verwijderd als calculated | |
| Calculated (creates CAL_ microflow under Private) | calculated | boolean | optioneel | weggelaten / false | Indien true, wist defaultValue en required | |
| Tags | tags | string[] | zoekchips | optioneel | [] | Trim; lege weg; case-insensitive dedupe |
| Default for entity names | defaultForEntities | string[] | hints voor entitynamen | optioneel | [] | Alleen soft ranking (verbergt nooit) |
| Aliases | aliases | string[] | extra zoektermen | optioneel | [] | Name is altijd doorzoekbaar |
2.3 Semantiek
`name`
Wordt geëmit als de attribuutnaam op de doelentity. Dezelfde string is de catalogus-override-key.
`type` / `length` / `defaultValue`
lengthalleen toegepast wanneer typeStringis (0= unlimited in Mendix).defaultValuegeschreven opDomainModels$StoredValue.defaultValueals de string die je invoerde (Boolean-voorbeelden in de seed gebruiken"true"; Decimal gebruikt"0").
`required`
Indien true (en niet calculated) voegt TBT na het aanmaken van het attribuut best-effort een Required-validatieregel toe (DomainModels$RequiredRuleInfo) met fouttekst:
{AttributeName} is required.
Bestaat er al een Required-regel voor dat attribuut, dan blijft die staan. Fouten worden gelogd; attribuut-emit slaagt alsnog.
`calculated`
Indien true:
- Zorgt voor microflow
CAL_{entity}_{attribute}onder mapModel/{entity}/Private(map-/naamsjablonen zijn via settings overschrijfbaar; pattern-idcal). - Stub End-return:
''/false/empty/0per type (Long-returntype wordtIntegerin de Microflow-API). - Attribuutwaarde =
CalculatedValuemet die microflow,passEntity: true. - Als pattern
caluitstaat in Settings → emit-fout:Calculated attributes are disabled in Settings (pattern cal). - Bestaande microflow met die naam → hergebruiken (niet opnieuw aanmaken).
Tags / aliases / `defaultForEntities`
- Picker-zoekscore: name (exact 100, prefix 80, contains 60), daarna aliases (55/45), daarna tags (30).
- Als de doelentitynaam matcht met
defaultForEntities(case-insensitive exact), krijgt de score +15 (softBoost). Presets worden nooit verborgen alleen omdat de entity niet matcht.
2.4 Wat de Attributes-picker schrijft
Voor elke geselecteerde preset (plan: skip als attribuutnaam al op de entity staat):
| Resultaat | Betekenis |
|---|---|
| created | Attribuut toegevoegd aan de entity; domain model opgeslagen als er iets is aangemaakt |
| skipped / exists | Pickerstatus: {name} already exists — skipped. |
| error | bijv. Could not load the target entity. of calculated-disabled-bericht |
Ook Required-regel en/of calculated microflow zoals hierboven.
2.5 Fouten, waarschuwingen en catalogusdiagnostiek
| Niveau | Bericht (uit de code) |
|---|---|
| editor persist | Could not save attribute preset. |
| laden | Document was not found. |
| laden | Document contents could not be read. Expected JSON matching the IR schema. |
| picker | Attribute name is required. |
| picker | {name} already exists — skipped. |
| emit | Could not load the target entity. |
| emit | Calculated attributes are disabled in Settings (pattern cal). |
| catalogus warn | Multiple Attribute Preset documents in module '{module}' share key '{key}' ({docs}); using '{winner}'. |
| catalogus info | '{module}.{doc}' overrides TBT default ({libDocs}) for key '{key}'. |
| catalogus warn | Multiple non-TBT modules define Attribute Preset key '{key}' ({modules}); using '{module}.{doc}'. |
Normalisatie: onbekende keys weg; ongeldig type → String; non-String length weg; calculated wist defaultValue/required; stringlijsten getrimd/gededupliceerd case-insensitive.
2.6 Voorbeelden
Voorbeeld A — eenvoudige Boolean
Editor: Name Active; Type Boolean; Default value true; Tags status; Aliases IsActive, Enabled.
{
"schemaVersion": 1,
"name": "Active",
"type": "Boolean",
"defaultValue": "true",
"tags": ["status"],
"defaultForEntities": [],
"aliases": ["IsActive", "Enabled"]
} Voorbeeld B — realistische String (geseede Email)
Editor: Name Email; Type String; Length 254; Required uit; Tags contact; Default for entity names Person, Customer, User, Contact; Aliases EmailAddress.
{
"schemaVersion": 1,
"name": "Email",
"type": "String",
"length": 254,
"required": false,
"tags": ["contact"],
"defaultForEntities": ["Person", "Customer", "User", "Contact"],
"aliases": ["EmailAddress"]
} Voorbeeld C — calculated
Editor: Name Total; Type Decimal; Calculated aan (Default value / Required verborgen).
{
"schemaVersion": 1,
"name": "Total",
"type": "Decimal",
"calculated": true,
"tags": [],
"defaultForEntities": [],
"aliases": []
}Op entity Order maakt dit attribuut Total (calculated) en microflow Order.CAL_Order_Total onder Model/Order/Private (stub die 0 teruggeeft), tenzij pattern cal is uitgeschakeld.
3. Gerelateerde flows (kort overzicht)
| UI | Validation Expression | Attribute Preset |
|---|---|---|
| Documenteditor | Formulier → genormaliseerde JSON-blob | Formulier → genormaliseerde JSON-blob |
| Validate… | Expression-presets (plus Mendix Rules uit een optionele map Validation/Rules/{Type}, als die bestaat); MF-pad neemt serverOnly mee | — |
| DTO… | Expression-presets op attribuut-nanoflows; sluit serverOnly uit | — |
| Attributes-picker | — | Catalogus mergen → attributen / rules / CAL_-MF’s emitten |