Как стать автором
Обновить

Комментарии 18

Само решение интересное, но почему изначально нельзя было перенести запросы в саму БД в виде хранимых процедур?

Нормально там запросы хранятся в свойствах Query-компонентов, зачем было мучиться с переносом их в rc? Чет вы деятельность на пустом месте замутили…

Нормально там запросы хранятся в свойствах Query-компонентов, зачем было мучиться с переносом их в rc? Чет вы деятельность на пустом месте замутили…

Не хранятся там они в Query-компонентах. Не в этом проекте. И старые «обмены» я не трогал, пока их не приходилось ремонтировать или дорабатывать. Всё это предназначалось в первую очередь для новых задач.


Запросы, как ни странно, определялись ранее в тексте программы, и загонялись последовательно в один и тот же query. Примерно так:


with ExportQuery do
try
  Close;
  SQL.Clear;
  SQL.Add('SELECT DISTINCT Alias1 = C1.Alias, Alias2 = C2.Alias, ...');
  SQL.Add('FROM ... JOIN ... ');
  SQL.Add('JOIN ...');
  SQL.Add(Format('LEFT JOIN ... WHERE GroupName = %s AND OperDate BETWEEN %s AND %s',
    [QuotedStr(GroupName), QuotedStr(DtoS(BeginDate)), QuotedStr(DtoS(EndDate))]));
  SQL.Add(Format('AND ...',
    [GetIntegerObject(SectorComboBox)]));
  Open;

А новых «обменов» у меня один-два в месяц. За это время порядка четырёхсот только запросов добавлено. В новых задачах я, кстати, сразу избавился от отдельной формы на каждую задачу. Во всех последующих — одна на всех форма (настройки там достаточно однообразные), декларативное описание потребных настроек и фреймворк. Первое время я использовал ещё дата-модули, но оседлав ресурсы отошёл от них — трудозатратно, много проблем с обобщением процессов… Кстати, помимо запросов я добавляю в ресурсы «преобразования» — фактически, DSL для конкретной задачи. Вроде такого (фрагмент), это вместо кода для формирования xml:


SKU
    code=(50)+ProductID
    shortname=(50)ProductName
    name=(100)ProductName
    article=(150)article
    producercode=(50)article
    parentcode=(50)+GroupID
    SKUType==30
    *[0]units=Cfg_Chicago_SkuUnits
    *[1]prices=Cfg_Chicago_SKUPrices

Теперь представьте себе задачу: после изменения схемы БД найти все запросы, где упомянуто конкретное поле. Читали выше о том, как в компонетнах хранится текст? Между тем, задача поиска контекста по запросам возникает регулярно, пусть и не всегда причины столь жёсткие. Открывать сотню с лишним форм и протыкивать компоненты, надеясь ничего не пропустить, потрошить не совсем текстовый dfm или поиск по проекту в современном редакторе — что бы Вы выбрали? Мне хватило ровно одного раза, чтобы от хранения запросов в компонентах отказаться сразу и навсегда.

Любопытно, спасибо. Вряд ли мне когда-нибудь пригодится это само по себе, но зато очень большой аргумент в копилку "дать проекту на D7 умереть и переписать с чистого листа" вместо того, чтобы пытаться в нём что-то править даже по мелочам.

Древняя некромантия с Delphi, perl, js, cmd… Велосипеды, костыли… Но работает! ;)

Какой капец…
Героически решаем проблему которую выдумали сами себе. Избыток знаний и энергии явственно мешает делать простые и понятные вещи.

Сборка запроса кодом это конечно полная фигня и переделать было нужно.
Запросы вполне себе хранятся в компонентах Query. Даже если они потом запускаются через единственный Query — то не проблема скопировать тест из другого.
Редактор запросов в виде компонента — ненужная хрень. У многих компонентов попытки такое сделать, но непонятно зачем, все равно нормально не получится, лучше запросы, с нормальными параметрами, редактировать во внешних специализированных для нужного sql-сервера программах. В моем случае это IBExpert. Запрос там пишется, отлаживается, потом просто вставляется копипастой в Query в программе.
Но это, понятно, тоже немного странно :) и в итоге хранение запросов файлах — нормальная тема, мне нравится. Но непонятно зачем вокруг этого делать такую сложную обвязку из сторонних инструментов. perl, js, какие-то левые внешние инструменты, поведение которых зависит от версии… в батнике увидел touch — тоже не виндовая программа, что-то стороннее…

