Сова пишет…
3.14K subscribers
345 photos
37 videos
5 files
417 links
Frontend Senior Fullstack Backend Lead и прочие слова.
Изучаю самые современные технологии.
Обучаю разработчиков как стать сильнее — https://frontend.vision.

По коллаборациям и сотрудничеству пишите в сообщения канала!
Download Telegram
Как же обстоят дела с веб-разработкой в расте?
Для начала стоит начать с сайта arewewebyet.org, он сможет ответить на вопросы о возможности переезда с Rails/Rjango/Flask на Rust, а также обозначить список более-менее рабочих библиотек и фреймворков.

Я спросил в чате @rustlang_ru какую библиотеку/фреймворк мне выбрать в качестве веб-сервера для своего проекта, в ответ получил много предложений, но настоятельно рекомендовали hyper.rs.
Что-ж, это низкоуровневая библиотека, не имеющая в комплекте роутинга, но с возможностью асинхронщины на futures. Результат моих попыток есть в https://github.com/sergeysova/instaclone-backend.rs/tree/feature/hyper. Я практически сразу уткнулся в необходимость написания своего роутера, так как хэндлить роуты вроде /users/:user_id библиотека не умеет. Такой подход мгновенно отбил у меня желание писать дальше.

Следующим кандидатом выступил фреймворк Fanta docs.rs/crate/fanta. Этот подход мне понравился сильно больше, так как имел в своем арсенале всё, что нужно для работы с базой и http-запросами. Fanta построена на совместной работе serde.rs для сериализации, tokio.rs для работы с сетью, diesel.rs для работы с базой. Мне показалось, что я сделал правильный выбор, но молодость фреймворка сказалась также очень быстро. Пришлось писать слишком много сервисного кода, и слишком мало бизнес-логики. Буквально дальше сгенеренного приложения не зашло. https://github.com/sergeysova/instaclone-backend.rs/tree/feature/fanta

После очередного холивара в чате Rust, я обратил внимание на rocket.rs, который до этого выкинул из поля зрения, по причине работе только на nightly версии rustc. В этот раз, я решил рассмотреть его поближе. И о чудо, оно заработало, очень просто и приятно. Весь бойлерплейтный код скрывается в макросах
#[get(“/users/<user_id>”)]
Имеется валидация параметров, входных данных, параметров query, cookies прямо из коробки да по структурам раста, и для этого не нужно писать лишнего кода, просто указать аргументы функции. Меня всё это очень сильно подкупило. Весьма неплохие гайды и документация по API сделали своё дело, за пару дней я написал несложное приложение с двумя роутами, прикрутил базу PostgreSQL с помощью diesel.rs и впридачу получил красивый логгер для отладки. Ко всему прочему, rocket из коробки имеет гайд по настройке пула соединений с базой на основе r2d2 docs.rs/crate/r2d2 и это прекрасно.

Вердикт: продолжаю использовать rocket + diesel для своего pet-проекта.
Решил перечитать документацию по API recompose и нашел отличный пример, почему стоит начать ипользовать recompose для компонентов-страниц.

Возможно, когда-нибудь напишу об этом подробнее
На счёт JWT небольшой текст.

Вижу смысл юзать его только для общения между серверами в неких опасных условиях.

Но почему оно того не стоит, можно прочесть по ссылке https://t.iss.one/why_jwt_is_bad
Я слез с Rocket, когда не получилось написать трейт/структуру для указания аутентификации для роута.
Рокет позволяет с помощью макроса на хендлере роута указать миддлвары. О request guards можно почитать здесь.

#[post("/search")]
fn search_handler(auth: AuthOptional) -> ... { ... }


Приблизительно, как это работает, макрос создает функцию которая будет вызываться каждый раз при запросе в указанный роут. В этой функции генерируется весь обслуживающий код, в том числе специальный вызов написанного хендлера с параметрами. Но так как Rust позволяет обратиться к дженерик типу (`T::some_method()`), перед вызовом хендлера, вызывает методы у типов его аргументов (`AuthOptional::from_request(&req)`).

Я хотел указывать там аутентификаци: опциональную или требуемую.
Опциональная: хендлер сам решает, что ему делать в случае отсутствия аутентификации. Чуть ли не через простой if session.is_auth().
Требуемая: миддлвара пропускает в хендлер только аутентифицированные запросы и кладет куда-то инстанс юзера, для удобства. Анонимам делает отлуп с 401 и для этого не надо писать лишнего кода.

В nodejs это решается просто и удобно:

router.post('/search', authOptional(), searchHandler)
router.post('/search/private', authRequired(), searchPrivateHandler)


Всё было хорошо до момента, когда мне понадобилось из такой миддлвары вернуть кастомную ошибку и обработать ее для всех роутов. Пообщавшись с автором, оказалось, что такой функциональности тупо нет. Может быть сейчас уже добавили или я тогда не так изъяснялся, не знаю. Но мне не сильно вкатила идея макросов для роутов, так как это жесткое прибивание гвоздями пути роутера к хендлеру.
Итак actix.

Сам actix — фреймворк построенный на модели акторов. Я не вникал в эту модель и паттерны, перед тем, как начать писать на actix-web.

Actix по другому строит роутинг. Более привычным способом для разработчиков nodejs 😄.

app.resource("/account", |route| {
route.method(Method::POST).with(account_handler)
});


Сами хендлеры не заворачиваются в макросы и остаются обычными функциями от аргументов. Но большинство хендлеров имеют тип Fn(..) -> impl Responder, это значит, что функция должна вернуть любой тип имплементирующий трейт Responder. Разработчики actix-web реализовали этот трейт для всех стандартных типов.
Но на самом деле, хендлер это тоже трейт — Handler (ссылка). В этом файле есть имплементация Handler для функции.

Итак, что это даёт? Actix также как и Rocket умеет принимать хендлеры с кастомными аргументами.

Типичный хендлер выглядит так.
fn common_handler(req: HttpRequest<State>) -> impl Responder { ... }

Но количество аргументов можно расширить через кортеж.
fn custom((req, auth, query): (HttpRequest<State>, Auth, Query<Info>)) -> impl Responder { ... }

Auth, Query<T> называются Extractors и их задача вытаскивать что-то из запроса в хендлер, тоже своеобразный middleware. Хотя actix имеет полноценные middlewares, экстракторы очень полезны, так как позволяют делать асинхронные экстракторы и выполнять отлуп запроса, до исполнения хендлера.

С таким подходом, реализовать отлуп required аутентификации становится в разы проще. Я написал две простеньких структуры Auth и AuthOptional. Обе реализуют трейт FromRequest<State>.
Auth делает отлуп запроса, если пользователь пришел без аутентификации или с дохлым токеном (и т.д.). AuthOptional просто дергает метод Auth::from_request но не делает отлуп пользователю, а заворачивает сессию в Option.
Ссылка на код в howtocards
Пример использования

Я не очень внимательно читал документацию по экстракторам и долго пытался понять как мне реализовать эту фичу. Пришлось связаться с автором actix и выяснить тонкости. Изначально, я хотел реализовать свой SessionBackend и пытаться крутиться вокруг него.

Посмотреть примеры работы с actix можно посмотреть в репозитории sentry в директории server.
Также интересные подходы в написании веб-сервера есть в исходниках crates.io.
Сегодня снова начал думать о правильном распределении свободного времени. Есть много разных методик, но я хочу снова попробовать матрицу Эйзенхауэра.
Сделал зеркало для тех кому удобнее читать в телеге

https://t.iss.one/this_week_in_react
Почему директории utils и helpers это свалка?

Начнем с того, как они появляются. Во время разработки проекта, программист старается выносить повторяющиеся куски кода в функции и переиспользовать их. В какой-то момент, в двух разных модулях требуется одна и та же функция, неопытный разработчик решает вынести функцию в отдельный модуль. Но почему-то останавливается на этом решении, создавая директорию/модуль utils или helpers, не думая о будущем этой части программы.

С ростом проекта, количество функций в этой директории растет. Также растет и количество разработчиков в проекте. Функций появляется все больше и больше. Зачастую никто не следит за структурой этих модулей и вполне возможно появление дублирования кода.

Как это решить?
Забудьте о директориях utils и helpers. Эти названия никак не доносят суть содержимого. А из правил чистого кода мы помним, название должно быть однозначным и отражать назначение.
Следует задуматься, почему вообще появилась необходимость в вынесении функции?
Почему они используются в таких разных частях проекта?

Возможно, ответом будет плохая организация кода всего проекта. Если хорошо поисследовать проект можно найти много кода, который достоин вынесения в отдельную библиотеку.
Если же этот код нельзя или не нужно выносить в полноценную библиотеку, его можно вынести в библиотеку внутри проекта. В случае JS, это можно сделать, создав полноценную библиотку внутри lib/.

Такой библиотеке нужно дать осмысленное однозначное название, написать документацию и тесты. Причем собрать в этой библиотеке весь связанный код. Например, вынести все функции работающие с датами во внутреннюю библиотеку lib/datetime. Префикс lib/ позволит избежать конфликтов имен, а также можно будет использовать простые и понятные имена. Конечно же, можно сделать npm scope, чтобы упростить будущую публикацию в npm. Например, @lib/datetime.

Чем это отличается от lib и helpers?
- весь код, выносится в полноценную библиотку с осмысленным названием, документацией и тестами
- lib явно описывает содержимое: это библиотека
- рефакторинг, публикация или удаление этой библиотеки становятся тривиальными задачами
Не так давно в чатах обсуждали (снова) чем отличается фреймворк от библиотеки.

https://dev.to/ben/whats-the-difference-between-a-library-and-a-framework-3blo

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

https://foobarbaz.club/why-i-stopped-using-snapshot-testing-with-jest/
Forwarded from 🦉
Ребята, вы давно просили видео с презентации React SPB Meetup об atomic design и feature slices, но нам запороли запись.
Поэтому я вынес ключевые слайды с комментариями в отдельный канал.

https://t.iss.one/joinchat/AAAAAFcbHfDlRV0eK8kt8w
У меня каждая директория описывает либо одну сущность, либо их набор.

features/ — набор сущностей
features/name — одна сущность
./pages/ — набор сущностей

с файлами похожая штука
./reducer.js — одна сущность
./actions.js — набор сущностей

Вложение директорий определяется вложением сущностей.

— как понять, что есть сущность, а что нет?
— нужно определиться, можно ли с “этим” работать не погружаясь в детали. Можно ли к “этому” обращаться как единой цельной структуре?

страница регистрации сущность? Мы можем ее заимпортить как цельную сущность — да. Можем изменить именно эту страницу — да. Страница цельная сама по себе — да.

ui — сущность? мы можем заимпортить ui и работать с ним в коде — да. Можно ли к нему обращатсья как к цельной структуре — нет(он не несет в себе функциональности, только его части по отдельности ее имеют).
Тем временем, я ухожу из коммерческой разработки на JavaScript.
Возможно, здесь будет больше постов о js и rust
@artalar подкинул статью: https://daneden.me/2018/01/05/subatomic-design-systems/

На первый взгляд выглядит проще Atomic Design, но имеет больше математических концепций.
Нашел очередной бандлер пакетов.

Эта штука позволит собрать почти любой package: javascript, typescript, reasonml, wasm и в несколько таргетов node, web, bin.

pika имеет минималистичный API и расширяемую систему плагинов.

Возможно, это сможет ускорить разработку библиотек. Сомнительно, что его можно использовать для сборки целых проектов.

https://github.com/pikapkg/pack
Пишу на жс 5+ лет. Из них писал 3 года на типизированном.
За это время успел полюбить и возненавидеть типизацию. В итоге сделал вывод, что типизация в жс, только замедляет разработку.
Потому что приходится тратить время на борьбу с тайпчекером, даже там, где по опыту знаешь, что всё збс будет работать.
А с тс вообще нельзя быть уверенным, что если тайпчек прошел, всё будет работать. Всё равно запускаешь код и проверяешь, что всё ок. Плюсы от тс во время разработки меркнут по сравнению с минусами.
Но типизация помогает в рефакторинге. Идешки подсказывают по методам достаточно хорошо. Но тут, что флоу, что тс, вполне хорошо справляются.

Но когда знакомишься с языком с настоящей типизацией, то писать на тс становится невозможно. На флоу ещё более менее, потому что там только хинты типов, не более. А тс вроде как язык, но мало того, что обманывает с типами, так ещё и никаких фич работы с типами не предоставляет.

Так что тс отправляется в мусорку. Флоу может быть и можно использовать. Из перспективных типизаций вижу только полноценные языки поверх жс: elm, reason, или ещё что.
Forwarded from artalar
Forwarded from artalar
События - это не команды.
Событие - это не намерение что-то сделать, а оповещение о том что случилось
Forwarded from artalar
В componenDidMount компонента не нужно, пожалуйста, тригерить экшен "loadData", правильнее стрегерить "componentMounted"