СЕМЁН ФОМИН

taxnuke@gmail.com

Санкт-Петербург, Россия

ОБРАЗОВАНИЕ
Санкт-Петербургский государственный политехнический университет
Институт Компьютерных Наук и Технологий
2016 — 2019
Программная инженерия
ОПЫТ РАБОТЫ
Get8
Санкт-Петербург, Россия
Веб-разработчик (Full-stack)
июль 2016 — январь 2019
  • выполнил более сотни задач по:
    • интеграции amoCRM с сайтами, мессенджерами, таск-менеджерами, другими CRM-системами и наоборот
    • созданию и доработке виджетов, входящих в топ магазина amoCRM
    • поддержке старого кода
    • изучению API сторонних сервисов и написанию для них библиотек на PHP (с фреймворком Phalcon)
  • решал организационные и технические вопросы со сторонними разработчиками
  • предложил, реализовал, документировал и внедрил утилиту автоматизации рутинных задач команды
Петротех
Санкт-Петербург, Россия
Интерн-разработчик Windows-приложений
март 2016 — июнь 2016
  • создал приложение-компаньон основного продукта, поставляемое клиентам из отраслей пищевой промышленнности и фармацевтики. В результате использования моей разработки, заказчики:
    • избавились от необходимости пользоваться платными аналогами от других фирм
    • получили современный, простой и эффективный инструмент, заточенный под узкие задачи
  • приобрел опыт верстки на языке XAML и разработки высокопроизводительных приложений на C#
  • работал под руководством кандидата технических наук, главного разработчика предприятия
Wukker
Санкт-Петербург, Россия
Интерн-разработчик iOS-приложений
август 2015 — сентябрь 2015
  • познакомился с методологией Agile
  • приобрел навыки работы в команде и с системой контроля версий git
  • выработал дисциплину удаленной работы
ЛИЧНЫЕ ПРОЕКТЫ НА РАЗЛИЧНЫХ ПЛАТФОРМАХ
Сервис для сжатия ссылок
Наподобие bit.ly, vk.cc, goo.gl и других
  • бэкенд на Node + Express
  • открытый REST-API
  • изоморфное приложение
  • гибкая архитектура для интернационализации и расширения приложения
  • интеграционные тесты с Mocha + Chai
  • мутационные тесты в Stryker (о котором я написал статью на Хабрахабр)
  • метрики поддерживаемости и покрытия тестами в Code Climate
  • непрерывная интеграция в Travis CI
  • деплой на Heroku (используется базовый dyno, поэтому сервис немного "задумчивый")
  • уведомления о необработанных ошибках приходят мне в Telegram
  • ORM (Mongoose) общение с базой данных MongoDB
  • а также ESLint, Babel, Gulp, Webpack, Bower
Личный блог
Разработка ПО и личная эффективность
  • собирается с помощью Metalsmith в статический сайт из шаблонов на Nunjucks (jinja2) + Sass
  • деплой на Surge
  • CDN + HTTP-proxy от Cloudflare
  • Яндекс.Метрика и примитивная SEO оптимизация
  • а также ESLint, Babel, Gulp, Webpack, Bower
Браузерный Тетрис
С функцией записи геймплея, VIM-режимом, на модульном ES2015
  • HTML5 Canvas
  • Без сторонних библиотек
  • Сборка в Rollup
Реализация алгоритма сортировочной станции Эдсгера Дейкстры (npm-пакет)
Преобразователь инфиксных и постфиксных математических выражений
  • мутационные тесты в Stryker
  • непрерывная интеграция в Travis CI
  • Git-хуки в Husky
  • кодстайл с ESLint
Flying Kotletkas JS
Фреймворк для генерации систем частиц в 3D
  • TypeScript
  • THREE.js
Решето Эратосфена
Реализация алгоритма Эратосфена на уровне битовых операций
  • C++ / JavaScript
  • OpenMP
  • Make
Collocore
Ядро для верстки билетов и шпаргалок к экзаменам
  • LaTeX
  • гибкая архитектура с возможностью конфигурирования проекта одним файлом
  • а также утилиты на JavaScript, Bash, Make
О СЕБЕ
Другая деятельность
Переводчик в TED
Владение английским языком на уровне Advanced позволило мне работать над субтитрами для конференций TED и научно-образовательных видео TED-Ed в качестве переводчика и редактора. Некоторые из видео, над которыми я работал:
Хобби
  • 3D-моделирование
  • пленочная фотография
  • гитара
