Я знаю, какое у вас сейчас лицо:
Но на самом деле идея не нова и изначально я даже думал не изобретать велосипед, ведь есть по описанию неплохой vba-blocks, с открытым исходным кодом. Бери не хочу.
Увы, так вышло, что я не умею писать скрипты в powershell, а у меня при установке какой-то из этих скриптов не отрабатывает и падает.
Если, кстати, кому-то интересно, попробуйте поправить, расскажите хоть как оно?
Ну а я беру палки и колеса, и начинаю лепить творить все же лепить.
Прежде чем мы начнем
1. Я не программист, я музыкант. Но сейчас моя профессия – разработчик VBA. Я не знаю как правильно писать код. Но учусь, кажется. И когда я делюсь своими мыслями – я тоже учусь. Если вы хотите помочь мне научиться, я буду только рад 😀
2. Далее пойдет многобуквенный текст о том, как автор делает для себя инструмент для упрощения работы с VBA. Кода нет. Вернее у меня то он есть, но его много и какие куски сюда добавлять я не знаю (да и надо ли оно).
3. Здесь вы не увидите очень крутую штуку, которую точно-приточно надо быстрее запихивать в мой проект, оспаде, как же я раньше жил без этого.
Это, по сути, личный инструмент автора, про который он (кто он? я, получается) решил поведать всемумирухабру.Так что если вы сюда за святым граалем — Thank you Mario! But our princess is in another castle!
А всем остальным приятного время провождения.
Что такое PackageManager
Не буду нудить скучными вырезками из википедии и постараюсь своими словами объяснить.
Package Manager – это npm, и pip, и nuget, и composer etc.
Ну вы поняли.
Нет? Тогда вот вам википедия под катом:
Система управления пакетами (также иногда «менеджер пакетов» или «пакетный менеджер») — набор программного обеспечения, позволяющего управлять процессом установки, удаления, настройки и обновления различных компонентов программного обеспечения. Системы управления пакетами активно используются в различных дистрибутивах операционной системы Linux и других UNIX-подобных операционных системах. Программное обеспечение представляется в виде особых пакетов, содержащих, помимо дистрибутива программного обеспечения, набор определённых метаданных, которые могут включать в себя полное имя пакета, номер версии, описание пакета, имя разработчика, контрольную сумму, отношения с другими пакетами. Метаданные сохраняются в системной базе данных пакетов.Информация из Википедии, свободной энциклопедии
Исходя из того, что делает менеджер пакетов, можно вывести список того, что же нам предстоит разработать.
Ну, вроде все просто:
Нужно создать консольное приложение, которое будет парсить....
...Так, стоп
Всё, да? Приплыли. Консольное. А как же в excel/word/access открывать консоль-то? Отдельно чтоли? Это же неудобно!
А Immediate Window только и умеет, что выводить информацию через Debug.Print
.
Да, но не совсем. Immediate (я буду называть ее вбансоль, чтоб понятней было) умеет выполнять процедуры и функции:
Ну, то есть, есть у нас какая-то функция, мы ее в вбансоли вызываем, она нам выдает какой-то результат. Уже можно парсить аргументы, получается.
Попробуем еще раз пройти по списку?
Нужно создать
консольноевбансольное приложение, которое будет парсить аргументы и исходя из переданной команды выполнять некие действия:
1. Инициализировать проект (init
)
2. Находить и устанавливать пакеты (install
)
3. Публиковать пакеты в некое хранилище (publish
)
4. Обновлять уже установленные пакеты (update
)
5. Удалять установленные пакеты из проекта (uninstall
)
Ну это база.
А зачем козе баян?
Нет, а действительно, зачем? Разве макросы настолько большие, что туда постоянно нужно что-то импортировать?
Да. Причем на моей практике таких макросов много. И так как VBAшники работают в современных IDE, а сам VBA – современный язык программирования, для которого как крупные компании, вроде google, так и энтузиасты пишут кучу разных пакетов/библиотек/фреймворков, ими нужно как-то управлять.
...
Ладно, на самом деле всё чуточку плачевней.
Откровенно говоря, я действительно в каждый проект импортирую кучу кода, который специально пишу для переиспользования. И да, все это пишется только для себя, как и любые VBA инструменты (надстройки не берем в расчет, наверное, как удобно; вы можете взять в расчет, а я не хочу).
Самый частый гость в моих проектах – класс модуль CRegExp
, который помогает взаимодействовать с регулярками (напишите в комменты, если интересно глянуть как он выглядит, закину на GitHub |=| там по сути обертка для VBScript.RegExp, так что наверное ничего интересного |=| я передумал, короче, не пишите в комменты).
И подобных гостей много. И импортировать все это каждый раз руками... я что, не программист чтоли?
До недавнего времени, для импорта пакетов я пользовался... Ни за что не догадаетесь... изобретенным для себя велосипедом в виде надстройки, которую подключал к проекту. В ней есть несколько функций, вызывая которые в вбансоли, можно было импортировать код, заранее сохраненный в какую-нибудь папочку, в подключенный проект и спокойно им пользоваться.
Очень удобно, поверьте.
И вот сейчас кодовая база моих пакетов начала разрастаться и ими стало неудобно управлять.
Отсутствует нормальное версионирование, например. Неудобно прописывать зависимости для того или иного пакета (а так как код переиспользуемый, некоторые пакеты ссылаются на другие пакеты).
И вот тут я понял, что настало время нового велосипеда...
А что это за кружочки?
🟢 – реализовано
🟡 – в процессе реализации, но уже что-то работает
🔴 – пока даже класс не создал
🟢Нулевая задача - а как звать то?
Итак, с чего начать новый проект?
Правильно – с названия. Я потратил на то, чтобы придумать название менеджера пакета для VBA почти полчаса, но теперь оцените:
ipm
– Immediate Package Manage
А? Каково?
Думали vpm
(vba package manager) увидеть, а вот и нет.
Искушенный читатель сразу заметил отсылку к другому менеджеру – npm. И да, для меня он стал идейным вдохновителем и лекалом всего проекта, поэтому велосипед будет изобретаться по его образу и подобию (со своей реализацией, конечно).
🟢Первая задача - модуль Package
Вспоминаем, что там у нас идет первым делом:
Инициализировать проект (
init
)
Вот с init
и начнем.
При вызове этой команды, вбансоль должна запросить:
-
Имя пакета
-
Версию
-
Автора
-
Описание
и записать все эти данные. В npm они хранятся в файле package.json.
И что, опять стоп? Как вбансоль диалог то делать будет?
Отставить панику, я все придумал.
Материал из лучшего телеграм канала о VBA по версии его админа – Дневник VBAшника
PackageManager: диалог в Immediate Window
...
Так вот, думал, как бы сделать аналог консольного диалога (в npm
, например, после вызова команды init
происходит диалог с пользователем, после чего из полученных данных формируется package.json
). Immediate Window
не совсем для таких вещей сделали. Но выдумать, таки, получилось.
Суть простая:
Пишем основную процедуру, в которой будет вызван диалог.
Заканчиваем выводом сообщения с названием следующей функции и нижним подчеркиванием, в которую ожидаем аргумент от пользователя.
Примерно так все выглядит:
Sub Start()
Debug.Print «Продолжить диалог?(y/n)»
Debug.Print «Continue _»
End Sub
Sub Continue(ByVal Choice As String)
If Choice = «y» then
Debug.Print «Продолжаем, следующая функция!»
ElseIf Choice = «n» then
Debug.Print «Ок.»
Else
Debug.Print «Неизвестная команда!»
End If
End Sub
В итоге в Immediate Window
получаем такой диалог:
Start ' жмем Enter
=> Продолжить диалог?(y/n) Continue _
' курсор будет находиться на этой строке, нужно будет ввести ответ в кавычках, например «n» и нажать Enter
=> Ок.
Причем ответ можно писать не в полных кавычках («y»), а только с первой («y). Работает одинаково.
Вот такая имитация cli
диалога. Не думаю, что кому-то пригодится, но кажется что штука забавная🙂
Далее, файл package.json.
Мы, как прогрессивные программисты, в VBIDE создавать файлы не можем, поэтому для этих целей будем использовать стандартный модуль и комментарии.
Выглядеть все должно примерно так:
Заметили последнюю строку? Ее на вкусное оставлю, затрону позже.
Тут, в принципе, все просто. Спарсили команду, создали (или нашли) модуль Package, записали полученную информацию.
Но.
В npm каждый импортируемый пакет идет со своим package.json. То есть таких файлов в проект импортируется ровно столько, сколько пакетов и их зависимостей будет импортировано.
А у нас что?
На этом все. Подписывайтесь на канал, ставьте лайки.
Первое решение, которое пришло мне в голову – лепить к названию модуля имя пакета. И думаете я придумал еще парочку и выбрал лучшее? Глупости какие.
В случае с примером будет CRegExpPackage
.
Это имя для экспорта.
А для init
, чтобы отделить основной пакет от импортных (шутка про импортозамещение), будем называть модуль ThisProjectPackage
.
Даже нативно получается.
🟡Вторая задача – пакетик нужен?
Идем дальше по списку:
Находить и устанавливать пакеты (
install
)
Ох, как тут все неоднозначно.
Во-первых, где хранить пакеты?
Ну в текущей итерации, ForMyselfMode
, вполне достаточно и локального хранилища.
Для того, чтобы первично задать путь к этому хранилищу, я решил сделать доп команду config
в которой нужно будет прописать значение опции --rootpath
– путь к папке со всеми пакетами.
Команда парсит путь и записывает его в переменную окружения PACK
для текущего пользователя .
Во-вторых, а как быть с версиями?
И вот над этим я сейчас скрипящими и кипящими мозгами думаю.
Вот вам синтетический пример:
Есть пакет Четверочка
, на который логотип наносит компания РисуемНаПакетах
версии 1.0.0.
А еще есть пакет Магнезия
, на который логотип наносит та же компания РисуемНаПакетах
, но более поздней версии – 2.0.0.
Мне нужны в проекте оба этих пакета, но они за собой потянут компанию РисуемНаПакетах
, и вот тут произойдет конфликт версий (придется брать версию 1.0.0 и 2.0.0).
В npm это решается очень просто – создается новая папка node_modules внутри папки загружаемого пакета и уже в нее импортируется нужная версия.
Внимание вопрос.
Если у вас есть мысли, как сделать совмещение версий в одном проекте, напишите коммент.
Вот тут правда, честно. Вообще не представляю.
И напоследок, в-третьих, при импорте пакета, информация о нем должна записаться в файлмодуль ThisProjectPackage
, а именно в раздел '@dependencies
, ну чтобы понятно было, какие пакеты используются в проекте. Более того, туда же нужно записать версию пакета, ну чтобы понятно было, какая версия пакета используется в проекте.
Выглядеть это должно примерно так:
Интересный факт (нет)
На текущей итерации у меня небольшой баг в этой команде – в зависимости основного проекта попадают пакеты и их зависимости (а должны только сами пакеты, т.к. их зависимости прописаны в их модуле Package), но это поправимо.
Не знаю зачем вам эта информация, просто решил рассказать. Вы же зачем-то аж сюда дочитали😁
🟢Третья задача – пакет с пакетами
Публиковать пакеты в некое хранилище (
publish
)
Какие тут могут быть проблемы?
Берем модуль, сохраняем в папку. Готово.
Ну в целом то да, но как зависимости то не сохранять в папку с основным пакетом?
У нас же задача в чем? Настроить удобное управление пакетами, то есть их импорт, экспорт, изменение и т.д.
Вот чтобы изменение было легко производить, нужно отделить мух от котлетзависимости от основного кода.
А что, а как?
А Rubberduck! нам на что?
Раз уж я очень крепко прикипел к этому аддону, решил задействовать его в своей надстройке.
Для тех кто не в курсе:
Ну уж про Rubberduck вы должны были слышать! Так ведь?
В общем для своего Code Eplorer'а этот аддон добавляет в каждый модуль комментарии.
Один из таких комментариев – '@Folder
.
Как следует из названия, он отображает информацию о том в какой папке лежит модуль (ну как папке... ну вы поняли).
Вот эту инфу мы и будем парсить.
Всего лишь нужно экспортируемый/публикуемый код поместить в папку src
в корневом каталоге. ThisProjectPackage
выносим в корень:
Всё, мухи отдельно, котлеты отдельно.
Еще одно обязательное условие, перед экспортом переименовать ThisProjectPackage
в [NameOfProject]Package
, а потом, и это не менее важно, вернуть название обратно.
Ну и, естественно, в нашем хранилище должна создаваться папка с названием пакета, в которой должна создаваться папка с номером версии, в которой должна создаваться папка src, в которую мы экспортируем весь проект + package модуль.
Зачем src? На самом деле не зачем, просто раньше я выносил package отдельно от основного кода.
Возможно надо фиксить, но мне пока лень и вдруг в этом есть смысл?
Устали? Ща побыстрому пробежимся по последним пунктам.
🔴Четвертая задача – у нас новые пакетики
И вот вы уже почти уснули, а я начинаю предпоследнюю главу своего умопомрачительного повествования.
Обновлять уже установленные пакеты (
update
)
Тут, как обычно, все очень просто.
Парсим package модуль, ищем там нужный нам пакет, проверяем до какой версии можно обновить, обновляем.
Самая короткая задача, наверное. Поправьте меня, может я ошибаюсь?
🔴Пятая задача – выкинь ты этот пакет
Удалять установленные пакеты из проекта (
uninstall
)
А вот тут есть что сказать по сложностям. Одно слово – зависимости.
Нужно пройтись по всем установленным пакетам и их зависимостям. Еще раз, по всем пакетам и по всем их зависимостям, гуглим Dependency hell.
И че? И где?
Как вы поняли, ipm
пока в разработке. Буквально вчерашняя ночь прошла в отладке трех команд из-за переименования модуля package. Но результат того стоит.
Сам проект я выложу в open source по завершению и отредактирую статью.
Если вам не понравилась статья, ни за что не подписывайтесь на мой телеграм. Не надо, правда. Ссылка еще в профиле есть.
А вы изобретаете велосипеды в VBA?
Автор:
ArtCapCorn