You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -40,21 +40,6 @@ Each directive updates the local variable scope immediately and produces **no ou
40
40
41
41
Variables defined this way are **local to the current mapping node** and automatically propagate to all of its descendants.
42
42
43
-
::: tip Scope & Propagation
44
-
45
-
1. **Immediate Effect:**
46
-
Each `!var` directive is evaluated in order. Once declared, the variable is available to all subsequent keys, values, and nested mappings **within the same mapping node**.
47
-
48
-
1. **Mapping‑Node Boundaries:**
49
-
Entering a nested mapping creates a new scope.
50
-
Variables declared in the parent mapping remain visible to the child, but variables declared inside the child mapping do **not** propagate back to the parent.
51
-
52
-
1. **Sequential Evaluation:**
53
-
Variables only apply to entries that appear **after** their declaration.
54
-
Earlier entries in the same mapping cannot see variables declared later.
55
-
56
-
:::
57
-
58
43
#### `!var` Syntax
59
44
60
45
Declare a variable using the key‑level form:
@@ -80,112 +65,13 @@ endpoint: "${api_url}/users"
80
65
endpoint: "http://localhost:8080/v1/users"
81
66
```
82
67
83
-
#### `!var` Scope Diagram
84
-
85
-
```text
86
-
parent-map:
87
-
├─ !var a: 1 ← defines `a` in this mapping
88
-
├─ key1: ${a} ← sees `a`
89
-
│
90
-
├─ child-map: ← new mapping node (inherits `a`)
91
-
│ ├─ key2: ${a} ← sees `a` but not `b`
92
-
│ │ (because `b` is declared *after* this entry)
93
-
│ ├─ !var b: 2 ← defines `b` only in this child mapping
If used inside a list item (e.g., `- !var foo: bar`), the list element becomes a mapping containing the directive.
105
-
If your list item must remain a scalar, declare the variable in the parent mapping instead.
106
-
68
+
::: tip Position dependence and usage
69
+
The top‑level `variables:` block is position independent (its variables are visible everywhere).
70
+
Inline `!var` directives are position dependent: they take effect where they appear and only affect subsequent entries in the same mapping node and its descendants.
71
+
`!var`can be used to **define new local variables** or to **override** existing ones.
72
+
See [Variable Scoping and Isolation](#variable-scoping-and-isolation) for more info.
107
73
:::
108
74
109
-
## Variable Scoping & Isolation
110
-
111
-
Understanding how variable scope flows is critical when designing complex modular compositions.
112
-
113
-
### Propagation & Isolation Rules
114
-
115
-
Inline variables follow a combination of sequential evaluation and lexical block isolation:
116
-
117
-
- **Sequential Propagation:** A `!var` directive applies to all subsequent entries (keys, values, and nested descendant blocks) evaluated after it within the same mapping context.
118
-
- **Downstream Inheritance:** Nested maps, templates (`!insert`), included files (`!include`), conditional branches (`!if`), and loop iterations (`!for`) inherit all variables in scope at their point of declaration.
119
-
- **Upward & Lateral Isolation:** Declarations made inside a child mapping block exist only within that branch and its descendants. They never leak upward to the parent mapping or laterally into adjacent sub-mappings.
120
-
121
-
#### Example: Scope Propagation & Isolation
122
-
123
-
```yaml
124
-
# Parent Mapping
125
-
!var prefix: "main"
126
-
127
-
# 1. Applies to subsequent keys, values, and nested descendants:
state: "${local_val}" # WARNING: local_val is undefined here
146
-
```
147
-
148
-
#### Example: Template, Package & Loop Isolation
149
-
150
-
Because control structures and modular includes create their own child mapping contexts, local variables declared inside them remain isolated to that iteration or file execution:
151
-
152
-
```yaml
153
-
templates:
154
-
component:
155
-
!var internal_id: "tpl_123"
156
-
id: "${internal_id}"
157
-
name: "${component_name}"
158
-
159
-
component_instance:
160
-
!insert
161
-
template: component
162
-
vars:
163
-
component_name: "sensor_main"
164
-
165
-
outer_id: "${internal_id}" # Warning: internal_id is undefined in outer scope
166
-
```
167
-
168
-
### Progressive Evaluation & Self-Referencing
169
-
170
-
Within a mapping block or sequence of key-form `!var` directives, variables evaluate sequentially from top to bottom.
171
-
172
-
- **Progressive Resolution:** A variable can reference previously defined variables within the same block or earlier single-form `!var` directives.
173
-
- **Sequential Re-assignment & Self-Reference:** Re-declaring an existing variable name evaluates the expression against the **current scope value** before updating the variable for subsequent substitutions. Prior substitutions retain the value active at the time they were evaluated.
174
-
175
-
```yaml
176
-
!var mode: "dev"
177
-
env_first: "${mode}" # Resolves to "dev"
178
-
179
-
!var mode: "prod"
180
-
env_second: "${mode}" # Resolves to "prod"
181
-
182
-
!var count: 10
183
-
!var count: "${count + 1}" # Evaluates ${count + 1} using current scope (10) -> 11
184
-
total_count: "${count}" # Resolves to 11
185
-
```
186
-
187
-
> **Note:** Referencing an undefined variable in a self-assignment (e.g., `!var count: "${count}"` when `count` does not exist in scope) logs an unresolved variable warning and evaluates to `null`.
# - Value access: ${enumerated_map[0][1].value} -> "one"
501
387
```
502
388
503
-
## Advanced Usage
389
+
## Advanced Topics
390
+
391
+
### Variable Scoping and Isolation
392
+
393
+
Variable scoping in YAML Composer follows a strict combination of **sequential evaluation**, **lexical mapping‑node boundaries**, and **downstream inheritance**. The two declaration mechanisms — the top‑level `variables:` block (global scope) and inline `!var` directives (local scope) — participate in the same unified scoping model. This section explains how they interact and lists the concrete rules you must follow.
394
+
395
+
#### Global vs Local: how `variables:` and `!var` interact
396
+
397
+
- **Global variables (`variables:` block)**
398
+
- Define the **initial scope** for the entire file.
399
+
- Are visible everywhere in the file, including included files, templates, and loops, regardless of where the `variables:` block appears in the document (i.e., `variables:` is position independent).
400
+
- Are evaluated as part of the file composition and act as the root values that inline `!var` directives may override locally.
401
+
- **Cannot** override system variables (e.g., `OPENHAB_CONF`, `__FILE__`, etc.).
402
+
403
+
- **Inline variables (`!var` directives)**
404
+
- Declare or reassign variables **inline at the key level** within a mapping.
405
+
- Update the local variable scope immediately and produce **no output key** in the final composed structure.
406
+
- Are **local to the mapping node** in which they appear and automatically propagate to that node’s descendants.
407
+
- Override global variables for that mapping and its descendants but do **not** change the `variables:` block itself.
408
+
- Are **position dependent**: a `!var` only affects entries that appear **after** it in the same mapping node. A `!var` placed at the root mapping behaves like a global override **from the point it appears onward** but does not retroactively change values already evaluated earlier in the file.
409
+
410
+
**Practical summary:** treat `variables:` as the file’s initial defaults (position independent) and `!var` as local, sequential declarations that take effect at the point they are evaluated (position dependent).
411
+
A `!var` may either **define a new local variable** or **override** an existing one; when a `!var` appears at the root mapping it behaves like a global override only for entries processed after it appears and does not retroactively change values already evaluated earlier in the file.
412
+
413
+
#### Core rules for `!var`
414
+
415
+
##### 1. Sequential Evaluation (Order Matters)
416
+
417
+
`!var`directives apply **immediately** and affect all subsequent keys, values, and nested mappings **within the same mapping node**.
418
+
419
+
- Earlier entries cannot see variables declared later.
420
+
- Reassigning a variable updates its value only for entries that appear after the reassignment.
421
+
- Reassignments evaluate expressions using the **current** scope value.
422
+
423
+
```yaml
424
+
!var mode: "dev"
425
+
first: "${mode}" # "dev"
426
+
427
+
!var mode: "prod"
428
+
second: "${mode}" # "prod"
429
+
```
430
+
431
+
##### 2. Mapping‑Node Boundaries (Lexical Scope)
432
+
433
+
Each mapping node defines a scope.
434
+
435
+
- Child mappings **inherit** all variables visible at the moment they are created.
436
+
- Variables declared **inside** a child mapping:
437
+
- apply only to that child and its descendants,
438
+
- do **not** propagate back to the parent,
439
+
- do **not** leak sideways into sibling mappings.
440
+
441
+
**Diagram:**
442
+
443
+
```text
444
+
parent-map:
445
+
├─ !var a: 1 ← defines `a` in this mapping
446
+
├─ key1: ${a} ← sees `a`
447
+
│
448
+
├─ child-map: ← new mapping node (inherits `a`)
449
+
│ ├─ key2: ${a} ← sees `a` but not `b`
450
+
│ │ (because `b` is declared *after* this entry)
451
+
│ ├─ !var b: 2 ← defines `b` only in this child mapping
Copy file name to clipboardExpand all lines: bundles/org.openhab.io.yamlcomposer/src/test/java/org/openhab/io/yamlcomposer/internal/YamlComposerVarTagTest.java
0 commit comments