The Boring Tool

Format reference

Field keys, tokens, stored JSON, and generate semantics for TBT custom documents.

This page describes the two custom document types registered by TheBoringTool (TBT) for Mendix Studio Pro 11.x:

Document type idReadable nameTypical use
TheBoringTool.ValidationExpressionTBT Validation ExpressionReusable validation condition + feedback message for Validate… and DTO…
TheBoringTool.AttributePresetTBT Attribute PresetReusable attribute definition for the Attributes picker

Content is stored as JSON inside a Studio Pro custom blob document. You edit it through the TBT form editor (not as raw JSON). The editor debounces changes and writes a normalized JSON blob via the model API.

Source of truth for this page: development branch schemas, Svelte editors, generate/DTO emitters, and catalog merge code.

1. TBT Validation Expression

1.1 What it is and where it lives

A TBT Validation Expression is a reusable validation template: a Mendix-style boolean expression, an optional parameter list (with optional operators), and a message template. At generate time TBT inlines the template into an Exclusive Split (expression condition) on an attribute validation microflow / DTO attribute nanoflow, and substitutes the message for Validation Feedback.

  • Document type id: TheBoringTool.ValidationExpression
  • Readable name in Studio Pro: TBT Validation Expression
  • New-document name hint: ValidationExpression
  • App Explorer: any module / folder. TBT loads all documents of this type in the app; there is no seeded library folder for validation expressions.
  • Create / edit: add a document of type TBT Validation Expression, then edit fields in the tab editor. There is no Save button in the form — changes persist automatically (debounced). Users do not edit raw JSON.

1.2 Editor fields and stored keys

Section headings in the editor: General, Params, Expression.

Editor labelStored keyTypeAllowed valuesRequiredDefaultNotes
(implicit)schemaVersionnumber literal 1always 1yes (always written)1Invalid / missing version is coerced to 1 on parse
Labellabelstringanyoptional""Catalog display name; if empty, the Studio Pro document name is used as the preset label
Documentationdocumentationstringanyoptional""Shown collapsed in Validate/DTO preset descriptions
Applies toappliesToValueType[]String, Integer, Long, Decimal, DateTime, Booleanoptional[]Checkboxes. Unknown types dropped; duplicates removed. HashedString attributes match when String is included
Server-onlyserverOnlybooleantrue / falseoptionalfalseSee §1.3
Default for attribute namesdefaultForAttributesstring[]attribute name hintsoptional[]Chip list. Trimmed; empties dropped; case-insensitive dedupe (first casing wins)
Param *N* nameparams[].namestringany (used as $Name / {Name} token)optional per param""Exact name is used in tokens; longer names substituted first
Param *N* typeparams[].typeValueTypesame as Applies tooptionalString if invalid
Required (on param)params[].requiredbooleanoptionalfalseUsed by Validate/DTO UI validation of usage values
Param *N* defaultparams[].defaultstring | number | boolean | nulltyped; empty clears; literal null → nulloptionalomittedInvalid typed input dropped
Operatorparams[].operator{ options: string[]; default: string }optionalomittedEnabling Operator seeds options: [">=", ">"], default: ">="
Param *N* operator optionsparams[].operator.optionsstring[]comma-separated in the formrequired if Operator onTrimmed; empties / duplicates dropped. Empty list → operator removed on normalize
Param *N* default operatorparams[].operator.defaultstringmust be one of optionsrequired if Operator onfirst option if invalid
ExpressionexpressionstringMendix expression templateoptional""Placeholders in §1.3
Messagemessagestringfeedback templateoptional""Placeholders in §1.3

Raw JSON is never presented for editing; only the form above.

1.3 Tokens, expression syntax, and semantics

Expression placeholders

TokenMeaning
$ValueReplaced with the attribute path, e.g. $Order/Age
$ParamNameReplaced with a typed literal from the usage value (or param default). Name must match params[].name exactly. Longer names win (so $MaxValue is not eaten by $Max)
{ParamName.operator}Replaced with the chosen operator string (usage → param default → first option)

The inlined result must be a valid Mendix microflow/nanoflow boolean expression (Exclusive Split expression condition). TBT does not compile-check it beyond string substitution.

Empty values: generated flows run the expression as-is, also when the attribute is empty. Write $Value = empty or ... unless the rule should reject empty values (for example, a "not blank" rule).

Literal formatting when substituting `$ParamName`:

