Сохранение прикладных настроек в формате YAML (постановка)

Материал из GedeminWiki
Перейти к: навигация, поиск
  1. Пространства имен (ПИ) -- это модули, содержащие объекты. Аналог настроек, которые мы сейчас имеем.
  2. Но, если настройки базируются на алгоритме сохранения в поток реляционных данных, со всеми вытекающими последствиями в виде затягивания сторонних объектов по ссылкам, то ПИ легковесны и не тянут ничего лишнего. В файл попадают только те объекты, которые непосредственно были включены разработчиком в ПИ.
  3. Для группового включения объектов (например, при добавлении макроса в ПИ должны добавиться и объект макрос и объект скрипт-функция) предусмотрен отдельный внешний механизм. В частности, диалоговое окно добавления объекта в ПИ содержит таблицу зависимых объектов, в которой пользователь может выбрать что именно должно войти в ПИ.
  4. ПИ целиком сохраняется в один YAML файл, который состоит из: свойств ПИ, списка ПИ, от которых зависит данное ПИ, списка объектов.
  5. Имя ПИ является именем YAML файла (без расширения).
  6. Процесс формирования файла с ПИ называется Сборкой. Процесс считывания файла с ПИ и созданием в БД объектов называется Загрузкой.
  7. При загрузке в ручном режиме обрабатывается только выбранное ПИ. Проверка на версию и дату изменения не производится.
  8. При загрузке в пакетном режиме рекурсивно обрабатывается список ПИ, от которых зависит выбранное ПИ.
  9. Загрузка в пакетном режиме может проходить с принудительной перезаписью всех ПИ (кроме помеченных флагом AlwaysOverwrite = False) или с учетом номера версии и/или даты изменения.
  10. Если при загрузке некоторый объект содержит ссылку на объект, который в базе данных отсутствует, то выдается сообщение об ошибке. Процесс загрузки прерывается.
  11. В файле ПИ мы не храним информацию о типах полей. Вместо этого ПИ содержит ссылки на ПИ со структурами данных (для пользовательских объектов) и номер требуемой версии БД (для системных объектов).
  12. При установленном свойстве AlwaysOverwrite, в результате загрузки ПИ на базу данных созданные объекты должны в точности соответствовать тому, что находится в дисковом файле. В процессе загрузки дополнительных вопросов о перезаписи не задается.
  13. Если в процессе загрузки ПИ с диска в базу, в базе уже присутствует прежняя версия ПИ, то объекты которые отсутствуют в дисковом файле (дисковых файлах), но присутствуют в прежней версии, считаются удаленными и удаляются физически из БД.
  14. Список ПИ хранится в таблице AT_NAMESPACE. Список объектов в -- AT_OBJECT. Зависимости между ПИ в -- AT_NAMESPACE_LINK.
  15. Если при сборке ПИ в базе данных отсутствует объект, то в системный лог записывается предупреждение. Сборка не прерывается. Запись об отсутствующем объекте удаляется из таблицы AT_OBJECT.
  16. Режим Разработчика (РР) -- это режим работы программы, доступный под учетной записью Administrator. РР активируется вручную или автоматически при обращении к Редактору скрипт-объектов, Дизайнеру форм или любой форме объектов метаданных или пространств имен.
  17. В РР объекты, которые были созданы, удалены или изменены фиксируются в таблице AT_OBJECT_LOG. Фиксация происходит на уровне бизнес-объектов.
  18. Из таблицы AT_OBJECT_LOG соответствующие записи удаляются: а) при сборке ПИ; б) при загрузке ПИ; в) вручную.

Структура таблиц

Свойства ПИ

Свойство Примечание
Name Наименование. При сборке и сохранении на диске используется как имя файла.
Caption Если задано, то отображается в окне загрузки пакетов. Если не задано, то используется Name.
FileName Полное имя файла из которого было загружено или в который было сохранено ПИ.
FileTimeStamp Дата файла с ПИ. Заполняется при загрузке ПИ из файла или при сборке ПИ и записи в файл. Может быть пустой для ПИ, которые созданы и еще не разу не собирались.
Version Номер версии. Проставляется вручную.
Optional При загрузке в пакетном режиме пользователю будет дана возможность выбрать загружать/не загружать. По умолчанию False.
Internal Не будет видна при загрузке в пакетном режиме. По умолчанию True.
DBVersion Требует версию структуры БД не ниже чем.
Comment Произвольный комментарий.

Свойства объекта в ПИ

