-
Ошибки
Главная беда этой нашей айтишечки — сообщения об ошибках, не связанные с их устранением. Примеров можно найти миллион, но вот вам один.
Собираю одну фигню в докере, и команда
apt-get update
падает с сообщением, что репозиторий не подписан:The repository 'http://archive.ubuntu.com/ubuntu focal InRelease' is not signed.
При этом на другом ноуте все собирается как надо.
Оказалось, что это мулька самого Докера, и лечится она командой prune:
docker image prune -f docker container prune -f
Очевидно, без гугла и SO эту проблему не решить в принципе.
В современной айтишке много абстракций, и каждая из них не знает о другой. Отсюда эти уродские сообщения: абстракция А считает что-то неподписанным, потому что в Б нет места. А не находит файлы, потому что они не смонтированы в Б. А валится, потому что в Б другая локаль или кодировка по умлочанию.
Задача хорошего программиста в том, чтобы давать понятные сообщения об ошибках. Наверное, вас тоже бесило, когда Java говорит:
port is already in use
. Блин, какой порт? У меня их десять в проекте, тяжело добавить цифру в сообщение?Или тот же Вим. По всему миру люди страдают, не знают как из него выйти. Вопрос на SO набрал миллион просмотров и растет дальше. И что делают разработчики вима? Вместо того, чтобы добавить бар с действиями как в Nano, объясняют, что пользователи тупые.
Напоминает мост в Питере со знаком “Газель не проедет”, где разбились сто Газелей. Вместо того, чтобы порвать на тряпки мэра и начальника ГИБДД, ставят знаки и постят картинки в Контакте.
То же самое происходит у нас. Вместо понятного сообщения мы ждем, что его предоставит вышестоящий слой. Но это бессмысленно: не сделаем мы — не сделает никто.
-
Фаервол
Лучшая вещь, что случилась со мной в последнее время — это установка фаервола на всех маках. Вот как это произошло.
Когда-то я жаловался, что программы без спроса лезут в интернет обновляться. Открыл Саблайм — а он показывает модалку, что вышла новая версия. Открыл то, другое, а там то же самое: скачай-обновись.
Идея как это победить простая — нужно отобрать у программы доступ в интернет. Я пошел настраивать встроенный фаервол, но оказалось, он убогий: закрывает только входящие соединения, а исходящие не трогает. В результате даже если добавить в список программу, она спокойно идет в сеть.
Путем гугления нашел программу Lulu. Хоть название и затасканное, программа просто супер: удобный интерфейс и настройка. Lulu делит программы на группы: родные Эпла, уже установленные и другие. Можно включать и выключать целые группы, а можно по отдельности. Правила формируются в разрезе программ, папок, регулярных выражений.
Можно настроить гибкий контроль по урлам. Например, дать программе доступ к хосту
sync.blabla.com
для синхронизации, но запретить обновления по хостуupdate.blabla.com
.Можно дать временный доступ по номеру процесса. Когда программа запуститься в cледующий раз, номер будет другим, и Lulu спросит опять.
Фаервол может работать в интерактивном режиме, показывая диалог на события, которых нет в правилах, а может молча разрешать или разрешать все, что происходит.
Для себя я сделал так: удалил все правила, кроме Эпловских, а затем настраивал вручную. Запретил все программы Адоба, офисного пакета, всякие редакторы вроде Саблайма — их задача работать с текстом, а не лазить за обновлениями. Запретил обновки для Dash, программы оффлайн-документации, и много что другое.
На старом ноуте обнаружил несколько вирусов. Это программы с названиями типа robstoaf, прописанные в автозагрузке. Время от времени они ходят в сеть на левые хосты. Возможно, крадут историю браузеров или это ботнет. Так что не слушайте байки про то, что под Мак вирусов нет.
Что немаловажно, Lulu запрашивает подтверждение, когда левая программа хочет добавить себя в автозагрузку. Всякое дерьмо вроде Zoom, MS Teams прописывают себя там, и в итоге запускаются в фоне, даже если вы не просили. Больше этого беспредела нет.
Программа так понравилась, что я хотел перевести автору денег, долларов сто. Хотел скинуть с Пейпала, а у него только патреон — то есть регистрируйся, вводи персональные данные, карту, подтверждай — извините, нет.
Кроме Lulu, у автора есть смежные утилиты для безопасности, например монитор клавиатуры на предмет прослушки и что-то еще второстепенное.
Словом, не знаю как жить без Lulu: теперь это первая программа, которую я ставлю на Мак. Как раз недавно ставил ее на свежий ноут. Неистово рекомендую!
Картинки, описание и загрузка здесь: https://objective-see.org/products/lulu.html
-
CSS is Awesome
Все видели картинку, где текст из заголовка не влезает в рамку. Ха-ха, кривой CSS, технологии, которые мы заслужили (с).
Хорошо, посмеялись, а теперь вопрос: как должно быть?
Представим, вы всемогущий бог, все браузеры мира под вашим контролем. Щелкните пальцами — и у всех эта картинка покажется так, как вы скажете. Что предпочтете?
Проблема в том, что на картинке типичное UB — undefined behaviour, неопределенные поведение. В литровую банку пытаются налить два литра воды. Простыми словами, впихнуть невпихуемое. Это невозможно, поэтому кто-то окажется в проигрыше.
На самом деле на картинке все правильно: при всем уродстве мы смогли прочитать текст. Если бы он был обрезан, мы бы его не увидели, то есть не получили бы вообще ничего. А так получили хотя бы часть, пусть и фраза там бестолковая.
Эту картинку я считаю крайне неудачной. Кто-то хотел пошутить, что CSS глючный, но выбрал плохой пример: выставил дурацкие ограничения (малую рамку и огромный шрифт) и смотрит, как программа не справляется. А она, между прочим, справилась — донесла до нас текст.
Так что шутка на троечку.
-
Фильмы про бомбу
В фильмах про ядерную бомбу повторяется дурацкий штамп. Якобы ученый работает из интереса, не задумываясь над последствиями, только по зову сердца. Но ВНЕЗАПНО, эффект превосходит все ожидания, бомба оказывается разрушительной, подтягиваются политики, и ученый такой — ох, что я наделал, нельзя допустить, АСТАНАВИТЕСЬ!!!111 Всякие метания и попытки вернуть все взад.
Это чушь собачья. Ученые прекрасно понимают, над чем они работают, а штамп нужен для того, чтобы высосать драму из пальца. Взять того же Фейнмана: после бомбы он и преподавал, и клеил женщин пачками, и два раза женился, и танцевал, играл, разгадывал секреты Майя и много что еще.
Так что не надо этой лапши про внезапное прозрение. Ну а если с кем-то и правда такое было, то надо было думать раньше.
-
With-http: a Clojure library for testing HTTP
The with-http library provides a macro to stub HTTP calls with a local Jetty server. it’s declarative, flexible, and extremely useful. I’ve been copying it through many projects, and now it’s time to ship it as a standalone library.
ToC
- Installation
- About
- The App routes
- Default handler
- Basic test
- JSON
- Slow responses
- Files & Resources
- Capturing requests
Installation
Lein:
[com.github.igrishaev/with-http "0.1.1"]
Deps.edn
{com.github.igrishaev/with-http {:mvn/version "0.1.1"}}
Pay attention: since the library is primarily used for tests, put the dependency in the corresponding profile or alias. Storing it in global dependencies is not a good idea as it becomes a part of the production code otherwise.
About
The library provides a
with-http
macro of the following form:(with-http [port app] ...body)
The
port
is the number (1..65535) and theapp
is a map of routes. When entering the macro, it spawns a local Jetty server on that port in the background. Theapp
map tells the server how to respond to calls.Now that you have a running server, point your HTTP API clients to
http://localhost:<port>
to imitate real network interaction. For example, for prod, a third-party base URL is https://api.some.cool.service but for tests, it is http://localhost:8088. This can be done using environment variables or the Aero library.Why not use
with-redefs
, would you ask? Well, althoughwith-redefs
looks like a solution at first glance, it’s questionable. Usingwith-redefs
means lying to yourself. You temporarily mute some pieces of the codebase pretending it’s OK, but it’s not.Often, bugs lurk in the code that you actually substitute using
with-redefs
, namely:-
you’ve messed up with MD5/SHA/etc algorithms to sign a request. Calling localhost would trigger that code and lead to an exception, but
with-redefs
would not. -
you process the response poorly, e.g. not taking Content-Type header or non-200 status code into account.
-
you cannot imitate delays and timeout exceptions when interacting with HTTP API.
The good news is, that the
with-http
macro can test all the cases mentioned above and much more.The App routes
The
app
parameter is a two-level map of the form:{path {method response}}
For example:
{"/foo" {:get {:status 200 :body "it was GET"} :post {:status 201 :body "it was POST"}}}
Calling
GET /foo
andPOST /foo
would return 200 and 201 status codes with different messages.The response might be:
- a Ring map;
- a Ring handler function that accepts a request map and returns a response map;
- an instance of
java.io.File
; - a resource:
(clojure.java.io/resource "some/file.txt")
; - a string.
Other examples:
{"/foo" {:get (fn [{:keys [params]}] (log/infof "Params: %s" params) {:status 200 :body "OK"})}} {"/some/json" {:get (io/resource "file.json")}}
The path might be a vector as well. During the preparation step, it will be compiled into a string, for example:
{["/foo/bar/" 42 "/test"] {:get {:status 200 :body "hello"}}} ;; becomes {"/foo/bar/42/test" {:get {:status 200 :body "hello"}}}
This is useful when the paths contain parameters.
The
make-url
function helps to build a local URL likehttp://localhost:<port>/<path>
. Its secondpath
argument is either a string or a vector which gets compiled into a string:(make-url PORT "/foo?a=1&b=2") ;; http://localhost:8899/foo?a=1&b=2 (make-url 8899 ["/users/" 42 "/reports/" 99999]) ;; http://localhost:8899/users/42/reports/99999
Default handler
The App mapping has a default handler which gets triggered when the client calls a non-existing route. By default, it’s 404 status page with the following JSON payload:
(def NOT-FOUND {:status 404 :body {:error "with-http: route not found"}})
You can override it by adding the
:default
key to the app map. The value might be a map, a function, a file and so on.{"/foo" {:get {:status 200 :body "hello"}} :default {:status 202 :body "I'm the default!"}}
Basic test
A simple test to ensure the macro works:
(deftest test-with-http-test-json (let [app {"/foo" {:get {:status 200 :body "test"}}} url (make-url PORT "/foo") {:keys [status body]} (with-http [PORT app] (client/get url))] (is (= 200 status)) (is (= "test" body))))
JSON
The Ring handler function produced from the
app
mapping is wrapped withwrap-json-response
andwrap-json-params
middleware layers. It means the body of the response might be a collection that gets dumped into JSON:(deftest test-with-http-test-json (let [body {:hello [1 "test" true]} app {"/foo" {:get {:status 200 :body body}}} url (make-url PORT "/foo") {:keys [status body]} (with-http [PORT app] (client/get url {:as :json}))] (is (= 200 status)) (is (= {:hello [1 "test" true]} body))))
Slow responses
To imitate slow responses, provide a function that sleeps for a certain amount of time:
{"/foo" {:get (fn [_] (Thread/sleep 10000) {:status 200 :body "OK"})}}
Then ensure you pass the timeout limit into your API call.
Files & Resources
Storing JSON responses in files is a good idea. Here is how you can serve them with the macro:
{"/foo" {:get (io/file "dev-resources/test.txt")}}
or
{"/foo" {:get (io/file "dev-resources/test.json")}}
Capturing requests
Another trick to improve your tests: ensure you pass the right parameters or headers to the HTTP API. Provide an atom and a handler function closed over that atom. Each time you receive a request, save it’s data to the atom and then validate them:
(deftest test-with-http-capture-params (let [capture! (atom nil) app {"/foo" {:get (fn [{:keys [params]}] (reset! capture! params) {:status 200 :body "OK"})}} url (make-url PORT "/foo?a=1&b=2") {:keys [status body]} (with-http [PORT app] (client/get url))] (is (= 200 status)) (is (= "OK" body)) (is (= {:a "1" :b "2"} @capture!))))
-
Скриншоты на винде
На винде есть утилита для скриншотов рабочего стола, называется “Ножницы” или вроде того (clip). И надо сказать, программа люто, бешено тупая.
Делаю скриншот и хочу сохранить — открывает диалог с именем “безымянный.PNG” (в английской версии untitled.PNG). Не будем комментировать расширение капсом; но блин, почему файл безымянный? Что мешало назвать его текущей датой с точность до минуты?
Далее, если в папке уже есть “безымянный” с прошлого раза, программа предложит его перезаписать. Что мешает проверить, что такой файл уже есть и хотя бы поставить единичку (двойку, тройку)?
В итоге как дурак дописываешь в конец чушь вроде 123, чтобы сохранить скриншот и куда-то отправить.
Вот как на Маке: когда делаешь скриншот, он сперва улетает в угол, и в течении 10 секунд его можно куда-то перетащить без сохранения на диск. А если не успел, он сохранится на рабочий стол с датой и временем в названии. Не нужно париться, сохранил или нет, перезаписал или нет.
Что мешало сделать также Микрософту? Они вообще своими программами пользуются?
-
Мы заслужили
Выражение “X, который мы заслужили” стало затасканным штампом.
- Киберпанк, который мы заслужили.
- Футбол, который мы заслужили.
- Релиз, который мы заслужили.
- Пайплайн, который мы заслужили.
Блин, кто мы? Чем и как заслужили?
Лет пять назад это было туда-сюда, но сегодня терпеть уже невозможно.
-
Картинки в Телеграме
Телеграм бесит ещё одной вещью. Если добавить к тексту картинку, текст поджимается под её ширину. Зачем, какой смысл? Текст превращается в мышиный хвост, справа сплошная пустота. Половина полезного места уходит в унитаз. Всё это ради чего? Чтобы что?
-
Пасхалки
Пожалуй, худшим фильмом, что я смотрел за последние годы, можно назвать “Петровы в гриппе”. В двух словах его можно описать как бессвязный треш. В те времена я читал Медузу, наслушался критика Долина и побежал на предпоказ как последний дурачок.
Фильм — набор флешбеков без какого-либо действия. Женщина убивает мужчину на детской площадке. Это происходит во дворе девятиэтажки, но никто этого не видит. Потом заболевает гриппом мальчик, ее сын. С работы приходит батя-алкаш. У него начинаются приходы и флешбеки со снегурочкой. Потом фильм рассказывает про снегурочку: нищета, театр, случайный секс, беременность. В конце сцена про алкашей и оживший труп.
Я не досидел до конца и ушел из зала, но потом вернулся за забытой вещью. К тому времени свет включили, но люди не расходились. Оказалось, к фильму был приставлен специально обученный человек, который после показа работал со зрителями: отвечал на вопросы “что хотел сказать автор”.
Вот оно, современное искусство: тебе не только продают говно, но ещё тратятся на человека, который объяснит, что это не говно, а режиссёр — тонкий художник.
Но пост не о плохом фильме — их и так хватает, — а о большом числе “пасхалок” в нём. Каждые пять минут в кадре появляются какие-нибудь слова на заборе, которые что-то значат. Это сделано столь топорно, что просто закрываешь глаза. Режиссёр как будто тычет с экрана: смотри, это пасхалочка, скрытый смысл, понимаешь? Улавливаешь?
От одной из сцен захотелось просто выругаться. Парень и девушка идут вдоль огромной инсталляции с буквами в человеческий рост. Буквы показаны наполовину, но распознать их можно. И когда пара шла мимо них, я подумал: наверняка буквы складываются в какой-то “мэссадж”, но читать было лень, потому что от пасхалок я уже устал.
И что вы думаете? Позже я прочёл в интернете, что те буквы складываются в “тебе пизда”. Очень оригинально! Какой талант режиссёра! Наверное, много думал над этой пасхалкой — больше, чем над сюжетом и персонажами.
Если серьёзно, то непонятно, зачем эта пасхалка и все другие. Какую задачу они решают? Фильм как был говном, так остался им со всеми пасхалками, разгаданными или нет. Если накрыть говно бантиком, получится говно с бантиком, верно?
Пожелаю режиссёру скорей понять это.
-
Куки
К сожалению, нам не везёт с веб-дизайнерами. Я имею в виду нам всем, не только мне. Открываешь любой сайт — и тебе стреляют плашкой, что сайт использует куки. Даже если нажмёшь ОК, через пару дней сайт забудет, что ты согласился и выстрелит опять. Или зайдешь с другого устройства и снова терпи. На телефоне вообще беда: плашка занимает в лучшем случае полэкрана, в худшем — весь.
Нам не повезло, потому что не нашлось дизайнера, который бы нормально встроил сообщение о куках в дизайн. Под “нормально” я имею в виду, чтобы оно не выскакивало, не выпадало, а было написано без анимаций и JavaScript.
Я уже говорил, что хорошие дизайнеры должны начинать с печатного дизайна. Есть лист бумаги, и нужно уместить на нём дизайн со всеми требованиями. Никаких тебе выпадашек и анимашек.
А требования бывают разные. Если алкоголь или табак — предупреждение Минздрава занимает не менее 10% от площади макета. Если банк — генеральная лицензия кеглем не менее стольки пунктов. Если кандидат на выборах — лицензия на участие, номер и прочие вещи. В роликах то же самое: показываешь БАД — внизу должна быть надпись “не является лекарством, проконсультируйтесь с врачом”. И не в первую секунду, а на протяжении всего ролика.
И дизайнеры как-то справляются с этим — выделяют площадь, увеличивают кегль, согласуют с юристами и надзорными органами. И только веб-дизайнеры, эти обезьянки с курсами HTTP/CSS/JavaScript, не могут доработать дизайн так, чтобы требование чиновников (тоже обезьянок) выполнялось без выпадашки.
Казалось бы: нужно уведомить посетителя, что сайт использует куки. Так напиши чуть ниже шапки: “этот сайт использует куки”. И ссылку на отдельную страницу, где подробно написано, что такое куки, а ниже форма с чекбоксами, какие куки хранить. Что мешает так сделать?
Но нет, дизайнер быдлокодит всплывающую форму, скрипты, стили. Получил инструменты, а пользуется ими во вред.
Чтоб у него в штанах что-нибудь выпало.