Минутка просвещения

Читать в телеге. Когда-то там были посты не только от меня.

Факторизация чисел

Иногда складывается ощущение, что в командной строке есть вообще все. Как import antigravity.

Наткнулся недавно абсолютно случайно на команду для факторизации чисел:

$ factor 1224234
1224234: 2 3 3 3 3 3 11 229
$ factor 24342342342342342342342342534512312123
24342342342342342342342342534512312123: 11 43053238739605007 51400086909245973599
$ factor 243423423423423423423423425345123121241
factor: ‘243423423423423423423423425345123121241’ is too large
СсылкаКомментировать

BPMN-нотация и Camunda

Business Process Model and Notation — еще один способ описания алгоритма или процесса в виде схемы. Разрабатывался с прицелом на то, чтобы нотация была понятна и бизнесу, и разработчикам, и даже менеджерам. TLDR в виде cheatsheet.

Продолжением этой идеи являются BPMN-движки: чтобы схема была не только в документации, но по ней генерировалсь и само управление процессом, вместо какого-нибудь самопального конечного автомата. Звучит неплохо: аналитик нарисовал, разработчик интегрировал где надо, и все готово. Реальность, конечно, немного посложнее, и код все равно надо будет писать. Как и с любым фреймворком, нужно потратить некоторое время на его изучение, за абстракции придется платить производительностью, а при столкновении с ограничениями придется выдумывать костыли. Однако разработку это неплохо ускоряет и плюшка в лице наглядности довольно значимая.

Один из самых популярных BPMN-движков сейчас — это Camunda. Оперсорс, Java, REST API, внутренний API, web-морда для админки — вот, наверно, основные ключи к успеху в текущих реалиях индустрии.

Подробнее про все это можно почитать, например, в блоге о BPMN.

СсылкаКомментировать

Баг горячих клавиш Xorg

У Xorg есть противный баг: горячие клавиши работают при нажатии, а не отжатии. Я его особо не замечал, потому что в большинстве приложений это не заметно (хотя было что-то подобное лет 10 назад в Firefox).

Однако когда постоянно начал использовать Slack, приложение которого написано на поганом электроне, то этот баг начал прям дико бесить: при переключении раскладки клавиатуры окно теряло фокус. По классике, есть открытый баг в трекере электрона, и даже не один. С одной стороны, вроде проблема не совсем в нем, но с другой — UX это портит конкретно, тем более, что проблема поправима.

Поправить баг на уровне системы можно установкой PPA.

СсылкаКомментировать

Как запустить что-то после всех тестов

Например, проверку покрытия или отправку отчета куда-нибудь.

Очевидный вариант — сделать кастомную задачу в gradle/sbt/maven, которая оборачивает или перезаписывает старую задачу test. Но если писать таск неохота, то есть и другой вариант, если у вас JUnit5.

Можно сделать кастомный TestExecutionListener, переопределив testPlanExecutionFinished. Чтобы его обнаружил JUnit, добавить полное имя класса в src/test/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener. Такая штука полезна для составления отчета по покрытию, например.

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

val launcherConfig = LauncherConfig.builder().enableTestExecutionListenerAutoRegistration(false).build()
val launcher = LauncherFactory.create(launcherConfig)

val request = LauncherDiscoveryRequestBuilder.request()
        .selectors(selectClass(CoverageTest::class.java))
        .build()

launcher.execute(request)

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

UPD: таск все-таки пришлось написать, с использованием exec-maven-plugin на фазе test, потому что вложенный движок тестов иногда не успевает отрапортовать о результатах до того, как основной завершил свою работу.

СсылкаКомментировать

Поиск пересечения файлов

Нашел прикольный способ найти пересечение двух файлов:

grep -Fxf file1 file2

выдаст строки, которые есть в обоих файлах, но с нюансом, что из второго файла останутся потенциальные дубликаты (которые можно отсеять через | sort | uniq).

Принцип работы довольно прост: берем паттерны из file1 (-f file1), говорим, что это строки, а не регулярки (-F), и ищем точное совпадение строк (-x) в file2. В интернете пишут, что это быстрее, чем приколы с сортироками и однострочниками на питоне.

Мне это пригодилось без -x, чтобы поискать в логах данные из CSV.

СсылкаКомментировать

Типы дат java.time

С java.time жить можно, хотя Joda Time у автора получился поудобнее. Памятка про отличия различных типов. TLDR: LocalDateTime с большой вероятностью бэкэндщику не нужен, а OffsetDateTime покроет большинство бытовых кейсов.

СсылкаКомментировать

Поиск неактуальных веток git

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

Главный помощник в этом деле — команда git ls-remote --heads [repoUrl], которая позволяет получить список веток репозитория без его клонирования. Удобно, когда тебе нужно 5+ реп обработать.

Со списком веток на руках можно сделать запрос к баг-трекеру, чтобы получить статус соответствующего тикета (если ветка содержит его идентификатор, разумеется). Мне не повезло и у меня Jira, хорошо хоть к ней есть подобие клиента. А дальше — закрываем ручками то, что в финальном статусе (можно автоматизировать и удаление, но как-то стремно). Полностью скрипт можно посмотреть в gist.

Если надо почистить локальные ветки, которые уже были удалены в удаленном репозитории — есть плагин, который это делает в пару кликов (VCS → Git → Delete old branches...). Правда он скорее всего будет одноразовый: вы же будете держать репу в чистоте после генеральной уборки?

СсылкаКомментировать

Про Slack

Долгое время я скептически относился к слаку, считая, что телега лучше. И для личного общения/ботов/перекидывания стикерами она все равно лучше. Но для работы слак оказался удобнее. В мае моя команда на него перешла и возвращаться в телегу вряд ли будет.

  1. Киллер-фича — это треды. Особенно, когда в чате дохрена людей и тебе не нужно читать ВСЕ. Вдобавок, когда обсуждается несколько тем одновременно, тележный чат быстро превращается в помойку. 100 непрочитанных сообщений в секунду будут читать только психи. Чаты на 100+ человек в телеге даже более-менее на 1 тему — это рак. Я вообще не понимаю, какая от них польза, особенно когда туда заходят новые люди с типовыми вопросами. Конечно, к тредам надо приучать, но как показала практика, через пару недель игры в злого полицейского останутся только совсем отъявленные "не такие как все".
  2. Настоящее отключение уведомлений. Не показывать сереньким, не показывать сереньким в архиве, а просто не показывать никак. Полезно для всяких чатов поддержки: понадобилось тебе что-то, зашел, спросил, в треде тебе ответили (и на это приходят уведомления разумеется), потом об этом чате забыл.
  3. Настоящие имена. Мелочь, но до переезда были случаи, когда пишешь кому-то в личку по рабочему вопросу и даже его пола не знаешь, не то что имени.
  4. Выключил рабочий комп = выключил работу. Никаких переписок в полночь о том, что надо бы завтра какую-нибудь фигню сделать.

Но по стикерам иногда скучаешь... Радужные попугаи — это круто, но огромную рожу Гарольда уже не вбросишь.

СсылкаКомментировать

Дебри рефлексии

Картинка с двумя стульями у меня родилась не просто так. Довольно типичная рабочая ситуация, в которую я вляпался на прошлой неделе. Ближе к концу я уже сел на третий стул пулл-реквестов и сделал их аж три штуки, но расскажу о дебрях, в которые зашел, пытаясь решить проблему через рефлексию.

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

@Override
String exampleMethod(){
    String firstPart = privateMethodToModify();
    String secondPart = super.exampleMethod();
    String thirdPart = anotherPrivateMethod();
    return firstPart + secondPart + thirdPart;
}

Первая реакция будет: "Чё тут делать-то? Отнаследуйся да переопредели!". Но есть нюанс: super будет уже не тот. А беспокоить дедушек в java нельзя.

Ок, может тогда получим указатели на exampleMethod и anotherPrivateMethod, и в месте вызова ручками все подергаем? Однако полиморфизм будет суров, и exampleMethod вызовется из дочернего класса, даже если указатель вы получали у родителя.

Что же делать? Сдаться и скопипастить дочерний класс, изменив в нем что нужно? Скорее всего да, но если вы хотите сидеть на пиках до конца, то есть еще один способ — MethodHandles. Это новый API, который не является заменой рефлексии, а дополнением к ней, в основном для всяких низкоуровневых манипуляций для разработчиков языков под JVM. С ним можно вызвать родительский exampleMethod для ребенка:

MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Child.class, MethodHandles.lookup());
MethodHandle baseHandle = lookup.findSpecial(Parent.class, "exampleMethod", MethodType.methodType(String.class), Child.class);
return (String) baseHandle.invoke(child);

Но, как всегда, есть нюансы. Во-первых, если java меньше 9 версии, то придется еще и рефлексию использовать, чтобы в приватные методы залезть (выглядит стремно). Во-вторых, в kotlin это работать из коробки пока не будет без специальных флагов, но в 1.4 эти флаги включат по умолчанию. 100% интеропа такие 100%.

Посмотреть примеры подробнее можно github. Познакомиться с API подробнее — тут, а узнать, зачем оно вообще нужно — тут.

СсылкаКомментировать