Личные качества
Усидчивость, здравый перфекционизм и низкая толерантность к любым неэффективным процессам, нахожу общий язык с кем угодно.

О создании функций в циклах JS

Если создать функцию внутри цикла, линтер мудро скажет: “Don’t make functions within a loop”, но почему этого не рекомендуется делать? Давайте разберемся. Функции в JavaScript являются объектами, и создавая их в цикле из функционального выражения, интерпретатор создает множество независимых одинаковых экземляров объектов с выделением под них памяти, что может навредить не только производительности, но и будущему сопровождению кода. Но если не следовать рекомендации, что в этом плохого? Что ж, попробуем в лоб наполнить массив абстрактных коллбэков функциями, которые завязаны на итератор для выполнения какой-то абстрактной работы var someCallbacks = []; function doStuff(index) { console.log(`Doing stuff with index ${index}`); } for (var i = 0; i < 5; i++) { someCallbacks.push(function() { doStuff(i); }); } for (var j = 0; j < someCallbacks.length; j++) { someCallbacks[j](); } // Doing stuff with index 5 // Doing stuff with index 5 // Doing stuff with index 5 // Doing stuff with index 5 // Doing stuff with index 5 Немного неожиданно? Область видимости i не ограничивается блоком цикла (потому что i объявлена с ключевым словом var, она как бы выше цикла, в начале текущей функции - это называется подъемом). Далее коллбэки проявляют свойства замыканий, ссылаясь к этой переменной i по цепочке областей видимости, а её итоговое значение после финального выражения первого цикла равно пяти. Есть несколько вариантов решения проблемы. es6 let var someCallbacks = []; function doStuff(index) { console.log(`Doing stuff with index ${index}`); } for (let i = 0; i < 5; i++) { // * someCallbacks.push(function() { doStuff(i); }); } for (var j = 0; j < someCallbacks.length; j++) { someCallbacks[j](); } // Doing stuff with index 0 // Doing stuff with index 1 // Doing stuff with index 2 // Doing stuff with index 3 // Doing stuff with index 4 Здесь (*) каждая итерация создает новую лексическую область видимости, связанную с предыдущей, формируя цепь. Таким образом в каждой итерации цикла существует независимая переменная i, и замыкания ссылаются на разные экземпляры i из конкретной итерации. Кстати, циклы с var в среднем на 1-2% быстрее циклов с let Если обстоятельства вынуждают писать на старом стандарте, есть еще способ с замыканием-оберткой. Замыкание-обертка var someCallbacks = []; function doStuff(index) { console.log(`Doing stuff with index ${index}`); } for (var i = 0; i < 5; i++) { (function(index) { someCallbacks.push(function() { doStuff(index); }); })(i); } for (var j = 0; j < someCallbacks.length; j++) { someCallbacks[j](); } // Doing stuff with index 0 // Doing stuff with index 1 // Doing stuff with index 2 // Doing stuff with index 3 // Doing stuff with index 4 И всё же, прежде чем создавать функции в цикле, нужно убедиться в том, что это того стоит и создание функции нельзя разместить вне цикла, вот так: var someCallbacks = []; function doStuff(index) { console.log(`Doing stuff with index ${index}`); } function makeCallback(index) { return function() { doStuff(index); }; } for (var i = 0; i < 5; i++) { someCallbacks.push(makeCallback(i)); } for (var j = 0; j < someCallbacks.length; j++) { someCallbacks[j](); } // Doing stuff with index 0 // Doing stuff with index 1 // Doing stuff with index 2 // Doing stuff with index 3 // Doing stuff with index 4

Периодическое голодание. Обоснование и реальная польза

