Обычно проекты, созданные в среде разработке My Visual Database, содержать только один модуль со скриптами – файл script.pas. Однако, если программа содержит много дополнительного функционала, то размер этого файла может быть огромным. Поэтому вполне логично разделить скрипты на логические части и хранить их в отдельных файлах.

Модульная архитектура улучшает восприятие исходного текста, позволяет повторно использовать отдельные модули, копируя нужные файлы в новый проект. Поэтому очередное улучшение “Справочника разработчика” связано с отображением модульной структуры проектов.

Хранимые данные

Поскольку заполнение сведений о модулях будет автоматическим, а модули могут храниться в иерархической системе папок, то структуру хранения данных нужно изменить, добавив несколько полей:

В поле path будет храниться полное имя модуля, поля parentID, nullID и isGroup служат для организации древовидного представления информации о модулях.

Формы

Форма табличного представления является динамической, поэтому для её создания понадобится всего лишь несколько сточек в файле dform.ini

[Unit_Tree] table=unit fields=unit.name,unit.status,unit.description,unit.editDate captions=Название,Статус,Описание,Дата ред. sort=ParentID,name parentField=parentID masterField=id_project isDetail=True parentControl=frmMain.tshUnit filter=id=-1
Code language: SQL (Structured Query Language) (sql)

Форма редактирования традиционная:

Так как в “Справочнике разработчика” реализована система локализации, необходимо добавить в файлы “English.txt” и “Русский.txt” перевод для элементов данной формы редактирования.

Обновление списка модулей

Процедура обновления UpdateProjectList_UpdateUnitList() может вызываться как отдельно (по кнопке на главной форме), так и в цикле выполнения UpdateProjectList_UpdateList(), поэтому отображение прогресса выполнения сделано в ней опционально. Второе важное отличие – автоматическое создание родительских узлов дерева, которые соответствуют физическим папкам для хранения фалов.

procedure UpdateProjectList_UpdateUnitList( AIDProject:integer; AShowProgress:boolean); // обновление информации по модулям проекта // AIDProject - ID проекта // AShowProgress - флаг отображения прогресса var tmpScriptDir: string; tmpFileList: string; tmpFiles: array of string; i: integer; tmpCount: integer; tmpMaxCount: integer; tmpUnitName: string; tmpSQL: string; tmpDataSet:TDataSet; tmpFileName: string; tmpIDParent: string; tmpName: string; // короткое имя tmpDirs: array of string; function FindParent:string; var j: integer; s: string; tmpPredResult: string; // поиск родительской папки по максимальному совпадению путей begin tmpSQL := 'SELECT id FROM unit WHERE (id_project='+IntToStr(AIDProject)+') and (isGroup = 1) and (path = '+StrToSQL(ExtractFilePath(tmpUnitName))+')'; Result := SQLExecute(tmpSQL); // если имя составное, а родитель не найден, то создать родителя! if (Result = '') and (tmpUnitName <> tmpName) then begin tmpPredResult := 'NULL'; tmpDirs := SplitString(tmpUnitName,'\'); s:=''; for j:= 0 to Length(tmpDirs)-2 do begin if s<>'' then s := s+'\'; s := s + tmpDirs[j]; // проверяем папки tmpSQL := 'SELECT id FROM unit WHERE (isGroup = 1) and (id_project = '+IntToStr(AIDProject)+') and (path = '+StrToSQL(s)+') '; Result := SQLExecute(tmpSQL); if Result = '' then begin tmpSQL := 'INSERT INTO unit (id_project,path,name,status,ParentID,isGroup) VALUES ('+IntToStr(AIDProject)+','+StrToSQL(s)+','+StrToSQL(ExtractFileName(s))+','+'"Новая папка",'+tmpPredResult+',1)'; SQLExecute(tmpSQL); Result := IntToStr( Last_Insert_ID() ); end; tmpPredResult := Result; end; end else if Result = '' then Result := 'NULL'; end; begin if AShowProgress then Progress(0,0,'Обновление данных о модулях'); try tmpScriptDir := SQLExecute('SELECT path FROM project WHERE id='+IntToStr(AIDProject))+'\script'; if not DirectoryExists(tmpScriptDir) then exit; // проверка имеющихся записей tmpSQL := 'SELECT count(*) FROM unit WHERE id_project='+IntToStr(AIDProject); tmpMaxCount := SQLExecute(tmpSQL); tmpCount := 0; tmpSQL := 'SELECT * FROM unit WHERE id_project='+IntToStr(AIDProject); SQLQuery(tmpSQL,tmpDataSet); try while not tmpDataSet.EOF do begin if AShowProgress then Progress(tmpCount,tmpMaxCount,'Проверка папок и файлов') else Application.ProcessMessages; tmpFileName := Trim( tmpScriptDir +'\'+ tmpDataSet.FieldByName('path').asString ); if (tmpDataSet.FieldByName('isGroup').asInteger = 0) AND (not FileExists( tmpFileName )) then begin tmpSQL := 'UPDATE unit SET status = "Модуль не найден" WHERE id = '+tmpDataSet.FieldByName('id').asString; SQLExecute(tmpSQL); end else if (tmpDataSet.FieldByName('isGroup').asInteger = 1) AND (not DirectoryExists( tmpFileName )) then begin tmpSQL := 'UPDATE unit SET status = "Папка не найдена" WHERE id = '+tmpDataSet.FieldByName('id').asString; SQLExecute(tmpSQL); end; tmpDataSet.Next; inc(tmpCount); end; finally tmpDataSet.Free; end; // добавление новых tmpFileList := Trim(GetFilesList(tmpScriptDir,'*.pas')); tmpFiles := SplitString(tmpFileList,chr(10)); tmpMaxCount := Length(tmpFiles); for i:=0 to Length(tmpFiles)-1 do begin if AShowProgress then Progress(i,tmpMaxCount,'Анализ модулей ') else Application.ProcessMessages; tmpUnitName := Trim( tmpFiles[i] ); tmpName := ExtractFileName(tmpUnitName); delete(tmpUnitName,1,Length(tmpScriptDir)+1); tmpSQL := 'SELECT count(*) FROM unit WHERE (id_project = '+IntToStr(AIDProject)+') AND (path = '+StrToSQL(tmpUnitName)+')'; if SQLExecute(tmpSQL) = 0 then // модуль не найден begin // tmpIDParent := FindParent; tmpSQL := 'INSERT INTO unit (id_project,path,name,status,ParentID,isGroup) VALUES ('+IntToStr(AIDProject)+','+StrToSQL(tmpUnitName)+','+StrToSQL(tmpName)+','+'"Новый модуль",'+tmpIDParent+',0)'; SQLExecute(tmpSQL); end; end; finally if AShowProgress then Progress(); end; end;
Code language: Delphi (delphi)