Свойство Примечание
Class Имя бизнес-класса.
SubType Подтип. Может отсутствовать, если подтип пустой.
AlwaysOverwrite При загрузке ПИ объект с таким флагом перезаписывает объект, существующий в БД. Если флаг не установлен, то существующий объект будет сохранен в неизменном виде. По умолчанию True.
DontRemove Если флаг установлен, то при удалении ПИ с объектами из базы, такой объект будет пропущен. По умолчанию False.
IncludeSiblings Если флаг установлен, то при сохранении объекта в файл автоматом добавятся в ПИ и сохранятся вложенные объекты. Например, если объект -- элемент дерева, то добавятся все подуровни. При этом HeadObject будет ссылаться на главный объект.
HeadObject Для составных частей сложного объекта свойство содержит РУИД главного объекта.

Файл ПИ

Файл ПИ начинается с указания номера версии структуры. Текущая версия 1.0. Тип строковый.

 %YAML 1.1
 StructureVersion: '1.0'
 ...

Пример файла ПИ

 %YAML 1.1
 StructureVersion: 1.0
 Properties: 
   RUID: 147108575_2060335630
   Name: 'Бухгалтерские счета'
   Caption: 'Бухгалтерские счета'
   Version: 1.0.0.13
   Optional: False
   Internal: True
 Uses: 
   - 147115598_2060335630 'Общие данные'
 Objects: 
   - 
     Properties: 
       Class: 'TgdcAcctChart'
       RUID: 300001_17
       AlwaysOverwrite: False
       DontRemove: False
       IncludeSiblings: False
     Fields: 
       PARENT: ~
       NAME: 'План счетов'
       ALIAS: 'План счетов'
       ANALYTICALFIELD: ~
       ACTIVITY: 'A'
       ...
   - 
     ... 

Окна для работы с ПИ

Диалоговое окно добавление объекта в ПИ

Содержит:

  1. Выпадающий список для выбора ПИ.
  2. Флаги AlwaysOverwrite, DontRemove и IncludeSiblings.
  3. Список связанных объектов, который содержит флаг для выбора объекта, название объекта, название ПИ, в которое он входит.
    1. Если объект не входит ни в какое ПИ, то последняя колонка для него не заполняется.
    2. Для объекта, который уже входит в некоторое ПИ флаг всегда снят и проставить его нельзя.
    3. Вверху списка находится кнопка Показать связи и числовое поле ввода Ограничить количество, в котором по умолчанию проставлено число 60.

Добавление в ПИ объекта, который еще не входит ни в какое ПИ:

  1. Поле выпадающего списка пустое или содержит наименование ПИ, сохраненное при последнем обращении к окну.
  2. Флаги AlwaysOverwrite, DontRemove и IncludeSiblings установлены в соответствии со значениями по умолчанию.
  3. Для сложных объектов список связанных объектов заполнен изначально. Например, если добавляем отчет, то в этом списке должны находятся: скрипт-функции Основная, Параметров и Событий, а также шаблон отчета и команда вызова в Исследователе.
    1. Объекты, которые не входят ни в какое ПИ изначально помечены галочками для включения в выбранное ПИ.
  4. По нажатию на кнопку Показать связи в список добавляются объекты, от которых в реляционной БД зависит текущий объект.
    1. У добавленных таким образом в список объектов галочка изначально не выставляется.
  5. По нажатию на кнопку Ок объекты и объекты, выбранные флагами в списке, добавляются в пространство имен.

Объект входит только в одно ПИ:

  1. Выпадающий список для выбора ПИ заполнен.
  2. Флаги AlwaysOverwrite и DontRemove заполнены значениями из базы данных.
  3. Для сложных объектов список связанных объектов заполнен изначально.
  4. Для перемещения объекта в другое ПИ необходимо выбрать в списке имя ПИ.
    1. При перемещении или удалении объекта также перемещаются или удаляются все объекты, отмеченные галочками в списке.


Для последнего предусмотрена кнопка, которая очищает поле выбора ПИ. Т.е. пустое поле ПИ и означает удалить объект из ПИ.

Объект входит в несколько ПИ:

Форма просмотра списка ПИ

Мастер-дитэйл. Сверху список ПИ, снизу список объектов по выбранному ПИ.

Диалоговое окно установки очередности объектов в ПИ