Что это такое Около года назад я наткнулся на явление под названием “Intermittent fasting”, или “Прерывистое (периодическое) голодание”. Суть идеи в том, чтобы принимать пищу так, как люди делали это десятки тысяч лет, до развития агрокультуры и обилия пищи. А именно, питаться: 1-2 раза в день (потому что 150000 лет назад не было иной возможности); потребляя минимум углеводов (потому что их практически не было во время становления человека, и они вообще не нужны в рационе людей); Углеводы являются готовой энергией, которая быстро превращается в глюкозу и используется организмом. Однако в глюкозу можно превратить и жиры с белками (в них содержится глицерол), этот процесс называется глюконеогенез, большая его часть происходит в печени, а так же в почках и малом кишечнике, это очень старый механизм. Таким образом, человек может всю жизнь совершенно успешно обойтись без потребления углеводов. Кому и зачем это нужно Проводилось множество научных исследований, посвященных голоданию во всех его формах, в том числе и прерывистому. Основываясь на исследованиях и опытах, можно выделить следующие плюсы: Предотвращение дегенеративных заболеваний мозга Эффективное снижение веса Улучшение когнитивных способностей Ускорение обмена веществ Улучшение работы иммунитета Выработка гормона роста и улучшение гормонального фона Предотвращение диабета 2 типа Долголетие В ходе многих экспериментов было обнаружено, что при снижении потребляения калорий на 30%, продолжительность жизни животных увеличивается на 30%. Какие возможны интервалы голодания Есть разные схемы голодания, но ни одна из них не разрешает есть торты запивая колой или хрустеть одну траву. Между голоданиями должны быть богатые питательными элементами полезные продукты в достаточном количестве (суточная норма). Метод Мартина Берхана 16 часов голодания, за которыми следуют 8 часов, в течение которых можно есть. В основном люди завтракают в 12:00 и делают еще один прием пищи до 20:00, после чего несколько часов бодрствуют и ложатся спать. Метод Брэда Пилона Голодание 2 раза в неделю на протяжении 24 часов. Это более эффективная схема, и именно на неё ориентированы большинство исследований. Насколько это безопасно Homo Sapiens на протяжении всего своего существования голодали и отлично к этому приспособились. По результатам исследования, человек с тяжелым ожирением смог успешно не есть пищу целых 382 дня, он принимал только необходимые микро- и макроэлементы в таблетках и пил кофе, после эксперимента он не набрал вес в течение 5 лет. Конечно, это далеко не прирывистое голодание, но это показывает, насколько хорошо наше тело умеет адаптироваться к условиям окружающей среды. Безусловно есть определенные группы лиц, которым даже подобное прерывистое голодание может быть противопоказано (больные желчекаменной болезнью например), поэтому лучше сначала проконсультироваться с хорошим врачом. При написании данной статьи использовались источники: WIΛ, HealthNerd

Неправильный код на Fortran

Когда речь заходит о неправильном, или плохом коде, то лично я в первую очередь вспоминаю свой опыт написания лабораторных на Фортране. Это удивительно, но в интернете на каждом шагу можно наблюдать невиданной изощренности вредные и громоздкие конструкции на этом языке. Сниппеты на Stackoverflow, библиотеки, решения задач на форумах – очень часто люди делают из Фортрана что-то не то. Пример сишности в коде на Фортран Я продемонстрирую ситуацию наглядно на примере задачи о подсчете вхождений подстроки в строку. На сайте Rosettacode.org висит такое решение: program Example implicit none integer :: n n = countsubstring("the three truths", "th") write(*,*) n n = countsubstring("ababababab", "abab") write(*,*) n n = countsubstring("abaabba*bbaba*bbab", "a*b") write(*,*) n contains function countsubstring(s1, s2) result(c) character(*), intent(in) :: s1, s2 integer :: c, p, posn c = 0 if(len(s2) == 0) return p = 1 do posn = index(s1(p:), s2) if(posn == 0) return c = c + 1 p = p + posn + len(s2) end do end function end program Разберем функцию countsubstring, непосредственно решающую задачу: function countsubstring(s1, s2) result(c) character(*), intent(in) :: s1, s2 integer :: c, p, posn c = 0 if(len(s2) == 0) return p = 1 do posn = index(s1(p:), s2) if(posn == 0) return c = c + 1 p = p + posn + len(s2) end do end function Здесь c – это количество вхождений, s1 – исходная строка, s2 – искомая подстрока, posn – текущая позиция, p – позиция, с которой начнется следующий поиск (в Фортране нумерация массивов идет от единицы). В данном примере автор все-таки использовал пару внутренних функций Фортрана (index для поиска позиции следующего вхождения и len для поиска длины строки), но подход в целом сишный. Как должно быть Для более грамотного решения задачи нам очень пригодятся такие Фортрановские фишки, как параллельный цикл do concurrent и сечения массивов. План такой: каждая итерация цикла будет независимо и параллельно брать назначенное ей сечение исходной строки и проверять, совпало ли оно с искомой подстрокой. Также сделаем функцию чистой и оставим старые наименования переменных везде, где это возможно. Готовое решение: pure function countsubstring(s1, s2) result(c) character(*), intent(in) :: s1, s2 integer :: N, M, i, c N = len(s1) M = len(s2) c = 0 if (M == 0 .or. N == 0) return do concurrent (i = 1:(N - M + 1)) if (s1(i:(i+M-1)) == s2) c = c + 1 enddo end function Кстати, исправленный алгоритм нашел на 2 вхождения больше, чем исходный в случае с countsubstring("ababababab", "abab"), и это правильно. В общем, когда пишете на Фотране, пишите на Фортране, это и проще, и эффективнее :)

