Дима Дорошев

Посты из блога в категории Dev:

Как же хорошо, что у всех интервалы времени измеряются в одинаковых единицах и фразу "через 15 минут" понимают одинаково во всем цивилизованном мире.

Нет такого, что у одного минута длится дольше, чем у другого. Или что в США вместо секунды используют какой-нибудь "тик", который равен 1.672 секунды, и день состоит не из 24 часов, а из 4 "квартосуток".

Если бы еще и такое было, программисты бы вообще свихнулись. Им и так хватает високосных секунд и часовых поясов.

Go: Отложенная обработка событий файловой системы

Допустим, вам нужно что-то сделать при возникновении какого-то события файловой системы. Например, перезапустить веб-сервер при изменении файлов. Довольно частая практика при разработке: повесить "слушателя" файловой системы, запустить приложение, сразу же после редактирования файлов "на лету" перекомпилировать проект и заново запустить его.

Этот сайт написан на Go, и недавно я решил добавить в него подобный hot-reload для markdown-файлов с постами: кладешь новый файл в папку, веб-сервер это замечает и переналивает внутренний in-memory-storage с публикациями без перезапуска самого себя. При этом мне хотелось именно "слушать" файловую систему, а не сканировать ее раз в несколько секунд.

Читать далее

Абстракции и наследование в Си - стреляем по ногам красиво

TL;DR https://github.com/pomidoroshev/c-inheritance

Иногда нет-нет да и хочется что-нибудь абстрагировать и обобщить в коде на Си. К примеру, хочешь ты принтануть содержимое структуры несколько раз, пишешь везде, как дурак, printf("%s %d %f\n", foo->bar, foo->baz, foo->boom), и интуитивно кажется, что есть способ сделать foo->print(foo), и так вообще со всеми структурами, не только с foo.

Возьмем пример: есть некий чувак с именем и фамилией, и есть птица, у которой есть имя и владелец.

typedef struct Person Person;
struct Person {
    char *first_name;
    char *last_name;
};

typedef struct Bird Bird;
struct Bird {
    char *name;
    Person *owner;
};

Чтобы вывести информацию про этих животных, кондовый сишник напишет просто две функции:

void Person_Print(Person *p) {
    printf("%s %s\n", p->first_name, p->last_name);
}

void Bird_Print(Bird *b) {
    printf("%s of %s %s\n", b->name, b->owner->first_name, b->owner->last_name);
}

И будет таки прав! Но что если подобных структур у нас много, а наш мозг испорчен веяниями ООП?

Читать далее

Docker Buildkit: Правильное использование --mount=type=cache

TL;DR Содержимое каталогов, смонтированных через --mount=type=cache, не сохраняется в docker-образе, поэтому кэшировать надо не целевые каталоги, а промежуточные.

В dockerfile:1.3 появилась возможность монтировать файловые системы во время построения образа, в том числе и в целях кэширования: можно кэшировать скачанные пакеты или промежуточные артефакты компиляции.

Например, пакет uwsgi каждый раз компилируется при установке, и это время хочется сократить, закэшировав весь каталог с пакетами:

# syntax=docker/dockerfile:1.3
FROM python:3.10

RUN mkdir /pip-packages

RUN --mount=type=cache,target=/pip-packages \
      pip install --target=/pip-packages uwsgi
> docker build -t pip-cache -f Dockerfile.pip .
# ...
[+] Building 14.6s (7/7) FINISHED

Выглядит, что все прошло успешно, но целевой каталог пуст:

> docker run -it --rm pip-cache ls -l /pip-packages
total 0

Что-то явно пошло не так.

Читать далее

Быстрый коммит и пуш

Хочу поделиться shell-функцией gacp (Git Add, Commit and Push), которую я придумал несколько месяцев назад и с тех пор использую ее примерно каждый час:

# fish
function gacp
    git add .
    git commit -m "$argv"
    git push origin HEAD
end
# bash/zsh
function gacp() {
    git add .
    git commit -m "$*"
    git push origin HEAD
}

Пример использования:

> gacp add some new crazy stuff
[master fb8dcc9] add some new crazy stuff
 <...>
Enumerating objects: 12, done.
 <...>
To github.com:foo/bar.git
   912c95d..fb9dcc9  master -> master

Наконец-то больше не придется городить цепочки с && и вставлять кавычки, когда надо написать в сообщении больше одного слова!

Ж К П

В любом текстовом процессоре (Microsoft Word, Google Docs, LibreOffice) кнопки "полужирный", "курсив" и "подчеркнутый" находятся рядом:

Десятилетиями в голове устаканивался паттерн о том, что это свойства одного порядка. Хочешь сделать полужирный курсив, нажимаешь две кнопки рядом:

Хочешь жирный и подчеркнутый:

Про подчеркнутый курсив не буду - это для особенных людей.

В HTML-тегах с этим тоже просто. Есть три тега: b, i и u. Можно вкладывать их друг в друга и получать желаемую комбинацию:

<b><i>полужирный курсив</i></b>
<b><u>полужирный подчеркнутый</u></b>
<i><u>пожалуйста, не надо</u></i>

А в CSS это совершенно разные свойства:

{
/* полужирный */
font-weight: bold;

/* курсив */
font-style: italic;

/* подчеркнутый */
text-decoration: underline;
}

Про font-weight еще более-менее понятно, но правила применения двух остальных свойств я уже 13 лет не могу запомнить. Если нужно сделать подчеркнутый текст, а подсказок взять негде, я могу просто перебрать все варианты, пока не заработает:

text-style: underline;
font-style: underline;
font-decoration: underline;
text-underline: true;

Очень бесит. Хочется либо так:

font-style: bold;
font-style: bold, italic;
font-style: italic, underline;

Либо так:

font-bold: true;
font-underline: true;
font-italic: true;

Большего мне и не надо.