Читать в телеге. Когда-то там были посты не только от меня.
Баг горячих клавиш 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
Долгое время я скептически относился к слаку, считая, что телега лучше. И для личного общения/ботов/перекидывания стикерами она все равно лучше. Но для работы слак оказался удобнее. В мае моя команда на него перешла и возвращаться в телегу вряд ли будет.
- Киллер-фича — это треды. Особенно, когда в чате дохрена людей и тебе не нужно читать ВСЕ. Вдобавок, когда обсуждается несколько тем одновременно, тележный чат быстро превращается в помойку. 100 непрочитанных сообщений в секунду будут читать только психи. Чаты на 100+ человек в телеге даже более-менее на 1 тему — это рак. Я вообще не понимаю, какая от них польза, особенно когда туда заходят новые люди с типовыми вопросами. Конечно, к тредам надо приучать, но как показала практика, через пару недель игры в злого полицейского останутся только совсем отъявленные “не такие как все”.
- Настоящее отключение уведомлений. Не показывать сереньким, не показывать сереньким в архиве, а просто не показывать никак. Полезно для всяких чатов поддержки: понадобилось тебе что-то, зашел, спросил, в треде тебе ответили (и на это приходят уведомления разумеется), потом об этом чате забыл.
- Настоящие имена. Мелочь, но до переезда были случаи, когда пишешь кому-то в личку по рабочему вопросу и даже его пола не знаешь, не то что имени.
- Выключил рабочий комп = выключил работу. Никаких переписок в полночь о том, что надо бы завтра какую-нибудь фигню сделать.
Но по стикерам иногда скучаешь… Радужные попугаи — это круто, но огромную рожу Гарольда уже не вбросишь.
Дебри рефлексии
Картинка с двумя стульями у меня родилась не просто так. Довольно типичная рабочая ситуация, в которую я вляпался на прошлой неделе. Ближе к концу я уже сел на третий стул пулл-реквестов и сделал их аж три штуки, но расскажу о дебрях, в которые зашел, пытаясь решить проблему через рефлексию.
Пусть у нас есть публичный метод, в котором надо немного подшаманить:
@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 подробнее — тут, а узнать, зачем оно вообще нужно — тут.
Случайная выборка из csv
Безо всяких питонов сделать случайную выборку данных из текстового файла можно командой shuf
:
cat dataset.csv | shuf | head -n100 | tee random_sample.csv
Закрытие старых ревью в Upsource
К сожалению, теорию разбитых окон можно наблюдать и в разработке. Поэтому периодически стоит наводить чистоту в коде, трекерах задач (можно называть это модной фразой “груминг бэклога”) и прочих штуках.
Недавно дошли руки до того, чтобы почистить незакрытые ревью в Upsource. Поскольку в проекте их было более 400 штук, а из коробки такой фичи нет, то в мою коллекцию добавился еще один скрипт для автоматизации всякой дичи.
Внезапно, у Upsource есть API, но есть нюанс: названия методов придется угадывать самостоятельно по названию DTO. В остальном все относительно просто: берем список ревью по подобранному методом тыка запросу, и закрываем по одному. Все вместе можно посмотреть в gist.
Форматирование строки в python
Я тот еще консерватор и не очень люблю новое. Поэтому статьи про
"hello, {}!".format("world")
"hello, {0} and {0} again!".format("world")
"hello, {name}".format(name="world")
вызывали у меня легкое раздражение: я не видел никаких существенных преимуществ перед старым добрым "hello %s" % "world"
. Но пора бы очнуться от летаргического сна, и посмотреть на Python 3.6+ (выпущенный в 2016).
Там есть f"hello, {name}!"
, у которого в скобках будет полноценное выражение, т.е. можно сделать f"hello, {''.join(map(chr, [87, 111, 114, 108, 100]))}!"
. Этот вариант хорош для большинства случаев, особенно с учетом того, что почти во всех распространенных языках строковая интерполяция плюс-минус так и работает. Кроме Java, но она всегда была немного “особенной”.
Пример случая, когда такая интерполяция не очень уместна: фиксированный формат, который используется несколько раз и вынесен в “константу”. Например, у меня в скриптах довольно часто встречается такое:
XX_PATH = "http://%s/some/api/path/%s"
...
response = requests.get(XX_PATH % (host, param), ...)
Тут придется садиться на один из стульев: %
или format
.