Хм… пришел в голову некоторый недостаток внешнего хранения. Можно ошибиться с указанием пути к запросу и это вылезет на стадии выполнения. Ошибиться с именем Query сложнее, не пройдет компиляция.

Мне бы виделся такой простой вариант.
Раз запросы хранятся в виде файлов, то значит есть загрузчик запросов в Query или через что они там выполняются. Пишем 2 варианта загрузчика —
1. непосредственно из внешних файлов, при отладочных запусках.
2. из ресурсов, при рабочем билде
Пишем батник для сборки рабочего билда, с предварительной сборкой ресурсов.

И ничего не нужно мониторить, редактируй что хочешь чем хочешь.

Cам пишу на Delphi7, «автор одной программы» которая используется внутри фирмы. Программе более 20 лет. Как потенциальный поддерживатель старых программ на старых дельфях, старпер, говорю — вы неоправданно усложняете порог вхождения в поддержку, используя множество сторонних инструментов, которые не являются необходимыми.
Старперы вроде меня, мало чего дополнительного знают, кроме собственно Delphi и минимума внешних инстументов. Чем меньше набор инструментов — тем проще мозгам старперов :)

Извините, хотел ответить Вам, но промахнулся — написал в корень. Ну да заодно пример добавлю — единственное место, где есть ссылка по имени ресурса. Это потом передаётся фреймворку, а он отдаёт готовый набор данных.


const
  PARTDEFS: array [0..nnn] of TCustomPartitionDef = (
    //**************************************************************
    // Отчёт «XXX»
    //**************************************************************
    // Продажи (нарастающим итогом)
    //**************************************************************
    (
      PartId: 'Sales';
      PartCaption: 'Продажи';
      DatasetSource: 'SQL_XXX_Sales';
      ConversionsSource: 'Cfg_XXX_Sales'; // Это тоже в ресурсах лежит
      FileNameTemplate: 'YYY_{{Filial}}_{{month}}_Sales.xls';
      EnabledFor: [emDay, emMonth];
    ),
...

Всё-таки я не очень внимательно прочитал Ваш комментарий, когда бежал на работу. Не понял сразу, что по ряду моментов Вы всё же со мной согласились, предложив в частностях альтернативу. Виноват, чего уж...


в батнике увидел touch — тоже не виндовая программа, что-то стороннее…

это из линукса, он ставится вместе с git. Git я использую БЕЗУСЛОВНО, вот просто без вариантов. Чего и всем программистам желаю. При этом без touch автоматизация работать будет, лишь замедлится реакция среды на изменения. Там ещё sleep есть в зависимостях, и его отсутствие тоже не критично (про всё это есть в README.md)


Редактор запросов в виде компонента — ненужная хрень

Согласен. Даже при том, что это не редактор, он только хранит код внутри себя. Для редактирования он использует редактор IDE, на скриншоте это видно. Ненужным он оказался именно по «некрасивому» способу хранения и по причине привязки к неплохому, но единственному редактору. Без мультикурсора :). Зато двойной клик на компоненте, открывающий сразу текст запроса (с подсветкой синтаксиса SQL, которая в Дельфи есть) сразу во вкладке основного редактора, всяко быстрее будет, чем выделить компонент, переключиться в Object Inspector, найти свойство sql и нажать на маленькую кнопку с многоточием (по двойному клику на AdoQuery, к примеру, открывается список параметров запроса, а вовсе не его текст). А потом в случае использования штатного компонента ещё на «Code editor» надо тыкнуть, если захотелось подсветить синтаксис. Но тупиковая ветвь, да. История. Эпос :).


Мне бы виделся такой простой вариант.
Раз запросы хранятся в виде файлов, то значит есть загрузчик запросов в Query или через что они там выполняются. Пишем 2 варианта загрузчика —
  1. непосредственно из внешних файлов, при отладочных запусках.
  2. из ресурсов, при рабочем билде
    Пишем батник для сборки рабочего билда, с предварительной сборкой ресурсов.

