Изменения

BugzillaORM

30 722 байта добавлено, 09:09, 24 июля 2012
м
Обновление модели
[[Категория:Разработка]]В Bugzilla половина кода в шаблонах, половина на основе полу-ORM’а Bugzilla::Object. Большая часть - часть — говнокод. С одной стороны - стороны — ORM бы туда, было бы классно, НО! ORM мало, и нужен не он! Все (Почти все существующие) ORM-движки - (кроме разве что [https://docs.djangoproject.com/en/dev/topics/db/models/ Django Models]) — это просто объектный интерфейс к базе данных. А нужно некое объектное ядро, которое бы позволяло создавать свои объекты, с полями различных типов, в том числе и ссылающимися на другие такие же объекты, с возможностью приписывания специальных особенностей полюполям, с общим механизмом общими механизмами хранения истории , рассылки уведомлений, системой прав и с автоматическим базовым CRUD-интерфейсом. == Идеи по созданию объектного ядра == === Объекты в Bugzilla === * баг* классификация, продукт, компонент* тег (ключевое слово / keyword), milestone, версия, статус бага, agreement (пример кастомного поля)* вложение, комментарий, флаг, тип флага* пользователь, группа* если таки будет ядро — ещё появляется метакласс сущности, объекты которого описывают типы сущностей
<graph>
Баг -> Флаг;
Баг -> Тег [dir=both, color=blue];
Баг [shape=box, style=filled, fillcolor="#e0e0ffffe0e0"];
Продукт [shape=box];
}
</graph>
Есть сущности:* баг* классификацияПричём, продуктпонятное дело, если баг принадлежит к некоторому продукту (через компонент* тег (ключевое слово / keyword), milestone, то и его версия, статус багаmilestone, agreement (кастомное поле!)* вложениедолжны принадлежать к тому же продукту. Сейчас в Bugzilla это реализуется так — для каждой сущности, комментарийсвязанной с багом, флагв баге есть «поле», тип флага* пользовательа для ограничения значений полей поля делаются «зависимыми» от других полей. Из-за этого там куча костылей и, группа* как ни страннопо сути, «сущность» — «метакласс» (класс сущности)повесить зависимость на отличное от поля «продукт» поле невозможно.
У сущностей есть:* ID — первичный ключ* атрибуты* ссылка на контролирующее поле и значения <br /> то есть если другая сущность ссылается на эту, другим полем она должна ссылаться и на контролирующую* способ преобразования в строку* ''Возможно, права доступа к сущности — привязка действий над сущностью (просмотр, правка, возможно, другие) к группам пользователей. Возможно, не нужно это сюда пихать.''=== Минусы текущей реализации ===
Чем плоха текущая багзильная реализация этого «добра»? Очень просто: '''почти ни хрена не настраивается''' - нельзя ни добавить свои возможности, ни отрубить ненужные встроенные. Ибо всё жёстко и криво (хардкод). А ещё:* История хранится только для багов, но не для остальных объектов.* Шаблоны переусложнены.* Нет "объектов", есть «значения полей», соответсвенно, им не добавишь атрибутов и не навесишь логики. * Значения кастом полей в таблице багов хранятся не ссылками по ID, а по именам, что создаёт геморрой для зависимых полей, так как чтобы точно идентифицировать значение, приходится брать ещё и значение поля, от которого оно зависит.* Для выпадающих списков есть как «контроль значений», так и «контроль видимости поля», что в корне '''''неверно'''''! Типа поле скрыто, но при этом есть варианты, которые можно выбрать? Бред! Или наоборот — поле доступно, но из вариантов — только пустое значение.* По факту, ни от чего, кроме продукта, зависимым значение не сделаешь.* Код полей не генерится автоматически, а ручками вписываются в шаблоны.* На всё это навёрнута туча неструктурированного кода, определяющего поведение «встроенных» полей. === Проект супер-объектного-ядра === Что предлагается сделать:* Есть объекты. У объектов есть поля (разных типов). В том числе, есть типы «multi-select» и «single-select», ссылающиеся на другие объекты.* Зависимости полей могут прописываться дополнительно, в виде ограничений вида: «баг.компонент.продукт == баг.продукт». Могут и не прописываться. Теоретически, могут прописываться несколько ограничений на одно поле, например, чтобы какой-нибудь атрибут зависел и от продукта, и от, скажем, операционной системы. Такое устройство наиболее гибкое и в частности позволяет багу иметь несколько полей, ссылающихся на один тип объектов.* Поля могут скрываться в зависимости от значений других полей. Селект-поля скрываются, если в них по зависимостям нечего выбирать. На поля остальных типов можно вешать «настройку видимости», подобную текущей багзильной реализации — просто тупо «поле = одно из значений…».* HTML-код полей должен генерироваться автоматически. Также желательна возможность задавать группировку полей в интерфейсе — например, опционально раскрываемые «Advanced Fields» (только общим fieldset’ом, а не как сейчас — просто скрытие полей в разных местах формы).* На поля можно назначить дополнительные обработчики, задающие более хитрое поведение. Сами обработчики пишутся в коде, по возможности — максимально абстрагированно от конкретного поля. Назначаться они при этом должны не из кода, а из интерфейса конфигурации модели. На этих дополнительных обработчиках должно работать всё! Примеры:** Платформа, ОС: функции угадывания значения по умолчанию на основе заголовков запроса.** Статус: есть ограничения переходов из состояния в состояние (workflow). Возможно, его нужно попытаться вынести в базовые настройки модели.** CC: специальное поведение при клонировании, смене Assignee и QA (старые добавляются в CC), специальное «значение по умолчанию», зависящее от компонента. Возможно, зависимые значения по умолчанию стоит как-то протащить в общее ядро, но это чуть более тонкий момент, чем всё остальное.** Keywords/теги: автосоздание новых значений.* История хранится централизованно для всех объектов.* Общий механизм рассылки изменений по почте для всех объектов каким-либо образом «связанным» пользователям. Настройки уведомлений на уровнях: событие (изменено значение поля, добавлена сущность) и отношение (собственно связь).* Поиск: адаптировать сильно прокачанный движок поиска из [http://wiki.4intra.net/Bugzilla4Intranet Bugzilla4Intranet]. Адаптировать долго не придётся, потому что с точки зрения read-only все типы полей останутся, по сути, те же самые. С другой стороны, его нужно обобщить, так что кое-что сделать всё-таки будет нужно. Но зато в итоге получится супер-инструмент — система выборок, которая отлично хавает большие базы, сложные ''и произвольные'' структуры запросов и не давится :) (доказано Bugzilla4Intranet)* Права доступа: Нужна опять-таки общая схема для всех объектов, с одной стороны, простая для вычисления системой, а с другой стороны, дающая достаточную гибкость разных настроек. По всей видимости, нужны с одной стороны опять-таки «связанные» пользователи, а с другой стороны — роли. Причём, пользователи могут быть «связанные» через какое-то поле — например, человек может быть связан с багом, потому что прописан как Global Watcher для продукта этого бага. Роли могут набираться из действий над полями различных объектов и права на просмотр объектов. Возможно, нужно давать возможность разграничивать доступ на просмотр к разным полям — например, в Bugzilla только группа time-tracker’ов может посмотреть информацию о рабочем времени. Таким образом наш трекер превратится, по сути, в модульное приложение, построенное вокруг очень прокачанного автоинтерфейса полей и объектов. Естественно, это уже не Bugzilla ни разу, но зато чем-то похоже на [http://roundup.sourceforge.net/ Roundup] — «конструктор трекеров». Однако и с ним различий много — roundup не умеет зависимых селект-полей, модель задаётся в коде, права доступа слабые, поиск слабый. === Обновление модели === Обновление базы багзильское (тупая последовательность операций):* (+) Очевидный порядок обновлений, нет проблем с их зависимостями друг от друга (новые просто дописываются в конец, порядок всегда правильный)* (-) Не проверяется, корректна ли схема БД после обновлений* (-) Обновления задаются именно для SQL БД, а не для метамодели* (-) Не очень красивая портянка в функции обновления БД Обновление базы наше:* AddType -> RenameFields -> AddFields -> ChangeFields -> DropFields* ChangeFields с помощью функций обновления* (???) Самый интересный вопрос - как сделать функции обновления не на SQL, в условиях когда модель в неконсистентном и вообще не знает, в каком состоянии?* После каждого атрибута шага запуск хука* Переименования задаются декларативно* Возможность Dry Run, просмотра и проверки последовательности обновлений* Зависимости обновлений в рамках каждого шага можно задать декларативно (хотя ситуация редкая)*: А может, просто стоит задавать их в ассоциативном массиве по порядку* Ещё может быть, что обновление одного типа зависит от обновления другого (причём это уже более частая ситуация) === Модель === Получается, что ядро модели состоит из следующих таблиц:* Типы объектов** Название типа** Дополнительное поведение типа* Поля** Ссылка на объект** ID поля** Название поля** Тип поля** Nullable? Т.е. разрешено ли пустое значение** Поле этого же объекта, контролирующее видимость данного (для не-select полей)** Связи поля (для select-полей)** Дополнительное поведение поля* Контроль видимости полей** Ссылка на поле** ID объекта, для которого оно видно* Транзакции (группы изменений)** Момент времени** Изменивший пользователь* История изменений** Ссылка на транзакцию** Тип объекта** Ссылка на объект** Ссылка на поле (кроме добавления объекта)** Старое значение** Новое значение* Один неудаляемый тип — пользователи (их поля, тем не менее, можно менять)* Связи объектов с пользователями** ID связи** Название связи** Ссылка на объект** Цепочка свойств, заканчивающаяся объектом «пользователь»* Пользовательские настройки оповещений** Ссылка на пользователя (либо пустая для настроек по умолчанию)** Ссылка на связь** Тип события: добавление/удаление/изменение** Ссылка на поле объекта (может быть пустая)* Роли (или группы, что примерно то же самое)** Включаемые роли** Включаемые разрешения* Разрешения (из которых состоят роли)** Ссылка на связь** Тип разрешения: просмотр/создание/удаление/изменение** Ссылка на поле объекта (пустая = весь объект)** Ссылка на значение поля (пустая = любое) === Типы полей === <tab sep="bar" class="wikitable" head="top">Тип | Параметры | ПредставленияСтрока | Длина | Input, textarea, rich editЛогический | | ФлажокЧисловой | Точность (M.N) | InputДата и/или время | Дата? Время? | Поле со всплывающим календарём, просто календарь, просто полеФайл | | Поле загрузки файлаSingle-select | Тип объекта | Радиобатон, HTML-селектMulti-select | Тип объекта | HTML-мультиселект, флажки, комбо-бокс</tab> == Текущее состояние == Базовые поля (то, чего не быть логически не может):* Объект, баг, пользователь, группа, комментарий к багу.* Продукт имеет специальный смысл — разграничение прав. Поэтому он тоже обязателен. У каждого поля есть:
* Тип
* Значение по умолчанию
* Вид показа в интерфейсе(по умолчанию?)* Копируется ли значение атрибута при клонировании сущности* Показывается ли атрибут в форме создания сущности* Ссылка на контролирующий атрибут той того же сущностиобъекта, что и этот, и на его значения
*: Если выбрано, означает, что атрибут показывается, только если другой атрибут той же сущности, которой принадлежит этот, имеет одно из заданных значений
 
Важно, что зависимые сущности хранятся в денормализованном виде, то есть если мы хотим видеть поля «продукт» и «компонент» у бага, а выбранный «компонент» однозначно определяет «продукт», то всё равно хранятся и являются атрибутами бага они оба.
Типы атрибутов:
* {{ok}} Строка
** E-mail адрес — особенности: опционально скрывается ради антиспама, может иметь настройки уведомлений
* Boolean
* {{ok}} DecimalДесятичное число
* Дата
* Время
* {{ok}} Дата+время
* Файл (вложение)
* Single-Select &rarr; ссылка на сущность (то есть «многие к 1»)
** ''Необязательный вариант синглселекта — «1 к 1».''
** {{ok}} существует в частичном виде — без выбора желаемой сущности
* Multi-Select &rarr; ссылка на несколько сущностей одного типа (то есть «многие ко многим»)
** Вариант мультиселекта — «список подчинённых» Два способа показа — поле со списком (то есть 1 ко многимкомбобокс). Например, список аттачментов бага, список комментов к багу.или мультиселект** {{ok}} существует в частичном виде — без выбора желаемой сущностиСписок тегов, с автодобавлением значений* Файл (вложение).== Существующие поля ==
Email-уведомления в общем виде задаются как уведомление на поле типа email, вытаскиваемое по цепочке из сущности (например bug.product.product_watcher.email) при изменении заданных атрибутов сущности.=== Поля багов ===
Поля багов перечислены ниже. Жирное «да» в колонке «можно отключить» означает, что отключать можно уже сейчас (скорее всего, через параметры типа usevotes и ти т. пп.). Нежирное «да» в колонках «можно отключить» и «можно менять тип» означают, что чисто теоретически логика работы Bugzilla это позволяет.
<tab sep=bar class=simpletable head=topleft>
поле | тип | можно отключить? | можно менять тип? | примечания
Примечания:
* Все поля-ссылки на пользователей показываются в виде строки либо в виде combo-box’а, и у них есть функция валидации — валидации — она угадывает юзеров по некорректным именам. ==== Логически минимальный набор полей ==== Совершенно точно, никогда и ни при каких условиях у бага не могут отсутствовать поля:* ID* Заголовок* История => reporter, время создания (creation_ts), время изменения (delta_ts), время оповещения по почте (lastdiffed)* КомментарииБез этого никакой «баг» смысла не имеет ни в одном баг-трекере. ==== Устаревшие поля ====
Устаревшие поля / поля, которые ХЗ зачем нужны в таблице полей багов:
* requestees.login_name
* setters.login_name
 
==== Вычисляемые поля ====
Вычисляемые поля багов:
</tab>
=== Поля вложений:=== 
<tab sep=bar class=simpletable head=left>
submitter | single-select, показ в виде select’а или строки, есть функция валидации (угадывания юзера по некорректному имени)
</tab>
=== Поля компонентов:===
<tab sep=bar class=simpletable head=top>
</tab>
Что ушло бы в ORM== Из чего состоит багзилла?==
* Custom Fields, то есть, управление полями багов.* Отключение стандартных полей типа OS, Hardware.* Изменение типов стандартных полей типа целочисленных приоритетов.* Добавление спецполей в продукты, компоненты и т. д. — сейчас это делается в коде.* Версионирование всех объектов. История бы хранилась унифицированно. Не было бы отдельно таблиц bugs_activity и longdescs, соответственно не было бы и тормозов при проверке «Only bugs changed between…»* Простые интерфейсы типа CRUD (Create/Read/Update/Delete), сейчас созданные непонятно каким копипастом. В основном имеется ввиду админка.* Права на редактирование объектов.* Валидаторы и подсказки значений.* Корректная валидация зависимостей полей друг от друга.* Часть SQL-запросов, написанных ручками в коде.* Создание новых объектов типа SCRUM карточек.* Мохнатая логика автоматической постановки/обновления багов из обработки входящих e-mailов и Excel-импорта=== С внешней точки зрения ===
Крупные блоки функционала в Bugzilla с точки зрения пользователя: * Можно было бы прикрутить статистику Создание, просмотр, изменение багов, история по любым объектамбагу** Форматирование комментариев* Создание, причём с просмотр, изменение вложений* Поиск багов, форма поиска* Графики одни, графики другие, Summarize Time Machine* Исходящая почта* Входящая почта* Запросы флагов* Напоминания (whining)* Регистрация, то есть просмотром статистики изменение, раздача групп пользователям* Пользовательские настройки* ''XML-импорт (в полудохлом виде)''* Web-сервисы (XML-RPC, JSON-RPC)* Sanity Check* Графы и деревья зависимостей* Голосование за любой прошедший момент временибаги ==== Наши крупные фичи ==== * SCRUM-карточки* RSS-лента активности* XML-Simple Web-сервисы* Проверки корректности* Excel-импорт* Информер* Глобальная авторизация* Today Worktime, Super Worktime Плюс вагон и маленькая тележка мелочи. : === Страница создания бага (enter_bug.cgi)===
Текущая логика страницы создания бага:
** списки пользователей, относящихся к багу в combo-box’ы
** опциональный запрет на ввод приоритета на основе конфигурации (letsubmitterchoosepriority)
** список флажков — флажков — ограничителей доступа группами
* значения полей по умолчанию:
** которые совсем по умолчанию
* напоминания о вводе времени
* предпросмотр комментариев
* постановка вложения сразу при создании добавление ссылки на старый аттач для клонированного из коммента бага
* переключатель Show Expert Fields
* Submit по Ctrl-Enter
[[Категория=== Обработчик создания бага (post_bug.cgi) === * Редирект на enter_bug.cgi если форма не заполнена* Проверка, не был ли уже использован этот token для постановки другого бага?* Вывод страницы с URL-шаблоном постановки бага, если попросили* Заполнение описания по шаблону, если использовался спецвид формы (например create_guided)* Отправка куки VERSION* Начало/конец транзакции в БД* Постановка собственно бага (Bug::create)* Постановка вложения сразу при создании бага* Постановка флагов на вложение* Постановка флагов на баг* Рассылка почты по багу, по его зависимостям* Вывод сообщения о том, что баг поставлен, о том, что CC-список обрезан по группе === Обработчик изменения бага (process_bug.cgi) === * Начало/конец транзакции в БД* SELECT FOR UPDATE багов* Удаление значений полей, равных dontchange (используется в групповом редактировании)* Угадывание юзеров по части логина/имени* Проверка коллизии по delta_ts, вывод только действительно изменённых полей в форму подтверждения* Проверка token формы* Загрузка следующего бага из списка ДО обновления текущего (O_O)* Проверка прав редактирования по всем багам* Установка значения продукта до всех остальных полей* Установка новых групп до остальных изменений, когда включён strict_isolation* Установка/изменение флагов* Установка зависимостей бага* Установка ключевых слов* Установка остальных полей* Установка некоторых значений только для обновлений отдельных багов (alias, cclist_accessible, reporter_accessible, isprivate на комментах)* БОльшая часть логики обновления поля CC* Вызов функции проверки strict_isolation* Перемещение багов (MOVE) — полуживое, хз как работает* И после всего этого — ещё изменения, status, resolution, dup_id* Теперь всё это ещё незакоммичено, вызов реальных обновлений из Bug.pm* Отправка почты по зависимостям, если статус поменялся с открытого на закрытый* Обрезание CC-списка по группе (наше)* Сообщение, если очищено remaining_time* Удаление голосов за баг, если переместили в другой продукт, и проверка голосо-подтвеждённости бага* Отправка почты CC, Assignee, Reporter’у, QA, старым Assignee, QA и CC* Отправка почты по багу, дубликатом которого был помечен данный* Добавление нескольких вложений (наше)* Отправка почты по флагам, через отдельный механизм сбоку* Редирект (наше) или показ следующего/того же/никакого бага* Не показывает ничего, если USAGE_MODE_EMAIL === Список/поиск багов (buglist.cgi) === * SuperWorkTime (наше доработко)* Редирект на форму поиска для пустого запроса* Редирект на форму поиска с добавленными пустыми полями, если на форме поиска жмут Add/Remove поле без JS* Редирект с POST’енного запроса на GET* Быстрый поиск* Посыл на хрен, если content пуст при включённом параметре specific_search_allow_empty_words* Обратная совместимость: format=rdf -> ctype=rdf, ctype=rss -> ctype=atom* ctype=js слать в хрен* Поддержка server-push* regetlastlist — открытие последнего просмотренного списка багов (берётся из куки)* Удаление колонки relevance из списка колонок, если юзер не просит полнотекстовый поиск* В buglist.cgi почему-то тусуются функции InsertNamedQuery, LookupSeries, GetQuip, GetGroups* Генерация имени файла, если попросили список не в html-формате* Выполнение запросов поиска* Сохранение и удаление сохранённых запросов из БД* Запуск series* Если сохранённый запрос не говорил нам свой формат, решаем что advanced* Вкуривание списка колонок (длинное, тварь)* Определение порядка сортировки* Подсчёт сумм значений полей таймтрекинга* Проверка доступа к багам и установка значения в implied или manual (х.з зачем нужно)* Массовое редактирование багов, в частности сборка пересечений доступных значений полей* Кодировка CSV (наше доработко) === Система прав === Какие в Bugzilla]]есть права / ограничения доступа? * Права доступа к продуктам** MANDATORY / SHOWN / DEFAULT / NA** Entry** Canedit** Editcomponents** Canconfirm** Editbugs* Private комментарии — видны только инсайдер-группе* Информация о таймтрекинге — видна только группе, списывающей время* reporter_accessible, cclist_accessible на отдельных багах* Опциональные группы на отдельных багах* Права на правку отдельных групп* Права editclassifications, editcomponents, editfields, editkeywords, editusers === Настройки оповещений о багах === Отношения:* Assignee, QA, Reporter, CC* Requestee или Setter флага (реально в Bugzilla прикручено сильно сбоку)* По идее — также создатель аттачмента, коммента (если разрешать править)* Watcher указанного выше* Global Watcher События:* Добавлен/удалён из указанного выше* Изменено одно из полей бага (м.б отдельные настройки, м.б вместе)* Баг / блокирующий баг меняет статус с закрытого на открытый или обратно* «Но кроме» UNCONFIRMED* «Но кроме» своих изменений == Что не так? == Что в Bugzilla не так? Что нужно поменять, чтобы она перестала быть говном? Да, я знаю, что почти всё, но дело не в этом — дело в том, что создавать систему «от противного» — есть хороший тон, ведущий к прогрессу. === Ядро === Самое жирное «не так» в Bugzilla — это слабость ядра. Если бы ядро было прокачанным, как в начале статьи, оно бы заменило: * Custom Fields, то есть, управление полями багов.* Отключение стандартных полей типа OS, Hardware.* Добавление спецполей в продукты, компоненты и т. д. — сейчас это делается в коде.* Версионирование всех объектов. История бы хранилась унифицированно. Не было бы отдельно таблиц bugs_activity и longdescs, соответственно не было бы и тормозов при проверке «Only bugs changed between…» либо комментарии бы дублировались в bugs_activity (логичнее)* Отправка почты, которая сейчас отправляется через 2 разных места.* Простые интерфейсы типа CRUD (Create/Read/Update/Delete), сейчас созданные непонятно каким копипастом. В основном имеется ввиду админка.* Система прав стала бы и проще (по сравнению с хрен-пойми-системой Mandatory/Default/Shown/NA), и имела бы больше возможностей.* Валидаторы и подсказки значений.* Корректная валидация зависимостей полей друг от друга.* Часть SQL-запросов, написанных ручками в коде.* Вся логика постановки/обновления багов из process_bug и post_bug, дублированная сейчас в обработке входящих e-mailов и Excel-импорте* Даже Excel- и XML-импорт, причём импортировать можно было бы вообще всё что угодно. Появились бы дополнительные возможности: * Создание новых объектов типа SCRUM карточек.* Изменение типов стандартных полей типа целочисленных приоритетов.* Можно было бы прикрутить статистику по любым объектам, причём с Time Machine, то есть просмотром статистики за любой прошедший момент времени. :) === Внешности === * Большая часть интерфейсов нуждается в полной переделке. Например:** На форме бага «Show Advanced Fields» должно быть сделано отдельной группкой полей (fieldset), а не скрытием полей прямо посреди формы в разных местах (пугает).** Страница настройки прав продукта — мегастрёмная. Таблица с группами там на хрен не нужна никому вообще.** Частично относится к ядру, но — интерфейс редактирования «контроля значений» кастом-полей это сейчас полная жесть — например, нельзя посмотреть список значений кастом поля для конкретного продукта (от которого зависят наборы значений).* Историю изменений бага показывать вперемешку с комментариями к багу (а-ля Trac). Туда же можно заинтегрировать и замешать коммиты из системы контроля версий.* Добавить ленту обновлений по багам, поиск «последних багов», что-то типа форум-функциональности.* Возможно, добавить Grid View с возможностью массовых обновлений в поиске.* Отрефакторить кучу действий, связанных с поиском, расположенных под списком багов.* Keywords переделать в «теги», то есть, сделать, чтобы они сами заводились новые.* <s>Заменить YUI на jQuery</s> в Bugzilla4Intranet уже, форму поиска сгруппировать (но не так, как в 4.0), сделать почти все фильтры добавляемыми, добавляемыми через JS, чтобы не перезагружалась страница каждый раз* Документацию «насытить» в вики, POD-документацию по коду туда же. === Кишки, кроме ядра === * Добавить глобальный объект статуса, в который сохранять почту, которую нужно отправить, и сообщения, которые сейчас руками сохраняются в сессию.* Использовать не CGI, а хеши параметров запроса, это улучшает и производительность, и переносимость, и спасает от кучи глюков, так как например, $cgi->param может вернуть как список, так и скаляр* Использовать не кучу CGI-скриптов, а несколько (мало) точек входа и классы, всю мохнатую логику перенести в них. Не использовать модуль CGI.pm вообще (!), потому что он прости господи даже без use strict написан и порождает некоторые глюки сам по себе. В крайней форме точки доступа — это один index.cgi, позволяющий работать через CGI, один server.fcgi (FastCGI), один модуль для апача, и м.б. один для HTTP::Server::Simple. Ибо по сути, интерфейс «запроса» тривиален и не требует никакого бешеного CGI.* Убрать ВСЕ строки (terms и т. п.) и описания ошибок из шаблонов и «длинного if’а», добавить отдельный уровень «локализации» / «таблицы строк», и переместить всё это в него. Потенциально также переместить туда же вообще все строки/тексты из шаблонов, для лёгкой локализации.* Обязательно оставить совместимость с PostgreSQL, но генерилка запросов не должна на это рассчитывать и должна работать оптимальнее, чем сейчас, например, в смысле дурацких подзапросов assigned_to=(select id from profiles where login_name=?) и т. п.* Желательно заменить Template::Toolkit на что-нибудь, хотя бы даже на моё поделие VMX::Template, чтобы не общаться с этим тормозом. Но как МИНИМУМ, даже если не заменять — убрать ВСЮ обратную связь с БД из шаблонов, ибо именно она убивает производительность в первую очередь! Ну ладно, не в первую, а во вторую, после дурацкого движка поиска. Хотя это и будет уже не MVC, а MVP :)