Процедура работает в две фазы:

  • Проверка имеющихся записей
  • Добавление новых

Отсутствие папки со скриптами не является ошибкой, так как могут быть проекты, отлично справляющиеся со своими задачами без единой строчки кода.

Форматирование исходных кодов

В модуль formatter.pas добавлена процедура Formatter_FormatDir(), которая умеет форматировать папки с исходными файлами.

const FORMATTER_PARAM_DIR = '-silent -config:%0.s -d %1.s -r '; // procedure Formatter_FormatDir( APath:string ); // форматирование исходного текста Delphi внешней утилитой - все файлы в папке, рекурсивно var tmpConfigFile: string; tmpFormatter: string; tmpParam: string; begin // утилита и файл конфигурации лежат в специальной папке внутри нашего проекта tmpFormatter := ExtractFilePath( Application.ExeName ) + FORMATTER_EXE; tmpConfigFile := ExtractFilePath( Application.ExeName ) + FORMATTER_CONFIG; // если чего-то не нашли, то возбуждаем исключение if not FileExists(tmpFormatter) then RaiseException('Утилита форматирования не найдена: '+tmpFormatter) else if (FORMATTER_CONFIG <> '') and not FileExists(tmpConfigFile) then RaiseException('Файл конфигурации не найден: '+tmpConfigFile) else begin // делаем конвертацию tmpParam := Format(FORMATTER_PARAM_DIR,[tmpConfigFile,APath]); OpenFile(tmpParam,tmpFormatter); end; end;
Code language: Delphi (delphi)

Внимание! В файле настройки правил для утилиты formatter.exe необходимо изменить следующий параметр форматирования:

<Option Name="DelphiFormatter.CompilerDirectivesCapitalization" Type="Borland.Together.OpenAPI3.Config.CAPITALIZATION" Category="DelphiCapitalization" Value="AsIs" Levels="Formatter" />
Code language: HTML, XML (xml)

Просмотр файла

Если выделить модуль, а затем в нижней части экрана выбрать вкладку “Код”, то в ней отобразится исходный код модуля с подсветкой синтаксиса.

Другие доработки

Форма dtfUnit_Tree создала прецедент, в котором форма одновременно является зависимой и отображает древовидные данные, поэтому семантику описания динамических форм пришлось доработать, разделив понятия

  • parentField – поле для хранения ссылки на родительский узел дерева
  • masterField – поле для хранения связи с главной таблицей

Доработки также затронули следующие процедуры и функции: DTF_Create(), frmUpdateProjectList_btnUpdate_OnClick(), UserApp_GetMasterID(), UserApp_UpdateDD().

Ссылки

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *