Пасхалки — самовыражение или баловство? / Хабр | Веб-студия Nat.od.ua
Пасхалки — самовыражение или баловство? / Хабр
Представьте, что в софте, веб‑сервисах и фильмах внезапно пропали бы все пасхалки. Что случилось бы с миром тогда? Кажется, что ничего. Разве что он стал бы чуть‑чуть менее смешным. Или нет? Давайте поговорим о том, почему возникли пасхалки, есть ли от них польза или вред.
Немного истории
Наверное, мало кто будет спорить, что скрытые послания в ПО, музыкальных альбомах, компьютерных играх интригуют и пробуждают любопытство, поднимают настроение и вызывают желание найти ещё что‑нибудь эдакое. И ведь находят! Например, скрытый глубоко в недрах macOS PDF‑документ от создателя биткоина Сатоши Накамото.
Вот что безумнее: то, что кто‑то его там разместил или то, что кто‑то вообще смог найти этот файл? Сложный вопрос, на которой у меня нет ответа. Зато я точно знаю ответ на другой вопрос: перестанем ли мы когда‑нибудь находить пасхальные яйца в программном обеспечении? Очевидно, что нет.
Год назад мы делились с вами небольшой подборкой относительно свежих пасхалок. А первой же в истории считают скрытое посление в компьютерной игре Adventure, вышедшей в 1979 году. Однако есть расследование, которое доказывает, что первое пасхальное яйцо было заложено в текстовый редактор TECO аж в 1968 году.
Секрет был в том, что пользователи могли создать новый файл TECO, набрав «MAKE», а затем имя файла, состоящее из одного слова. Но если ввести «MAKE LOVE», компьютер радовал ответом «NO WAR» — отсылка к популярному антивоенному лозунгу того времени. Подробнее эта история описана здесь.
Зачем вообще нужны пасхалки
Зачем вообще разработчики добавляют в код недокументированные функции? Потому что могут. Потому что хотят сделать мир чуточку добрее, подарив кому‑то радость от неожиданной находки. Потому что чувствуют себя недооценёнными — мотивация разная. Третий из перечисленных вариантов, кстати, вполне обычное дело.
Например, пасхалка, созданная программистом Atari Уорреном Робинеттом в видеоигре Atari 2600 Adventure, появилась благодаря его желанию получить признание за трудную работу. Как рассказывал Уоррен, они были ботаниками, которых ни во что не ставили, но от которых требовали многого. Владельцы компании делали всё, чтобы не дать разработчикам игр хоть какое‑то влияние, поэтому что тогда им пришлось бы больше платить.
Знаменитое пасхальное яйцо Adventure отдает должное создателю игры
Уоррену не понравился такой подход. Никаких нормальных денег. Никакого признания и уважения со стороны компании. Разработчикам прямо говорили: «вашу работу может сделать любой». Возможно, отчасти по этой причине Atari потерпела крах. Почти все игроделы уволились, а те, кого наняли вместо них, просто не знали, как делать ту работу, которую делали их предшественники.
Подобное желание доказать себе и всему миру вполне понятно. Представьте, что вы вложили в проект кучу времени и сил, перерабатывали и отказывали себе в развлечениях. А когда всё сделано, вас толком не благодарят, а платят почти столько же, сколько уборщику, который всё это время убирал из вашего офиса коробки из‑под пиццы и пустые банки энергетиков.
Само собой возникает желание отдать тайную дань уважения самому себе: поместить фотографию, аудиозапись, ещё какие‑то материалы о себе. Так сказать, вырезать свои инициалы на подсыхающем бетоне.
Есть и другая мотивация создания скрытых посланий — поощрение поклонников. Например, неанонсированная песня The Beatles «Her Majesty», написанная Полом Маккартни. Она идёт через 14 секунд после последнего трека в альбоме и изначально отсутствовала на обложке. Согласитесь, приятно обнаружить такой бонус от любимого исполнителя.
Можно сказать и так: технологии стали настолько сложными, что позволяют прятать самые разные вещи у всех на виду.
Вредны ли пасхалки
Итак, с мотивацией разобрались. Хочу ещё заметить, что во многих западных компаниях очень простой подход к пасхалкам: «не спрашивать о них». Потому что клиентам может не понравиться шутка, которую они внезапно обнаружат в своём ПО.
Но давайте посмотрим на это развлечение с другой стороны.
Сразу скажу: в большинстве случаев пасхалки в ПО безвредны и просто немножко расширяют возможности приложения. Многие компании, продающие ПО, даже не знают, какой сюрприз спрятали в коде штатные разработчики.
И это поднимает вопрос с информационной безопасностью ПО, который способен принести кучу проблем в будущем. Добавленные из лучших побуждений шуточные пасхалки могут стать одним из каналов атаки.
Простой пример: PHP используется многочисленными веб‑серверами самых разных типов. соответственно, велика вероятность того, что атакуемый сайт создан на PHP. Но вы, возможно, знаете, что по умолчанию PHP поставляется с несколькими пасхалками (вот хабрастатья от 2010 года). И эти пасхалки показывают, какую версию PHP использует сайт. Соответственно, злоумышленникам становится проще подобрать эксплойт.
Вот так спрятанные недокументированные функции могут стать угрозой для конечных пользователей, даже если разработчик ничего такого не хотел. Поэтому, например, ещё в 2005 году в блоге Microsoft появилась предупреждающая запись о том, что нельзя добавлять пасхалки в код продукта (в комментариях прямо говорится, что найденная пасхалка является основанием для немедленного увольнения).
Впрочем, это не значит, что пасхалки в Windows пропали. В прошлом году стало известно, что если вы удерживаете курсор мыши на шестерёнке в Блокноте, то вы можете заставить её вращаться. Уволили ли кого-то за это? Мне неизвестно.
Заключение
Собственно, никаких выводов делать не буду. У каждого есть своё мнение на эту тему, и каждый будет прав. Поэтому предлагаю просто отдать должное такой интересной традиции и накидать в комментариях свежих и забавных пасхалок, с которыми вы сталкивались в работе.
Что ещё интересного есть в блоге Cloud4Y
→ Информационная безопасность и глупость: необычные примеры
→ NAS за шапку сухарей
→ Взлом Hyundai Tucson, часть 1, часть 2
→ Столетний язык программирования — какой он
→ 50 самых интересных клавиатур из частной коллекции
Как сделать декларативный роутинг диалогов в Angular на примере Taiga UI / Хабр | Веб-студия Nat.od.ua
Как сделать декларативный роутинг диалогов в Angular на примере Taiga UI / Хабр
Привет! Я Дима, разработчик онлайн-бухгалтерии. Предлагаю на примере простой задачи разобрать два подхода к созданию модальных окон, связанных с url: императивный и декларативный.
Часто на фронтенде нужно открывать модальные окна по определенному пути. Из коробки ангуляр не предоставляет такой возможности, так же как и популярные ui-kit-библиотеки. И разработчики каждый раз ищут способ, как это сделать.
Условие задачи
Нужно открыть по url /page/dialog диалог поверх страницы page.
Подход первый — императивный
На странице page в методе ngOnInit или конструкторе проверяем текущий url. Если там содержится путь до диалога — открываем диалог. Звучит понятно и просто, в коде выглядит так:
@Component({…})
class PageComponent implements OnInit {
constructor(
private readonly router: Router,
private readonly tuiDialogService: TuiDialogService,
) {}
ngOnInit() {
// Если это путь до диалога, то открываем его
if (this.router.url.includes(‘dialog’)) {
this.tuiDialogService.open(DialogContentComponent).subscribe();
}
}
}
В Taiga UI диалоги открываются методом open, который возвращает Observable. Кстати, про холодный и горячий Observable в rxjs рассказывал мой коллега:
Наблюдение за температурой: введение в холодные и горячие Observable в rxjs
По мере изучения RxJS разработчик рано или поздно сталкивается с такими понятиями, как cold и hot ob…
habr.com
При подписке на Observable диалог открывается, а при отписке — закрывается. Чтобы по url на диалог открывалась страница page, нужно сконфигурировать роутер:
const router: Routes =
Теперь при переходе по прямой ссылке /page/dialog откроется диалог поверх страницы. Важно учесть, что при закрытии диалога нужно менять роутинг обратно на родительскую страницу. Пример реализации:
@Component({…})
class DialogContentComponent {
constructor(private readonly location: Location) {}
goBack(): void {
this.location.back();
}
}
При закрытии диалога вызывается метод, возвращающий на предыдущую страницу. Но есть неприятный нюанс: если открыть диалог по прямой ссылке, например, с главной страницы Google, то при закрытии случится редирект обратно на Google. Эту проблему можно исправить, если знать «обратный путь» от диалога до страницы.
@Component({…})
class DialogContentComponent {
constructor(
private readonly router: Router,
private readonly route: ActivatedRoute
) {}
goBack(): void {
this.router.navigate(
В app-component находится
Диалог открывается поверх страницы.
Удобно конфигурировать.
Удобно возвращаться.
Универсально для любых диалогов.
Роутинг ангуляра в минимальном варианте требует два параметра: path и component, или модуль страницы вместо компонента. Особенность в том, что роутинг может работать только с двумя сущностями: компонентом и lazy-loading-модулем, который все равно рендерит компонент. Получается, что роутеру в любом случае нужно подавать компонент.
Диалоги в TaigaUI открываются через вызов метода у сервиса диалогов, а значит, нужен такой компонент-обертка, который будет внутри себя открывать диалог.
С компонентом-оберткой понятно, а как задавать path для диалога? Под path можно использовать путь относительно родителя — path: ‘path/to/dialog’.
Еще один важный момент — открытие диалога поверх страницы, а не вместо нее.
Добиться этого можно в два шага:
Добавить на страницу
, чтобы появилась возможность рендерить контент поверх страницы:
// page.component.html
Разместить конфигурацию роута на диалог в children-свойстве конфига страницы. В противном случае диалог будет рендериться в
на уровне выше вместо текущей страницы:
// app-routing.module.ts
const routes: Routes =
}
]
Теперь реализуем компонент-обертку, открывающую переданный компонент диалога:
@Component({…})
class RoutableDialogComponent {
constructor(
tuiDialogService: TuiDialogService,
route: ActivatedRoute
) {
tuiDialogService.open(
new PolymorpheusComponent(
route.snapshot.data
)
).subscribe();
}
}
Вуаля! Можно открывать любой диалог по любому пути на любой странице. Но осталась проблема с закрытием диалога и возвращением на родительский url.
В TaigaUI диалоги открываются подпиской на метод открытия диалогов. При закрытии диалога подписка завершается. Используем этот факт для возвращения:
@Component({…})
class RoutableDialogComponent {
constructor(…) {
tuiDialogService
.open(…)
.subscribe({
// при завершении потока возвращаемся
complete: () => this.navigateToParent(),
});
}
private navigateToParent(): void {
…
}
}
Чтобы отвязать компонент-обертку от конкретных путей, вынесем параметр backUrl в конфигурацию роутера:
// routable-dialog.component.ts
private navigateToParent(): void {
this.router.navigate(], {
relativeTo: this.route
});
}
// app-routing.module.ts
const routes: Routes =
}
]
Было бы здорово не писать backUrl руками, а вычислять из пути. Для этого можно сделать хелпер-генератор роута:
// хелпер принимает только два нужных параметра — компонент и путь
function tuiGenerateDialogableRoute(component, path): Route {
return {
path,
component: RoutableDialogComponent,
data: {
dialog: component,
// вычисляет обратный путь заменой сегментов пути на две точки
backUrl: path.split(‘/’).map(() => ‘..’).join(‘/’)
}
}
}
Хелпер помогает в двух моментах:
Скрывает детали реализации backUrl.
Позволяет правильно формировать роут, не давая ошибиться программисту.
В результате итоговый конфиг роутера для страницы с диалогом будет таким:
const routes: Routes =
}
]Что получилось
Теперь на любой странице можно отобразить любой диалог по любому пути. При этом конфигурация будет в привычном декларативном стиле с минимальным количеством кода.
О возвращении можно не задумываться: все скрыто за деталями реализации. И такое решение можно переиспользовать по всему проекту.
Это решение мы сделали в онлайн-бухгалтерии и перенесли в UI Kit — Taiga UI. Посмотреть документацию о routable dialog можно в описании пользовательского интерфейса. Там же есть и вариант для lazy-loading-диалогов.
Если есть вопросы — жду в комментариях, давайте обсудим!
Что произошло в мире Python за последний месяц / Хабр | Веб-студия Nat.od.ua
Что произошло в мире Python за последний месяц / Хабр
Раз в месяц мы в Moscow Python Podcast собираемся и обсуждаем свежие релизы, заинтересовавшие нас инструменты и статьи. В апреле поговорили об исследовании экосистемы Django от JetBrains, LTS‑релизе Django 4.2, релизе Pandas 2.0 и свежих PEP.
Вы можете посмотреть на нас:
Или послушать на платформах — Apple, Google, Spotify, Яндекс.Музыка, сайт подкаста. А ниже текстовая выжимка выпуска.
LTS-релиз Django 4.2
LTS означает Long‑Term Support, релиз будут поддерживать три года до 2026.
Самые интересные на наш взгляд обновления:
Реализовали поддержку psycopg3. От psycopg2 в будущем планируют отказаться, но сейчас поддерживаются обе либы.
Добавили темную тему, в админке можно ее переключить. Раньше она тоже была доступна, но надо было ковыряться.
Новые асинхронные методы — asave(), adelete() и arefresh_from_db() для моделей; aadd(), aclear(), aremove() и aset() для relation managers.
На смену DEFAULT_FILE_STORAGE и STATICFILES_STORAGE приходит настройка STORAGES, которая позволяет объявлять столько файловых стореджей, сколько нужно.
И теперь минимальная версия для проектов на Postgres — 12.
Почитать про все обновления релиза можно здесь.
Pandas 2.0
Вышел релиз Pandas 2.0, подробности по ссылке, а коротко об обновлениях ниже:
Если раньше Pandas работал только поверх NumPy, то в новой версии можно выбрать в качестве бэкенда Apache Arrow.
Работа с отсутствующими значениями и строками стала эффективнее при использовании Arrow backend.
Copy‑on‑write — ленивое копирование, при котором реальное копирование данных откладывается до тех пор, пока мы не начнем изменять данные. Позволяет в ряде случаев, сохраняя идиоматичность кода, ускорить что‑то в 10, а при удачном стечении звезд и в 100 раз. Это уменьшит количество ошибок и не скажется на производительности, когда нам нужно только читать данные.
Числовые индексы теперь могут быть не только 64-х разрядные (Int64, UInt64, Float64), но и любых других числовых типов.
У datetime и timedelta теперь можно указывать единицу измерения. Если раньше Pandas приводил все к наносекундам, теперь можно явно указать что‑то типа dtype=”datetime64”.
Свежее исследование Django-экосистемы от JetBrains
JetBrains выкатили свежее исследование экосистемы Django, где можно почитать, как десятки тысяч разработчиков со всего мира используют Django. Ниже интересные на наш взгляд цифры:
Django 3.1 и ниже используют в основном опытные разработчики, новички выбирают новые версии. Опытные разработчики, скорее всего, сидят на каком‑то большом монолитном проекте, которому уже много лет, и каждое обновление требует дополнительных сил и времени.
80% опрошенных используют Postgres, 40% — SQLite, и только 28% — MySQL. 54% используют Redis, а Memcached используют только 16%.
Pytest, unittest и pytest‑django, они примерно одинаково популярны. Pytest — 41%, unittest — 36%, pytest‑django — 31%.
А по поводу джаваскриптовских фреймворков можно сказать, что React, Vue.js и неожиданно jQuery на первых местах, то есть 36% используют React, 32% используют jQuery, 25% используют Vue.js. И растет использование htmx.
Еще можно посмотреть, например, на популярность фронтенд‑фреймворков CSS. 62% используют Bootstrap, 22% — TailwindCSS. Тут мы видим, что Bootstrap все еще сильно‑сильно опережает.
Относительно Cloud‑провайдеров. Мы видим, что Amazon Web Services до сих пор на первом месте, 44%. Догоняющие — это Digital Ocean и неожиданно, Heroku. 23% и 21% соответственно. Все остальные, Google Cloud Platform, Microsoft Azure, PythonAnywhere, там 12, 8, 10 процентов, Linode 7%. Fly.io, новой игрок, уже целых 3%, он опередил какие‑нибудь Openshift и Openstack, что, в общем‑то говоря, очень и очень неплохо.
Доминирующая IDE все так же VS Code, 42%, догоняет PyCharm, 38%, и пользователей Vim традиционно 7%, Sublime Text 5%, и EMacs 2%. 1% пишут с использованием Notepad++.
Короткой строкой
В апреле отвечали на вопросы зрителей. Сравнили FastAPI и AIOHTTP, обсудили, как дебажить асинхронный код, как не сломать типы при использовании декоратора и другие вопросы. И готовим ближайшие выпуски:
про обучение Python поговорим c Артемом Рудаковским из Лицея Академии Яндекса 28 апреля.
новости за апрель обсудим 5 мая.
PEP 709 — предлагают инлайнить list/dict/set comprehensions вместо того чтобы, как сейчас, создавать nested‑функцию на этапе создания байткода. Получаем 11% выигрыша в скорости работы и минимум побочных эффектов.
Отклонили PEP 582 c pythonpackages. Решает простые задачи для новичков, но не является лучшей альтернативой виртуальным окружениям.
PEP 710 — предлагают сохранять ссылку и хеш для каждого установленного пакета.
«Подождите, не успеваю записывать код …». Я слушал это пару лет и в итоге написал раздатчик изменений кода для студентов | Веб-студия Nat.od.ua
«Подождите, не успеваю записывать код …». Я слушал это пару лет и в итоге написал раздатчик изменений кода для студентов
Всем привет! На связи Александр Разыграев, я разрабатываю модуль ESMP Metrica, а в свободное время преподаю web-разработку студентам в институте, также преподавал на курсах переподготовки.
Часть студентов во время занятия повторяют действия и код за преподавателем. Примерно треть из них часто не успевала и сигнализировала мне, например, останавливали словами: «Подождите, я не успеваю записывать код …».
Как решал эту проблему:
если писали короткий пример, то брал паузу и ждал пока не успевающие перепишут код;
если разрабатывали более длинный пример или одно большое приложение за занятие, то отправлял отдельный файл с кодом или весь проект приложения, например, архивом в чат или коммитами в git.
Но ощущал какую-то неудовлетворённость. Во-первых, понимал, что это не решение, а скорей экстренная помощь. Во-вторых, у меня не получалось работать на опережение, т. е. я не мог понять, как часто отправлять код, а иногда, увлекшись, забывал. Какого-то другого решения этой проблемы у своих коллег я не встречал.
В этой статье привожу свой опыт, как я ушёл от этой проблемы, написав свой раздатчик изменений кода. Статья будет полезна преподавателям IT.
Предыстория
Шесть лет назад я взялся за дополнительную работу преподавателем на вечерних курсах переподготовки «Web Разработчик (NodeJS)», проходящих очно в университете.
Студенты совершенно разные:
по возрасту – от 20 лет до пенсионеров;
по профессии – из IT (однажды пришел обучаться тимлид со своей командой) и не из IT (адвокаты, бухгалтера, военные) или студенты старших курсов любых вузов;
по уровню подготовки (например, приходили студенты, у которых это был уже третий по счёту курс по web-разработке).
Объединяла их высокая мотивация учиться. Наш руководитель наставлял нас, что нам необходимо подстраиваться под студентов и стараться помочь им обойти возникающие препятствия.
Одним их таких препятствий стала описанная выше проблема.
Спустя несколько лет преподавания на этих курсах я осознал возможное решение. Всякий раз перед тем, как осуществить тестовый запуск кода, мне необходимо его сохранить, и вот в этот момент автоматически можно отправлять изменения кода студентам.
Окрылённый этой идеей, я пообещал студентам, что на следующем занятии покажу самодельный инструмент, который доставит изменения кода через web-интерфейс сразу после сохранения мною изменённого файла с кодом. Использовать я буду тот же стек (html, css, js, nodejs), что и преподаю, и мы сможем получившуюся программу разобрать как пример. Студентам понравилась эта идея, так как и преподаватель получил домашнее задание со сроком исполнения до следующего занятия (в моём случае это два будних вечера).
Первый вечер
В первом приближении под своим раздатчиком изменений кода я задумал web-приложение. Фронт предоставляет студенту информацию об изменениях кода. Серверная часть на компьютере преподавателя выполняет с одной стороны функции web-сервера, а с другой стороны отслеживает изменения нужных для занятия файлов и передает информацию об изменениях во фронт.
За первый вечер я:
Получил путь к папке, за файлами которой будет происходить отслеживание изменений через аргумент командной строки.
Начал отслеживание изменений файлов (с расширениями: js, json, css, html) в заданной папке за исключением файлов, связанных с модулями и их установкой (package-lock.json и файлы в папке node_modules). Для этого использовал функцию watch нативного модуля fs в Node.js.
Реализовал чтение изменённых файлов из файловой системы и хранение в оперативной памяти приложения их последних версий.
Добавил отдачу web-интерфейса просмотра изменённых файлов с кодом (модуль express).
Организовал общение между web-интерфейсом и сервером (модуль socket.io), чтобы по инициативе серверной стороны передавать на фронт информацию об изменённых файлах и выводить преподавателю в консоль количество студентов, использующих web-интерфейс инструмента.
Создал web-интерфейс для просмотра изменённых файлов состоящий из левого блока навигации по файлам и правого блока просмотра изменённого файла с подсветкой кода (библиотека prismjs).
Web-интерфейс: получение сгенерированного файла package.json, затем обновление его после установки пакета express.js, затем получение пустого созданного файла app.js и обновление его
Второй вечер
Явный недостаток сделанного за прошлый вечер инструмента в том, что он не умеет показывать, что изменилось в файлах, поэтому за второй вечер хотелось это доработать.
Для определения изменений между двумя версиями файлов я реализовал сохранение изменённых файлов в файловую систему с указанием версий. И вот тут я обнаружил, что при некоторых изменениях (например, копирование файла из другого проекта в папку, за которым ведётся наблюдение) watch запускал пачку событий (например, rename и за ним несколько change). Но так как опыта использования watch ранее не было, пришлось потоптаться в интернете, чтобы найти решение. Очень помогла статья «How to Watch for Files Changes in Node.js», в которой нашёл описание своих проблем и как их решить. Удалось усовершенствовать использование watch. Единственное, не предусмотрел обработку удаления наблюдаемого файла, решил отложить на будущее, так как времени не так много оставалось.
А далее я:
Реализовал очистку сохранённых ранее файлов истории отслеживания изменений при запуске инструмента.
Добавил определение изменений между двумя версиями файлов (модуль diff) и сохранение результатов в оперативной памяти приложения.
Доработал web-интерфейс: в навигационной части – вывод изменённых файлов с указанием версий, в правом блоке – вывод названия папки, за которой следим, и просмотра файла с подсвечиванием строк кода, которые добавлены или изменены (выделение удаленных строк не успел сделать).
Web-интерфейс: получение пустого созданного файла app.js и нескольких его обновлений
Использование и развитие инструмента
Студентам инструмент понравился. Единственное, они попросили добавить в web-интерфейс кнопку сохранить в буфер обмена для быстрого копирования содержимого изменённого файла. Доработок не потребовалось, так как библиотека prismjs умеет это делать, мне нужно было только добавить это в сборку библиотеки.
Со своей стороны, заметил, что теперь, если студенты не успевали переписать за мной код, а я давно не сохранял изменённый файл с кодом, они просто напоминали мне это сделать. Спустя неделю работы со своим инструментом у меня появилась привычка сохранять изменённый файл с кодом сразу после небольших изменений.
С этим потоком мне повезло. Я и все студенты подключали ноутбуки к одной wifi-точке, и студенты могли обратиться к web-интерфейсу на моём ноутбуке. Но такой расклад бывает не всегда. Поэтому наметил следующий шаг доработки инструмента – это разделение его на две программы. Правда взялся я за эту доработку через полгода, уже со следующим потоком студентов.
Удалось доработать следующее:
Разделил инструмент на две программы. Первая будет на компьютере преподавателя отслеживать изменения нужных для занятия файлов и передавать информацию об изменениях второй программе. Вторая программа web-приложение, располагаемое на хостинге, которое формирует вехи изменения файлов и показывает их студенту. Для общения между программами использовал модуль socket.io.
Добавил опциональный аргумент командной строки первой программы на очистку прошлой истории проекта, что позволит продолжать историю проекта с прошлого занятия.
Ещё через полгода началась пандемия, и очные курсы на год превратились в дистанционные. В новых условиях я и многие мои коллеги начали записывать трансляции занятий и отправлять их студентам. А я свой раздатчик кода использовал почти на всех занятиях. Это позволило облегчить процесс отслеживания изменений кода, особенно для студентов, которые для дистанционных занятий использовали один монитор.
Последний этап развития инструмента пришёлся на прошлый год. Взялся за отслеживание удаления файлов. Не получалось однозначно определить по генерируемому событию rename от watch, что удалена папка или файл. Поэтому, как только я понял, что для этого мне необходимо перед запуском отслеживания изменений в папке сканировать все её файлы и хранить об этом информацию, я решил перейти на пакет chokidar, который умеет разделять события файлов и папок.
А далее удалось реализовать следующее:
Вынес настройки фильтрации наблюдения за файлами в конфиг. Не удобно каждый раз вносить эти правки в код. Например, в начале мне требовались базовые web-файлы (с расширениями: js, json, css, html), а с расширением материалов курса мне ещё потребовалось делится файлами с расширением .vue (однофайловые компоненты vue.js).
Добавил в правый блок web-интерфейса вывод содержимого изменённого файла с подсвечиванием областей между строк кода, в которых были удалены строки.
Web-интерфейс: получение созданного файла app.js и его обновления
Вот такой получился самодельный инструмент. Свои желания я реализовал. Дальнейшее его развитие вижу уже в создании многопользовательского инструмента (чтобы можно было одновременно делиться кодом от разных преподавателей для разных групп студентов), но для этого нужна база данных, хранилище файлов и т.п.
Кого заинтересовал инструмент, он тут: первое приложение и второе приложение.
Использование созданного раздатчика изменений кода позволило мне уйти от проблемы, связанной с тем, что часть студентов не успевала переписывать за мной код, но, решив эту проблему, меня стали чаще останавливать студенты, например, словами «Подождите, я не успеваю осознать код». А вот это уже совсем другая задача для преподавателя.
P.S. Сейчас для этих целей возможно использовать и готовые решения. Например, для этой задачи может подойти совместная работа Live Share в Visual Studio Code, где студентам настраивается доступ к сеансу совместной работы только для чтения. Как-нибудь это опробую.
15 материалов для самостоятельного изучения / Хабр | Веб-студия Nat.od.ua
15 материалов для самостоятельного изучения / Хабр
Привет, Хабр! Мы на курсе Практикума по веб-разработке постоянно смотрим, что новенького появилось в сети для самостоятельного обучения веб-технологиям. И ресурсов в доступе довольно много. Они разнообразны и обучают современным технологиям в разных форматах: видео, учебники, интерактивные курсы, гайды и доклады. Если вы учитесь на курсах или уже работаете во фронтенде, эти материалы помогут улучшить навыки и избавиться от белых пятен. Делимся найденными сокровищами, вот они:
1. W3schools
Это огромный ресурс с материалами и упражнениями по разным технологиям и языкам программирования. И веб-технологии тоже не исключение, здесь можно найти множество туториалов и примеров по HTML, CSS и JavaScript.
W3schools →
2. learn.javascript
Возможно, один из самых известных онлайн-учебников по JavaScript. Он существует уже давно, и я когда-то тоже узнавал из него подробности о работе JavaScript. Учебник подробно рассказывает про JavaScript — от самых азов до продвинутых тем. И всё это с интерактивными примерами кода, а в конце каждой статьи вы можете пройти задания, чтобы проверить свои знания.
learn.javascript.ru →
3. JavaScript 30
На этом ресурсе вы сможете сделать 30 интересных мини-проектов на JavaScript, HTML и CSS. Здесь будет возможность не только попробовать уже изученные азы, но также узнать много нового, особенно про различные DOM API.
JavaScript 30 →
4. Eloquent JavaScript
Если вам больше нравится изучать язык по учебнику, то эта книга отлично подойдёт. Книга поэтапно расскажет про основы языка, даст возможность написать небольшой проект, а затем объяснит сложные концепции и принципы работы языка в браузере.
Eloquent JavaScript →
5. You Don’t Know JS Yet (book series)
Самая известная серия книг про JavaScript, его устройство и тонкости. Книги разделены по темам, поэтому можно брать только те, которые интересны. Но каждую книгу стоит прочитать — темы раскрыты очень подробно и глубоко. Перед подготовкой к собеседованию эта серия книг будет просто незаменима.
You Don’t Know JS Yet (book series) →
6. JavaScript Allongé
Довольно необычная книга, которая рассказывает про функциональное программирование на JavaScript. Несмотря на то что книга рассказывает и про азы языка, я бы рекомендовал приступить к ней, когда у вас уже будут базовые знания и представления.
JavaScript Allongé →
7. Джейк Арчибальд. В цикле — JSConf.Asia
Один из самых известных докладов, подробнейшим образом рассказывающий про Event loop и устройство асинхронности в JavaScript. Доклад до сих пор не потерял актуальности — это must watch для любого JavaScript-программиста. До него самым известным был доклад Филиппа Робертса: Что за чертовщина такая event loop? Но доклад Арчибальда актуализирует эту тему и раскрывает больше подробностей.
8. Фронтенд — это не больно
Огромная статья с набором ссылок, которые помогут вам стать лучше как фронтенд-разработчик. Статья в основном нетехническая, но рассказывает о том, как можно сделать свою жизнь лучше, в какую сторону расти и о чём стоит задуматься.
Фронтенд — это не больно →
Некоторые перечисленные материалы используют и на курсе веб-разработки Яндекс Практикума. Студенты курса и их наставники делятся друг с другом полезными материалами в специальном чате, вместе обсуждают варианты решений задач из учебников и примеры в туториалах. Так материал усваивается лучше и уж точно интереснее.
9. MDN Web Docs
Один из популярнейших источников, где веб-программисты ищут информацию о функциях из JavaScript или работы свойства в CSS. Но помимо этого на MDN опубликован огромный набор статей по веб-технологиям. Секция Guides содержит туториалы, которые помогут начать изучать HTML, CSS и JavaScript. Впоследствии вы ещё точно не раз вернётесь на этот сайт, когда будете искать информацию. На ресурсе много англоязычных статей, но у MDN есть отдельная команда волонтёров, которые переводят статьи на русский язык.
MDN Web Docs →
10. Doka.guide
Если предыдущий сайт был изначально англоязычным, то Дока — полностью русскоязычный. На Доке опубликовано множество статей как про отдельные методы и функции в JavaScript, теги в HTML и свойства в CSS, так и про архитектуру приложений и доступность. Текст старательно пишется так, чтобы его было легче воспринимать новичкам. Почти каждая статья содержит интересные практические советы по теме.
Doka.guide →
11. react.dev
React — один из самых популярных фреймворков для написания приложения на JavaScript. Совсем недавно команда React сделала перезапуск своего сайта и создала учебные материалы для пользователей. Гайд подробно рассказывает про свойства фреймворка и даёт возможность попробовать его на интерактивных примерах.
react.dev →
12. Scrimba Learn React
Большой образовательный ресурс с интерактивными уроками на множество тем. В этом курсе вы сможете написать несколько приложений на React и попробовать использовать фреймворк в полную силу.
Scrimba Learn React →
13. Youtube-канал «Фронтенд»
Отличный канал, в котором регулярно появляются видео докладов с митапов и конференций, которые проводит Яндекс, такие как Я.Субботник или Я ❤️ Фронтенд. А ещё там публикуют образовательные видеолекции с прошлых выпусков Школы Разработки Интерфейсов. На канале вы сможете почерпнуть множество актуальных знаний о работе веб-технологий и различных инструментов.
14. Frontend masters
Огромный набор видео по различным темам и веб-фреймворкам. Видеолекции ведут известные в индустрии разработчики, работающие в крупных компаниях. Доступ платный, но взамен вы получаете огромное количество углубленного материала.
Frontend masters →
15. Egghead
Это ресурс с набором коротких видео, лаконично, но подробно раскрывающим тему. Большая часть доступна по подписке, но для ознакомления вы сможете найти множество открытых уроков.
Egghead →
Это далеко не все ресурсы, которые могут быть полезны веб-разработчику. Делитесь любимыми в комментариях — будем расширять список вместе.
Генерация сайтов с помощью AI / Хабр | Веб-студия Nat.od.ua
Генерация сайтов с помощью AI / Хабр
Генерация контента сайтов с помощью AI может быть полезна в некоторых случаях. Однако, важно понимать, что AI-сгенерированный контент не всегда будет качественным или соответствовать потребностям вашей аудитории.
В этой статье я расскажу о своем опыте, сколько это стоит и каких можно добиться результатов.
Приветствие от ChatGPT
Дисклеймер
Автор не является экспертом в генерации контента, он просто делится своим личным опытом и результатами, достигнутыми на основе своих ограниченных знаний и опыта.
Идея
Основная задача заключалась в проверке реакции поисковых систем, в частности Google, на контент, созданный с помощью AI. При этом основа и структура сайта были задуманы таким образом, чтобы быть осмысленными, узкотематическими и изначально полезными, а генерируемый контент использовался для дополнительного наполнения сайта.
Для упрощения технической части, я взял один из своих прошлых проектов, который был заброшен.
Несколько лет назад я начал работу над этим проектом, но из-за пандемии COVID-19 мои планы были нарушены, и проект остался в начальной стадии разработки. Я использовал основной код и шаблоны из этого проекта — и это заметно визуально.
нулевой блин – донор шаблона и движка
Первый блин
Первоначально было необходимо понять, как создавать уникальный контент и как использовать генерированный контент, чтобы получить полезный результат — по крайней мере, в глазах Google.
Как настоящий мечтатель, я начал с выбора забавного доменного имени и выбора направления для будущего сайта. Сначала я решил создать каталог для Тихоокеанского Северо-Запада (англ. Pacific Northwest). Однако возник вопрос: каталог чего? Моя первая идея была создать каталог салонов красоты, но я недооценил их количество и доступность информации об них. В конечном итоге, я решил создать каталог школ, где обучают специалистов в области красоты. Это решение имеет преимущество в том, что школ меньше и информация о них более структурирована, что делает ее более удобной для парсинга.
В итоге, спустя несколько месяцев экспериментов в свободное время, я создал свой первый сайт – первый опыт в стопке моих будущих AI проектов.
первый блин – комом
Технические моменты
Сайт работает на PHP с использованием Symfony Framework. На моей основной работе я уже много лет не занимаюсь PHP, но в личных проектах продолжаю использовать его, так как это для меня более удобно и привычно.
Города и Штаты США можно легко найти в свободном доступе в форме готовых датасетов.
Каталог лицензированных школ также доступен в свободном доступе в рамках проекта об открытой информации.
С помощью API OpenAI я смог создать описания для категорий, школ и городов/штатов, соответствующих тематике сайта. Кроме того, я использовал API Bing для получения изображений для всех разделов сайта.
За два-три месяца экспериментов я освоил работу с генерацией контента и готов был перейти к более широкой тематике, охватывающей всю территорию США, а не только отдельный регион.
Второй блин
Для второго сайта я решил выбрать более широкую тематику, и мой выбор остановился на медсестрах — массовой и востребованной профессии.
Я потратил дополнительное время на переписку кода более универсальным способом, чтобы была возможность генерировать контент для разных профессий и тематик.
Спустя 5 недель было готово мое второе творение.
Второй блин – каталог курсов медсестер
В техническом плане, второй сайт был улучшенной версией первого, с упором на более универсальный движок и переработанной системой генерации.
Третий и четвертый блин
Для создания третьего сайта, я выбрал тематику обучения профессиональных поваров возможно, после просмотра фильма «The Menu».
Повар полезная профессия
Этот сайт я создал за 3 дня.
Я осознал, что, не будучи экспертом ни в одной из областей, мне трудно оценивать качество сгенерированного контента. Поэтому я решил создать еще один сайт на тему, которая мне ближе и более понятна.
Четвертый всадник генерации контента
Этот сайт был сгенерирован за 1 вечер, причем большая часть времени ушло на получение данных от API с минимальным участием человека.
Про деньги
Давайте посчитаем, сколько денег я потратил на оплату услуг и сервисов, которые мне понадобились для генерации 4-х сайтов. Я не буду учитывать свое время, так как я занимался этим в формате хобби, и для меня это не потраченное время, а скорее отдых от корпоративной работы.
Домены($92):
pnw.beauty $22
nurseakademy.com $12
cooks.school $46
sdecollege.com $12
Приблизительная стоимость использования Digital Ocean составляет около $120 в год, однако я использую его не только для этих экспериментов, но и для множества других проектов.
Сам сайт в своей основе является статическим, за исключением административной панели, и он обслуживается через бесплатный CDN Cloudflare. К тому же, через этот сервис можно получить бесплатный SSL-сертификат.
Для получения картинок я использовал API Bing, который имеет простой и понятный интерфейс. По отчетам, я потратил на это $97.
Однако судя по всему при регистрации мне дали какие то бесплатные кредиты и по итогам мой счет составил $0 — спасибо Майкрософт за щедрость.
o
Я ожидал, что основные расходы возникнут в связи с использованием сервиса OpenAI. Это понятно, что это не дешевый сервис, кроме того, ценовая политика не очень прозрачна и зависит от объема генерируемого текста и используемой модели.
На этих картинках видно как росли мои потребности и с ними росли расходы:
в феврале я начал эксперименты
в марте я начал генерировать контент для сайтов во всех штатах США – ценник вырос
В апреле я сгенерировал контент для двух сайтов
Total: $122
Индексация гуглом
Так как сайты новые, сложно предсказать, как Google будет реагировать на гибридный контент. Сайт pnw.beauty уже набрал некоторую посещаемость в несколько сотен человек в день, но это связано с тем, что он уже некоторое время существует. Остальные сайты только начали свое существование и потребуется некоторое время для их индексации Google.
Выводы
Лично для себя я считаю эксперимент удачным — мне удалось пощупать AI генерацию контента на практике и на выходи получились сайты которые могут приносить хоть и небольшую но пользу так как содержат упорядоченную информацию по конкретной тематике.
Окупятся ли затраты вопрос открытый — скорее всего гугл не будет высоко ранжировать сайты подобного содержания — в обратном случае появится миллионы конкурентов которые будут размывать трафик от низкочастотных запросов.
Что дальше
На данный момент у меня нет планов на размещение рекламы или какую-либо монетизацию этих четырех сайтов. Они останутся моими личными экспериментами и будут существовать в течение года (пока не истекут домены) без какой-либо коммерческой активности.
В случае, если один из сайтов начнет проявлять положительную динамику в плане интереса аудитории, я, возможно, продолжу его развитие в той или иной форме.
Качество контента тоже вызывает вопросы – скорее всего его можно улучшить если настроить запросы к OpenAI более внимательно и вдумчиво или использовать человека в качестве редактора.
Московский политех не слышал про конфиденциальность / Хабр | Веб-студия Nat.od.ua
Московский политех не слышал про конфиденциальность / Хабр
Почему не стоит подавать документы через сайт Московского Политеха, какая угроза персональным данным абитуриента и к чему может привести утечка данных? Обо всем этом — в данной статье.
Дисклеймер: я не квалифицированный специалист по компьютерной или информационной безопасности. Цель статьи — ознакомить читателя с имеющейся «недоработкой» сайта Московского Политехнического Университета и дать повод задуматься о других способах подачи документов, если читатель особенно трепетно относится к размещению персональных данных в интернете. Приятного чтения!
Как родилась статья
В связи с обновлением правил приема 1 апреля 2023 года началась работа приемной комиссии Московского политехнического университета (он же МПУ, он же Политех). Плохо это или хорошо — решать каждому самостоятельно, однако, я считаю, что если подать документы раньше, то это позволит избавиться от лишних забот во время всеобщей волны подачи документов. В такие периоды из головы и не такое может вылететь.
Политех я выбрал как один из вузов для поступления в магистратуру после выпуска с «айтишной» специальности одного из лучших региональных вузов. А МПУ как раз располагает неплохим общежитием, а также парочкой интересных мне программ. Поэтому недолго думая я зарегистрировался на сайте Политеха, чтобы подать документы онлайн, не тратя деньги на перелет в Москву или на отправку почтой.
Примечание: изначально, конечно, я хотел подать через Госуслуги — все-таки к ним больше доверия в плане сохранности персональных данных. Но там приемная кампания начинается лишь с июня, поэтому вариант оставался один — регистрация личном кабинете на сайте университета.
Что знает Политех об абитуриенте
Для регистрации стандартно требуется ФИО, дата рождения, почта, пароль и, — внимание — паспортные данные. Не знаю, зачем они нужны уже на этом этапе. Для того чтобы ботов и фейков не регистрировали?..
Уже «звоночек», но да бог с ним, регистрируемся.
Это ведь сайт известного столичного университета, мои персональные данные наверняка будут в безопасности
🙂
После регистрации сайт сразу предложил заполнить анкету, в которую любезно и автоматически были вписаны введенные на предыдущем этапе мои паспортные данные.
В анкете требуют также стандартный набор данных о себе для поступления: гражданство, сотовый, прописка, адрес проживания, сканы СНИЛСа и паспорта (в т. ч. 19 страница) и т. п.
В итоге получается неплохой набор персональных данных абитуриента:
- ФИО;
- Дата и место рождения;
- Пол;
- Email (он же логин) и пароль;
- Номер мобильного (или даже два, с учетом запасного);
- Данные паспорта (первая страница, страница с пропиской и 19 страница, на которой находятся данные о предыдущих и заграничных паспортах) и его сканы;
- Номер документа СНИЛС и его скан.
Все это хранится на серверах МПУ. И пароль в этом списке неслучайно.
Политех знает логин и пароль абитуриента.
Он сам дает это понять, высылая после регистрации на указанную почту письмо с логином и паролем для входа.
Давайте просто сделаем вид, что не видим опечаток в автоматической рассылке
Почему хранить пароли юзеров — плохо?
В письме выше Политех прислал мне логин и пароль. Для того чтобы вставить в письмо эти данные система должна их откуда-то взять. Системы берут данные из базы данных, но перед этим данные должны попасть в эту базу. Очевидно, что логин и пароль были записаны в базу как обычный текст после регистрации — после отправки на сервер формы с логином, паролем, ФИО и т.п.
Вывод: логины и пароли абитуриентов (а может и не только) Политех хранит в базе «как есть». И нет гарантий, что после отправки этого письма зарегистрировавшемуся эти данные пропадают из базы.
Проблема заключается в том, что, как и (почти) любая база данных, база Политеха не защищена от утечек на 100%. Равно как и вся система не защищена от взлома на 100%. Соответственно, данные из базы могут попасть в руки злоумышленников или вовсе в открытый доступ. В данном случае, «стащив» лишь логины и пароли пользователей, злоумышленники могут получить доступ ко всему списку персональных данных, указанных в предыдущем разделе статьи. А это не есть хорошо, думаю, и так понятно.
Как можно было бы решить проблему
— Но ведь для авторизации пользователя система должна знать его логин и пароль! Нужно ведь знать правильный пароль ввел юзер или нет, — возразит неравнодушный читатель.
— Правда, но лишь отчасти, — ответит автор.
Возможное решение — хэширование. Если быть точным, то аутентификация с помощью хэша.
Небольшой ликбез: хэширование — это процесс преобразования каких-либо данных в строку фиксированной длины, состоящей из букв и цифр. Этот процесс осуществляется с помощью т.н. криптографических хэш-функций, к примеру, SHA-1, RIPEMD-160, MD5 и др.
Для интересующихся оставлю ссылку на хорошую статью, в которой автор описывает их «природу» более подробно.
Если говорить по сути этой статьи, то хэширование относится к тем видам математических операций, легко вычисляемых «в одну сторону», но довольно сложно обращаемых «вспять».
Пример: ученик начальной школы сможет перемножить два простых числа, например,
709 * 941 = 667169. Но провернуть обратную операцию — узнать множители из произведения — уже задача нетривиальная.
Аналогично с хэшированием: можно хэшировать слово «амогус» с помощью функции, например, SHA-1, получив строку «f49440bf4d3ce333c46b6bc86618937fe39eaa2e». Но для того, чтобы перевести эту строку обратно в слово потребуется довольно много времени и вычислительных мощностей.
Теперь рассмотрим пример касательно авторизации:
Допустим,
мой логин — user1,
а мой пароль — qwerty123 (никогда не используйте такой пароль).
Их я придумал при регистрации на каком-то сайте.
На первый взгляд системе можно просто взять их в таком виде и записать в базу данных или в виде пары «логин-пароль», или в условную таблицу со столбцами «Логин» и «Пароль», или как-либо еще. А потом, когда пользователь пытается войти на сайт, сравнивать то, что он ввел в полях «Логин» и «Пароль», с тем, что хранится в базе данных соответственно.
Логин совпадает?
Пароль совпадает?
Отлично, добро пожаловать на сайт, user1!
Это было бы хорошо, если бы не возникала описанная ранее проблема с безопасностью и утечками.
Проблема решается хэшированием (как минимум) пароля.
Введенный пароль хэшируется при регистрации аккаунта, и в базу данных на сервере записывается этот самый хэш — сгенерированная хэш-функцией строка символов.
В моем случае:
qwerty123 —> 5cec175b165e3d5e62c9e13ce848ef6feac81bff.
Таким образом, вместо пары «логин-пароль» (user1 — qwerty123) в базе хранится пара «логин-хэш» (user1 — 5cec175b165e3d5e62c9e13ce848ef6feac81bff), которая вызывает намного меньше интереса у «воришек данных».
По этой причине известные компании типа Google, Apple, Microsoft и пр. не присылают пароль на почту, если нажать «Забыли пароль». Они просят придумать новый пароль потому, что старый они просто не знают. У них есть хэш, сделанный на основе пароля, но нет самого пароля.
В таком случае процедура авторизации проходит подобным образом: после нажатия пользователем кнопки «Войти» пароль сначала прогоняется через тот же алгоритм хэширования, который использовался при регистрации, и только затем отправляется на сервер, чтобы провести проверку: верен ли логин и верен ли хэш.
(Также возможен вариант с хэшированием пароля уже на сервере, это увеличит производительность сайта на клиенте, но с точки зрения безопасности все же не рекомендуется.)
Если значения логина и хэша совпадают с тем, что было записано в базу при регистрации — «добро пожаловать на сайт, user1».
Если нет, то «неверный логин или пароль».
Это решение намного безопаснее с точки зрения потенциальных утечек. Как минимум, это осложнит доступ к данным со стороны недоброжелателей. Однако все мы понимаем, что ничего невозможного нет 🙂
Будьте аккуратны, уважаемые читатели.
Если компания присылает в письме ваши реквизиты для входа, стоит десять раз подумать, прежде чем оставлять на ее сайте какие-либо персональные данные.
Интерфейс дерева комментариев. Сравниваем Хабр и клиенты Reddit; переделываем Хабр | Веб-студия Nat.od.ua
Интерфейс дерева комментариев. Сравниваем Хабр и клиенты Reddit; переделываем Хабр
Я люблю древовидные комментарии. Для них сложно найти лучшую альтернативу. Интерфейсы форумов из двухтысячных выглядят слишком огромными для сообщений в два-три предложения. В линейном потоке сообщений мессенджера бывает сложно понять, кому кто отвечает. А имиджборды мне приходилось на полном серьезе учиться читать.
Одна из самых старых и самых популярных площадок с деревьями комментариев — это Reddit. Правда, большая часть его пользователей едина в одном: его интерфейс ужасен. Но его API открыто, поэтому на выбор есть множество клиентских приложений, особенно для мобильных телефонов — на картинке ниже Joey, Relay, Slide и Boost, а есть еще и другие. Их авторы — как правило, и сами недовольные стандартным интерфейсом Реддита — потратили много времени и сил на поиск удобного интерфейса для комментариев. И большая их часть пришла к очень похожим вариантам дизайна.
В этой статье я хочу разобрать, чем мне так нравится такой дизайн деревьев комментариев, сравнить его с деревьями комментариев мобильной версии Хабра, и попробовать пофантазировать, как бы могли выглядеть комменты Хабра в интерфейсе клиентов для Reddit.
Основы
Какой паттерн интерфейса я называю в этой статье “деревом комментариев”?
Для каждого комментария есть не более одного другого, с которым он связан как продолжение диалога.
Будем называть такой комментарий родительским, а комментарий-ответ — дочерним. Строго говоря, это не всегда именно ответ. Например, это может быть продолжение предыдущего комментария того же автора.
Дочерние комментарии расположены “ниже” в иерархии на странице, чем родительские.
Как правило, это реализуется отступами слева, но почти всегда есть еще и другие маркеры. Из-за несоответствия этому признаку я не называю комменты в твиттере древовидными.
В пример приведу как раз те интерфейсы, которые хотел бы сегодня разобрать. Слева — комментарии на Хабре, справа — комментарии в одном из клиентов Реддита (Boost).
А теперь давайте разберем их по косточкам.
Отступы
В первую очередь, можно обратить внимание на то, как по-разному отображается иерархия комментариев. На Хабре это довольно большие отступы слева, плюс точки по числу уровней вложенности (до определенного максимума):
Комментарии в Boost тоже используют отступы, но намного меньшие. Это позволяет им использовать намного больше пространства для, собственно, текста. У Boost примерно такой же потолок глубины отступов — правда, продолжение он предлагает читать на другой странице. Но даже при такой вложенности текст на экране все еще занимает намного большую часть места.
Как бы выглядел Хабр с такими отступами? На реальном телефоне поменять стили сложновато, поэтому я использовал DevTools в Firefox, чтобы проверить. Включил Responsive Design Mode с пресетом Galaxy S10/S10+ Android 11 и набрал в консоли следующее:
$$(“.tm-comment-thread__comment > *”)
.forEach(el => el.style.cssText += “margin-left: 0;”)
$$(“.tm-comment-thread”)
.forEach(el => el.style.cssText += “position: unset; margin-left: 5px;”)
$$(“.tm-comment-thread__breadcrumbs”)
.forEach(el => el.style.cssText += “display: none;”)
Сразу видно, что теперь на один экран влезают не два комментария, а уже три. И они не ютятся на правой половине экрана. Но намного хуже стала считываться вложенность. Как с этим борется Boost?
Индикаторы вложенности
Посмотрим еще раз на комментарии в обоих приложениях. И там, и там есть дополнительные индикаторы вложенности: кружочки на Хабре, полосы в Boost. Но на Хабре, как мне кажется, они считываются гораздо хуже. Давайте сравним, и даже дадим Хабру фору — уберем влияние цвета:
Кружочки на Хабре расположены слишком далеко от кружочков соседних комментариев, и их слишком много, чтобы посчитать. В итоге, основную работу по обозначению глубины берут на себя отступы. Думаю, поэтому их и сделали настолько большими.
Boost же использует вертикальные полосы на всю высоту комментария. Из-за того, что верхние и нижние концы этих полос очень близко к соседним, намного легче сравнить их вложенность.
$$(“.tm-comment-thread__comment”)
.forEach(el => el.style.cssText += `border-left: solid 5px grey; margin-left: -10px;`)
$$(“.tm-comment-thread__children”)
.forEach(el => el.style.cssText += “padding-top: 0;”)
$$(“.tm-comment-thread”)
.forEach(el => el.style.cssText += “margin-bottom: 0;”)
$$(“.tm-comment-thread__comment”)
.forEach(el => el.style.cssText += “padding-top: 10px; border-top: solid 1px grey;”)
Посмотрим, что получилось. Слева — оригинал, в центре — до применения последнего блока кода, справа — после.
Цвет как индикатор глубины вложенности
Еще одним важным индикатором вложенности в Boost является цвет полосы слева. Основной принцип прост — комментарии на близких, но различных уровнях вложенности должны иметь разные цвета, причем чем ближе вложенность, тем ближе цвет. Давайте накостылим это для мобильного Хабра:
var depth = el => {
let d
for (d = 0; el; el = el.parentElement) {
if (el.classList.contains(“tm-comment-thread”)) d++
}
return d
}
var color = el => `hsl(${30 * depth(el)} 70% 60%)`
$$(“.tm-comment-thread__comment”)
.forEach(el => el.style.cssText += `border-left: solid 5px ${color(el)};`)
Панель действий
В Boost панель с кнопками действий для комментариев по умолчанию скрыта и открывается по длинному нажатию на комментарий. Это позволяет использовать еще больше пространства под сам текст комментариев. На Хабре, видимо, по инерции с десктопного интерфейса, кнопки отображаются под каждым из комментов.
Важную метаинформацию, вроде даты и рейтинга, Boost отображает справа от имени пользователя, там много пространства для этого. Для нашей демонстрации я немножко считерю, и скрою футер совсем. Просто представьте в своем воображении числа справа от ника.
$$(“.tm-comment-footer”).forEach(el => el.style.cssText += “display: none;”)
Результаты
Что у нас получилось? На один экран теперь влезает чуть ли не в два раза больше комментариев, при этом их глубина и вложенность считываются как минимум не хуже. Я не уверен, вписывается ли такой дизайн в стиль Хабра, но как минимум Реддит так читать очень удобно. Да и в целом, мне сложно придумать более удачный дизайн для дерева комментариев, чем тот, к которому — возможно, даже независимо! — пришли почти все приложения для Реддита.
Весь мой костыльный код из статьи
Проверялось в Responsive Design Mode из Firefox с пресетом Galaxy S10/S10+ Android 11.
// Отступы
$$(“.tm-comment-thread__comment > *”)
.forEach(el => el.style.cssText += “margin-left: 0;”)
$$(“.tm-comment-thread”)
.forEach(el => el.style.cssText += “position: unset; margin-left: 5px;”)
$$(“.tm-comment-thread__breadcrumbs”)
.forEach(el => el.style.cssText += “display: none;”)
// Индикаторы вложенности
$$(“.tm-comment-thread__comment”)
.forEach(el => el.style.cssText += `border-left: solid 5px grey; margin-left: -10px;`)
$$(“.tm-comment-thread__children”)
.forEach(el => el.style.cssText += “padding-top: 0;”)
$$(“.tm-comment-thread”)
.forEach(el => el.style.cssText += “margin-bottom: 0;”)
$$(“.tm-comment-thread__comment”)
.forEach(el => el.style.cssText += “padding-top: 10px; border-top: solid 1px grey;”)
// Цвет как индикатор глубины вложенности
var depth = el => {
let d
for (d = 0; el; el = el.parentElement) {
if (el.classList.contains(“tm-comment-thread”)) d++
}
return d
}
var color = el => `hsl(${30 * depth(el)} 70% 60%)`
$$(“.tm-comment-thread__comment”)
.forEach(el => el.style.cssText += `border-left: solid 5px ${color(el)};`)
// Панель действий
$$(“.tm-comment-footer”).forEach(el => el.style.cssText += “display: none;”)
[Перевод] Релизы Safari — это ад для разработчиков | Веб-студия Nat.od.ua
Релизы Safari — это ад для разработчиковНа прошлой неделе выкатили Safari 16.4, и для нас это стало кошмаром. Мы разрабатываем браузерное приложение для создания игр под названием Construct. Ранние версии Safari 16.4 ломали открытие проектов, предпросмотр проектов и весь контент, опубликованный при помощи Construct, каждый раз по-разному. Я захотела поделиться своим опытом, чтобы пользователи, разработчики, регуляторы и сама Apple знали, через что нам пришлось пройти из-за, казалось бы, рутинного релиза Safari.
Разработчики большинства браузеров предоставляют предрелизные версии для предварительного тестирования. Например, ежедневно обновляются Chrome Canary и Firefox Nightly, кроме того, существуют более редкие dev- и beta-релизы. Apple предоставляет Safari Technology Preview (STP), но она совместима только с macOS, и не обновляется по какому-нибудь открыто опубликованному графику. Похоже, это происходит примерно раз в две недели. Предрелизные браузеры обычно довольно шероховаты и содержат очевидные проблемы, которые достаточно быстро устраняют. Однако когда они начинают переходить в состояние беты, необходимо присмотреться повнимательнее. Поэтому когда 16 февраля было заявлено о выпуске Safari 16.4 beta 1 (тоже без какого-либо публичного графика), мы начали присматриваться и обнаружили множество проблем.
Читать дальше →
введение в холодные и горячие Observable в rxjs / Хабр | Веб-студия Nat.od.ua
введение в холодные и горячие Observable в rxjs / Хабр
По мере изучения RxJS разработчик рано или поздно сталкивается с такими понятиями, как cold и hot observable. А на технических собеседованиях в команды, которые используют RxJS, можно услышать вопросы по этой теме.
Например, чем горячий поток отличается от холодного? Можно ли холодный поток превратить в горячий и наоборот? И если да, то как это сделать?
В статье попробуем разобраться в теме и найти ответы.
Аналогия из реальной жизни
При поиске материала по теме часто встречается пример из реальной жизни, который должен помочь нам в понимании.
Представьте, что недавно вышел новый фильм, который можно посмотреть на платформе Netflix или сходить на него в кино. Посмотреть фильм на Netflix в любое время и с самого начала — это похоже на то, как работает холодный поток.
Для просмотра фильма в кинотеатре нужно прийти к началу сеанса (определенному времени), а если опоздаете, то фильм начнется без вас и вы пропустите его часть или весь целиком. Это похоже на то, как работает горячий поток.
Неплохая аналогия, расскажу, что она значит в реальной жизни.
Горячий и холодный observable в коде
В документации сказано:
Cold Observable — это поток, источник данных которого создается внутри конструктора Observable.
Hot Observable — это поток, источник данных которого создается снаружи конструктора Observable.
Попробуем отобразить это в коде. В качестве источника данных может выступать что угодно. Например, WebSocket, таймеры, массивы и так далее.
Для простоты и наглядности создадим функцию getCurrentDate, которая будет возвращать значение текущей даты и времени, и используем ее как источник данных.
Следуя определению, создадим холодный observable и подпишемся на него три раза в разные промежутки времени.
function coldDate() {
return new Observable((subscriber) => {
const value = getCurrentDate();
subscriber.next(value);
});
}
const observable = coldDate();
const s1 = observable.subscribe(console.log);
setTimeout(() => {
const s2 = observable.subscribe(console.log);
}, 2000);
setTimeout(() => {
const s3 = observable.subscribe(console.log);
}, 4000);
Запустив код, мы увидим, что у каждого подписчика значение даты и времени свое, которое генерируется в момент подписки на observable.
Теперь посмотрим, какое поведение будет, когда observable будет горячим.
function hotDate() {
const value = getCurrentDate();
return new Observable((subscriber) => {
subscriber.next(value);
});
}
const observable = hotDate();
const s1 = observable.subscribe(console.log);
setTimeout(() => {
const s2 = observable.subscribe(console.log);
}, 2000);
setTimeout(() => {
const s3 = observable.subscribe(console.log);
}, 4000);
В этом случае значение даты и времени у всех то, что было сгенерировано еще при создании observable. Стоит уточнить, что источник данных для горячего observable может находиться и за пределами функции фабрики.
От того, где инициализируется источник данных, будет зависеть, какие значения станут получать подписчики. Но почему так происходит?
Внутренности Observable
Чтобы понять, почему функция subscribe создает или не создает источник данных, вспомним, как устроен Observable.
class Observable {
constructor(private onSubscribe: (observer: Observer) => Subscription) {}
subscribe(observer: Observer) {
this.onSubscribe(observer);
}
}
При вызове функции subscribe у инстанса класса Observable мы вызываем функцию onSubscribe, которую передавали в конструктор. Значит, в cold observable источник данных будет создаваться каждый раз заново, потому что он создается в этом колбэке, в отличие от hot observable.
Основные различия:
Свойства потока Hot observable
Свойства потока Cold observable
Источник данных создается снаружи конструктора observable, поэтому данные в нем могут меняться, даже если нет подписчиков
Поток делится значениями, пришедшими ему из источника данных, со всеми своими подписчиками
Источник данных создается внутри конструктора Observable
Источник данных для каждого подписчика свой, а значит, и значения тоже свои
Как подогреть или остудить observable
После того как мы разобрались с разницей горячих и холодных потоков, давайте превратим горячий поток в холодный и наоборот. Сам поток превратить в какой-то другой нельзя, только создать новый на основе него.
Создать холодный поток на основе горячего нельзя, потому что источник находится где-то вовне и пересоздавать его для каждого нового подписчика мы не можем.
А вот сделать горячий поток на основе холодного можно. Например, создать какой-нибудь subject и подписываться на него. В качестве источника данных возьмем холодный observable, и значения, которые поступают из него, будем передавать в subject.
const observable = coldDate();
const subject = new Subject
observable.subscribe(subject);
const s1 = subject.subscribe(console.log)
Тогда все наши подписчики будут получать одни и те же значения. Этот же трюк проделывают и операторы мультикастинга — такие как share или shareReply:
const hot$ = cold$.pipe(shareReply(1))
Более подробно тему мультикастинга разберем в следующей статье.
Отмечу, что потоки, использующие операторы мультикастинга, иногда называют теплыми потоками, так как источник данных в этих потоках будет создан только в момент первой подписки. Для всех остальных подписчиков он будет расшарен. И после того, как последний подписчик отпишется, источник данных перестанет выдавать значения. А при новых подписчиках источник данных будет создан вновь с самого начала.
Заключение
Надеюсь, статья помогла разобраться с горячими и холодными потоками и понять, как они работают. Спасибо за внимание! Если у вас есть вопросы, интересные примеры или кейсы по работе с потоками — буду рад обсудить в комментариях.