#такое дня
Уже довольно поздно. Отметьтесь реакциями, кто ещё онлайн.
Наберём достаточное количество — скину историю смешного бага, который у нас недавно произошёл по невнимательности.
Не наберём — скину её утром. Никуда она не денется.
Upd. Хорошо разогнались. Сейчас всё будет.
Уже довольно поздно. Отметьтесь реакциями, кто ещё онлайн.
Наберём достаточное количество — скину историю смешного бага, который у нас недавно произошёл по невнимательности.
Не наберём — скину её утром. Никуда она не денется.
Upd. Хорошо разогнались. Сейчас всё будет.
🤩67👍36🤡4❤1🤬1
#баг дня
Преамбула
Некий сервис, назовём его Doodle Lampolytics, заставил всех своих клиентов переползти с модели подсчёта просмотров страниц на модель подсчёта событий.
Суть проблемы сервиса очевидна: постраничная модель слабо подходила для серьёзной аналитики данных в веб-приложениях. Даже элементарные лендинги стали гораздо сложнее, чем просто переходы по ссылкам.
И если раньше мы в нагрузку к «просмотру» передавали так называемые индивидуальные размерности и метрики, привязанные к сессии, то теперь — каждое событие может нести индивидуальный их набор.
Не в последнюю очередь это связано с ужесточением запретов на слежку за посетителями сайтов.
Поскольку в типичном проекте зачастую присутствует не одна сотня событий/страниц, то переезд сопровождается знатным перетряхиванием всей аналитики.
Суть
Суд да дело, приходим мы к идее передачи события с неким контекстом:
В одном из постов выше я уже показывал, что этот самый контекст (context) обогащает собой общие для всех событий параметры, превращая событийную модель в некое подобие сессионной.
Передаём туда, например, productId и orderId — и получаем трекинг истории конкретного продукта или заказа. Удобно, когда продукт сильно кастомизированный. Можно накинуть туда optionId и следить, какие продукты какими опциями нашпиговывают.
Таким образом, мы отсылаем объекты вида:
Потом объект из camelCase конвертируется в snake_case, как того требует документация, и дёргается некий API. Запомните этот момент.
И всё, поехали в продакшен. События уходят, счастье. Заодно, те же самые события, за некоторым исключением, можно пихать в сервисы вроде Sentry.
И всё бы ничего, но интерфейс Doodle Lampolytics настолько проклят, что оказалось намного проще выгружать данные оттуда сразу в Doodle LargeQuery и SQL-ем их запрашивать.
Но в какой-то момент времени мне это радикально надоело. Мне нравилась простота старого интерфейса, и я решил разобраться в новом. Проблема была в том, что я там не мог найти не только половину событий, но и их параметры.
Я почитал документацию, нашёл где создавать свои размерности и метрики на основе передаваемых параметров — в админке — захожу туда и наблюдаю следующую картину:
— Какого хера?! — заорал я, я же был в офисе.
Но параметры с ı вместо i исчислялись десятками, в то время как остальные — десятками тысяч. И я зачем-то успокоился. Подумал, что это косяк ламполитики, с которым можно разобраться попозже.
Но сегодня я зашёл в наш прекрасный чат, зачем-то ткнул в аватарку одного из подписчиков и увидел у него в истории некую песню с названием Bakı.
И тут до меня резко дошло, какой я идиот.
Прежде чем читать дальше, вы можете посмотреть на код функции, которой мы преобразуем camelCase в snake_case: https://github.com/jonschlinkert/snakecase/blob/master/index.js
Особо внимательные, наверное, уже догадались, в чём проблема. А ведь мы даже тесты написали.
Поскольку не все пройдут по ссылке, я приведу кусочек:
Ничего не смущает? Я подожду.
...
Да, правильно. Если забыть передать локаль (язык клиента, сервера в нашем случае), то будет взята переменная среды.
И для кого-то из наших клиентов в этой переменной оказался турецкий (или узбекский) язык.
В которых нет буквы i, вместо неё — ı.
Вы все правильно поняли, мы не полностью контролируем среду выполнения. Это так называемый Doodle AppsScript. Он исполняется на языке, в котором открыт офисный инструмент посетителя.
Естественно, мы локаль передать забыли. Хороший вопрос, почему мы вообще не проверили это поведение изначально... но тут как есть.
Стыдно? Стыдно. Зря я винил Doodle Lampolytics.
Преамбула
Некий сервис, назовём его Doodle Lampolytics, заставил всех своих клиентов переползти с модели подсчёта просмотров страниц на модель подсчёта событий.
Суть проблемы сервиса очевидна: постраничная модель слабо подходила для серьёзной аналитики данных в веб-приложениях. Даже элементарные лендинги стали гораздо сложнее, чем просто переходы по ссылкам.
И если раньше мы в нагрузку к «просмотру» передавали так называемые индивидуальные размерности и метрики, привязанные к сессии, то теперь — каждое событие может нести индивидуальный их набор.
Не в последнюю очередь это связано с ужесточением запретов на слежку за посетителями сайтов.
Поскольку в типичном проекте зачастую присутствует не одна сотня событий/страниц, то переезд сопровождается знатным перетряхиванием всей аналитики.
Суть
Суд да дело, приходим мы к идее передачи события с неким контекстом:
trackEvent('event_name', context) {}
В одном из постов выше я уже показывал, что этот самый контекст (context) обогащает собой общие для всех событий параметры, превращая событийную модель в некое подобие сессионной.
Передаём туда, например, productId и orderId — и получаем трекинг истории конкретного продукта или заказа. Удобно, когда продукт сильно кастомизированный. Можно накинуть туда optionId и следить, какие продукты какими опциями нашпиговывают.
Таким образом, мы отсылаем объекты вида:
{
productId,
optionId,
orderId,
locale,
}
Потом объект из camelCase конвертируется в snake_case, как того требует документация, и дёргается некий API. Запомните этот момент.
И всё, поехали в продакшен. События уходят, счастье. Заодно, те же самые события, за некоторым исключением, можно пихать в сервисы вроде Sentry.
И всё бы ничего, но интерфейс Doodle Lampolytics настолько проклят, что оказалось намного проще выгружать данные оттуда сразу в Doodle LargeQuery и SQL-ем их запрашивать.
Но в какой-то момент времени мне это радикально надоело. Мне нравилась простота старого интерфейса, и я решил разобраться в новом. Проблема была в том, что я там не мог найти не только половину событий, но и их параметры.
Я почитал документацию, нашёл где создавать свои размерности и метрики на основе передаваемых параметров — в админке — захожу туда и наблюдаю следующую картину:
product_id,
product_ıd,
option_id,
option_ıd
— Какого хера?! — заорал я, я же был в офисе.
Но параметры с ı вместо i исчислялись десятками, в то время как остальные — десятками тысяч. И я зачем-то успокоился. Подумал, что это косяк ламполитики, с которым можно разобраться попозже.
Но сегодня я зашёл в наш прекрасный чат, зачем-то ткнул в аватарку одного из подписчиков и увидел у него в истории некую песню с названием Bakı.
И тут до меня резко дошло, какой я идиот.
Прежде чем читать дальше, вы можете посмотреть на код функции, которой мы преобразуем camelCase в snake_case: https://github.com/jonschlinkert/snakecase/blob/master/index.js
Особо внимательные, наверное, уже догадались, в чём проблема. А ведь мы даже тесты написали.
Поскольку не все пройдут по ссылке, я приведу кусочек:
const lowercase = (input = '', options) => input.toLocaleLowerCase(options?.locale);
Ничего не смущает? Я подожду.
...
Да, правильно. Если забыть передать локаль (язык клиента, сервера в нашем случае), то будет взята переменная среды.
И для кого-то из наших клиентов в этой переменной оказался турецкий (или узбекский) язык.
В которых нет буквы i, вместо неё — ı.
Вы все правильно поняли, мы не полностью контролируем среду выполнения. Это так называемый Doodle AppsScript. Он исполняется на языке, в котором открыт офисный инструмент посетителя.
Естественно, мы локаль передать забыли. Хороший вопрос, почему мы вообще не проверили это поведение изначально... но тут как есть.
Стыдно? Стыдно. Зря я винил Doodle Lampolytics.
🤩23👍9❤3
#заметка дня
Иногда полезно спорить с мэтрами по поводу разных штук. Не стоит всё подряд принимать на веру.
Вот, например, Кори Хаус, весьма известный консультант по React, предложил буквально следующее: чтобы не запутаться в бесконечных хуках useEffect в React, вместо неименованной функции передавайте именованную.
То бишь, вместо:
Предлагается
На первый взгляд выглядит разумно, не правда ли?
Самодокументирующийся код без комментариев, хорошо видно намерение, сразу понятно, используется или нет.
Так вот, товарищи. Это далеко не самое лучшее решение, да ещё и многословное. Гораздо более разумно будет создать свой собственный хук.
Что такое свой собственный хук? Вы не поверите, это буквально тот же самый useEffect, но вынесенный в функцию:
Да, внутри функции кастомного хука можно держать useState, обращаться к внешним источникам данных и возвращать что угодно. Это, например, рекомендуемый способ использования того же React Query и официальная рекомендация команды React для переиспользования логики: https://react.dev/learn/reusing-logic-with-custom-hooks
Почему такой подход лучше?
Потому что, обладая всеми преимуществами варианта, предложенного Кори, он не только сокращает размер основного компонента но и легко тестируется!
Да, представьте себе, вы задолбаетесь правильно тестировать компонент с даже двумя-тремя встроенными хуками. А вынесенные отдельно — не только легко мокаются и подменяются, но и прекрасно тестируются сами по себе.
Так что команда React могла бы назвать раздел документации не "как переиспользовать", а "как тестировать". Это важнее.
В общем, не забываем про голову, котаны. Готов выслушать ваши сомнения, впрочем 🙂
#react #javascript #hooks
Иногда полезно спорить с мэтрами по поводу разных штук. Не стоит всё подряд принимать на веру.
Вот, например, Кори Хаус, весьма известный консультант по React, предложил буквально следующее: чтобы не запутаться в бесконечных хуках useEffect в React, вместо неименованной функции передавайте именованную.
То бишь, вместо:
useEffect(() => {
// do stuff
}, [...deps]);
Предлагается
useEffect(function doSomething() {
// do stuff
}, [...deps]);
На первый взгляд выглядит разумно, не правда ли?
Самодокументирующийся код без комментариев, хорошо видно намерение, сразу понятно, используется или нет.
Так вот, товарищи. Это далеко не самое лучшее решение, да ещё и многословное. Гораздо более разумно будет создать свой собственный хук.
Что такое свой собственный хук? Вы не поверите, это буквально тот же самый useEffect, но вынесенный в функцию:
function useDoSomething(deps) {
useEffect(() => {
// some effect
}, [...deps]);
}
Да, внутри функции кастомного хука можно держать useState, обращаться к внешним источникам данных и возвращать что угодно. Это, например, рекомендуемый способ использования того же React Query и официальная рекомендация команды React для переиспользования логики: https://react.dev/learn/reusing-logic-with-custom-hooks
Почему такой подход лучше?
Потому что, обладая всеми преимуществами варианта, предложенного Кори, он не только сокращает размер основного компонента но и легко тестируется!
Да, представьте себе, вы задолбаетесь правильно тестировать компонент с даже двумя-тремя встроенными хуками. А вынесенные отдельно — не только легко мокаются и подменяются, но и прекрасно тестируются сами по себе.
Так что команда React могла бы назвать раздел документации не "как переиспользовать", а "как тестировать". Это важнее.
В общем, не забываем про голову, котаны. Готов выслушать ваши сомнения, впрочем 🙂
#react #javascript #hooks
👍19🤡1
#такое дня
Решил принести вам на ночь. В 2013 году в официальной документации React предлагалось использовать jQuery для AJAX-запросов, а в качестве mock-сервера использовать встроенный в Python HTTP-сервер.
Правда, я, помню, использовал для таких задач сервер, встроенный в PHP... ну кто что умел.
С тех пор прошло 10 лет. Где мы теперь, котаны? :)
Решил принести вам на ночь. В 2013 году в официальной документации React предлагалось использовать jQuery для AJAX-запросов, а в качестве mock-сервера использовать встроенный в Python HTTP-сервер.
Правда, я, помню, использовал для таких задач сервер, встроенный в PHP... ну кто что умел.
С тех пор прошло 10 лет. Где мы теперь, котаны? :)
🤩6
#инструмент дня
Раз уж упомянули пару постов назад Кори Хауса, то давайте ещё чего-нибудь по его рекомендации.
И сегодня это инструмент для поиска дубликатов кода: jscpd.
Сразу ссылка на GitHub: https://github.com/kucherenko/jscpd
Как получаются дубликаты, клоны кода? Да от плохих абстракций. Сами по себе они ничем не плохи, ну есть у вас повторы там-сям, кому от этого плохо?
Ну, пока лежат — никому. Но если в одном таком куске кода баг — это лишь вопрос времени, когда он себя проявит в другим таком же куске. А чинить-то его везде надо.
Ну и на фронтенде банально раздувается размер бандла. Тоже такое себе.
Вообще, клонированный код это показатель того, что где-то у вас плохо с абстракциями, одна и та же логика применяется к, казалось бы, разным сущностям и так далее. Само по себе, это не так плохо, не просто же так в оппозиции к DRY (do not repeat yourself) существует WET (write everything twice).
Но иметь инструмент для поиска совсем уж лишнего — весьма неплохо.
А для тех, кому надо докопаться до сути, имеется целое исследование по поиску клонов кода, на 115 страниц: https://research.cs.queensu.ca/TechReports/Reports/2007-541.pdf
Но все, что я могу сказать — это что jscpd является реализацией детектора копий первого типа 🫠
Пользуется кто-нибудь чем-то подобным в своих проектах? Делитесь.
#js #clone #lint
Раз уж упомянули пару постов назад Кори Хауса, то давайте ещё чего-нибудь по его рекомендации.
И сегодня это инструмент для поиска дубликатов кода: jscpd.
Сразу ссылка на GitHub: https://github.com/kucherenko/jscpd
Как получаются дубликаты, клоны кода? Да от плохих абстракций. Сами по себе они ничем не плохи, ну есть у вас повторы там-сям, кому от этого плохо?
Ну, пока лежат — никому. Но если в одном таком куске кода баг — это лишь вопрос времени, когда он себя проявит в другим таком же куске. А чинить-то его везде надо.
Ну и на фронтенде банально раздувается размер бандла. Тоже такое себе.
Вообще, клонированный код это показатель того, что где-то у вас плохо с абстракциями, одна и та же логика применяется к, казалось бы, разным сущностям и так далее. Само по себе, это не так плохо, не просто же так в оппозиции к DRY (do not repeat yourself) существует WET (write everything twice).
Но иметь инструмент для поиска совсем уж лишнего — весьма неплохо.
А для тех, кому надо докопаться до сути, имеется целое исследование по поиску клонов кода, на 115 страниц: https://research.cs.queensu.ca/TechReports/Reports/2007-541.pdf
Но все, что я могу сказать — это что jscpd является реализацией детектора копий первого типа 🫠
Пользуется кто-нибудь чем-то подобным в своих проектах? Делитесь.
#js #clone #lint
👍14
#фишка дня
Даже две, но об одном и том же: как сделать "альбомный" бэкдроп у картинки.
Что такое бэкдроп? Это фон, который полностью зависит от контента, например, заливается усредненным цветом. Вы наверняка видели похожие эффекты в разных аудиоплеерах, отсюда и название — альбомный. Да и на ютубе раньше было модно заполнять края у вертикальных видео тем же размытым видео.
Итак, два варианта.
1. От Артура Бьена. Надо предупредить, он немного упоротый:
а) заполняем фон контейнера однопиксельными копиями картинки:
б) накладываем сверху псевдоэлемент с небольшим тюном цвета:
в) если что-то надо наложить сверху, стоит использовать режим смешивания overlay, чтобы не вляпаться в идентичный цвет.
Пример: https://codepen.io/alinaki/pen/MWxrjZV
2. Всё бы хорошо, но эти бесконечные копии изображения... ох. Поэтому, есть достаточно интересный вариант от Темани Афифа aka CSS Challenges.
Будем использовать border-image. Отгрызаем полоски изображения по границе и повторяем их:
Полученный эффект лучше всего смотрится на небольшого размера блоках: https://codepen.io/alinaki/pen/LYaeRvw
По-моему, стоит поиграть с настройками размытия ещё чуток, но эффект точно интереснее.
Если вы не знакомы с border-image, вот свежая статья от автора эффекта, собственно: https://www.smashingmagazine.com/2024/01/css-border-image-property/
Ну и, конечно, можно просто размыть картинку саму за собой. Но это было бы слишком скучно. Во втором варианте даже присутствует эффект Philips Ambilight.
#css #trick #backdrop #blur
Даже две, но об одном и том же: как сделать "альбомный" бэкдроп у картинки.
Что такое бэкдроп? Это фон, который полностью зависит от контента, например, заливается усредненным цветом. Вы наверняка видели похожие эффекты в разных аудиоплеерах, отсюда и название — альбомный. Да и на ютубе раньше было модно заполнять края у вертикальных видео тем же размытым видео.
Итак, два варианта.
1. От Артура Бьена. Надо предупредить, он немного упоротый:
а) заполняем фон контейнера однопиксельными копиями картинки:
background-image: url(var(--bg));
background-size: 1px 1px;
background-repeat: repeat;
б) накладываем сверху псевдоэлемент с небольшим тюном цвета:
div::after {
--backdrop: invert(0.2) contrast(0.8) saturate(1.7) blur(8px);
backdrop-filter: var(--backdrop);
}
в) если что-то надо наложить сверху, стоит использовать режим смешивания overlay, чтобы не вляпаться в идентичный цвет.
mix-blend-mode: overlay;
Пример: https://codepen.io/alinaki/pen/MWxrjZV
2. Всё бы хорошо, но эти бесконечные копии изображения... ох. Поэтому, есть достаточно интересный вариант от Темани Афифа aka CSS Challenges.
Будем использовать border-image. Отгрызаем полоски изображения по границе и повторяем их:
div: before {
content:"";
border- image:
var (--img) 2/
calc(50% - var (--w)/2 + var (--b)) /
calc (1.5*var(--b));
filter: contrast(.8) blur(var(--b));
}
Полученный эффект лучше всего смотрится на небольшого размера блоках: https://codepen.io/alinaki/pen/LYaeRvw
По-моему, стоит поиграть с настройками размытия ещё чуток, но эффект точно интереснее.
Если вы не знакомы с border-image, вот свежая статья от автора эффекта, собственно: https://www.smashingmagazine.com/2024/01/css-border-image-property/
Ну и, конечно, можно просто размыть картинку саму за собой. Но это было бы слишком скучно. Во втором варианте даже присутствует эффект Philips Ambilight.
#css #trick #backdrop #blur
❤25👍2🤩2
This media is not supported in your browser
VIEW IN TELEGRAM
#статья дня
На связи опять стилизация скроллбаров. Мы только недавно обсудили, как избежать некоторых связанных с этим багов в Safari, как подоспел Chrome 121 с интересным обновлением.
И что он нам принёс? Вы не поверите, поддержу стандартных правил scrollbar-width и scrollbar-color.
В Firefox они с 2018 года, а Chrome всё это время использовал правила от WebKit. Впрочем, все пользовались и было пофигу.
Пример:
Естественно, по этому поводу блог разработчиков Google Chrome разродился огромной статьёй, в которой прошёлся по типам скроллбаров (оверлей и классический), по анатомии скроллбара (трек, движок/лифт, кнопки) и по особенностям совместимости со старыми правилами с префиксом -webkit (Safari всё там же, да).
Ссылка на статью: https://developer.chrome.com/docs/css-ui/scrollbar-styling
Мне стандарт не нравится, он весьма ограничен по своим возможностям и результат получается так себе. Надеюсь, это временно и релиз означает, что дело сдвинулось с мёртвой точки.
#css #scrollbar
На связи опять стилизация скроллбаров. Мы только недавно обсудили, как избежать некоторых связанных с этим багов в Safari, как подоспел Chrome 121 с интересным обновлением.
И что он нам принёс? Вы не поверите, поддержу стандартных правил scrollbar-width и scrollbar-color.
В Firefox они с 2018 года, а Chrome всё это время использовал правила от WebKit. Впрочем, все пользовались и было пофигу.
Пример:
.scroller {
--scrollbar-color-thumb: hotpink;
--scrollbar-color-track: blue;
--scrollbar-width: thin;
--scrollbar-width-legacy: 10px;
}
/* Modern browsers with `scrollbar-*` support */
@supports (scrollbar-width: auto) {
.scroller {
scrollbar-color: var(--scrollbar-color-thumb) var(--scrollbar-color-track);
scrollbar-width: var(--scrollbar-width);
}
}
Естественно, по этому поводу блог разработчиков Google Chrome разродился огромной статьёй, в которой прошёлся по типам скроллбаров (оверлей и классический), по анатомии скроллбара (трек, движок/лифт, кнопки) и по особенностям совместимости со старыми правилами с префиксом -webkit (Safari всё там же, да).
Ссылка на статью: https://developer.chrome.com/docs/css-ui/scrollbar-styling
Мне стандарт не нравится, он весьма ограничен по своим возможностям и результат получается так себе. Надеюсь, это временно и релиз означает, что дело сдвинулось с мёртвой точки.
#css #scrollbar
👍11
This media is not supported in your browser
VIEW IN TELEGRAM
#фишка дня
Стопудово вы делали эффекты как на видео через три div-а или span-а. Ну просто потому что трансформации на SVG это абсолютная боль.
Типа такого: https://codepen.io/alinaki/pen/abXpvyQ
Да, пример очень простой, но даже это на SVG бывает проблемно санимировать.
Хотя, казалось бы, для этого и предназначено.
А вся проблема в том, что для SVG определение координат для преобразований происходит немного иначе, нежели чем для элементов. Выходов из ситуации есть несколько.
Первый, от Аны Тюдор: исправить viewBox, поставив вместо 0, 0 (левый верхний угол) — -width/2,-height/2, соответственно, исправив остальные координаты.
Второй, интереснее, от Джея: указать следующие правила в CSS:
Правило transform-box исправит положение координатной сетки, от которой мы уже сменим дефолтную точку отсчёта для преобразований — transform-origin.
По-умолчанию, кстати, transform-box установлен как view-box. То есть, в нашем примере, заполняет лишь 24 пикселя по каждой стороне🤡
А вот и, собственно, пример:
https://codepen.io/alinaki/pen/YzBNyEz
Не бойтесь анимировать SVG, котаны. Просто не полагайтесь на дефолт.
#css #transition #svg #бородач
Стопудово вы делали эффекты как на видео через три div-а или span-а. Ну просто потому что трансформации на SVG это абсолютная боль.
Типа такого: https://codepen.io/alinaki/pen/abXpvyQ
Да, пример очень простой, но даже это на SVG бывает проблемно санимировать.
Хотя, казалось бы, для этого и предназначено.
А вся проблема в том, что для SVG определение координат для преобразований происходит немного иначе, нежели чем для элементов. Выходов из ситуации есть несколько.
Первый, от Аны Тюдор: исправить viewBox, поставив вместо 0, 0 (левый верхний угол) — -width/2,-height/2, соответственно, исправив остальные координаты.
Второй, интереснее, от Джея: указать следующие правила в CSS:
transform-box: fill-box;
transform-origin: 50% 50%;
Правило transform-box исправит положение координатной сетки, от которой мы уже сменим дефолтную точку отсчёта для преобразований — transform-origin.
По-умолчанию, кстати, transform-box установлен как view-box. То есть, в нашем примере, заполняет лишь 24 пикселя по каждой стороне
А вот и, собственно, пример:
https://codepen.io/alinaki/pen/YzBNyEz
Не бойтесь анимировать SVG, котаны. Просто не полагайтесь на дефолт.
#css #transition #svg #бородач
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18
#новость дня
Итак, мы писали Джаваскрипт в браузере, в консоли, на серверах, на микроконтроллерах, в играх, в кофеварках, в аудиоплеерах, телевизорах, в макросах офисных пакетов, в NoSQL базах данных...
Но до сих пор не писали его в хранимых процедурах MySQL!
Итак, встречайте: JavaScript Stored Programs in MySQL. И соответствующий пост в блоге Oracle: https://blogs.oracle.com/mysql/post/introducing-javascript-support-in-mysql
Зачем? Ну для разных целей:
1. Извлечение данных, очевидно
2. Форматирование
3. Примерный поиск
4. Валидация данных
5. Собственные алгоритмы сжатия, сериализации и десериализации данных
Пример:
Работает всё, ожидаемо, на GraalVM. Почему ожидаемо? Ну потому что Oracle и Java/JVM :)
Что, котаны, пишем стартап на MySQL? 😬
#mysql #sql #js
Итак, мы писали Джаваскрипт в браузере, в консоли, на серверах, на микроконтроллерах, в играх, в кофеварках, в аудиоплеерах, телевизорах, в макросах офисных пакетов, в NoSQL базах данных...
Но до сих пор не писали его в хранимых процедурах MySQL!
Итак, встречайте: JavaScript Stored Programs in MySQL. И соответствующий пост в блоге Oracle: https://blogs.oracle.com/mysql/post/introducing-javascript-support-in-mysql
Зачем? Ну для разных целей:
1. Извлечение данных, очевидно
2. Форматирование
3. Примерный поиск
4. Валидация данных
5. Собственные алгоритмы сжатия, сериализации и десериализации данных
Пример:
CREATE FUNCTION gcd_js (a INT, b INT) RETURNS INT
LANGUAGE JAVASCRIPT AS $$
let [x, y] = [Math.abs(a), Math.abs(b)];
while(y) [x, y] = [y, x % y];
return x;
$$;
Работает всё, ожидаемо, на GraalVM. Почему ожидаемо? Ну потому что Oracle и Java/JVM :)
Что, котаны, пишем стартап на MySQL? 😬
#mysql #sql #js
🤡10🤩4👍1
#заметка дня
Есть такой элемент —
Пишете в него некий HTML/SVG код, потом клонируете и вставляете куда вам надо.
На этом месте апологеты фреймворков ухмыльнулись, но вы же не одни на свете, да?
Важно понимать, что содержимое шаблона — это фрагмент (обёртка над реальными нодами, если грубо), поэтому обращаться с ним надо соответственно.
Почему для этой цели не использовать просто скрытый
Ну второе вообще не в кассу, это просто абьюз. А первое:
- Контент внутри темплейта не будет загружаться, пока не склонирован явно: ни картинки, ни стили, ни скрипты
- Валидатор игнорирует содержимое, хоть
- Поисковые системы знают про
- Ну и именование говорит само за себя. Сахар все любят.
#html #template #бородач
Есть такой элемент —
template
. Название говорит само за себя: это шаблон. Пишете в него некий HTML/SVG код, потом клонируете и вставляете куда вам надо.
На этом месте апологеты фреймворков ухмыльнулись, но вы же не одни на свете, да?
const content = template.content. firstElementChild.cloneNode(true);
Важно понимать, что содержимое шаблона — это фрагмент (обёртка над реальными нодами, если грубо), поэтому обращаться с ним надо соответственно.
Почему для этой цели не использовать просто скрытый
div
или — привет из прошлого — script type="text/template"
?Ну второе вообще не в кассу, это просто абьюз. А первое:
- Контент внутри темплейта не будет загружаться, пока не склонирован явно: ни картинки, ни стили, ни скрипты
- Валидатор игнорирует содержимое, хоть
dd
храните там, хоть td
- Поисковые системы знают про
template
и точно не индексируют. А с div hidden
всё не так однозначно.- Ну и именование говорит само за себя. Сахар все любят.
#html #template #бородач
👍11❤3
#тип дня
Кричащий банан 🍌 (да-да) принёс шикарную фишку TypeScript: как типизировать ключ объекта без каста.
Решение: заранее объявите тип переменной перебора как
Пруф на TS Playground.
#typescript #cast #бородач
Кричащий банан 🍌 (да-да) принёс шикарную фишку TypeScript: как типизировать ключ объекта без каста.
Решение: заранее объявите тип переменной перебора как
keyof typeof
. Всё, вы великолепны.Пруф на TS Playground.
#typescript #cast #бородач
👍22❤4
#число дня
В дружественном чате прошло очередное ежеквартальное обсуждение разрешений макетов и экранов.
Я не буду пока репостить свои статьи на тему, лишь задам вопрос.
Кто догадается, что такое 1536x864 на этой годовой статистике разрешений экранов мониторов?
Между прочим, 11% дисплеев.
https://gs.statcounter.com/screen-resolution-stats/desktop/worldwide
При встрече угощу кофе того, кто догадается первым.
Ну или кого вообще встречу.
#css #resolution
В дружественном чате прошло очередное ежеквартальное обсуждение разрешений макетов и экранов.
Я не буду пока репостить свои статьи на тему, лишь задам вопрос.
Кто догадается, что такое 1536x864 на этой годовой статистике разрешений экранов мониторов?
Между прочим, 11% дисплеев.
https://gs.statcounter.com/screen-resolution-stats/desktop/worldwide
При встрече угощу кофе того, кто догадается первым.
Ну или кого вообще встречу.
#css #resolution
👍6
#статья дня
Злые Марсиане выпускают крутейшие статьи с завидной регулярностью. Вот очередная: https://evilmartians.com/chronicles/html-best-practices-for-login-and-signup-forms
На сей раз — про ошибки при разработке форм. И их, согласно статье, 11. Давайте кратко пробежимся.
1. Ставь autocomplete в нужное положение (username, current-password etc.).
Мы тут, кстати, обсуждали возможные значения и даже проблемы.
2. type="email", тут всё ясно.
3. Интерактивные элементы должны быть кнопками или ссылками, а не дивами (и ведь находятся же).
4. Не забывай про существование тега form (и такие тоже существуют).
5. Не используй placeholder как label.
6. Помни о label, особенно для галочек.
7. Состояние :focus должно быть визуально подтверждено.
8. Маркируй невидимые поля для скринридеров.
9. Не прерывай ввод внезапной валидацией (бесит).
10. Исключи случайную повторную отправку формы.
11. Помни о сетевых задержках.
В статье полно примеров и объяснений. Проверяйте свои формы, котаны.
#form #ux #бородач
Злые Марсиане выпускают крутейшие статьи с завидной регулярностью. Вот очередная: https://evilmartians.com/chronicles/html-best-practices-for-login-and-signup-forms
На сей раз — про ошибки при разработке форм. И их, согласно статье, 11. Давайте кратко пробежимся.
1. Ставь autocomplete в нужное положение (username, current-password etc.).
Мы тут, кстати, обсуждали возможные значения и даже проблемы.
2. type="email", тут всё ясно.
3. Интерактивные элементы должны быть кнопками или ссылками, а не дивами (и ведь находятся же).
4. Не забывай про существование тега form (и такие тоже существуют).
5. Не используй placeholder как label.
6. Помни о label, особенно для галочек.
7. Состояние :focus должно быть визуально подтверждено.
8. Маркируй невидимые поля для скринридеров.
9. Не прерывай ввод внезапной валидацией (бесит).
10. Исключи случайную повторную отправку формы.
11. Помни о сетевых задержках.
В статье полно примеров и объяснений. Проверяйте свои формы, котаны.
#form #ux #бородач
❤20
This media is not supported in your browser
VIEW IN TELEGRAM
#заметка дня
А как вы, котаны, боретесь с багами?
Даже не так. Как боретесь — понятно, пишете код. Но каков процесс?
Мы вот у себя пытаемся играть в Zero Bug Policy. Это не значит, что в продукте нет багов и никогда их больше не будет. Это значит, что ни один баг не должен оставаться незамеченным или проигнорированным. По каждому обязательно принимается решение.
1. Баг в продакшене:
Создаётся user story (здесь и далее сторя). Отправляемся чинить или писать статью о том, почему это не баг.
2. Баг найден в процессе функционального тестирования
Должно быть принято решение:
a) Если он мешает процессу и блокирует релиз — немедленно создаётся сторя и баг уходит в работу
б) Если процессу работы не мешает и релиз уйдёт как есть — обновляем описание основной стори. Ни стори ни подзадачи не создаётся.
3. Примерно то же самое происходит если баг найден в процессе регресс-тестирования:
Либо создаётся задача и чинится, либо не создаётся ничего, если мы удовлетворены обходными путями или наличием технической документации по этому поводу.
Всплытие багов в регресс-тестировании означает, что имеются большие пробелы функциональном тестировании или даже планировании.
Мы раз в две недели проводим Bug Busting встречу на которой баги распределяются по категориям или закрываются как неактуальные. Наличие багов в бэклоге означает, что процесс сломан или баги не столь важны.
Да, может показаться, что мы просто заметаем проблемы под ковёр — но это не так.
Во-первых, наличие багов в бэклоге портит планирование, что в свою очередь портит настроение разработчикам.
Во-вторых, мы можем быть уверен, что баг от саппорта как минимум прочитал кто-то кроме, собственно, человека его создавшего.
В-третьих, ничто не вечно под луной. Сегодня ты тратишь три дня на баг, влияющий на мелкого пользователя, а завтра вся фича переписывается или выкидывается.
А у вас чо как?
#dev #process #bugs
А как вы, котаны, боретесь с багами?
Даже не так. Как боретесь — понятно, пишете код. Но каков процесс?
Мы вот у себя пытаемся играть в Zero Bug Policy. Это не значит, что в продукте нет багов и никогда их больше не будет. Это значит, что ни один баг не должен оставаться незамеченным или проигнорированным. По каждому обязательно принимается решение.
1. Баг в продакшене:
Создаётся user story (здесь и далее сторя). Отправляемся чинить или писать статью о том, почему это не баг.
2. Баг найден в процессе функционального тестирования
Должно быть принято решение:
a) Если он мешает процессу и блокирует релиз — немедленно создаётся сторя и баг уходит в работу
б) Если процессу работы не мешает и релиз уйдёт как есть — обновляем описание основной стори. Ни стори ни подзадачи не создаётся.
3. Примерно то же самое происходит если баг найден в процессе регресс-тестирования:
Либо создаётся задача и чинится, либо не создаётся ничего, если мы удовлетворены обходными путями или наличием технической документации по этому поводу.
Всплытие багов в регресс-тестировании означает, что имеются большие пробелы функциональном тестировании или даже планировании.
Мы раз в две недели проводим Bug Busting встречу на которой баги распределяются по категориям или закрываются как неактуальные. Наличие багов в бэклоге означает, что процесс сломан или баги не столь важны.
Да, может показаться, что мы просто заметаем проблемы под ковёр — но это не так.
Во-первых, наличие багов в бэклоге портит планирование, что в свою очередь портит настроение разработчикам.
Во-вторых, мы можем быть уверен, что баг от саппорта как минимум прочитал кто-то кроме, собственно, человека его создавшего.
В-третьих, ничто не вечно под луной. Сегодня ты тратишь три дня на баг, влияющий на мелкого пользователя, а завтра вся фича переписывается или выкидывается.
А у вас чо как?
#dev #process #bugs
👍8🤩3
#такое дня
Ну что, котаны. Это случилось. Figma стала просить денег за DevMode.
Если вы жили последние месяцев 8 под камнем, то Figma перенесла специфичные для разработчиков фишечки в отдельный режим. Теперь режимы редактирования, просмотра и экспорта в код разнесены практически насовсем.
Что такое DevMode и какие преимущества он давал? Ну и даёт, для тех, кто платит.
1. Автоматическое отображение отступов и применённого лейаута прямо на холсте
2. Инспекция боксовой модели контейнеров
3. Автогенерация кода (CSS, iOS Swift/SwiftUI, Android Java/Kotlin и через плагины ещё с десяток платформ, включая Flutter)
4. Автоматическая генерация дизайн-токенов для выбранной платформы.
5. Сравнение версий дизайна
6. Именование слоёв
7. Более удобный экспорт ассетов и контента.
Что меня бесит больше всего, что Figma буквально пробила свой путь в наши сердца отображая CSS и отступы на холсте по-умолчанию. А теперь сделала такую себе кодогенерацию и хочет денег. Если ты учишься, это малость неудобно. Если зарабатываешь на фигме деньги — ну окей, возможно, стоит и заплатить.
Впрочем, для базовой работы DevMode может оказаться и не нужен.
1. Отступы на холсте всё так же отображаются по зажатой клавише Option/Alt.
2. Настройки эффектов никто не скрывал, они легко считываются.
3. Для сложных случаев, как минимум, в вебе, всегда можно установить расширение. Например, Inspect Styles (на скрине). Оно вернёт на место как CSS-переменные (из токенов), так и все нужные правила.
Напоминаю, что бездумно копировать отступы и координаты из генератора кода в Figma не надо. Вёрстка не про это. А вот настройки шрифта, фона и эффектов — да, очень уютно.
Делитесь вашими любимыми плагинами в комментариях, котаны!
А вообще, не забывайте о Pixso и PenPot! PenPot ещё и опенсорсный, можно у себя развернуть.
#figma #design #greed #money
Ну что, котаны. Это случилось. Figma стала просить денег за DevMode.
Если вы жили последние месяцев 8 под камнем, то Figma перенесла специфичные для разработчиков фишечки в отдельный режим. Теперь режимы редактирования, просмотра и экспорта в код разнесены практически насовсем.
Что такое DevMode и какие преимущества он давал? Ну и даёт, для тех, кто платит.
1. Автоматическое отображение отступов и применённого лейаута прямо на холсте
2. Инспекция боксовой модели контейнеров
3. Автогенерация кода (CSS, iOS Swift/SwiftUI, Android Java/Kotlin и через плагины ещё с десяток платформ, включая Flutter)
4. Автоматическая генерация дизайн-токенов для выбранной платформы.
5. Сравнение версий дизайна
6. Именование слоёв
7. Более удобный экспорт ассетов и контента.
Что меня бесит больше всего, что Figma буквально пробила свой путь в наши сердца отображая CSS и отступы на холсте по-умолчанию. А теперь сделала такую себе кодогенерацию и хочет денег. Если ты учишься, это малость неудобно. Если зарабатываешь на фигме деньги — ну окей, возможно, стоит и заплатить.
Впрочем, для базовой работы DevMode может оказаться и не нужен.
1. Отступы на холсте всё так же отображаются по зажатой клавише Option/Alt.
2. Настройки эффектов никто не скрывал, они легко считываются.
3. Для сложных случаев, как минимум, в вебе, всегда можно установить расширение. Например, Inspect Styles (на скрине). Оно вернёт на место как CSS-переменные (из токенов), так и все нужные правила.
Напоминаю, что бездумно копировать отступы и координаты из генератора кода в Figma не надо. Вёрстка не про это. А вот настройки шрифта, фона и эффектов — да, очень уютно.
Делитесь вашими любимыми плагинами в комментариях, котаны!
А вообще, не забывайте о Pixso и PenPot! PenPot ещё и опенсорсный, можно у себя развернуть.
#figma #design #greed #money
👍20❤6🤩1
#такое дня
Давайте немного о жизни офисных крыс. Пятница, вечер. Почему бы и нет.
Как правило, я хожу ежедневно в офис, потому что мне не нравится работать из дома.
Точнее, не так. Я работал из дома 10 лет, надоело.
Но суть не в этом. В офисе, конечно же, есть кофемашины. Два автомата, наливающих фильтр-кофе, и две капсульные, Nespresso Vertuoline.
Офис-менеджер регулярно закупает новые капсулы, от эспрессо на 40 мл, до американо на 230 и тыквенного мексиканского на 350. Я предпочитаю делать капучино на эспрессо по утрам, но многие не парятся и просто берут капсулы на объём побольше.
Но что-то пошло не так и заказаны были только эспрессо и двойные эспрессо (40 и 80 мл). Любители американо и всякой отравы взвыли. Никто не хотел греть чайник, чтобы разбавить 80 до 230.
Надо было что-то делать, помимо срача в Slack.
Объём кофе абсолютно одинаковый, что в капсуле для эспрессо на 40, что на 230 мл. Отличаются лишь давление и объём воды.
При всём при этом, если пропустить одну капсулу дважды — получается ерунда.
Так вот, в кофемашинах Nespresso давление (точнее, капсула быстро раскручивается, чтобы это давление создать) и объём воды кодируются штрих-кодом на крышке капсулы.
Дело за малым — взломать штрих-код и подменить его на капсуле.
Взламывать, конечно, никто ничего не собирался.
Ведь есть Reddit, на котором была выложена статья с описанием структуры штрих-кода:
Где ширина чёрной (1) и серебряной (0) полоски кодируется повтором единиц и нулей. Потом делится на пять групп, а каждая группа имеет секцию с битами чётности, 01.
Естественно, там же на реддите нашли и скрипт: https://github.com/arandomdev/NespressoBarcodes
Коллега, которому очень хотелось американо, не сдержался, запустил скрипт для кофе нужного объёма, распечатал и вырезал результат.
И да, всё получилось! На иллюстрации. Жалко только, что самим скрипт писать не пришлось :)
#office #life
Давайте немного о жизни офисных крыс. Пятница, вечер. Почему бы и нет.
Как правило, я хожу ежедневно в офис, потому что мне не нравится работать из дома.
Точнее, не так. Я работал из дома 10 лет, надоело.
Но суть не в этом. В офисе, конечно же, есть кофемашины. Два автомата, наливающих фильтр-кофе, и две капсульные, Nespresso Vertuoline.
Офис-менеджер регулярно закупает новые капсулы, от эспрессо на 40 мл, до американо на 230 и тыквенного мексиканского на 350. Я предпочитаю делать капучино на эспрессо по утрам, но многие не парятся и просто берут капсулы на объём побольше.
Но что-то пошло не так и заказаны были только эспрессо и двойные эспрессо (40 и 80 мл). Любители американо и всякой отравы взвыли. Никто не хотел греть чайник, чтобы разбавить 80 до 230.
Надо было что-то делать, помимо срача в Slack.
Объём кофе абсолютно одинаковый, что в капсуле для эспрессо на 40, что на 230 мл. Отличаются лишь давление и объём воды.
При всём при этом, если пропустить одну капсулу дважды — получается ерунда.
Так вот, в кофемашинах Nespresso давление (точнее, капсула быстро раскручивается, чтобы это давление создать) и объём воды кодируются штрих-кодом на крышке капсулы.
Дело за малым — взломать штрих-код и подменить его на капсуле.
Взламывать, конечно, никто ничего не собирался.
Ведь есть Reddit, на котором была выложена статья с описанием структуры штрих-кода:
01111010010001001000010110100111100101000100100001011010011110010100100010001001101010111010010010001000010110101011100101000100100010011010
Где ширина чёрной (1) и серебряной (0) полоски кодируется повтором единиц и нулей. Потом делится на пять групп, а каждая группа имеет секцию с битами чётности, 01.
Естественно, там же на реддите нашли и скрипт: https://github.com/arandomdev/NespressoBarcodes
Коллега, которому очень хотелось американо, не сдержался, запустил скрипт для кофе нужного объёма, распечатал и вырезал результат.
И да, всё получилось! На иллюстрации. Жалко только, что самим скрипт писать не пришлось :)
#office #life
👍29❤3🤩2
#инструмент дня
И снова на связи Глеб, автор extended-fetch. Слово автору.
Недавно потребовалось проанализировать зависимости в проекте (была проблема с транзитивными) и искал удобный инструмент для визуализации дерева зависимостей.
В итоге нашел https://npmgraph.js.org/
Что дает инструмент:
- анализ пакета из репозитория npm
- анализ package.json
- отображение интерактивного графа зависимостей (самое вкусное)
- выделение цветом заивисимостей по степени актуальности / по типу модуля (esm/cjs) / по числу мэйнтейнеров
- для выбранного модуля доступен детальный отчет по клику (на скриншоте пример)
Всем деревьев зависимостей, котаны!
Напоминаю, что кто угодно может выкатить пост на канал, если есть чем поделиться. Не стесняйтесь!
#node #dependencies
И снова на связи Глеб, автор extended-fetch. Слово автору.
Недавно потребовалось проанализировать зависимости в проекте (была проблема с транзитивными) и искал удобный инструмент для визуализации дерева зависимостей.
В итоге нашел https://npmgraph.js.org/
Что дает инструмент:
- анализ пакета из репозитория npm
- анализ package.json
- отображение интерактивного графа зависимостей (самое вкусное)
- выделение цветом заивисимостей по степени актуальности / по типу модуля (esm/cjs) / по числу мэйнтейнеров
- для выбранного модуля доступен детальный отчет по клику (на скриншоте пример)
Всем деревьев зависимостей, котаны!
Напоминаю, что кто угодно может выкатить пост на канал, если есть чем поделиться. Не стесняйтесь!
#node #dependencies
❤16👍8
#статья дня
Надо было эту статью на фишки раздербанить, ну да ладно. Итак, задача: сделать сетку, элементы которой должны иметь четкое и, возможно, изменяемое соотношение сторон.
Передавать соотношение сторон можно через классы, конечно, но мы в 2024 году, так что будем использовать CSS-переменные (`--ratio: 4/3`). Дальше имеются два варианта:
1. Задать соотношение сторон всей строке через, удивительное рядом, складывание дробей: 4/3 + 2/3 = 6/3. Сразу можно понять, что минусов у решения достаточно.
2. flex-grow. Легко и просто.
Подробнее о решениях и ходе мысли в статье:
https://9elements.com/blog/building-a-combined-css-aspect-ratio-grid/
Флексы рулят, котаны :)
#css #grid #flex #бородач
Надо было эту статью на фишки раздербанить, ну да ладно. Итак, задача: сделать сетку, элементы которой должны иметь четкое и, возможно, изменяемое соотношение сторон.
Передавать соотношение сторон можно через классы, конечно, но мы в 2024 году, так что будем использовать CSS-переменные (`--ratio: 4/3`). Дальше имеются два варианта:
1. Задать соотношение сторон всей строке через, удивительное рядом, складывание дробей: 4/3 + 2/3 = 6/3. Сразу можно понять, что минусов у решения достаточно.
2. flex-grow. Легко и просто.
Подробнее о решениях и ходе мысли в статье:
https://9elements.com/blog/building-a-combined-css-aspect-ratio-grid/
Флексы рулят, котаны :)
#css #grid #flex #бородач
👍16
#фишка дня
Как подсказать пользователю мобильного устройства, что сейчас произойдёт при отправке формы?
Места на экране мало, ещё и клавиатура съест.
Очень просто! Использовать атрибут enterkeyhint!
Спека: https://html.spec.whatwg.org/multipage/interaction.html#input-modalities:-the-enterkeyhint-attribute
Поддержка: https://caniuse.com/?search=enterkeyhint
Пользуемся!
#mobile #form #html #бородач
Как подсказать пользователю мобильного устройства, что сейчас произойдёт при отправке формы?
Места на экране мало, ещё и клавиатура съест.
Очень просто! Использовать атрибут enterkeyhint!
Спека: https://html.spec.whatwg.org/multipage/interaction.html#input-modalities:-the-enterkeyhint-attribute
Поддержка: https://caniuse.com/?search=enterkeyhint
Пользуемся!
#mobile #form #html #бородач
👍30❤1
This media is not supported in your browser
VIEW IN TELEGRAM
#статья дня
Что делать, если использовать вариативный шрифт очень хочется, но он тяжёлый как мамка читера?
Например, вариативный Roboto весит 785 Кбайт. Да я в 785 Кбайт умещу код стартапа на миллион. Нужна же лишь часть начертаний и глифов, не все!
Стефан Юдис смог уменьшить этот размер до 58 Кбайт используя два инструмента:
Glyphhanger: https://www.stefanjudis.com/notes/glyphhanger-a-tool-subset-and-optimize-fonts/
И Slice: https://www.stefanjudis.com/notes/slice-an-app-to-remove-variable-font-axes/
Первый помогает оставить лишь нужные символы (глифы), а второй — убрать/ограничить оси вариативности.
Прекрасные инструменты, целый новый мир открывают.
#fonts #tools #бородач
Что делать, если использовать вариативный шрифт очень хочется, но он тяжёлый как мамка читера?
Например, вариативный Roboto весит 785 Кбайт. Да я в 785 Кбайт умещу код стартапа на миллион. Нужна же лишь часть начертаний и глифов, не все!
Стефан Юдис смог уменьшить этот размер до 58 Кбайт используя два инструмента:
Glyphhanger: https://www.stefanjudis.com/notes/glyphhanger-a-tool-subset-and-optimize-fonts/
И Slice: https://www.stefanjudis.com/notes/slice-an-app-to-remove-variable-font-axes/
Первый помогает оставить лишь нужные символы (глифы), а второй — убрать/ограничить оси вариативности.
Прекрасные инструменты, целый новый мир открывают.
#fonts #tools #бородач
👍15❤4