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-idLeesbare naamTypisch gebruik
TheBoringTool.ValidationExpressionTBT Validation ExpressionHerbruikbare validatieconditie + feedbackbericht voor Validate… en DTO…
TheBoringTool.AttributePresetTBT Attribute PresetHerbruikbare 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.

EditorlabelOpgeslagen keyTypeToegestane waardenVerplichtDefaultOpmerkingen
(impliciet)schemaVersiongetalliteraal 1altijd 1ja (wordt altijd geschreven)1Ongeldige / ontbrekende versie wordt bij parse naar 1 gezet
Labellabelstringwillekeurigoptioneel""Weergavenaam in de catalogus; indien leeg wordt de Studio Pro-documentnaam als presetlabel gebruikt
Documentationdocumentationstringwillekeurigoptioneel""Samengevouwen getoond in Validate/DTO-presetbeschrijvingen
Applies toappliesToValueType[]String, Integer, Long, Decimal, DateTime, Booleanoptioneel[]Checkboxes. Onbekende types worden genegeerd; duplicaten verwijderd. HashedString-attributen matchen wanneer String is aangevinkt
Server-onlyserverOnlybooleantrue / falseoptioneelfalseZie §1.3
Default for attribute namesdefaultForAttributesstring[]hints voor attribuutnamenoptioneel[]Chiplijst. Getrimd; lege waarden weg; case-insensitive dedupe (eerste casing wint)
Param *N* nameparams[].namestringwillekeurig (gebruikt als $Name / {Name}-token)optioneel per param""Exacte naam in tokens; langere namen eerst gesubstitueerd
Param *N* typeparams[].typeValueTypezelfde als Applies tooptioneelString indien ongeldig
Required (op param)params[].requiredbooleanoptioneelfalseGebruikt door Validate/DTO-UI-validatie van gebruikswaarden
Param *N* defaultparams[].defaultstring | number | boolean | nullgetypeerd; leeg wist; letterlijk null → nulloptioneelweggelatenOngeldige getypeerde input wordt weggelaten
Operatorparams[].operator{ options: string[]; default: string }optioneelweggelatenOperator aanzetten seed’t options: [">=", ">"], default: ">="
Param *N* operator optionsparams[].operator.optionsstring[]kommagescheiden in het formulierverplicht als Operator aanGetrimd; lege / duplicaten weg. Lege lijst → operator verwijderd bij normalisatie
Param *N* default operatorparams[].operator.defaultstringmoet in options zittenverplicht als Operator aaneerste optie indien ongeldig
ExpressionexpressionstringMendix expression-templateoptioneel""Placeholders in §1.3
Messagemessagestringfeedbacktemplateoptioneel""Placeholders in §1.3

Ruwe JSON wordt nooit ter bewerking getoond; alleen het formulier hierboven.

1.3 Tokens, expressionsyntax en semantiek

Expression-placeholders