Param typeEmpty usageNon-empty usage
Integer / Long / Decimal0trimmed text as-is (e.g. 42)
Booleanfalsetrue only if trimmed lower-case is true, else false
DateTimeemptyif value starts with [, used as-is; else quoted Mendix string (' → '')
String (default)''quoted Mendix string

Message placeholders

TokenMeaning
{AttributeName}Attribute name at generate / UI prefills
{ParamName}Param usage value (display = trimmed string); longer names first
{1}Left intact for Validation Feedback argument wiring (attribute value)

`serverOnly`

  • `false` (default): available to both Validate… (microflow) and DTO… attribute nanoflow paths.
  • `true`: still included on the Validate… microflow preset list (includeServerOnly: true). On the DTO nano path, expressions with serverOnly: true are excluded (includeServerOnly: false). Tracked as skipped document ids for nano copy.

`appliesTo` matching

A preset is offered for an attribute when appliesTo contains the attribute’s type, or the attribute is HashedString and appliesTo contains String.

`defaultForAttributes` auto-select

When Validate/DTO adds an attribute that has no rules yet, TBT looks for expression presets whose defaultForAttributes contains the attribute name (case-insensitive exact). If several match, the one with the smallest label via localeCompare wins.

1.4 What generate writes into the model

Validate… (attribute validation microflow) for an expression rule:

  1. Exclusive Split caption = preset label (or document name).
  2. Expression split condition = inlineValidationExpression(...) result (e.g. $Person/Age >= 18).
  3. Split documentation = TBT.ExpressionUsage: + JSON { documentId, params } for round-trip reload.
  4. On the false branch: call to mark validation context invalid + Validation Feedback on the attribute.
  5. Feedback template text = the (already substituted) message from the dialog; argument {1} is bound to $Entity/Attribute.

DTO… (attribute nanoflow): same inlining + TBT.ExpressionUsage: documentation + substituted message, on the nanoflow exclusive split — but only if the expression is not serverOnly.

Expression rules never use Mendix Rules; they always use an expression exclusive split.

1.5 Errors, warnings, and normalization

SituationUser-visible / logged text
Persist failure in editorCould not save validation expression. (+ details)
Document missing on loadDocument was not found.
Unreadable / non-JSON contentsDocument contents could not be read. Expected JSON matching the IR schema.
Hand-edited split no longer matches presetTooltip: Condition no longer matches the preset; Save will rewrite it
Required param empty in Validate UIparameter {name} is required for {preset label} (wording from generate editor)

Normalization on parse / save (silent):

  • Non-object JSON → empty document (schemaVersion: 1, empty strings/arrays, serverOnly: false).
  • Unknown keys dropped.
  • Invalid appliesTo entries dropped; invalid param objects dropped; invalid param type → String.
  • Invalid param default for the type → omitted.
  • Operator with no options → omitted; default operator coerced into options.
  • defaultForAttributes: trim, drop empties, case-insensitive dedupe.

1.6 Examples

Example A — simple (no params)

Editor

FieldValue
LabelNot blank
DocumentationTrimmed string must not be empty
Applies toString
Server-onlyunchecked
Default for attribute names(none)
Expressiontrim($Value) != ''
Message{AttributeName} must not be blank

Stored 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"
}

For attribute Name on entity Person, generate inlines to trim($Person/Name) != '' and message Name must not be blank.

Example B — realistic with params + operators

Editor

FieldValue
LabelRange
DocumentationInclusive/exclusive bounds via operators
Applies toInteger, Long, Decimal
Server-onlyunchecked
Default for attribute namesAge
Param 1 name / type / required / defaultLower / Integer / yes / 0
Param 1 Operatoron; options >=, >; default >=
Param 2 name / type / required / defaultUpper / Integer / yes / 100
Param 2 Operatoron; options <=, <; default <=
Expression$Value = empty or ($Value {Lower.operator} $Lower and $Value {Upper.operator} $Upper)
Message{AttributeName} must be between {Lower} and {Upper}

Stored 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}"
}

With usage Lower=18/>=, Upper=65/<= on $Order/Age → $Order/Age = empty or ($Order/Age >= 18 and $Order/Age <= 65), message Age must be between 18 and 65.

Example C — server-only

Same as Example A but Server-only checked → "serverOnly": true. Offered in Validate…; omitted from DTO… nanoflow expression presets.

2. TBT Attribute Preset

2.1 What it is and where it lives

A TBT Attribute Preset describes one attribute to create on a domain entity (type, length, default, required, optional calculated microflow, and catalog search metadata).

  • Document type id: TheBoringTool.AttributePreset
  • Readable name: TBT Attribute Preset
  • New-document name hint: AttributePreset
  • Seeded folder: module TheBoringTool → folder AttributePresets (created/filled on extension register if missing)
  • Override key: stored field name — exact, case-sensitive. App-module presets with the same name override TheBoringTool-shipped ones
  • Create / edit: form editor (auto-persist). Raw JSON is not user-edited. You can also add presets under any module; the picker merges the whole catalog

2.2 Editor fields and stored keys

