Zum Inhalt

Variablen-Scope in verschachtelten Generates

Wenn Du eine <variable> aus einem äußeren <generate> in ein verschachteltes <generate> verschiebst, ändert sich der Besitzer des Werts. Mache diese Ownership in jedem script=-Ausdruck sichtbar, damit im Review eindeutig ist, ob der aktuelle oder der Parent-Datensatz gemeint ist.

Review-Regeln

  • Verwende this.<name> für einen Wert aus dem aktuellen verschachtelten Generate.
  • Verwende this.parent.<name>, wenn der direkte Parent den Wert besitzt.
  • Verwende einen expliziten benannten Pfad nur, wenn das Modell bewusst einen bereits aufgebauten verschachtelten Zweig adressiert.
  • Verwende root.<name> für einen Wert aus dem Setup-Scope.
  • Lies Memstore-Daten über die öffentliche Scripting-API. Bilde keinen Memstore-Datensatz mit einem lokal konstruierten dict nach, nur um ihn zwischen Scopes zu verschieben.

Schnelle Entscheidungshilfe

Du willst lesen... Bevorzugte Form
Einen Wert im aktuellen verschachtelten <generate> this.varName
Einen Wert im direkten Parent-<generate> this.parent.varName
Einen Setup-Wert root.varName
Einen bewusst benannten Zweig child.grand_child.varName

Ein unqualifizierter Name kann weiterhin auf einen äußeren Wert zeigen. Auf der obersten Ebene ist das kompakt, wird aber mehrdeutig, sobald derselbe Name in mehreren verschachtelten Generates vorkommt.

Beispiel 1: Memstore-Werte über die Scripting-API

Der Helfer hält die Memstore-Kommunikation hinter der unterstützten schreibgeschützten API:

script/scope_memstore.scr.py
1
2
3
4
5
from datamimic_ee.scripting import load_memstore


def first_memstore_value(product_name: str):
    return load_memstore(product_name)[0]["value"]

Ein Wert des Parents

scope-outer.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<setup>
    <execute uri="script/scope_memstore.scr.py"/>

    <generate name="outer_values" count="1" target="mem">
        <key name="value" constant="outer"/>
    </generate>

    <generate name="batch" count="1">
        <variable name="selected" script="first_memstore_value('outer_values')"/>

        <generate name="datFileCreation" count="1">
            <key name="selected" script="this.parent.selected"/>
        </generate>
    </generate>
</setup>

selected gehört zu batch. Das verschachtelte Generate liest den Wert daher explizit aus this.parent.

Aktueller und Parent-Wert mit demselben Namen

scope-shadowing.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<setup>
    <execute uri="script/scope_memstore.scr.py"/>

    <generate name="outer_values" count="1" target="mem">
        <key name="value" constant="outer"/>
    </generate>
    <generate name="inner_values" count="1" target="mem">
        <key name="value" constant="inner"/>
    </generate>

    <generate name="batch" count="1">
        <variable name="selected" script="first_memstore_value('outer_values')"/>

        <generate name="datFileCreation" count="1">
            <variable name="selected" script="first_memstore_value('inner_values')"/>

            <key name="selected_current" script="this.selected"/>
            <key name="selected_parent" script="this.parent.selected"/>
            <key name="selected_named" script="datFileCreation.selected"/>
        </generate>
    </generate>
</setup>

Die drei Ausdrücke machen ihre Ownership reviewbar: this.selected und datFileCreation.selected lesen inner; this.parent.selected liest outer.

Beispiel 2: Cascade Generate und this.id

scope-cascade.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<setup>
    <generate name="cascade_generate_test" count="1">
        <variable name="id" generator="IncrementGenerator"/>
        <key name="outer_id" script="id"/>

        <generate name="first_inner_generate" count="2">
            <variable name="id" generator="IncrementGenerator"/>
            <key name="first_inner_id" script="this.id"/>

            <generate name="second_inner_generate" count="2">
                <variable name="id" generator="IncrementGenerator"/>
                <key name="second_inner_id" script="this.id"/>

                <generate name="third_inner_generate" count="2">
                    <variable name="id" generator="IncrementGenerator"/>
                    <key name="third_inner_id" script="this.id"/>
                </generate>
            </generate>
        </generate>
    </generate>
</setup>

Das äußere id bleibt unqualifiziert, weil es auf der obersten Ebene liegt. Jedes verschachtelte id verwendet this.id; so bleibt trotz gleicher Namen eindeutig, welcher Wert gelesen wird.

Beispiel 3: Zugriff über benannte Pfade

scope-named-path.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<setup>
    <generate name="parent" count="2">
        <variable name="count_global" script="2"/>

        <generate name="child" count="{count_global}">
            <variable name="idx" generator="IncrementGenerator"/>
            <key name="child_id" script="child.idx"/>

            <generate name="grand_child" count="{child.idx}">
                <variable name="index" generator="IncrementGenerator"/>
                <key name="grand_child_id" script="child.grand_child.index"/>
                <key name="parent_idx" script="child.idx"/>
            </generate>
        </generate>
    </generate>
</setup>

Verwende einen benannten Pfad, wenn der Pfad selbst fachlich zum Modell gehört. Für einen rein lokalen Wert bleibt this.index leichter zu prüfen.

Beispiel 4: Explizite Pfade in nestedKey

scope-nested-key.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<setup>
    <generate name="customer" count="1">
        <generate name="data" count="1">
            <nestedKey name="send_info" type="list" count="2">
                <variable name="email" constant="[email protected]"/>
                <variable name="sms" constant="+49123456789"/>

                <nestedKey name="contact" type="list" count="3">
                    <key name="email" script="data.send_info.email"/>
                    <key name="sms" script="data.send_info.sms"/>
                </nestedKey>
            </nestedKey>
        </generate>
    </generate>
</setup>

Der Pfad zeigt, dass contact bewusst Werte von send_info wiederverwendet.

Beispiel 5: Gemeinsam außen, lokal innen

scope-shared-local.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<setup>
    <generate name="batch" count="1">
        <variable name="customer_id" constant="C-1000"/>

        <generate name="invoice" count="1">
            <variable name="lineIndex" generator="IncrementGenerator"/>
            <key name="customer_id" script="this.parent.customer_id"/>
            <key name="invoice_line" script="this.lineIndex"/>
        </generate>

        <generate name="deliveryNote" count="1">
            <variable name="lineIndex" generator="IncrementGenerator"/>
            <key name="customer_id" script="this.parent.customer_id"/>
            <key name="delivery_line" script="this.lineIndex"/>
        </generate>
    </generate>
</setup>

Die gemeinsam genutzte Customer-ID bleibt im Parent. Jedes Kind behält seinen eigenen lokalen Zeilenzähler und qualifiziert beide Zugriffe.

Fehlerdiagnose

Symptom Wahrscheinliche Ursache Korrektur
variable not found Der Wert gehört zu einem anderen Scope Verwende this.<name>, this.parent.<name> oder den beabsichtigten benannten Pfad
Falscher Wert ohne Fehler Ein unqualifizierter Name wurde gegen einen äußeren Wert aufgelöst Qualifiziere aktuelle und Parent-Ownership explizit
Ein Pfad bricht nach einer Nesting-Änderung Der benannte Zweig hat sich geändert Bevorzuge this.* für lokale Werte und reserviere benannte Pfade für bewussten Zweigzugriff
Memstore-Logik legt Runtime-Objekte offen Das Modell umgeht die öffentliche Grenze Verschiebe den Zugriff nach datamimic_ee.scripting.load_memstore() oder load_memstore_page()

Verwandte Dokumentation