refactor(meta-compile): единый data-driven эмиттер StandardAttributes вместо форка (v1.17)

Свернул два форка (Emit-StandardAttributes безусловный + Emit-StandardAttributesProfiled) в
ОДНУ функцию, поведение которой правят справочники, а не код:
- stdAttrConditionalTypes (пока {Catalog}) — типы, где блок только при DSL-ключе standardAttributes;
- stdAttrProfile[тип] — профиль материализованного блока.
Прочие 13 типов (не в справочниках) ведут себя ровно как раньше (безусловный all-default).

Убирает легаси-мост и будущий rename: вызов Catalog вернулся к каноничному
Emit-StandardAttributes(i,'Catalog'); нет второй функции/суффикса _profiled. Миграция типа =
+строчка в два справочника + переснять снэпшоты, кода не трогаем.

Поведение не изменилось: byte-match блока на синтетике, регресс 34/34 (ps+py), снэпшоты не тронуты.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-01 11:21:39 +03:00
parent df5c1ee17d
commit ed42d5e9cb
2 changed files with 40 additions and 47 deletions
@@ -876,39 +876,34 @@ function Emit-StandardAttribute {
X "$indent</xr:StandardAttribute>"
}
# Единый эмиттер блока StandardAttributes — поведение правят ДАННЫЕ, не форк кода:
# - stdAttrConditionalTypes: типы, где блок материализуется платформой ТОЛЬКО при кастомизации
# ≥1 стандартного реквизита → в DSL это наличие ключа `standardAttributes`. Нет ключа → блок опущен.
# Прочие типы (не в множестве) → блок эмитится всегда (текущее поведение, пока их правило не выведено).
# - stdAttrProfile[тип]: профиль материализованного блока (пусто = schema-дефолт), поверх — DSL-override.
# Миграция типа = добавить его в stdAttrConditionalTypes + stdAttrProfile и переснять снэпшоты; КОД НЕ ТРОГАЕМ.
$script:stdAttrConditionalTypes = @('Catalog')
function Emit-StandardAttributes {
param([string]$indent, [string]$objectType)
$attrs = $script:standardAttributesByType[$objectType]
if (-not $attrs) { return }
X "$indent<StandardAttributes>"
foreach ($a in $attrs) {
Emit-StandardAttribute "$indent`t" $a
}
X "$indent</StandardAttributes>"
}
# Профильный+условный эмиттер блока StandardAttributes (общий, ключёван типом).
# Блок материализуется платформой ТОЛЬКО при кастомизации ≥1 стандартного реквизита → в DSL это
# наличие ключа `standardAttributes` (map имяРеквизита → {synonym, fillChecking, fillFromFillingValue,
# fullTextSearch, dataHistory}). Наличие ключа = блок включён; база = профиль типа (stdAttrProfile),
# поверх — DSL-override. Ключа нет → блок опускаем. Пока подключён только Catalog; при пилоте прочих
# типов: добавить их профиль в stdAttrProfile + переключить их вызов сюда (+ переснять снэпшоты).
function Emit-StandardAttributesProfiled {
param([string]$indent, [string]$objectType)
if ($null -eq $def.standardAttributes) { return }
$conditional = $script:stdAttrConditionalTypes -contains $objectType
$sa = $def.standardAttributes
if ($conditional -and $null -eq $sa) { return } # условный тип без кастомизации → блока нет
$profile = $script:stdAttrProfile[$objectType]; if (-not $profile) { $profile = @{} }
$attrs = $script:standardAttributesByType[$objectType]
X "$indent<StandardAttributes>"
foreach ($a in $attrs) {
$ov = @{}
if ($profile.ContainsKey($a)) { foreach ($k in $profile[$a].Keys) { $ov[$k] = $profile[$a][$k] } }
$d = $def.standardAttributes.$a
if ($d) {
if ($null -ne $d.synonym) { $ov['Synonym'] = "$($d.synonym)" }
if ($d.fillChecking) { $ov['FillChecking'] = "$($d.fillChecking)" }
if ($null -ne $d.fillFromFillingValue) { $ov['FillFromFillingValue'] = if ($d.fillFromFillingValue) { 'true' } else { 'false' } }
if ($d.fullTextSearch) { $ov['FullTextSearch'] = "$($d.fullTextSearch)" }
if ($d.dataHistory) { $ov['DataHistory'] = "$($d.dataHistory)" }
if ($conditional -and $sa) {
$d = $sa.$a
if ($d) {
if ($null -ne $d.synonym) { $ov['Synonym'] = "$($d.synonym)" }
if ($d.fillChecking) { $ov['FillChecking'] = "$($d.fillChecking)" }
if ($null -ne $d.fillFromFillingValue) { $ov['FillFromFillingValue'] = if ($d.fillFromFillingValue) { 'true' } else { 'false' } }
if ($d.fullTextSearch) { $ov['FullTextSearch'] = "$($d.fullTextSearch)" }
if ($d.dataHistory) { $ov['DataHistory'] = "$($d.dataHistory)" }
}
}
Emit-StandardAttribute "$indent`t" $a $ov
}
@@ -1304,7 +1299,7 @@ function Emit-CatalogProperties {
$defaultPresentation = Get-EnumProp "DefaultPresentation" "defaultPresentation" "AsDescription"
X "$i<DefaultPresentation>$defaultPresentation</DefaultPresentation>"
Emit-StandardAttributesProfiled $i "Catalog"
Emit-StandardAttributes $i "Catalog"
X "$i<Characteristics/>"
X "$i<PredefinedDataUpdate>Auto</PredefinedDataUpdate>"
X "$i<EditType>InDialog</EditType>"
@@ -866,37 +866,35 @@ def emit_standard_attribute(indent, attr_name, ov=None):
X(f'{indent}\t<xr:ChoiceParameters/>')
X(f'{indent}</xr:StandardAttribute>')
# Единый эмиттер блока StandardAttributes — поведение правят ДАННЫЕ, не форк кода (см. коммент в .ps1).
# std_attr_conditional_types: типы, где блок только при кастомизации (DSL-ключ standardAttributes).
# Прочие типы → блок всегда (текущее поведение). Миграция типа = +строчка в оба справочника + снэпшоты.
std_attr_conditional_types = {'Catalog'}
def emit_standard_attributes(indent, object_type):
attrs = standard_attributes_by_type.get(object_type)
if not attrs:
return
X(f'{indent}<StandardAttributes>')
for a in attrs:
emit_standard_attribute(f'{indent}\t', a)
X(f'{indent}</StandardAttributes>')
# Профильный+условный эмиттер блока StandardAttributes (общий, ключёван типом; см. коммент в .ps1).
def emit_standard_attributes_profiled(indent, object_type):
conditional = object_type in std_attr_conditional_types
sa = defn.get('standardAttributes')
if sa is None:
if conditional and sa is None:
return
profile = std_attr_profile.get(object_type, {})
attrs = standard_attributes_by_type[object_type]
X(f'{indent}<StandardAttributes>')
for a in attrs:
ov = dict(profile.get(a, {}))
d = sa.get(a) if isinstance(sa, dict) else None
if d:
if d.get('synonym') is not None:
ov['Synonym'] = str(d['synonym'])
if d.get('fillChecking'):
ov['FillChecking'] = str(d['fillChecking'])
if d.get('fillFromFillingValue') is not None:
ov['FillFromFillingValue'] = 'true' if d['fillFromFillingValue'] else 'false'
if d.get('fullTextSearch'):
ov['FullTextSearch'] = str(d['fullTextSearch'])
if d.get('dataHistory'):
ov['DataHistory'] = str(d['dataHistory'])
if conditional and isinstance(sa, dict):
d = sa.get(a)
if d:
if d.get('synonym') is not None:
ov['Synonym'] = str(d['synonym'])
if d.get('fillChecking'):
ov['FillChecking'] = str(d['fillChecking'])
if d.get('fillFromFillingValue') is not None:
ov['FillFromFillingValue'] = 'true' if d['fillFromFillingValue'] else 'false'
if d.get('fullTextSearch'):
ov['FullTextSearch'] = str(d['fullTextSearch'])
if d.get('dataHistory'):
ov['DataHistory'] = str(d['dataHistory'])
emit_standard_attribute(f'{indent}\t', a, ov)
X(f'{indent}</StandardAttributes>')
@@ -1233,7 +1231,7 @@ def emit_catalog_properties(indent):
X(f'{i}<Autonumbering>{autonumbering}</Autonumbering>')
default_presentation = get_enum_prop('DefaultPresentation', 'defaultPresentation', 'AsDescription')
X(f'{i}<DefaultPresentation>{default_presentation}</DefaultPresentation>')
emit_standard_attributes_profiled(i, 'Catalog')
emit_standard_attributes(i, 'Catalog')
X(f'{i}<Characteristics/>')
X(f'{i}<PredefinedDataUpdate>Auto</PredefinedDataUpdate>')
X(f'{i}<EditType>InDialog</EditType>')