Хак с ботами и закрытыми каналами в Telegram

Удобно получать уведомления в Telegram, но как ботам написать в канал, если он закрытый? Пройдемся по процессу с самого начала. Чтобы создать бота Открываем Telegram и начинаем общение с ботом по имени @BotFather, этот бот порождает новых ботов, выдает токены, позволяет конфигурировать и удалять их. Команда /start включает бота и обычно пишется сама. С помощью команды /newbot создаем нового бота, и следующим сообщением передаем его имя, которое будет отображаться для него в чате. Я напишу просто Test. Следующим сообщением надо отправить логин для бота, он должен оканчиваться на bot. Я напишу test0004bot, чтобы наверняка был свободен. В итоге мы создали нового бота и получили его токен для доступа по API. Чтобы написать в закрытый канал Предположим, что закрытый канал уже создан (там пара кликов) и приступим к колдовству. Добавляем бота в администраторы Из определения закрытого канала, в поиске его не найти, и в него не может написать простой пользователь, только администраторы имеют такое право (в том и отличие от группы, где все более-менее равны и могут общаться). То есть нашего бота нужно добавить в администраторы. Здесь есть один момент! В некоторых клиентах Telegram, при наборе логина бота в поле для добавления администратора, нужно продолжать набирать символы до конца, хотя подсказок нет и вообще кнопка неактивна, все в итоге будет в порядке! Заполучаем необходимые данные Исходя из документации Telegram Bot API, нам нужен chat_id, чтобы бот мог вообще написать в чат, но где его взять для закрытого канала? Заходим на WEB-версию Telegram и выбираем наш закрытый канал, обращаем внимание на ссылку в адресной строке, в моем случае это https://web.telegram.org/#/im?p=c1099761134_142.... Нас интересует часть между =c и _, в моем случае это 1099761134. Это почти chat_id, для работоспособности к этому числу надо приписать слева -100, то есть в моем случае -1001099761134. Готово. Отправляем запрос к API Далее нам остается отправить POST-запрос к https://api.telegram.org/bot<токен>/sendMessage, в тело которого я добавлю следующий JSON: { "chat_id": -1001099761134, "text": "123" } и не забуду добавить соответствующий HTTP-заголовок (Content-Type: application/json), чтобы сервер корректно воспринял JSON. Ответ: { "ok": true, "result": { "message_id": 3, "chat": { "id": -1001099761134, "title": "Test Channel", "type": "channel" }, "date": 1493376264, "text": "123" } } положительный. Вот и готово!

Нелады с Mina? Не паникуем!

Несколько часов пытались автоматизировать деплой по инструкции на официальном сайте Mina, и уже появились сомнения насчет своего психического здоровья? Перепробовали все примеры кода из документаций и с хабра, сменили текстовый редактор на vim, пересобрали ядро и подняли для тестов 3 сервера с разными дистрибутивами в разных точках планеты, и всё равно нихрена не работает? На самом деле всё в порядке, виноват Infinum! Сейчас я объясню, что произошло! Мне срочно нужно починить! Вот, держите ваш ключ к разгадке. Что еще за Infinum? В 2014 году проект Mina переживал стагнацию и практически не поддерживался. Тогда компания Infinum решила проявить инициативу, связалась с разработчиками, попросила о передаче себе прав на проект, с условием, что он будет поддерживаться и развиваться. У них получилось, они переписали 90% кода и по сей день занимаются Mina. И что же произошло? Ничего особенного, просто с версии 1.0 (середина октября 2016) проект почти полностью утратил совместимость с другими версиями, но об этом никто не кричит, а сайт проекта вообще радостно повествует о том, как легко пользоваться устаревшими на полгода командами. Вот и выходит, что следуешь всем инструкциям, а Mina не работает :) Откуда вся эта инфа? С записи конференции Voxxed Days Belgrade 2016 у которой на данный момент умопомрачительные 29 просмотров.