Форма синхронизации списка ПИ в базе данных с файлами на диске

  1. Основное пространство занимает список, состоящий из трех частей. Идея организации списка позаимствована у Total Commander. Слева идет информация о ПИ в базе данных, справа -- о ПИ на диске, а между ними располагается колонка с пиктограммкой выбранного действия. Сверху располагается панель команд и фильтров, а снизу -- область вывода сообщений.
  2. Список отсортирован по имени ПИ. Кроме имени выводится номер версии и дата изменения.
  3. Пользователь указывает каталог, где искать файлы с ПИ. Пользователь определяет надо ли сканировать подкаталоги.
  4. Если в процессе сканирования каталогов найдены одноименные файлы, то только первый файл берется для синхронизации. Предупреждения о всех дубликатах выводятся в панели сообщений.
  5. Если структура файла не соответствует файлу ПИ, то такой файл пропускается, а информация о нем выводится в панели сообщений.
  6. Действие для синхронизации определяется следующим образом:
    1. Если ПИ присутствует слева, но отсутствует справа, то оно помечается для сборки и записи на диск. Если присутствуют подкаталоги, то перед записью файла у пользователя будет запрошено место для размещения файла.
    2. Если ПИ присутствует справа, но отсутствует слева, то оно помечается для загрузки.
    3. Если ПИ присутствует с обоих сторон, то направление синхронизации определяется сначала по номерам версий, а при их равенстве по дате изменения файла.
  7. Пользователь может менять операцию синхронизации.
  8. Пользователь может открыть окно для сравнения версий ПИ из базы данных и дискового файла.

Форма установки пакетов

Форма содержит:

  1. поле с путем к папке, в которой лежат файлы пространств имен
  2. кнопку, которая открывает окно выбора папки
  3. кнопку, которая запускает сканирование папки
  4. древовидный список пакетов с чекбоксами
  5. информационное поле, где выводится наименование, версия, дата, расположение файла, комментарий к пакету и т.п.
  6. легенду
  7. кнопку, которая запускает установку выбранных пакетов

Форма похожа на существующее окно установки пакетов, но вместо простого списка содержит древовидный. Дерево строится на основании зависимостей между найденными в папке и ее подпапках файлах пространств имен.

Пример дерева:

 Комплексная автоматизация
   Отдел кадров
     Зарплата
   Материальный склад
     Учет основных средств
   Торговля и склад
   Бухгалтерия
 Касса
   Драйвер кассы ...
   Драйвер кассы ...

Пример информации о пакете:

 Фискальные регистраторы
 Версия: 20 - новая версия
 Изменен: 25.06.2012

 Требуемые версии: 
   EXE-файла: ограничений нет.
   Базы данных: ограничений нет.

 РУИД: 147014815_1094345388

 Путь: C:\Golden\setting\ККС\
 Файл: Фискальные регистраторы.xml

Изначально все чекбоксы сняты. Цветом элементов и начертанием шрифта показываются неустановленные пакеты, новые версии и т.п.

Установка/снятие чекбокса распространяется на все вложенные уровни.

Форма сравнения версий ПИ

Форма отражения хода выполнения операции по сборке/загрузке ПИ

Форма для работы с дубликатами объектов в разных ПИ

Сценарии работы

  1. Создание ПИ:
    1. Через форму просмотра.
    2. Через диалоговое окно добавления объекта в ПИ.
    3. При загрузке с диска, если такого ПИ нет в базе.
  2. Добавление объекта в ПИ:
    1. Вручную с использованием диалогового окна добавления объекта.
    2. Автоматически для сложных объектов (макрос, отчет, форма и т.п.).
    3. В полуавтоматическом режиме на основе списка зависимых объектов.
    4. В полуавтоматическом режиме на основе списка из AT_OBJECT_LOG.
    5. На основе списка объектов базы данных (метаданные, макросы, отчеты и т.п.), не входящих в ПИ.
    6. Для объектов с установленным флагом Вложенные в момент сборки ПИ происходит анализ данных и автоматическое добавление в ПИ объектов, которых там еще нет.
    7. При загрузке ПИ с диска, если в нем присутствую объекты, которых нет в ПИ в базе данных.
  3. Удаление объектов из ПИ:
    1. Вручную, с использованием диалогового окна.
    2. Автоматически, при удалении сложного объекта (макрос, отчет, форма и т.п.).
    3. При загрузке ПИ, если в загружаемом ПИ такой объект отсутствует.
    4. В момент сборки, если объект не найден в базе данных.
    5. В полуавтоматическом режиме на основе списка из AT_OBJECT_LOG.
    6. На основе списка объектов, входящих в ПИ, но отсутствующих в базе данных.
  4. Перемещение объекта в другое ПИ:
    1. Вручную с помощью диалогового окна редактирования объекта ПИ.
    2. При загрузке с диска, если такой объект уже присутствует в БД, но находится в другом ПИ, он перемещается в загружаемое ПИ.
  5. Просмотр изменений, сделанных на базе, на основе сравнения сгенерированного текста с файлом.
  6. Откат изменений через загрузку ПИ из файла.
  7. Удаление ПИ.
    1. Удаление ПИ через форму просмотра. Все объекты остаются в базе данных.
    2. Удаление ПИ через форму просмотра. Объекты из базы данных удаляются, за исключением помеченных флагом DontRemove.

