Как же обстоят дела с веб-разработкой в расте?
Для начала стоит начать с сайта 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. В этот раз, я решил рассмотреть его поближе. И о чудо, оно заработало, очень просто и приятно. Весь бойлерплейтный код скрывается в макросах
Имеется валидация параметров, входных данных, параметров query, cookies прямо из коробки да по структурам раста, и для этого не нужно писать лишнего кода, просто указать аргументы функции. Меня всё это очень сильно подкупило. Весьма неплохие гайды и документация по API сделали своё дело, за пару дней я написал несложное приложение с двумя роутами, прикрутил базу PostgreSQL с помощью diesel.rs и впридачу получил красивый логгер для отладки. Ко всему прочему, rocket из коробки имеет гайд по настройке пула соединений с базой на основе r2d2 docs.rs/crate/r2d2 и это прекрасно.
Вердикт: продолжаю использовать rocket + diesel для своего pet-проекта.
Для начала стоит начать с сайта 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-проекта.
На счёт JWT небольшой текст.
Вижу смысл юзать его только для общения между серверами в неких опасных условиях.
Но почему оно того не стоит, можно прочесть по ссылке https://t.iss.one/why_jwt_is_bad
Вижу смысл юзать его только для общения между серверами в неких опасных условиях.
Но почему оно того не стоит, можно прочесть по ссылке https://t.iss.one/why_jwt_is_bad
Я слез с Rocket, когда не получилось написать трейт/структуру для указания аутентификации для роута.
Рокет позволяет с помощью макроса на хендлере роута указать миддлвары. О request guards можно почитать здесь.
Приблизительно, как это работает, макрос создает функцию которая будет вызываться каждый раз при запросе в указанный роут. В этой функции генерируется весь обслуживающий код, в том числе специальный вызов написанного хендлера с параметрами. Но так как Rust позволяет обратиться к дженерик типу (`T::some_method()`), перед вызовом хендлера, вызывает методы у типов его аргументов (`AuthOptional::from_request(&req)`).
Я хотел указывать там аутентификаци: опциональную или требуемую.
Опциональная: хендлер сам решает, что ему делать в случае отсутствия аутентификации. Чуть ли не через простой
Требуемая: миддлвара пропускает в хендлер только аутентифицированные запросы и кладет куда-то инстанс юзера, для удобства. Анонимам делает отлуп с
В nodejs это решается просто и удобно:
Всё было хорошо до момента, когда мне понадобилось из такой миддлвары вернуть кастомную ошибку и обработать ее для всех роутов. Пообщавшись с автором, оказалось, что такой функциональности тупо нет. Может быть сейчас уже добавили или я тогда не так изъяснялся, не знаю. Но мне не сильно вкатила идея макросов для роутов, так как это жесткое прибивание гвоздями пути роутера к хендлеру.
Рокет позволяет с помощью макроса на хендлере роута указать миддлвары. О 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 😄.
Сами хендлеры не заворачиваются в макросы и остаются обычными функциями от аргументов. Но большинство хендлеров имеют тип
Но на самом деле, хендлер это тоже трейт —
Итак, что это даёт? Actix также как и Rocket умеет принимать хендлеры с кастомными аргументами.
Типичный хендлер выглядит так.
Но количество аргументов можно расширить через кортеж.
С таким подходом, реализовать отлуп required аутентификации становится в разы проще. Я написал две простеньких структуры
Ссылка на код в howtocards
Пример использования
Я не очень внимательно читал документацию по экстракторам и долго пытался понять как мне реализовать эту фичу. Пришлось связаться с автором actix и выяснить тонкости. Изначально, я хотел реализовать свой
Посмотреть примеры работы с actix можно посмотреть в репозитории sentry в директории server.
Также интересные подходы в написании веб-сервера есть в исходниках crates.io.
Сам 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.
Сова пишет… via @like
Почему директории utils и helpers это свалка?
Начнем с того, как они появляются. Во время разработки проекта, программист старается выносить повторяющиеся куски кода в функции и переиспользовать их. В какой-то момент, в двух разных модулях требуется одна и та же функция, неопытный разработчик решает вынести функцию в отдельный модуль. Но почему-то останавливается на этом решении, создавая директорию/модуль utils или helpers, не думая о будущем этой части программы.
С ростом проекта, количество функций в этой директории растет. Также растет и количество разработчиков в проекте. Функций появляется все больше и больше. Зачастую никто не следит за структурой этих модулей и вполне возможно появление дублирования кода.
Как это решить?
Забудьте о директориях utils и helpers. Эти названия никак не доносят суть содержимого. А из правил чистого кода мы помним, название должно быть однозначным и отражать назначение.
Следует задуматься, почему вообще появилась необходимость в вынесении функции?
Почему они используются в таких разных частях проекта?
Возможно, ответом будет плохая организация кода всего проекта. Если хорошо поисследовать проект можно найти много кода, который достоин вынесения в отдельную библиотеку.
Если же этот код нельзя или не нужно выносить в полноценную библиотеку, его можно вынести в библиотеку внутри проекта. В случае JS, это можно сделать, создав полноценную библиотку внутри lib/.
Такой библиотеке нужно дать осмысленное однозначное название, написать документацию и тесты. Причем собрать в этой библиотеке весь связанный код. Например, вынести все функции работающие с датами во внутреннюю библиотеку lib/datetime. Префикс lib/ позволит избежать конфликтов имен, а также можно будет использовать простые и понятные имена. Конечно же, можно сделать npm scope, чтобы упростить будущую публикацию в npm. Например, @lib/datetime.
Чем это отличается от lib и helpers?
- весь код, выносится в полноценную библиотку с осмысленным названием, документацией и тестами
- lib явно описывает содержимое: это библиотека
- рефакторинг, публикация или удаление этой библиотеки становятся тривиальными задачами
Начнем с того, как они появляются. Во время разработки проекта, программист старается выносить повторяющиеся куски кода в функции и переиспользовать их. В какой-то момент, в двух разных модулях требуется одна и та же функция, неопытный разработчик решает вынести функцию в отдельный модуль. Но почему-то останавливается на этом решении, создавая директорию/модуль 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://dev.to/ben/whats-the-difference-between-a-library-and-a-framework-3blo
В двух словах:
- В случае фреймворка, вы встраиваете свой код в его архитектуру
- Но библиотеку вы самостоятельно встраиваете в собственную архитектуру
DEV Community
What's the difference between a library and a framework?
Having a look at this comment by @kayis from another thread, I'm wondering if you agree with this st...
Наконец-то, хоть кто-то написал почему снапшот тесты компонентов бесполезны. Хотел уже давно описать, но руки перегорели.
https://foobarbaz.club/why-i-stopped-using-snapshot-testing-with-jest/
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
Поэтому я вынес ключевые слайды с комментариями в отдельный канал.
https://t.iss.one/joinchat/AAAAAFcbHfDlRV0eK8kt8w
Сова пишет… via @like
У меня каждая директория описывает либо одну сущность, либо их набор.
с файлами похожая штука
Вложение директорий определяется вложением сущностей.
— как понять, что есть сущность, а что нет?
— нужно определиться, можно ли с “этим” работать не погружаясь в детали. Можно ли к “этому” обращаться как единой цельной структуре?
страница регистрации сущность? Мы можем ее заимпортить как цельную сущность — да. Можем изменить именно эту страницу — да. Страница цельная сама по себе — да.
ui — сущность? мы можем заимпортить ui и работать с ним в коде — да. Можно ли к нему обращатсья как к цельной структуре — нет(он не несет в себе функциональности, только его части по отдельности ее имеют).
features/ — набор сущностейfeatures/name — одна сущность./pages/ — набор сущностейс файлами похожая штука
./reducer.js — одна сущность./actions.js — набор сущностейВложение директорий определяется вложением сущностей.
— как понять, что есть сущность, а что нет?
— нужно определиться, можно ли с “этим” работать не погружаясь в детали. Можно ли к “этому” обращаться как единой цельной структуре?
страница регистрации сущность? Мы можем ее заимпортить как цельную сущность — да. Можем изменить именно эту страницу — да. Страница цельная сама по себе — да.
ui — сущность? мы можем заимпортить ui и работать с ним в коде — да. Можно ли к нему обращатсья как к цельной структуре — нет(он не несет в себе функциональности, только его части по отдельности ее имеют).
Тем временем, я ухожу из коммерческой разработки на JavaScript.
Возможно, здесь будет больше постов о js и rust
Возможно, здесь будет больше постов о js и rust
@artalar подкинул статью: https://daneden.me/2018/01/05/subatomic-design-systems/
На первый взгляд выглядит проще Atomic Design, но имеет больше математических концепций.
На первый взгляд выглядит проще Atomic Design, но имеет больше математических концепций.
@sredov раздобыл такой подход: https://medium.com/ge-design/ges-predix-design-system-8236d47b0891
В его основе лежит @AtomicDesign
В его основе лежит @AtomicDesign
Medium
GE’s Predix Design System
Scaling Atomic Design for the Enterprise
Нашел очередной бандлер пакетов.
Эта штука позволит собрать почти любой package: javascript, typescript, reasonml, wasm и в несколько таргетов node, web, bin.
pika имеет минималистичный API и расширяемую систему плагинов.
Возможно, это сможет ускорить разработку библиотек. Сомнительно, что его можно использовать для сборки целых проектов.
https://github.com/pikapkg/pack
Эта штука позволит собрать почти любой package: javascript, typescript, reasonml, wasm и в несколько таргетов node, web, bin.
pika имеет минималистичный API и расширяемую систему плагинов.
Возможно, это сможет ускорить разработку библиотек. Сомнительно, что его можно использовать для сборки целых проектов.
https://github.com/pikapkg/pack
GitHub
GitHub - FredKSchott/pika-pack: 📦⚡️ Build your npm package using composable plugins. https://www.pika.dev/blog/introducing-pika…
📦⚡️ Build your npm package using composable plugins. https://www.pika.dev/blog/introducing-pika-pack/ - GitHub - FredKSchott/pika-pack: 📦⚡️ Build your npm package using composable plugins. https://...
Сова пишет… via @like
Пишу на жс 5+ лет. Из них писал 3 года на типизированном.
За это время успел полюбить и возненавидеть типизацию. В итоге сделал вывод, что типизация в жс, только замедляет разработку.
Потому что приходится тратить время на борьбу с тайпчекером, даже там, где по опыту знаешь, что всё збс будет работать.
А с тс вообще нельзя быть уверенным, что если тайпчек прошел, всё будет работать. Всё равно запускаешь код и проверяешь, что всё ок. Плюсы от тс во время разработки меркнут по сравнению с минусами.
Но типизация помогает в рефакторинге. Идешки подсказывают по методам достаточно хорошо. Но тут, что флоу, что тс, вполне хорошо справляются.
Но когда знакомишься с языком с настоящей типизацией, то писать на тс становится невозможно. На флоу ещё более менее, потому что там только хинты типов, не более. А тс вроде как язык, но мало того, что обманывает с типами, так ещё и никаких фич работы с типами не предоставляет.
Так что тс отправляется в мусорку. Флоу может быть и можно использовать. Из перспективных типизаций вижу только полноценные языки поверх жс: elm, reason, или ещё что.
За это время успел полюбить и возненавидеть типизацию. В итоге сделал вывод, что типизация в жс, только замедляет разработку.
Потому что приходится тратить время на борьбу с тайпчекером, даже там, где по опыту знаешь, что всё збс будет работать.
А с тс вообще нельзя быть уверенным, что если тайпчек прошел, всё будет работать. Всё равно запускаешь код и проверяешь, что всё ок. Плюсы от тс во время разработки меркнут по сравнению с минусами.
Но типизация помогает в рефакторинге. Идешки подсказывают по методам достаточно хорошо. Но тут, что флоу, что тс, вполне хорошо справляются.
Но когда знакомишься с языком с настоящей типизацией, то писать на тс становится невозможно. На флоу ещё более менее, потому что там только хинты типов, не более. А тс вроде как язык, но мало того, что обманывает с типами, так ещё и никаких фич работы с типами не предоставляет.
Так что тс отправляется в мусорку. Флоу может быть и можно использовать. Из перспективных типизаций вижу только полноценные языки поверх жс: elm, reason, или ещё что.
Forwarded from artalar
События - это не команды.
Событие - это не намерение что-то сделать, а оповещение о том что случилось
Событие - это не намерение что-то сделать, а оповещение о том что случилось
Forwarded from artalar
В componenDidMount компонента не нужно, пожалуйста, тригерить экшен "loadData", правильнее стрегерить "componentMounted"