Введение в ncurses на C++

В универе задали лабораторную: реализовать элемент интерфейса “всплывающее окно с сообщением” под DOS на C++, используя ООП подход. Ну спасибо тебе за актуальность, дорогой универ, нынче DOS и Turbo Vision наше всё, такой ценный опыт… К счастью, преподаватель отнёсся с пониманием и разрешил использовать современную кроссплатформенную библиотеку ncurses для рисования псевдографики, о ней я сегодня и расскажу. Долой пиксели, даешь знакоместа! Что такое ncurses и где оно обитает Ncurses (произносится “энкёрсиз”) – это библиотека, которая существенно упрощает создание интерфейсов на псевдографике и позволяет при разработке вообще не думать о том, например, какую экранирующую последовательность символов нужно послать терминалу для того, чтобы текст выводился ^[[0;31;40mКРАСНЫМ цветом. Мало того, что эти закорючки тяжело осмыслить, так они еще и для разных терминалов разные бывают. Вообще, чтобы как-то облегчить жизнь, разработчики UNIX решили хранить в специальном файле termcap (а в будущем terminfo) все те возможности, которые предоставляет конкретный терминал. Этот файл позволяет приложениям смотреть, какие экранирующие последовательности можно посылать, в него смотрит и библиотека ncurses, избавляя пользователя-программиста от написания лишнего кода для, так скажем, кросс-терминальности. Пожалуй, самыми известными программами, в которых для интерфейса используется ncurses, являются htop, GNU Midnight Commander и инструмент графической настройки конфигурации ядра Linux, хоть с какой-то из них сталкивался каждый. Как установить Mac brew install ncurses Linux sudo apt-get install libncurses5-dev libncursesw5-dev Windows Под Windows и DOS есть PDCurses, которая практически полностью совместима с ncurses. Одной строчкой не ставится, вот тут есть вся необходимая информация по ней. Написание программ При компиляции программ важно не забывать приписать флаг -lncurses, чтобы всё нормально залинковалось. Разберем простейшую программу с комментариями и пояснениями, и заглянем глубже. Hello World! #include <ncurses.h> int main() { initscr(); // Инициализация и переход в curses режим printw("Hello World!"); // Напечатать строку в воображаемое окно refresh(); // Вывести на настоящий экран изменения getch(); // Ждать нажатие клавиши endwin(); // Освобождение памяти, переключение терминала в обычный режим return 0; } Вот тот странный момент с printw и refresh, да, сейчас объясню. Дело в том, что для производительности будет лучше, если программист сначала сделает все необходимые операции по изменению данных на воображаемом экране (в буффере), и только потом (после refresh) на настоящем экране обновятся те участки, которые действительно изменились в конечном итоге. Инициализация Помимо initscr есть и другие функции, которые можно повызывать для кастомизации текущей сессии. Рассмотрим лишь некоторые из них, чтобы статья не превратилась в документацию. raw и cbreak Обычно, терминал буфферит символы, которые набирает пользователь до переноса строки, но большинству программ набираемые символы требуются сразу же во время их набора. Указанные выше функции позволяют управлять буфферизацией. Разница заключается в том, как будут обрабатывается управляющие последовательности вроде CTRL-Z и CTRL-C. В raw режиме они попадают в программу, не генерируя сигналов, а в cbreak режиме они интерпретируются драйвером терминала и генерируют соответствующие им сигналы. echo и noecho Эти функции контролируют вывод напечатанного на экран. noecho отключает вывод, echo наоборот включает. Обычно в программах используется noecho и вывод производится другими, более гибкими средствами. keypad Позволяет чтение клавиш F1, F2, стрелок и тд. Чтобы включить для стандартного экрана, вызывается как keypad(stdscr, TRUE). curs_set; Включает и отключает отображения курсора на экране, чтобы отключить курсор вызывается curs_set(FALSE) Цвета В ncurses есть удобный механизм для работы с цветами. В первую очередь, для того, чтобы проверить, поддерживается ли в текущем терминале функция цветов вообще, есть функция has_colors(), если вернулась истина, то start_color() и погнали. Важное понятие в ncurses – это цветовые пары, они определяют, каким цветом мы выводим и на каком фоне. Парам присваивают целочисленные значения: init_pair(1, COLOR_RED, COLOR_BLACK) Мы сделали цветовую пару, которая пишет красным по черному, кстати, если лень писать константы, можно писать просто соответствующие им циферки: Цвет Циферка COLOR_BLACK 0 COLOR_RED 1 COLOR_GREEN 2 COLOR_YELLOW 3 COLOR_BLUE 4 COLOR_MAGENTA 5 COLOR_CYAN 6 COLOR_WHITE 7 Ну а если стандартные цвета недостаточно кислотные для вашей хакерской проги, то можно сделать свои! Вот так это делается: /** * @param 1 как назовем цвет * @param 2 сколько красного * @param 3 сколько зеленого * @param 4 сколько синего */ init_color(COLOR_RED, 700, 0, 0); RGB-значения лежат в интервале от 0 до 1000. Если что-то пошло не так, или ваш терминал не умеет работать с цветами, то функция init_color вернет ERR. Окна Окна – это воображаемые экраны, определенные в curses. Окно в данном контексте не означает то окно с рамочкой в Turbo Pascal, которое можно таскать по экрану. При инициализации ncurses, создается воображаемое окно stdscr, которое является представлением открытого терминала (настоящего экрана) размером с этот терминал. На это окно уже можно сразу выводить строки или другие окна, передавать его как параметр в функцию и тд. printw("Hi There !!!"); refresh(); Данный код выведет строку в stdscr в текущей позиции курсора. Аналогично вызов refresh, работает только с stdscr. После создания нового окна, для выполнения таких же действий с ним, а не с stdscr, вызывают функции, которые начинаются на w и принимают как параметр окно, с которым производятся действия, вот так для окна с именем win: wprintw(win, "Hi There !!!"); wrefresh(win); Окно с именем win имеется в виду, что это имя указателя на память данных с окном в куче. Такая конвенция именования соблюдается во всех функциях, которые можно применить и к stdscr, и к другим окнам. Таким образом, библиотека предоставляет разработчику все необходимые механизмы для создания интерфейсов в терминале, код своей сделанной лабораторной с комментариями я привел ниже. Лабораторная Message.h #ifndef MESSAGE_H #define MESSAGE_H #include <ncurses.h> /** * Задача: * Разработать визуальную компоненту Message (окно с сообщением) с различными вариантами оформления: без pамки (Mode=0), одинаpная pамка (Mode=1). */ class Message { private: WINDOW *mainWindow, *messageWindow; char *message; int height, width, top, left, mainCP, msgCP, bgCP; bool borders, hidden; void drawWindows(); void eraseWindows(); public: Message(int left, int top, char *message, int mainCP, int msgCP, int bgCP, bool borders); ~Message(); void Show(); void Hide(); void moveUp(); void moveDown(); void moveLeft(); void moveRight(); }; #endif Message.cpp #include "Message.h" /** * Конструктор окна сообщения * @param left координата левого верхнего угла по x * @param top координата левого верхнего угла по y * @param message сообщение к выводу * @param borders флаг для полей вокруг сообщения */ Message::Message(int left, int top, char *message, int mainCP, int msgCP, int bgCP, bool borders) { this->height = 10; this->width = 30; this->top = top; this->left = left; this->borders = borders; this->message = message; this->mainCP = mainCP; this->msgCP = msgCP; this->bgCP = bgCP; this->drawWindows(); } /** * Деструктор */ Message::~Message() { this->eraseWindows(); delwin(this->mainWindow); delwin(this->messageWindow); } /** * Метод для перерисовки окна сообщения */ void Message::drawWindows() { if (!this->hidden) { this->mainWindow = newwin(this->height, this->width, this->top, this->left); if (this->borders) box(this->mainWindow, 0 , 0); wbkgd(this->mainWindow,COLOR_PAIR(this->mainCP)); mvwaddstr(mainWindow, 1, 3, "You have got a message!"); wrefresh(this->mainWindow); this->messageWindow = newwin(6, 26, this->top + 3, this->left + 2); wbkgd(this->messageWindow,COLOR_PAIR(this->msgCP)); mvwaddstr(messageWindow, 0, 1, this->message); wrefresh(this->messageWindow); refresh(); } } /** * Метод для стирания окна сообщения */ void Message::eraseWindows() { if (this->borders) wborder(this->mainWindow, ' ', ' ', ' ',' ',' ',' ',' ',' '); wbkgd(mainWindow, COLOR_PAIR(this->bgCP)); wclear(this->mainWindow); wrefresh(this->mainWindow); refresh(); } /** * Передвинуть окно сообщения на один знакосимвол влево */ void Message::moveLeft() { this->left--; this->eraseWindows(); this->drawWindows(); } /** * Передвинуть окно сообщения на один знакосимвол вправо */ void Message::moveRight() { this->left++; this->eraseWindows(); this->drawWindows(); } /** * Передвинуть окно сообщения на один знакосимвол вверх */ void Message::moveUp() { this->top--; this->eraseWindows(); this->drawWindows(); } /** * Передвинуть окно сообщения на один знакосимвол вниз */ void Message::moveDown() { this->top++; this->eraseWindows(); this->drawWindows(); } /** * Скрыть окно */ void Message::Hide() { this->eraseWindows(); this->hidden = true; } /** * Показать окно */ void Message::Show() { this->hidden = false; this->drawWindows(); } main.cpp #include <ncurses.h> #include "Message.h" int main() { // Инициализация знакосимвольного пространства и цветовых пар initscr(); start_color(); init_pair(1,COLOR_YELLOW,COLOR_BLUE); init_pair(2,COLOR_RED,COLOR_GREEN); init_pair(3,COLOR_CYAN,COLOR_BLACK); // Отключение вывода с клавы и курсора, всякого буфферинга cbreak(); keypad(stdscr, TRUE); noecho(); curs_set(FALSE); // Задание размеров и цветов для окна сообщения int starty = 10, startx = 10; int messageColorPair = 2, mainColorPair = 1, bgColorPair = 3; bool needBorders = true; wbkgd(stdscr,COLOR_PAIR(bgColorPair)); // Цвет для фона применим сразу refresh(); // Первоначальное обновление экрана printw("Made by Semyon Fomin using ncurses\nApril 13th 2017"); char chr[] = "Press F9 to exit..."; // Создаем экземпляр класса окна сообщения Message *myMessage = new Message(startx, starty, chr, mainColorPair, messageColorPair, bgColorPair, needBorders); int ch; while((ch = getch()) != KEY_F(9)) { switch(ch) { case KEY_F(1): myMessage->Hide(); break; case KEY_F(2): myMessage->Show(); break; case KEY_LEFT: myMessage->moveLeft(); break; case KEY_RIGHT: myMessage->moveRight(); break; case KEY_UP: myMessage->moveUp(); break; case KEY_DOWN: myMessage->moveDown(); break; } } endwin(); }

Формат даты для Jekyll

Только что столкнулся с первой, скажем так, “задачей” при создании блога и решил воспользоваться этим, чтобы написать первый тестовый пост здесь. Итак, в Jekyll настройка отображение даты делается с помощью фильтров шаблонизатора Liquid, но, как и стоило ожидать, нельзя просто поменять в каком-то конфиге два символа и получить интернационализацию для всех фильтров по всему блогу, всё немного хитрее и есть два варианта: 1. Костыль Такого вида внушительная конструкция, которая будет тащиться по всему блогу и которую по какой-то причине на каждом шагу советуют {% raw %} {% assign m = page.date | date: "%-m" %} {{ page.date | date: "%-d" }} {% case m %} {% when '1' %} Januar {% when '2' %} Februar {% when '3' %} März {% when '4' %} April {% when '5' %} Mai {% when '6' %} Juni {% when '7' %} Juli {% when '8' %} August {% when '9' %} September {% when '10' %} Oktober {% when '11' %} November {% when '12' %} Dezember {% endcase %} {{ page.date | date: "%Y" }} {% endraw %} 2. Свой Liquid-фильтр Поскольку в 99.9% случаев мне будет нужен всего один формат даты, я решил, что можно потратить 5 минут и дописать две конкатенации к одному из выложенных на StackOverflow сниппетов на Ruby, вот что получилось module DateFilter MONTHS = %w(Января Февраля Марта Апреля Мая Июня Июля Августа Сентября Октября Ноября Декабря) def russian_long_month(input) input.strftime("%d") + " " + MONTHS[input.strftime("%m").to_i - 1] + " " + input.strftime("%Y") end end Liquid::Template.register_filter(DateFilter) Всё, дальше это дело надо сохранить в файл с любым именем и поместить в директорию _plugins блога, теперь можно использовать собственный фильтр, вот так это будет выглядеть {% raw %}{{ post.date | russian_long_month }}{% endraw %}