Формат проєкту Ramus
Створено: 2026-09-06 План переходу: FORMAT_MIGRATION_PLAN.md Вихідний стан до змін: START_REVIEW.md
Повний опис того, що лежить у проєкті. Призначений і для людини, і для агента, який правитиме ці файли, і для того, хто дописуватиме формат.
Зміст
- Що таке проєкт
- Синтаксис: діалект YAML
- Ідентифікатори
- Посилання на атрибути
project.ramusattributes.yamlqualifiers/*.yaml- Як записані значення
- Довідник типів атрибутів
- Модель IDEF0 у цих файлах
- Потоки:
streams.yaml,properties/,attachments/ .local/— стан інтерфейсу- Чого у проєкті немає
- Рецепти безпечного редагування
- Версія схеми й сумісність
- Старий формат
.rsf - Межі формату
- Що перевірено
- Як звірити цю доку з кодом
1. Що таке проєкт
Проєкт — каталог. Усередині текстові файли YAML і вкладення як є.
Модель.ramus/
├── project.ramus опис проєкту: версія, плагіни, лічильники
├── attributes.yaml усі атрибути моделі
├── qualifiers/ по файлу на класифікатор
│ ├── enterprise-activity--jk1apy.yaml
│ └── f-sectors--cbbfj4.yaml
├── properties/ налаштування моделі (потоки /properties/*)
├── attachments/ вкладення користувача (звіти, файли)
├── streams/ інші потоки, якщо трапляться
├── .gitignore містить рядок «.local/»
└── .local/ стан інтерфейсу; у сховище версій не йде
Каталог із програмою не пов’язати засобами робочого столу, а файл — можна,
тому точка входу — project.ramus: саме він реєструється як тип файлу
application/x-ramus-project. Застосунок приймає і каталог, і цей файл усюди,
де очікується шлях до проєкту: у діалозі відкриття, у рядку запуску, у
переліку останніх файлів.
Розширення каталогу .ramus — для ока; застосунок його не вимагає, ознакою
проєкту є наявність project.ramus усередині.
Проєкт можна тримати в git прямо як є. Збереження без змін не чіпає
жодного файлу, тож git status мовчить, доки модель справді не змінилася.
Формат розрахований і на агента
Модель читається, обмірковується й правиться звичайними текстовими засобами — без розбору бінарника й без запущеного застосунку. Що робить це надійним:
- Діалект YAML навмисно вузький — блоковий стиль, сортовані ключі, лапки на рядках, літеральний блок для багаторядкового тексту, без якорів, псевдонімів і перенесення рядків, дублікат ключа — помилка читання. Подробиці — розділ 2.
- Запис детермінований, тож правка дає diff рівно того, що зачепили, а повторне збереження незміненої моделі не дає нічого.
- Ідентифікатори стабільні й оборотні, вид сутності входить у перемішування — елемент і класифікатор ніколи не поділять один ідентифікатор (розділ 3). Атрибути адресуються за іменем, тож файл читається без словника (розділ 4).
- Читання не виконує коду:
Loadзі snakeyaml-engine не створює довільних Java-об’єктів, і тег!!із чужого файлу лишиться текстом.
Цей документ придатний, щоб віддати його агенту як контекст цілком. Коротший робочий протокол — що читати перед правкою, як перевірити результат і де зупинитися — у AGENT_GUIDE.md.
2. Синтаксис: діалект YAML
YAML 1.2, кодування UTF-8. Правила, яких дотримується запис — і яких варто дотримуватися при ручній правці:
| Правило | Навіщо |
|---|---|
| блоковий стиль, відступ мап 2 пробіли | рядкові зміни видно в diff; елементи списку — на рівні свого ключа |
| рядки завжди в одинарних лапках | немає «норвезької проблеми»: 'no', 'on', '1.0' лишаються рядками |
багаторядковий текст — літеральним блоком \| |
у diff видно змінений рядок, а не весь абзац |
числа й true/false — без лапок |
тип видно з написання |
| ключі — без лапок, якщо безпечні | F_BOUNDS: замість 'F_BOUNDS': |
без якорів і псевдонімів (&, *) |
однакове значення у двох місцях лишається двома значеннями |
| без перенесення довгих рядків | рядок у файлі = рядок значення |
| ключі мап відсортовані | порядок не залежить від того, як лягла пам’ять |
| дублікати ключів — помилка читання | помилка не проходить мовчки |
Читання свідоме: використовується Load зі snakeyaml-engine, який не вміє
створювати довільні Java-об’єкти. Тег !! із чужого файлу не перетвориться на
виконання коду.
Правити руками можна вільніше, ніж пише сам застосунок: подвійні лапки, відсутність лапок там, де значення однозначне, інший порядок ключів — усе це прочитається. Наступне збереження зведе файл до канонічного вигляду.
3. Ідентифікатори
Кожен класифікатор, атрибут і елемент має ідентифікатор із шести символів:
jk1apy, mva0fy, 442a84.
Це не випадковий рядок і не хеш вмісту, а оборотне перетворення числового
ключа в базі: мережа Фейстеля на 30 біт, алфавіт 0-9a-z без i, l, o,
u (щоб не плутати з 1 і 0). Звідси властивості:
- ідентифікатор не змінюється ніколи — ні від перейменування, ні від правок, ні від порядку збереження;
- сусідні числові ключі дають несхожі ідентифікатори, тож переплутати їх оком важко;
- вид сутності входить у перемішування: елемент і класифікатор з тим самим номером мають різні ідентифікатори, і підставити один замість іншого не вийде непомітно;
- ключі, більші за 2³⁰, записуються як
_<число за основою 32>— префікс_навмисно поза алфавітом.
Імена файлів класифікаторів — <читабельна-назва>--<ідентифікатор>.yaml.
Перейменування класифікатора змінює ім’я файлу, ідентифікатор — ні.
Правило для правок: ідентифікатори не вигадують. Новий елемент простіше додати в застосунку, а вже потім правити його значення у файлі.
4. Посилання на атрибути
Атрибути адресуються за іменем: F_BOUNDS, Name, Опис. Так файл
читається без словника.
Якщо в проєкті два атрибути з однаковим іменем, обидва отримують суфікс:
Name#a3f2c1. Правило локальне для проєкту й обчислюється при записі, тож
однакові імена в різних проєктах на файли не впливають.
Відповідність «посилання → атрибут» задає attributes.yaml; читач бере її
звідти, а не розбирає рядок, тому суфікс — просто частина ключа.
Класифікатори й елементи посилаються одне на одного ідентифікаторами, не іменами: усередині значень посилання машинні, і прив’язувати їх до назв, які користувач змінює щодня, було б крихко.
5. project.ramus
schema: 4
application: 'Ramus'
application-version: '2.0.2'
minimum-version: '2.0'
plugins:
- 'Attribute.IDEF0.Sector'
- 'IDEF0'
sequences:
crosspoint_sequence: 31
ordinates__sequence: 1
Перелік plugins тут скорочено: у справжньому проєкті їх десятки, від
Attribute.Core.Date до IDEF0, і всі відсортовані.
| Поле | Що це |
|---|---|
schema |
версія розкладки файлів; читач відмовляється від чужої |
application |
назва застосунку, який записав |
application-version |
його версія; міток часу немає навмисно |
minimum-version |
нижче цієї версії застосунок відмовиться відкривати |
plugins |
плагіни, без яких проєкт не відкрити |
sequences |
лічильники плагінів |
plugins — та сама перевірка, що й у .rsf: проєкт, який посилається на
невідомий плагін, краще не відкривати взагалі, ніж відкрити з мовчазною
втратою даних, якими плагін завідував.
sequences містить лише лічильники плагінів (номери перетинів стрілок,
напрямних). Ключі основних таблиць рушій відновлює сам, піднімаючи лічильник
над найбільшим наявним ключем.
6. attributes.yaml
schema: 4
attributes:
- ref: 'QualifierAttributes'
id: '460xra'
name: 'QualifierAttributes'
type: 'Core.ElementList'
system: true
properties:
qualifier1: 'geq786'
qualifier2: '0140fv'
- ref: 'Name'
id: 'gsjy4v'
name: 'Name'
type: 'Core.Text'
comparable: true
| Поле | Що це |
|---|---|
ref |
як на цей атрибут посилаються всі інші файли |
id |
ідентифікатор, з якого відновлюється числовий ключ |
name |
показувана назва |
type |
Плагін.Тип — визначає, який плагін розуміє значення |
comparable |
лише коли true: за атрибутом можна впорядковувати |
system |
лише коли true: атрибут створює плагін, а не користувач |
properties |
конфігурація плагіна для цього атрибута |
properties — не декорація. Для Core.ElementList там записано, які саме
класифікатори зв’язує атрибут; без цього значення елементів не відновити. Для
Core.Variant — перелік допустимих варіантів. Перелік типів, що мають
властивості, — у розділі 9.
Атрибути перелічені в порядку зростання числового ключа, тобто в порядку створення.
7. qualifiers/*.yaml
schema: 4
id: 'jk1apy'
name: 'Enterprise activity'
name-attribute: 'Name'
attributes:
- 'Name'
- 'Description'
system-attributes:
- 'F_BOUNDS'
- 'F_FONT'
- 'HierarchicalAttribute'
elements:
- id: 'mva0fy'
name: 'Enterprise activity'
values:
F_BOUNDS:
height: 80.0
width: 144.0
x: 324.0
y: 180.0
F_FONT:
name: 'Dialog'
size: 12
style: 0
Name: 'Enterprise activity'
Списки system-attributes і values тут скорочено — справжній елемент несе
ще F_BACKGROUND, F_TYPE, дати ревізії й інше.
| Поле | Що це |
|---|---|
id |
ідентифікатор класифікатора |
name |
назва |
system |
лише коли true: класифікатор створює плагін |
name-attribute |
який атрибут вважається назвою елемента |
attributes |
стовпчики класифікатора, у порядку показу |
system-attributes |
атрибути, які додає плагін; порядок несуттєвий, тому відсортовані |
elements[].id |
ідентифікатор елемента |
elements[].name |
назва в таблиці елементів; здебільшого порожня й тоді відсутня |
elements[].values |
значення, ключі відсортовані за посиланням на атрибут |
Порядок attributes значущий — це порядок стовпчиків у таблиці. Порядок
system-attributes і elements — ні; елементи впорядковані за ключем, щоб
файл не змінювався без потреби.
Класифікатор із system: true створює плагін. Такі не варто ні видаляти, ні
перейменовувати: у них лежать базові функції моделей IDEF0, звіти, сектори й
дзеркала метаданих рушія — див. розділ 10.
8. Як записані значення
Плагін атрибута зберігає значення як набір рядків, іноді в кількох таблицях. Формат подає найкоротшу з можливих форм:
| Випадок | Вигляд | Приклад |
|---|---|---|
| одне змістове поле, один рядок | скаляр | Name: 'Опис' |
| кілька полів, один рядок | мапа | F_BOUNDS: { x: 1.0, … } |
| кілька рядків | список мап | F_SECTOR_POINTS: [{…}, {…}] |
| кілька таблиць | список списків | F_PROJECT_PREFERENCES: [[…], […]] |
Які типи згортаються до скаляра — у розділі 9.
Порожні поля не пишуться, і відсутність поля означає «значення немає» — не
типове значення класу. Тому дописане вручну поле з’явиться в моделі, а
видалене — зникне. Це не дрібниця: наприклад, у SectorPersistent
alternativeText: '' і відсутній alternativeText — різні стани, від яких
залежить розкладка підписів стрілок.
Типи скалярів:
| Тип поля | Запис | Приклад |
|---|---|---|
text |
рядок в одинарних лапках | 'Реализованная продукция' |
long, integer |
ціле | -16711936 |
double |
дробове | 80.0 |
date |
ISO-8601 UTC | '2009-09-03T11:36:00.000Z' |
binary |
base64 | 'AQAAAB0AAAD…' |
element, qualifier, attribute |
ідентифікатор або -1 |
'2np969', -1 |
-1 у полі-посиланні означає «посилання немає». Посилання на сутність, якої в
проєкті немає, при записі теж перетворюється на -1: у старих файлах
трапляються ключі видалених елементів, і формат не вигадує їм адресата.
Поля elementId, attributeId, valueBranchId у файл не потрапляють — вони
задані контекстом, у якому значення записане.
9. Довідник типів атрибутів
Схема виведена з анотацій самих персистентів, тому новий тип атрибута з’являється тут автоматично, без правок серіалізатора. Звірити з кодом — розділ 19.
Core
| Тип | Значення | Властивості |
|---|---|---|
Core.Text |
скаляр value : text |
— |
Core.Long |
скаляр value : long |
— |
Core.Double |
скаляр value : double |
— |
Core.Boolean |
скаляр value : integer |
— |
Core.Date |
скаляр value : date |
— |
Core.Currency |
скаляр value : double |
attribute, code : text |
Core.Price |
position : integer, startDate : date, value : double |
— |
Core.Variant |
скаляр variantId : long |
attribute, position, value : text, variantId |
Core.OtherElement |
скаляр otherElement : element |
attribute, qualifier, qualifierAttribute |
Core.ElementList ⚙ |
element1Id : element, element2Id : element, connectionType : text |
qualifier1, qualifier2, connectionTypes : text |
Core.Hierarchical ⚙ |
parentElementId : element, previousElementId : element, iconId : long |
— |
Core.Icon ⚙ |
name : text, icon : binary |
— |
Core.File |
name, path, lastModifiedTime, uploadTime |
— |
Core.HTMLText |
немає — вміст лежить у потоці | — |
Core.Table |
— | attribute, otherAttribute, name, subName |
⚙ — системний тип.
Core.File і Core.HTMLText тримають самі байти не в таблиці, а в потоці
за шляхом /elements/<елемент>/<атрибут>/Core/<ім'я>; у проєкті це файл під
attachments/. У Core.File персистент описує вкладення (ім’я, час), у
Core.HTMLText персистента немає взагалі.
Core.ElementList — зв’язок «багато до багатьох» між двома класифікаторами.
Один зв’язок належить обом кінцям одразу, тому у файлі він трапляється двічі:
у значеннях елемента з qualifier1 і елемента з qualifier2. Читач другий
запис відкидає. Дублювати вручну не потрібно, але й не шкідливо.
IDEF0
| Тип | Значення |
|---|---|
IDEF0.FRectangle ⚙ |
x, y, width, height : double |
IDEF0.Font ⚙ |
name : text, size : integer, style : integer |
IDEF0.Color ⚙ |
скаляр color : integer (ARGB, як Color.getRGB()) |
IDEF0.Type ⚙ |
скаляр type : integer |
IDEF0.DecompositionType ⚙ |
скаляр type : integer |
IDEF0.OunerId ⚙ |
скаляр ounerId : long |
IDEF0.Status ⚙ |
type : integer, otherName : text |
IDEF0.AnyToAny ⚙ |
otherElement : element, elementStatus : text |
IDEF0.DFDSName |
shortName : text, longName : text |
IDEF0.TextLabel ⚙ |
position, text, x, y, width, height, fontName, fontStyle, fontSize, color |
IDEF0.Sector ⚙ |
вигляд стрілки, див. нижче |
IDEF0.SectorBorder ⚙ |
borderType, functionType : integer, function : element, crosspoint : long, tunnelSoft : integer |
IDEF0.SectorPoint ⚙ |
position : integer, pointType : integer, xPosition, yPosition : double, xOrdinateId, yOrdinateId : long |
IDEF0.SectorProperties ⚙ |
showText, showTilda, transparent : integer, textX, textY, textWidth, textHieght, tildaPos : double |
IDEF0.ProjectPreferences ⚙ |
дві таблиці: налаштування моделі та перелік читачів |
IDEF0.VisualData ⚙ |
скаляр data : binary — див. межі |
IDEF0.Sector— вигляд стрілки:alternativeText : text,showText,textAligment,createState : integer,createPos : double, лінія- (
strokeKind : text,strokeWidth,strokeDashPhase, `strokeMiterLimit : - double
,strokeEndCap,strokeLineJoin,strokeType : integer,strokeDash - text
— довжини штрихів через кому), шрифт (fontName,fontStyle,fontSize) іcolor : integer. ПолеvisualAttributes : binary` — застаріле: воно лишається заповненим у моделях, збережених до винесення вигляду в іменовані поля, і читається лише тоді, коли іменованих полів немає.
IDEF0.ProjectPreferences — єдиний тип із двома таблицями, тож його значення
має вигляд списку списків: зовнішній список — таблиці, внутрішній — рядки.
Схематично (у зразках із dest/doc це значення не задане, тому справжнього
прикладу навести нема з чого):
F_PROJECT_PREFERENCES:
- - projectName: 'Діяльність підприємства'
projectAutor: 'Іванов'
createDate: '2009-09-03T11:36:00.000Z'
modelLetter: 'A'
- - reader: 'Петров'
date: '2009-09-10T08:00:00.000Z'
Порожня таблиця дає порожній список [] на своєму місці.
Eval, Chart
| Тип | Значення |
|---|---|
Eval.Function ⚙ |
function : text (вираз), autochange : integer, qualifierAttributeId, qualifierTableAttributeId : long |
Chart.Link |
скаляр otherElementId : element |
Chart.TableChart |
властивість: скаляр otherElementId : element |
10. Модель IDEF0 у цих файлах
У Ramus немає окремої «таблиці діаграм»: діаграма — це елементи системних класифікаторів. Знати, який класифікатор за що відповідає, необхідно, щоб правити модель у файлах.
| Класифікатор | Що тримає |
|---|---|
F_BASE_FUNCTIONS |
по одному елементу на модель — корінь дерева функцій |
F_MODEL_TREE |
дерево моделей проєкту |
F_SECTORS |
стрілки: по елементу на сектор |
F_STREAMS |
потоки — те, що стрілки переносять; назва стрілки живе тут |
F_REPORTS_QUALIFIER |
звіти; їхні тіла — у attachments/ |
IconsQualifier |
піктограми |
QUALIFIER_EVAL_FUNCTION_DEPENDENCES |
залежності обчислюваних атрибутів |
QUALIFIER_CHARTS, QUALIFIER_CHART_SETS, QUALIFIER_CHART_LINKS |
діаграми модуля Chart |
PLAN_LIST |
плани модуля планування |
QualifiersQualifier, AttributesQualifier |
дзеркала метаданих рушія: по елементу на класифікатор і на атрибут |
HistoryQualifier |
залишок механізму гілок; лишається без елементів |
Дзеркала (QualifiersQualifier, AttributesQualifier) містять по елементу на
кожен класифікатор і на кожен атрибут проєкту. Вони потрібні, щоб на
класифікатор чи атрибут можна було послатися як на звичайний елемент — саме
через них працює, наприклад, Core.ElementList між класифікаторами. Правити
їх руками не варто: рушій узгоджує їх сам.
Функційні блоки — це елементи звичайного, користувацького класифікатора
моделі (у прикладі — Enterprise activity). Кожен блок несе:
| Атрибут | Що це |
|---|---|
F_BOUNDS |
прямокутник блока на діаграмі |
F_FONT, F_BACKGROUND, F_FOREGROUND |
вигляд |
F_TYPE, F_DECOMPOSITION_TYPE |
тип блока й тип декомпозиції |
F_STATUS |
стан («чернетка», «затверджено», …) |
F_AUTHOR, F_CREATE_DATE, F_REV_DATE, F_SYSTEM_REV_DATE |
хто й коли |
F_OUNER_ID, F_LINK |
власник і посилання |
F_PAGE_SIZE |
розмір сторінки діаграми |
F_TEXT_LABELS |
вільні підписи на діаграмі декомпозиції |
F_VISUAL_DATA |
залишок старого блоба, див. межі |
HierarchicalAttribute |
місце в дереві: батько й попередник |
Стрілка — елемент F_SECTORS з такими атрибутами:
| Атрибут | Що це |
|---|---|
F_FUNCTION_SECTOR |
на якій діаграмі (елемент функції) стрілка намальована |
F_SECTOR_STREAM |
який потік переносить (елемент F_STREAMS) |
F_SECTOR_BORDER_START / _END |
до чого прикріплені кінці |
F_SECTOR_POINTS |
ламана: точки в порядку position |
F_SECTOR_ATTRIBUTE |
вигляд лінії, шрифт, колір |
F_SECTOR_PROPERTIES |
розташування підпису |
Кінець стрілки (SectorBorderPersistent) описаний так:
function— елемент функційного блока, до якого прикріплений кінець, або-1, якщо кінець на межі діаграми;functionType— сторона блока,borderType— сторона діаграми; обидва в одних константах:0— праворуч,1— знизу,2— ліворуч,3— зверху;crosspoint— вузол розгалуження, якщо кінець зливається з іншими стрілками.
У нотації IDEF0 сторона й задає роль: ліворуч — вхід, зверху — керування, праворуч — вихід, знизу — механізм. Окремого поля «роль» немає — це причина, чому розкладку ще не відділено від семантики (етап 4 плану).
Точка ламаної (SectorPointPersistent) має і власні координати
(xPosition, yPosition), і посилання на спільні напрямні
(xOrdinateId, yOrdinateId). Напрямна — це вертикаль або горизонталь, на
якій сидять точки кількох стрілок: посунувши її, застосунок рухає їх разом.
pointType — орієнтація напрямної, що виходить із точки: 0 — паралельно
осі x, 1 — осі y.
11. Потоки
Дані, що живуть поза таблицями, описані в streams.yaml:
schema: 4
properties:
- path: 'idef0.xml'
file: 'properties/idef0.xml'
attachments:
- element: 'hqw94n'
attribute: 'F_REPORT_NAME'
name: 'report.0.xml'
file: 'attachments/hqw94n/F_REPORT_NAME/report.0.xml'
other:
- path: '/elements/769/17/report.0.xml'
file: 'streams/elements/769/17/report.0.xml'
properties/*— налаштування моделі (/properties/idef0.xml, формат сторінки); версіонуються;attachments/*— вкладення користувача: тіла звітів, значенняCore.FileіCore.HTMLText. Ім’я файлу виводиться з самого вкладення, а не з лічильника: інакше додавання одного зсувало б усі наступні, і git показував би зміну там, де її немає;other/*— усе інше, якщо трапиться.
file каже, де лежить, решта полів — куди прив’язати при відкритті. Файл
можна перейменувати, якщо виправити file. Символи, неприйнятні в іменах
файлів, у шляху замінені дефісом — тому name зберігається окремо й
відновлює початковий шлях точно.
12. .local/ — стан інтерфейсу
Розкладка вікон, відкриті вкладки, словник перевірки орфографії — усе, що
надходило потоками /user/*. Змінюється від кожного кліку й персональне, тому
лежить окремо, а .gitignore проєкту його виключає.
.gitignore при збереженні доповнюється, а не переписується: це файл
користувача, і його власні правила лишаються на місці.
Втрата .local/ не втрачає нічого з моделі.
13. Чого у проєкті немає
Історії та гілок. Їх веде сховище версій: git log замість таблиць
branches, attributes_history, qualifiers_history. Проєкт завжди описує
один стан. Журнал скасування живе лише в межах сеансу.
application_preferencies. Ця таблиця в коді нічим не читається й не
пишеться — переносити її означало б тягнути мертвий вантаж.
Міток часу збереження. Вони робили б кожне збереження унікальним, і git
показував би зміну там, де модель не змінювалася. Дати всередині моделі
(F_REV_DATE тощо) — інша річ, вони частина даних і зберігаються.
14. Рецепти безпечного редагування
Загальне правило: правити значення — безпечно, вигадувати ідентифікатори — ні. Створення й видалення сутностей краще робити в застосунку, а у файлах уточнювати вміст.
Перейменувати функційний блок. Знайти елемент у файлі класифікатора
моделі, змінити значення атрибута-назви (name-attribute каже, який це). Поле
elements[].name міняти не треба — воно не показується.
Посунути блок. F_BOUNDS того самого елемента: x, y — лівий верхній
кут, width, height — розмір. Стрілки, прикріплені до блока, застосунок
перерахує при відкритті не повністю: точки ламаної лишаться там, де були.
Змінити колір або шрифт. F_BACKGROUND, F_FOREGROUND — ціле ARGB
(-16711936 — зелений). F_FONT — мапа з name, size, style.
Перейменувати стрілку. Назва живе не в стрілці, а в потоці: знайти
елемент F_SECTORS, узяти F_SECTOR_STREAM, за цим ідентифікатором знайти
елемент у F_STREAMS і змінити F_STREAM_NAME. Один потік може обслуговувати
кілька стрілок — тоді перейменуються всі.
Додати підпис на діаграму. У списку F_TEXT_LABELS елемента функції
дописати запис із position на одиницю більшим за останній, text, x, y,
width, height. position має бути унікальним: це ключ, і два підписи з
однаковим значенням злипнуться в один.
Масова заміна тексту. Звичайний sed по qualifiers/*.yaml працює, бо
рядки не переносяться і не екрануються складно. Після заміни варто відкрити
проєкт у застосунку й зберегти — файли зведуться до канонічного вигляду.
Перевірити правку. Відкрити проєкт і зберегти. Якщо git diff показує
лише те, що ви змінювали, — правка коректна; якщо файл перебудувався
несподівано, значення було записане у формі, яку читач зрозумів інакше.
Чого не варто робити: видаляти системні класифікатори або їхні елементи,
міняти id будь-де, знімати system: true, правити sequences вниз.
15. Версія схеми й сумісність
schema: 4 — версія розкладки файлів. Читач відмовляється відкривати
іншу версію, а не намагається вгадати. Механізму міграції між версіями схеми
поки немає: він з’явиться разом із першою несумісною зміною.
minimum-version і plugins описують вимоги до застосунку, а не до формату:
перше порівнюється з версією застосунку, друге — з переліком підключених
плагінів.
Проєкт, збережений цією версією, старішим Ramus не відкриється — вона не знає
ні каталогів проєкту, ні плагіна Attribute.IDEF0.TextLabel.
16. Старий формат .rsf
.rsf — ZIP-архів із XML-дампами таблиць бази. Відкривається, але не
записується. Відкритий .rsf не перезаписується: перше збереження веде до
діалогу з іменем нового проєкту, тобто перехід відбувається явно й видимо.
Пакетне перетворення без застосунку:
./gradlew :ramus-core-demo:rsfToYaml -Prsf=<файл.rsf> -Pout=<каталог>
./gradlew :ramus-core-demo:yamlToRsf -Pin=<каталог> -Prsf=<файл.rsf>
`
Зворотний конвертер потрібен лише для обміну зі старими збірками.
.rsf лишається і як внутрішній транспорт там, де він ніколи не був форматом
зберігання: обмін класифікаторами між файлами, серверні резервні копії,
передача дампа мережевому клієнту.
17. Межі формату
Типи атрибутів, чий плагін не підключений, не переносяться. У деяких
старих зразках трапляються атрибути типу Season.PlanField, а плагіна
Season у коді немає. .rsf зберігав такі таблиці як непрозорий дамп; новий
формат записує значення через плагін, тож без плагіна їх нема чим прочитати.
Конвертер повідомляє про кожен такий атрибут.
F_VISUAL_DATA у старих моделях лишається блобом. Геометрія стрілок
переїхала в таблиці (версія блоба 3), але файл, збережений раніше, несе її у
двійковому полі, доки застосунок не перезбереже відповідну діаграму. Після
цього поле скорочується до позначки версії. Окрема давня вада: перезбереження
кореневої діаграми файлу першої версії псує граничні стрілки — вона існувала
до цих змін, але саме на шляху міграції й спрацьовує.
Розкладку ще не відділено від семантики. Координати лежать поруч зі зв’язками, у тих самих значеннях атрибутів, а роль стрілки виводиться зі сторони блока, а не задана явно. Розділення — етап 4 плану.
Кілька полів-посилань лишаються числами. Вони оголошені як long, хоча
зберігають ключі сутностей. Найпомітніші:
| Поле | На що вказує |
|---|---|
F_BASE_FUNCTION_QUALIFIER_ID |
класифікатор моделі |
F_LINK |
елемент, на який посилається блок |
HierarchicalPersistent.iconId |
елемент IconsQualifier |
Eval.Function.qualifierAttributeId |
атрибут |
crosspoint у кінці стрілки |
вузол розгалуження (не елемент) |
Ті, що однозначно вказують на елементи й мають власний клас персистента, вже
переведені на ідентифікатори. F_BASE_FUNCTION_QUALIFIER_ID і F_LINK —
принципово інший випадок: обидва мають тип Core.Long, тобто користуються
тим самим LongPersistent, що й будь-яке число в моделі. Перевести їх можна
лише правилом на рівні атрибута, а не персистента, — цього поки немає.
Практичний наслідок: у файлі ці поля виглядають як звичайні числа, і при ручній правці треба пам’ятати, що це числовий ключ бази, а не ідентифікатор із шести символів. Цикл збереження їх не псує — ключі відновлюються точно.
18. Що перевірено
Модуль storage-test і тести local-client:
| Перевірка | Тест |
|---|---|
проєкт → .rsf → проєкт дає ті самі файли |
ProjectRoundTripTest.projectSurvivesRsfRoundTrip |
| відкрити й зберегти без змін не чіпає жодного файлу | resavingProjectChangesNothing |
| діаграми після циклу малюються так само | diagramsLookIdenticalAfterRoundTrip |
проєкт відкривається за project.ramus |
projectOpensByItsDescriptionFile |
| стан інтерфейсу не потрапляє у версійовану частину | interfaceStateGoesToLocalDirectory |
.gitignore користувача не переписується |
там само |
| новий елемент не займає чужого ключа | ProjectEditingTest.newElementDoesNotReuseExistingKey |
| правка переживає збереження й відкриття | editedValueSurvivesSaveAndReopen |
| порожній проєкт зберігається й відкривається | freshProjectSavesAndOpens |
| знімок для відновлення після збою не обнуляється | ProjectRecoveryTest |
| шлях користувача перетворюється на каталог проєкту | local-client: ProjectNamingTest |
| вигляд 56 діаграм не змінився | DiagramGoldenTest.diagramsMatchGolden |
| знімок не залежить від шрифтів машини й дня | DiagramDeterminismTest |
| правила емітера YAML | YamlFormatTest |
| ідентифікатори оборотні | StableIdsTest |
Порівняння діаграм — точне, без допуску: відмальовування детерміноване, а дати й шрифти зафіксовані в самому тесті.
Чого не перевірено: жодної дії в графічному інтерфейсі. Застосунок запускається й відкриває каталог проєкту, але «Зберегти», діалог «Зберегти як» і відтворення журналу поверх знімка проходилися лише на рівні рушія.
19. Як звірити цю доку з кодом
Схема типів атрибутів виводиться з анотацій персистентів, тому її можна надрукувати з коду й порівняти з розділом 9:
./gradlew :storage-test:test --tests '*DumpSchemaTest*' \
-Dramus.schema.target=/tmp/schema.txt
Подивитися, як виглядає справжній проєкт:
./gradlew :ramus-core-demo:rsfToYaml \
-Prsf="dest/doc/en/Enterprise activity.rsf" -Pout=/tmp/Enterprise.ramus