TokenBetekenis
$ValueVervangen door het attribuutpad, bijv. $Order/Age
$ParamNameVervangen 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`:

ParamtypeLeeg gebruikNiet-leeg gebruik
Integer / Long / Decimal0getrimde tekst as-is (bijv. 42)
Booleanfalsetrue alleen als getrimde lower-case true is, anders false
DateTimeemptyals waarde met [ begint, as-is; anders gequote Mendix-string (' → '')
String (default)''gequote Mendix-string

Message-placeholders

TokenBetekenis
{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 met serverOnly: true uitgesloten (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:

  1. Exclusive Split-caption = preset label (of documentnaam).
  2. Expression split condition = resultaat van inlineValidationExpression(...) (bijv. $Person/Age >= 18).
  3. Split-documentation = TBT.ExpressionUsage: + JSON { documentId, params } voor round-trip reload.
  4. Op de false-tak: aanroep om de validation context ongeldig te maken + Validation Feedback op het attribuut.
  5. 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

SituatieGebruikerszichtbare / gelogde tekst
Persist-fout in editorCould not save validation expression. (+ details)
Document ontbreekt bij ladenDocument was not found.
Onleesbare / non-JSON-inhoudDocument contents could not be read. Expected JSON matching the IR schema.
Handmatig bewerkte split komt niet meer overeen met presetTooltip: Condition no longer matches the preset; Save will rewrite it
Verplichte param leeg in Validate-UIparameter {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-default voor het type → weggelaten.
  • Operator zonder options → weggelaten; default-operator wordt in options gedwongen.
  • defaultForAttributes: trimmen, lege weg, case-insensitive dedupe.

1.6 Voorbeelden

Voorbeeld A — eenvoudig (geen params)

Editor

VeldWaarde
LabelNot blank
DocumentationTrimmed string must not be empty
Applies toString
Server-onlyuit
Default for attribute names(geen)
Expressiontrim($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

VeldWaarde
LabelRange
DocumentationInclusive/exclusive bounds via operators
Applies toInteger, Long, Decimal
Server-onlyuit
Default for attribute namesAge
Param 1 name / type / required / defaultLower / Integer / ja / 0
Param 1 Operatoraan; options >=, >; default >=
Param 2 name / type / required / defaultUpper / Integer / ja / 100
Param 2 Operatoraan; 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 → map AttributePresets (aangemaakt/gevuld bij registratie van de extensie indien ontbrekend)
  • Override-key: opgeslagen veld name — exact, case-sensitive. App-module-presets met dezelfde name overschrijven 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.

EditorlabelOpgeslagen keyTypeToegestane waardenVerplichtDefaultOpmerkingen
(impliciet)schemaVersion1altijd 1ja1Bij parse naar 1 gezet
NamenamestringMendix-attribuutnaam; tevens cataloguskeyzou niet-leeg moeten zijn om in de catalogus te verschijnen""Getrimd bij parse. Lege name → overgeslagen in catalogus-merge
TypetypeenumString, Integer, Long, Decimal, DateTime, BooleanjaString indien ongeldig
Lengthlengthnumbergeheel getal ≥ 0; alleen String; 0 = unlimitedoptioneelweggelatenVerborgen voor non-String; verwijderd als type ≠ String
Default valuedefaultValuestringMendix StoredValue-wireformaatoptioneelweggelatenVerborgen bij Calculated; verwijderd als calculated
Requiredrequiredbooleanoptioneelweggelaten / falseVerborgen bij Calculated; verwijderd als calculated
Calculated (creates CAL_ microflow under Private)calculatedbooleanoptioneelweggelaten / falseIndien true, wist defaultValue en required
Tagstagsstring[]zoekchipsoptioneel[]Trim; lege weg; case-insensitive dedupe
Default for entity namesdefaultForEntitiesstring[]hints voor entitynamenoptioneel[]Alleen soft ranking (verbergt nooit)
Aliasesaliasesstring[]extra zoektermenoptioneel[]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`

  • length alleen toegepast wanneer type String is (0 = unlimited in Mendix).
  • defaultValue geschreven op DomainModels$StoredValue.defaultValue als 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:

  1. Zorgt voor microflow CAL_{entity}_{attribute} onder map Model/{entity}/Private (map-/naamsjablonen zijn via settings overschrijfbaar; pattern-id cal).
  2. Stub End-return: '' / false / empty / 0 per type (Long-returntype wordt Integer in de Microflow-API).
  3. Attribuutwaarde = CalculatedValue met die microflow, passEntity: true.
  4. Als pattern cal uitstaat in Settings → emit-fout: Calculated attributes are disabled in Settings (pattern cal).
  5. 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):

ResultaatBetekenis
createdAttribuut toegevoegd aan de entity; domain model opgeslagen als er iets is aangemaakt
skipped / existsPickerstatus: {name} already exists — skipped.
errorbijv. 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

NiveauBericht (uit de code)
editor persistCould not save attribute preset.
ladenDocument was not found.
ladenDocument contents could not be read. Expected JSON matching the IR schema.
pickerAttribute name is required.
picker{name} already exists — skipped.
emitCould not load the target entity.
emitCalculated attributes are disabled in Settings (pattern cal).
catalogus warnMultiple Attribute Preset documents in module '{module}' share key '{key}' ({docs}); using '{winner}'.
catalogus info'{module}.{doc}' overrides TBT default ({libDocs}) for key '{key}'.
catalogus warnMultiple 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.

UIValidation ExpressionAttribute Preset
DocumenteditorFormulier → genormaliseerde JSON-blobFormulier → 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