Предложение интересное, но не лишённое крупного недостатка. Работа приложения в момент разработки сильно отличается в основном функционале от того, что уходит конечным пользователям. Отладочные запуски не вскрывают проблем со сборкой ресурсов. Тут либо специально тестировать, либо признать что «так сойдёт» и когда-нибудь напороться.
И обратите внимание, что это предложение исключает только оперативный мониторинг, который во всей этой затее — самая простая часть! Создавать rc-файл для компилятора всё равно чем-то надо. Либо вручную (Фе!), либо опять же кодогенерация.

Начну с того, ресурсах у меня только четыреста из семисот пятидесяти файлов — запросы. Например, там тежит экселевский шаблон для некого интегратора. Однако именно запросы были причиной.


Дополню тем, что моя программа только генерирует валидный для Дельфи код и автоматизирует рутину: своевременно добавляет файл к списку для компилятора и компилирует. Всё. А, шифрует ещё в конкретном проекте, но это как раз излишество в общем случае. Я лишь перевожу ресурсы из статики ("созданы раз и навсегда") в динамику ("редактирую на лету").


Да, так, как Вы сказали, тоже можно. Но в моём случае плюсы усложнения для меня лично перевесили. В десятках мелочей у меня сократились трудозатраты (за день набегает ого-го).
Смоделируем ситуацию.
Добавляем новый обмен. Варианты:


  • скопировал шаблон формы или дата-модуля, подключил к проекту. Ой! Полез в dpr удалять автоматическое инстанцирование. Экспортов много (почти двести), инстанцировать их мне надо лишь по потребности.
  • или скопировал модуль (два или три модуля, но не суть). Подключил к проекту. Всё.

Далее. Работаем в тексте отдельного модуля (НЕ в коде формы) хотим отредактировать запрос. Варианты:


  • переключаемся в форму, находим компонент, выделяем мышкой, переключаемся в свойства, тыкаем мышкой на Свойство sql, тыкаем мышкой на маленький квадратик с многоточием, опционально чтобы было удобнее и была подсветка синтаксиса — тыкаем мышкой "открыть в редакторе". Ну ладно, если я использую свой TSqlVar, нахожу его на форме и делаю даблклик. Хочу плюшек? Копипаста во внешний редактор, правка, копипаста обратно (на этом этапе максимум ошибок по невнимательности). Переключаюсь обратно в код нужного модуля — сначала в редактор кода, затем на нужную вкладку.
  • или жму alt + tab, переключаясь в редактор, где уже открыт нужный запрос и вся папка ресурсов на сайдбаре. Редактирую со всеми плюшками, включая автодополнение и мультикурсор. Сохраняю и переключаюсь обратно alt+tab. Всё. Допустим, я делаю это раз тридцать за день. И какой вариант для меня лучше?

В условиях, когда помимо двух активных проектов отвлекаешься на оперативную техподдержку, это жирный плюс. Вышел из редактора, всё сразу сохранилось и будет использовано. И это только редактирование запросов.


Да, имена ресурсов не проверяются на этапе компиляции, это позднее связывание. Но я их указываю ровно один раз в декларативном описании этапа экспорта — копипастой из rc. Зато это описание — константа типа запись, по компактности и/или читаемости переплёвывающая вызов метода с параметрами. К тому же, описания сгруппированы в отдельном модуле, а не размазаны по коду. Факты — отдельно, поведение — отдельно. Следующее прикосновение к запросу — когда фреймворк предоставляет мне открытый уже датасет.

Однако, компиляция проекта из-под IDE Delphi 7 является чёрным ящиком без малейшей возможности прикрутить к ней хоть что-то своё. У неё просто нет ни одного хука, чтобы зацепить собственный обработчик

А точно ли так? По-моему, IOTACompileServices в Delphi 7 уже были доступны, я, помнится, прикручивал свой кодогенератор, генерящий часть файлов перед компиляцией проекта. Правда, за давностью лет уже не помню точно версию, у меня была и Delphi 6, и Delphi 2007, но проект я тот начинал ещё на шестерке.

А вот за это прямо-таки большущее человеческое спасибо. Зная, что именно искать, уже немало полезного нашёл. Обязательно исследую этот вопрос на днях.
Да, в семёрке оно уже есть (впервые появилось в 5-й). И в приложенных исходниках интерфейсы нашёл.

Я бы рассмотрел переписывание всех батников с сопутствующим зоопарком утилит на единственный exe написанный на этом же Delphi. Минимум зависимостей от прочих инструментов, и минимум зоопарка технологий, что положительно скажется на уровне вхождения в поддержку, иными программистами.

Практикую иногда такое дело. Вместо написания батника, отладка которого занимает катастрофически много времени, набросать то же самое на Delphi занимает меньше времени, отладка намного проще, и никаких внешних зависимостей.
Осталось перевести проект на Lazarus
И работать только под Linux.
<:o)

Дома — только линукс, но никакого Паскаля. Я его, конечно, умею, и даже люблю — по заповеди, как самого лучшего врага :).


Я предпочитаю Java, она ламповая и тёплая. Я её умею, и кое-что именно на ней крутится (серверное). Жаль, давно не приходилось тот проект дорабатывать, релакс же буквально, под Idea-то Ultimate! Оцените иронию: дефицитная ныне специализация на Delphi позволяет мне оплачивать лично для себя коммерческую "Идею", чтобы отдохнуть от Delphi. Но вообще на текущем месте с Java чисто инфраструктурные проблемы, да и вообще под неё надо ломать унаследованную парадигму "настольный клиент — sql сервер", а это больно, и, прямо скажем, не от меня зависит.


Вот и погоняю некроконструктов. Потому, что если зомби не ест, не пьёт и не спит, да ещё и работает добросовестно, то заменить его на того, кто хочет кушать, просит особых условий и вообще состоит в профсоюзе — не самая лучшая идея с точки зрения экономической целесообразности, к сожалению.

А откуда взялось требование хранить запросы именно в ресурсах exe?

Если хранить запросы в БД, по ряду причин бывает неудобно, или нельзя, то почему бы просто не скомпоновать дополнительный файл, лежащий рядом с exe и не засунуть запросы в него?
Тем более, что как я понял, у вас даже есть универсальная форма, и часть отчетов формируется чисто написанием еще одного запроса и конфига к форме?
Останется один вопрос — как-то обозначить версии, что бы одно к другому подходило и случайно не подсунули не тот файл к не тому exe.
Но проблемы с ресурсами это решило бы, путем отмены самого вопроса :)

Программа-монитор может запускать самодельный компоновщик файла с запросами, перекомпиляция самой программы даже не требуется.

В тестовом задании, когда устраивался на работу, так и сделал. Сказали: "круто, но..."
Мешают два обстоятельства. С одной стороны, ресурсы всё же связаны с кодом — каждая задача имеет не только запросы, но и код, чрезмерное абстрагирование всё усложнило бы до безобразия. И до кучи деплой а-ля колхоз. Настольное приложение, кто-то обновляет на автомате, кто-то нет, а кто-то только думает, что на автомате. Поскольку техподдержка тоже на мне, рассинхронизация версий кода и ресурсов — лишняя точка для прорастания проблем. Поэтому единый артефакт сильно облегчает сопровождение. Или обновили, или нет.

Если приложение универсальное и конфигурится запросами, то я бы эти запросы вынес во внешний файл любого удобного формата. Дальше написал бы на чем проще и удобней тулзу, которая бы собирала этот файл в случае изменения запросов. К файлу можно прикрутить шифрование, если в нем содержится чувствительная информация. Дальше сам основной exe файл уже бы не трогался вообще. Таким образом можно даже не разворачивать Delphi на той машине, где будут правиться запросы. Ну и некрокода можно будет касаться как можно реже.

Подобные проблемы знаю не понаслышке, так как поддерживаю одну софтину, написаную еще в 2001 году на C++Bulder 6. Хорошо, что править ее приходится довольно редко.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории