Архив рубрики: программизм

Почти что Перл с Апачем

Новости географии: в одном люксембургском углу находится никому не известная деревня Шенген, ближайшая к ней железнодорожная платформа, буквально на другом берегу реки — Perl (по-русски всё-таки Перль), это же имя носит и муниципалитет (в других переводах — коммуна), и крупнейший его населённый пункт. А рядом с ними — Apach, но по-русски это не Апач, а, если верить википедии, Апаш, хотя, думаю, немцы могут прочесть и как Апах. При этом все три эти деревни — в разных странах.

Карта

Дорогой язык

Вышел отчёт по собранным Мойкругом зарплатам на середину 2018 года — https://vc.ru/flood/43849-zarplaty-it-specialistov-na-seredinu-2018-goda

В отчёте есть график, показывающий зависимость программистских доходов от используемых языков:

График

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

Тестирование перловых mojolicious-приложений в Geany

Программировать, используя какую-нибудь могучую интегрированную среду разработки (IDE) — хорошо и зачастую удобно: там «из коробки» могут предоставляться различные удобные штуковины — компиляция, отладка, тестирование, работа с системами контроля версий. Однако некоторые системы при всём своём могуществе оказываются не совсем подходящими — например, могут много весить и сильно тормозить. Приходится выбирать что-нибудь полегче, например, Geany.

В Geany есть (в том числе и средствами дополнительных модулей) всякое:

  • подсветка синтаксиса,
  • организация файлов в проекты,
  • поиск текста как в текущем файле, так и в произвольном их наборе с обходом подкаталогов,
  • поиск парных скобок и тэгов HTML/XML, а также переход по ним,
  • составление оглавления используемых функций,
  • компиляция либо проверка синтаксиса с подсветкой ошибок и быстрым переходом к ним.

С отладчиком в Geany пока не удалось разобраться, а вот процесс тестирования кода можно сделать более удобным.

Итак, у нас есть:

  • IDE Geany,
  • Веб-приложение, написанное на языке Perl с использованием фреймворка Mojolicious и системы управления модулями carton,
  • Желание запускать тесты почаще и попроще, без лишних переключений из редактора в терминал.

Geany позволяет для каждого проекта задать список действий: как общих для всего проекта, так и специфичных для конкретного типа файлов — найти настроки можно в меню Project → Properties → вкладка Build либо Build → Set Build Commands.

Настройки команд в Geany

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

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

The first occurrence of each of the following character sequences in each of the command and working directory fields is substituted by the items specified below before the command is run.

  • %d — substituted by the absolute path to the directory of the current file.
  • %e — substituted by the name of the current file without the extension or path.
  • %f — substituted by the name of the current file without the path.
  • %p — if a project is open, substituted by the base path from the project.
  • %l — substituted by the line number at the current cursor position.

то есть, некоторые имена файлов и пути к папкам можно указывать специальными переменными.

Вторая команда в списке тех, что зависят от типа файла, получает в меню кирпичную иконку и (по умолчанию) клавишу F9 для быстрого запуска. Клик по кнопке с кирпичом, расположенной на панели инструментов под меню также вызовет выполнение этой второй команды.

Пробуем выполнить тест — в окно Compiler выводятся результат выполнения. Если есть ошибки, они будут выделены и в этом окне, и в исходном коде теста.

Результат тестирования в Geany

Сфинкс спрятался? Сделаем туннель, но ненадолго

Ситуация: жил-был Сфинкс (поисковая система Sphinx) на старом сервере, да пришла пора на новый переезжать. Нужный порт на новом месте доступен скриптам, что живут там же, а снаружи — нет и не надейтесь. Результат — кое-где тесты покраснели.

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

ssh -L 9312:localhost:9312 server.name

Однако в таком виде она неудобна: команду надо запускать в одном окне терминала, тесты — в соседнем, а после завершения тестов надо ещё и закрывать SSH-сессию в первом окне.

man ssh

В инструкции (man ssh) пишут:

SYNOPSIS
     ssh [-1246AaCfGgKkMNnqsTtVvXxYy] [-b bind_address]
         [-c cipher_spec] [-D [bind_address:]port] [-E log_file]
         [-e escape_char] [-F configfile] [-I pkcs11]
         [-i identity_file] [-J [user@]host[:port]] [-L address]
         [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option]
         [-p port] [-Q query_option] [-R address] [-S ctl_path]
         [-W host:port] [-w local_tun[:remote_tun]]
         [user@]hostname [command]