Sections: Attribute, Catalog.

Editor labelStored keyTypeAllowed valuesRequiredDefaultNotes
(implicit)schemaVersion1always 1yes1Coerced to 1 on parse
NamenamestringMendix attribute name; also catalog keyshould be non-empty to appear in catalog""Trimmed on parse. Empty name → skipped in catalog merge
TypetypeenumString, Integer, Long, Decimal, DateTime, BooleanyesString if invalid
Lengthlengthnumber≥ 0 integer; String only; 0 = unlimitedoptionalomittedHidden for non-String; stripped if type ≠ String
Default valuedefaultValuestringMendix StoredValue wire formatoptionalomittedHidden when Calculated; stripped if calculated
Requiredrequiredbooleanoptionalomitted / falseHidden when Calculated; stripped if calculated
Calculated (creates CAL_ microflow under Private)calculatedbooleanoptionalomitted / falseWhen true, clears defaultValue and required
Tagstagsstring[]search chipsoptional[]Trim; drop empties; case-insensitive dedupe
Default for entity namesdefaultForEntitiesstring[]entity name hintsoptional[]Soft ranking only (never hides)
Aliasesaliasesstring[]extra search termsoptional[]Name is always searchable

2.3 Semantics

`name`

Emitted as the attribute name on the target entity. Same string is the catalog override key.

`type` / `length` / `defaultValue`

  • length applied only when type is String (0 = unlimited in Mendix).
  • defaultValue written onto DomainModels$StoredValue.defaultValue as the string you entered (Boolean examples in the seed use "true"; Decimal uses "0").

`required`

When true (and not calculated), after creating the attribute TBT best-effort adds a Required validation rule (DomainModels$RequiredRuleInfo) with error text:

{AttributeName} is required.

If a Required rule for that attribute already exists, it is left alone. Failures are logged; attribute emit still succeeds.

`calculated`

When true:

  1. Ensures microflow CAL_{entity}_{attribute} under folder Model/{entity}/Private (folder/name templates are settings-overridable; pattern id cal).
  2. Stub End return: '' / false / empty / 0 by type (Long return type becomes Integer in the Microflow API).
  3. Attribute value = CalculatedValue with that microflow, passEntity: true.
  4. If pattern cal is disabled in Settings → emit error: Calculated attributes are disabled in Settings (pattern cal).
  5. Existing microflow with that name → reuse (no recreate).

Tags / aliases / `defaultForEntities`

  • Picker search scores name (exact 100, prefix 80, contains 60), then aliases (55/45), then tags (30).
  • If the target entity name matches defaultForEntities (case-insensitive exact), score gets +15 (softBoost). Presets are never hidden solely because the entity does not match.

2.4 What the Attributes picker writes

For each selected preset (plan: skip if attribute name already on the entity):

ResultMeaning
createdAttribute pushed onto the entity; domain model saved if any created
skipped / existsPicker status: {name} already exists — skipped.
errore.g. Could not load the target entity. or calculated disabled message

Also creates Required rule and/or calculated microflow as above.

2.5 Errors, warnings, and catalog diagnostics

LevelMessage (from code)
editor persistCould not save attribute preset.
loadDocument was not found.
loadDocument 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).
catalog warnMultiple Attribute Preset documents in module '{module}' share key '{key}' ({docs}); using '{winner}'.
catalog info'{module}.{doc}' overrides TBT default ({libDocs}) for key '{key}'.
catalog warnMultiple non-TBT modules define Attribute Preset key '{key}' ({modules}); using '{module}.{doc}'.

Normalization: unknown keys dropped; invalid type → String; non-String length dropped; calculated clears defaultValue/required; string lists trimmed/deduped case-insensitively.

2.6 Examples

Example A — simple 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"]
}

Example B — realistic String (seeded Email)

Editor: Name Email; Type String; Length 254; Required unchecked; 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"]
}

Example C — calculated

Editor: Name Total; Type Decimal; Calculated checked (Default value / Required hidden).

{
  "schemaVersion": 1,
  "name": "Total",
  "type": "Decimal",
  "calculated": true,
  "tags": [],
  "defaultForEntities": [],
  "aliases": []
}

On entity Order this creates attribute Total (calculated) and microflow Order.CAL_Order_Total under Model/Order/Private (stub returning 0), unless pattern cal is disabled.

UIValidation ExpressionAttribute Preset
Document editorForm → normalized JSON blobForm → normalized JSON blob
Validate…Expression presets (plus Mendix Rules from an optional Validation/Rules/{Type} folder, if present); MF path includes serverOnly—
DTO…Expression presets on attribute nanoflows; excludes serverOnly—
Attributes picker—Merge catalog → emit attributes / rules / CAL_ MFs