Сделать

Конвертация существующих настроек

Основная проблема при конвертации существующих настроек в новый формат -- это избавление от дубликатов объектов в потоке. Сами дубликаты после конвертации можно найти с помощью следующего запроса:

SELECT
  o.objectclass, o.subtype, o.objectname, o.xid, o.dbid, list(n.name), COUNT(*)
FROM 
  at_object o JOIN at_namespace n ON n.id = o.namespacekey
WHERE
  o.xid > 147000000
GROUP BY
  o.objectclass, o.subtype, o.objectname, o.xid, o.dbid
HAVING
  COUNT(*) > 1
ORDER BY
  COUNT(*) DESC

Предпоследняя колонка показывает список ПИ, включающих объект, а последняя -- их количество.

RECREATE PROCEDURE at_del_duplicates (
  DeleteFromID INTEGER,
  CurrentID INTEGER,
  Stack VARCHAR(32000))
AS
  DECLARE VARIABLE id INTEGER;
  DECLARE VARIABLE nsid INTEGER;
BEGIN
  IF (:DeleteFromID <> :CurrentID) THEN
  BEGIN
    FOR
      SELECT o1.id
      FROM at_object o1 JOIN at_object o2
        ON o1.xid = o2.xid AND o1.dbid = o2.dbid
      WHERE o1.NAMESPACEKEY = :DeleteFromID
        AND o2.NAMESPACEKEY = :CurrentID
      INTO :id
    DO BEGIN
      DELETE FROM at_object WHERE id = :id;
    END
  END
 
  FOR
    SELECT l.useskey
    FROM at_namespace_link l
    WHERE l.namespacekey = :CurrentID
      AND POSITION(('(' || l.useskey || ')') IN :Stack) = 0
    INTO :nsid
  DO BEGIN
    EXECUTE PROCEDURE at_del_duplicates (:DeleteFromID, :nsid,
      :Stack || '(' || :nsid || ')');
  END
END
 
EXECUTE block
AS
  DECLARE variable id INTEGER;
BEGIN
  FOR SELECT id FROM at_namespace INTO :id
  do EXECUTE PROCEDURE at_del_duplicates(:id, :id, '');
END

Поиск кольцевых ссылок

CREATE OR ALTER PROCEDURE at_p_findnsrec (InPath VARCHAR(32000), InFirstID INTEGER, InID INTEGER)
  RETURNS (OutPath VARCHAR(32000), OutFirstID INTEGER, OutID INTEGER)
AS
  DECLARE VARIABLE ID INTEGER;
  DECLARE VARIABLE NAME VARCHAR(255);
BEGIN
  FOR
    SELECT l.useskey, n.name
    FROM at_namespace_link l JOIN at_namespace n
      ON n.id = l.useskey
    WHERE l.namespacekey = :InID
    INTO :ID, :NAME
  DO BEGIN
    IF (POSITION(:ID || '=' || :NAME || ',' IN :InPath) > 0) THEN
    BEGIN
      OutPath = :InPath || :ID || '=' || :NAME;
      OutID = :ID;
      OutFirstID = :InFirstID;
      SUSPEND;
    END ELSE
    BEGIN
      FOR
        SELECT OutPath, OutFirstID, OutID
        FROM at_p_findnsrec(:InPath || :ID || '=' || :NAME || ',', :InFirstID, :ID)
        INTO :OutPath, :OutFirstID, :OutID
      DO BEGIN
        IF (:OutPath > '') THEN
          SUSPEND;
      END
    END
  END
END
SELECT DISTINCT
  f.OutPath
FROM
  at_namespace n LEFT JOIN at_p_findnsrec('', n.id, n.id) f
    ON f.OutFirstID = n.id
WHERE
  f.OutPath > ''

NeedModify

Обработка флага NeedModify.

Методика тестирования

  1. Берем чистую эталонную базу и загружаем на нее пакет настроек из двоичного потока. Сохраняем ее копию. Это исходная база данных.
  2. После загрузки, преобразуем настройки в пространства имен и сохраняем в YAML файлы.
  3. Берем чистую эталонную базу и загружаем на нее пакет из пространств имен. Это конечная база данных.
  4. Сравниваем метаданные исходной и конечной баз данных. Они должны полностью совпадать.
  5. Сравниваем данные исходной и конечной баз данных. Они должны совпадать за исключением идентификаторов.

Для сравнения можно воспользоваться выгрузкой метаданных и данных базы в SQL скрипт с помощью утилиты IBExpert.

Персональные инструменты
Пространства имён

Варианты
Действия
Навигация
Инструменты