...
     -f      Requests ssh to go to background just before
             command execution.

То есть, ssh позволяет и команду выполнить, и перед этим уйти в фоновый режим. Приме́ним полученные знания:

ssh -fL 9312:localhost:9312 server.name sleep 5

Такая команда откроет туннель, не выводя ничего в терминал, подождёт пять секунд и закроется — почти то, что надо!

Осталось исключить рытьё тоннелей на сервере

test `uname -n` != 'server' && ssh -fL 9312:localhost:9312 server.name sleep 5

и скрестить открытие туннеля с тестированием. Тесты в перловом веб-приложении, написанном с использованием микрофреймворка Mojolicious::Lite, могут вызываться различными путями — и как ./application.pl test, и командой prove, и как-нибудь ещё — я, например, обычно создаю Makefile с нужными мне задачами и тесты выполняю командой make test — мне так удобнее. Чтоб не рассматривать все возможные варианты тестирования, надо поместить открытие туннеля прямо в тест. Если конфигурация приложения хранится в каком-либо отдельном файле (YAML хорошо для этого подходит — в Моджолишисе есть плагин для чтения ЯМЛ-конфигов), можно команду открытия туннеля хранить рядом с остальными настройками — это лучше, чем пихать её в тест. А в тесте останется лишь вызвать её после создания объекта Test::Mojo:

my $t = Test::Mojo->new();

system($t->app->config->{'sphinx'}->{'tunnel'}) == 0
or warn "Cannot open SSH tunnel to Sphinx: $!";

Тесты зеленеют, можно спокойно идти заниматься музыкой 🙂

P.S. Если вместо system применить функцию exec, то тест не будет выполняться до тех пор, пока не закроется туннель — тест будет ждать завершения дочернего процесса и в итоге так и останется красным.

Картину, корзину, картонку

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

Один из вариантов — использовать carton. Для тех, кому лень читать по-английски, есть перевод — статья Вячеслава Тихановского «Локальная установка и использование Perl-модулей» из журнала Pragmatic Perl за 2014 год.

Дерево каталогов

Carton берёт список модулей, ставит их локально (root не нужен) вместе с зависимостями и позволяет запускать приложения с таким вот набором модулей. Список используемых модулей (если carton ещё ничего не ставил) можно достать из скриптов и своих модулей:

find . -type f -name \*p\[lm\] -exec egrep -n '^use ' '{}' ';' \
 | cut -d' ' -f 2 | sort -u

Чтоб указать необходимость установки какого-нибудь Module::Name, достаточно добавить в файл cpanfile строку

requires 'Module::Name';

Carton умеет ставить нужные версии модулей, однако в документации этот момент вскользь упомянут, но не описан должным образом. Если захотеть, например, поставить старую версию модуля Sphinx::Search (это потребуется, если сам Сфинкс не новый) и написать

requires 'Sphinx::Search', '0.28';

то вместо желаемой версии будет установлена свежая (0.31 по состоянию на март 2018) — похоже, такая запись указывает минимально допустимую версию. Если поменять код на

requires 'Sphinx::Search', '== 0.28';

и снова выполнить carton install, версия поменяется

Installing modules using /home/.../cpanfile
Successfully installed Sphinx-Search-0.28 (downgraded from 0.31)
1 distribution installed

Команда carton exec может пригодиться для запуска не только самого́ приложения, но и чего-нибудь ещё:

carton exec prove -l

Простая drag’n’drop-передача файлов в перловое приложение на Mojolicious::Lite

Хочу упростить загрузку файлов пользователем на некоторые сайты, сделанные на Mojolicious::Lite — нужна обработка нескольких файлов за раз плюс поддержка drag and drop — это удобно, когда надо загрузить несколько файлов, которые в проводнике файловом менеджере либо просмотрщике картинок отображаются не рядом.

Естественно, ищу готовые примеры, чтоб не изобретать велосипед. Нашёл два:

  • Один из них красивый и работает (надо брать!), но примеры серверной части для него — не на перле. Понятно, что можно взять имеющиеся примеры (на пхп и питоне) и перевести их. Либо погуглить тщательнее.
  • Другой — маленький и простой, и даже конкретно под Mojolicious::Lite, но не работает, потому как был написан во времена, когда автор активно пилил Моджолишес, не обращая внимания на обратную совместимость — мне уже приходилось сталкиваться с необходимостью допиливания старых приложений, которые не взлетали на новом Моджо. Пара взмахов напильником — и оно заработало.

Pull request

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

Тесты зеленеют

Для разминки и в честь приближения весны поковырял один зелёный сайт.

Обновление Mojolicious приложения

Попутно выяснил странную штуку: почему-то перловое приложение на Mojolicious::Lite всегда запускает тесты в режиме отладки вместо боевого несмотря на явное указание

./app.pl test -m deployment

Хотя раньше, вроде, разница была. Или не было?

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

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

Как засунуть имиджмеджиком растровые картинки в PDF нужного размера

Для несложных преобразований картинок: изменение размера, увеличение резкости, добавление надписей и преобразование типов — большой и тяжёлый графический редактор не нужен — достаточно взять ImageMagick. ImageMagick знает не только кучу разных растровых форматов файлов, но и кое-какие векторные, в том числе PDF, однако преобразование в них требует иногда плясок с бубном: например, если для преобразования толпы PNG-файлов написать

convert *png result.pdf

то результат может и не понравиться. Путём экспериментов с найденным на всяких форумах кодом получился такой рецепт для преобразования в PDF размером 210×297 мм (A4) кучи растровых файлов, отсканированных с разрешением 600 точек на дюйм

convert *png -units pixelsperinch -density 600 -resize 100% \
 -gravity center -background white -extent 4960x7016 result.pdf

размер растровых файлов не меняется, они располагаются в центре получившихся страниц.

PDF-файл с разным заполнением страниц

Для картинок с разрешением 300 dpi цифры будут чуть другими:

convert *png -units pixelsperinch -density 300 -resize 100% \
-gravity center -background white -extent 2480x3508 result.pdf

Для 150 dpi числа надо ещё вдвое уменьшить:

convert *png -units pixelsperinch -density 150 -resize 100% \
-gravity center -background white -extent 1240x1754 result.pdf

Из Лилипонда в Музскор

Из всего, в чём доводилось набирать ноты последние лет двадцать, самые красивые получаются в Лилипонде — там и шрифт хороший (хотя Bravura ещё лучше), и расположение нот на листе достаточно компактное. Но в MuseScore набирать проще плюс можно услышать любую ноту да и все изменения видны сразу — не надо ждать, пока компиляция завершится. Лет десять назад MuseScore, как и Denemo, можно было использовать лишь для предварительного набора — сборку и доводку приходилось делать в Лилипонде, потому что Музскор не способен был нормально расставить ноты по странице. Со временем Музскор улучшился — теперь его можно использовать для всего процесса — до самого конца — вывода на печать или в готовый PDF-файл.

И встал вопрос открытия лилипондовых файлов Музскором — нынешние его версии умеют работать с MusicXML, но формат Лилипонда не понимают совсем. Сам же LilyPond умеет читать всякое (точнее, в его состав входит скрипт на питоне, перегоняющий в формат Лилипонда ноты из MusicXML, ABC, MIDI и, вроде, ETF), но выводить в это всякое не желает.

Поэтому воспользуемся сторонним ПО. Например, выяснилось, что Frescobaldi умеет экспортировать ноты в формат MusicXML, но по умолчанию эта возможность в нём отключена. Включить можно в настройках: Edit → Preferences → Generap Preferences → Enable experimental features.

Настройки Frescobaldi

После перезапуска Фрескобальди в его меню File → Export можно найти пункт Export to MusicXML. А уж MusicXML можно открыть и в Музскоре, который будет ругаться на на корявость файла,

Сообщение о невалидном XML

Сообщение о повреждённом файле

но всё равно (если повезёт?) его откроет

MuseScore — результат импорта

Вместо экспорта через Frescobaldi можно с командной строки использовать python-ly:


ly musicxml solo.ly -o solo.musicxml

Результат почти полностью совпадает с тем, что выдаёт Фрескобальди — скорее всего, этот же скрипт и вызывается.