Ruby https://digest.evrone.ru/ ru Возврат к истокам https://digest.evrone.ru/20230918 <img src="/sites/default/files/styles/card_m/public/2023-09/image1.png?itok=bC9RkMtA" width="960" height="540" alt="Возврат к истокам" loading="lazy" typeof="Image" /> <br /> <p>Принимать непопулярные решения сложно. Но это не помешало Дэвиду Хейнемейеру Ханссону заявить, что он с удовольствием откажется от TypeScript в будущем релизе Turbo 8. Вместе с причинами этого поступка мы расскажем о втором превью Ruby 3.3.0 и узнаем о том, что такое I.D.E.A.L. по версии Злых Марсиан.</p> <h2>DHH: TypeScript мне только мешает</h2> <p>Два года назад, в первый день Ruby Russia’21, DHH громко заявил о новом стеке технологий Hotwire (Turbo 7 + Stimulus 3). Об этом мы <a href="https://digest.evrone.ru/20211101" rel=" noopener" target="_blank">писали</a> в одном из наших дайджестов. Забавно совпало — снова громкое заявление накануне <a href="https://rubyrussia.club/?utm_source=mail&amp;utm_medium=digest&amp;utm_campaign=Rubyrussia" rel=" noopener" target="_blank">Ruby Russia’23</a>. Теперь DHH заявил, что в следующем мажорном обновлении Turbo 8 будет <a href="https://world.hey.com/dhh/turbo-8-is-dropping-typescript-70165c01" rel=" noopener" target="_blank">убрана поддержка TypeScript</a>. Это вызвало бурные обсуждения в сообществе, так что мы решили узнать причину такого решения.</p> <p>Холивар вокруг статической и динамической типизации продолжается уже много лет. Идея наполнить JS явными типами и иметь возможность проверять их компилятором нравится многим программистам, но не DHH. Для него необходимость использования компилятора и «загрязнение кода» указанием типов никак не добавило радости процессу разработки, скорее наоборот. Лёгкие вещи стали сложнее, а трудные превратились в мучение. Так что решение выпилить TS из Turbo 8 стало очевидным способом облегчить себе жизнь и одновременно сделать код чище.</p> <p>Важно понимать, что у разработчиков никто не отбирает возможность писать на TS или включать библиотеки, которые используют TS. Но, по мнению DHH, в конечном итоге запуск кода в браузере будет означать запуск JS. А значит наиболее правильным будет использовать те «плюшки», которые даёт динамическая типизация. Своего рода возвращение к истоками, ведь JS изначально был свободен от статической типизации. Возвращение к этой свободе будет для Turbo 8 лучшим выбором (по мнению Дэвида). </p> <h2 id="article_title_7">Митапы</h2> Онлайн <h3>Ruby Russia</h3> <p>30 сентября 2023</p> <p>Один день, 14 новейших докладов, запись в высоком разрешении — всё это <a href="https://rubyrussia.club/?utm_source=digest&amp;utm_medium=site&amp;utm_campaign=Rubyrussia" rel=" noopener" target="_blank">RubyRussia’23</a>. Мы соберём рубистов со всей России и ближнего зарубежья, чтобы с ведущими разработчиками обсудить будущее нашего любимого языка и всего, что построено вокруг него.</p> <p>Онлайн-конференция — одновременно шоу для коллег-разработчиков и площадка для обсуждений. Присоединяйтесь к нашей трансляции: вас ждут ёмкие 20-минутные доклады, общение с коллегами по стеку и развлечения от наших партнёров.</p> <p><a class="info-button" href="https://rubyrussia.club/?utm_source=digest&amp;utm_medium=site&amp;utm_campaign=Rubyrussia" rel=" noopener" target="_blank">Регистрация</a></p> <h2>Второе превью Ruby 3.3.0</h2> <p>Мы уже <a href="https://digest.evrone.ru/20230601" rel=" noopener" target="_blank">упоминали</a> выход первого превью Ruby 3.3.0 и дебют RJIT. Из подробностей можно отметить, что пока будет поддерживаться только архитектура x86-64 на Unix. Разумеется, пока что RJIT лишь экспериментальная функция и годится лишь для тестовых целей.</p> <p>Ещё одно интересное предложение поступило относительно парсинг-генератора. В данный момент Ruby полагается на Bison, и это вызывало некоторые проблемы с совместимостью. <a href="https://bugs.ruby-lang.org/users/7872" rel=" noopener" target="_blank">Kaneko Yuichiro</a> предложил заменить Bison на <a href="https://github.com/yui-knk/lrama" rel=" noopener" target="_blank">Lrama LALR</a>. Этот парсинг-генератор создан на чистом Ruby и более устойчив к ошибкам. Matz согласился с предложением и отметил, что это поможет решить проблемы совместимости в будущих версиях.</p> <p>Большая часть изменений коснётся YJIT. Помимо улучшенной поддержки аргументов splat и rest, было внесено множество доработок для увеличения производительности. Удалось даже разогнать знаменитый эмулятор <a href="https://github.com/mame/optcarrot" rel=" noopener" target="_blank">optcarrot</a> более чем в 3 раза. Метаданные для скомпилированного кода теперь занимают меньше памяти, а приложения для архитектуры ARM64 получаются компактнее. Помимо этого появилась интересная фича, позволяющая поставить YJIT на паузу. Это можно использовать, чтобы включать YJIT в работу только после запуска приложения.</p> <h2>I.D.E.A.L. от Злых Марсиан</h2> <p>Наши инопланетные «коллеги по цеху» на днях выпустили отличный сборник best practices по использованию HTTP-клиентов. Шутливое название I.D.E.A.L. поневоле ассоциируется с боевой системой V.A.T.S. (Vault-Tec Assisted Targeting System) из Fallout 3. Здесь же аббревиатура представляет собой набор принципов:</p> <ul> <li aria-level="1">I — Investing in stability (mandatory)</li> <li aria-level="1">D — Debugging uncertainties (mandatory)</li> <li aria-level="1">E — Exploring the client (mandatory)</li> <li aria-level="1">A — Advancing with abstractions (opinionated)</li> <li aria-level="1">L — Laying out a sound structure (opinionated)</li> </ul> <p>HTTP-соединения по своей природе нестабильны: пакеты теряются, а сервисы могут вести себя непредсказуемо. Для достижения первого принципа инвестирования в стабильность потребуется чётко определить рамки, при выходе за которые пора поднимать тревогу. Грамотно настроенные таймауты соединения не позволят вашим сервисам зависать на неопределённое время. К этому же принципу стоит отнести настройку повторных попыток подключения.</p> <p>В мире нет ни одного программиста, которому не приходилось сталкиваться с отладкой неопределённого поведения приложения. Здесь для соблюдения принципа Debugging uncertainties имеет смысл вести журнал внешних запросов. Поначалу он может показаться дополнительной бесполезной рутиной, но это впечатление обманчиво. Когда вы столкнётесь с неопределенным поведением, этот журнал станет вашим козырем в рукаве. Вести такой журнал можно как собственными силами, так и с помощью готового решения, например, <a href="https://evilmartians.com/opensource/yabeda" rel=" noopener" target="_blank">Yabeda</a>.</p> <p>Почитать про остальные принципы можно в <a href="https://evilmartians.com/chronicles/its-dangerous-to-go-alone-take-our-guide-to-the-ideal-http-client" rel=" noopener" target="_blank">оригинальной статье</a> Evil Martians.</p> <h2 id="article_title_8">Вакансии</h2> <p>Удаленка / Офис</p> <h3>Evrone </h3> <p>Мы открыты для новых Ruby-разработчиков. В Evrone можно работать удалённо с первого дня, мы поддерживаем и оплачиваем участие в Open-source проектах и выступления на конференциях, а расти в грейдах можно с помощью честной системы проверки навыков и менторства.</p> <p><a class="info-button" href="https://jobs.evrone.ru/?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_9_2023" rel=" noopener" target="_blank">Подробнее</a></p> Mon, 18 Sep 2023 10:26:02 +0000 Evrone digest /20230918 Код, который пишет код https://digest.evrone.ru/20230803 <img src="/sites/default/files/styles/card_m/public/2023-08/Vgnhjfdjhg.png?itok=0GSAeLO7" width="960" height="540" alt="Код, который пишет код" loading="lazy" typeof="Image" /> <br /> <p>Время летит незаметно и пришла пора выпустить наш августовский Ruby-дайджест. В нём поговорим про метапрограммирование и устроим небольшую Q&amp;A-сессию на тему сборки мусора. Бонусом расскажем, как использовать платформу Jupiter notebook не с Python, а с Ruby.</p> <h2>Метапрограммирование в Ruby</h2> <p>Многие разработчики мечтают освоить метапрограммирование, но придерживаются принципа из фильма «О чём говорят мужчины»: </p> <p>«— А так, а так она мечта, её не нужно трогать руками. </p> <p>— Слав, не трогай руками!»</p> <p>Перед тем, как тратить своё драгоценное время, стоит задуматься: «А что даст это знание?». Если кратко, то метапрограммирование поможет:</p> <ul> <li aria-level="1">cоблюдать <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel=" noopener" target="_blank">принцип DRY</a> (Don't repeat yourself);</li> <li aria-level="1">эффективно переиспользовать собственный код;</li> <li aria-level="1">легче решать проблемы со сторонними зависимостями, в которых используется метапрограммирование.</li> </ul> <p>Освоение метапрограммирования нельзя назвать простым, это скорее «задача со звёздочкой». Здесь редко встречаются инструменты, позволяющие облегчить жизнь программиста. Поначалу будет много непонятных штук, странного синтаксиса и причудливых блоков кода. Но это всё пройдёт.</p> <p>В начале даже нет нужды сразу погружаться во все тонкости. Хорошей идеей будет освоить хотя бы три простых метода: send, define_method и method_missing:</p> <p>Send обеспечивает динамическую передачу методов объектам. При этом необязательно знать, каким будет объект или метод во время написания кода. Вызов делается следующим образом: my_object.send(:method_name). А для передачи дополнительных аргументов используется: my_object.send(:method_name, argument1, argument2...). Send может принимать именованные аргументы или методы. Это даёт возможность сильно упростить код. Например:</p> def sell_or_refund_or_return_item(item, action) if action == "sell" item.sell elsif action == "refund" item.refund elsif action == "void" item.void elsif action == "return" item.return end end <p>Его можно легко переписать, передав метод в качестве аргумента. Вызываем его через send. Profit:</p> def process_item(item, action) return unless item.respond_to?(action) item.send(action) end <p>Будьте осторожны, поскольку send может вызывать как общедоступные, так и приватные методы. Если надо убедиться, что вызван именно общедоступный метод, лучше воспользоваться public_send.</p> <p>Если не испугались этого примера и хотите узнать больше об остальных методах, то рекомендуем почитать <a href="https://blog.appsignal.com/2023/07/26/an-introduction-to-metaprogramming-in-ruby.html" rel=" noopener" target="_blank">профильную статью</a> в блоге AppSignal.</p> <h2>Q&amp;A по GC</h2> <p>В одном из наших <a href="https://digest.evrone.ru/20220804" rel=" noopener" target="_blank">прошлых дайджестов</a> мы рассказывали, как уберечь приложение от рук OOM-киллера и предотвратить утечки памяти. Сегодня попробуем взглянуть на этот механизм чуть пристальнее в формате Q&amp;A.</p> <p>Где в Ruby живёт сборщик мусора? В файле gc.c. Помимо сборки мусора, код из этого файла отвечает за выделение и управление памятью. Сборщик мусора полностью контролирует весь жизненный цикл созданных объектов, за редким исключением.</p> <p>Все ли типы объектов работают одинаково? Формально можно разделить все типы на 2 группы. Первая — immediates. Объекты этой группы типов ведут себя как остальные, но при этом не подчиняются сборщику мусора. Например, nil, true/false, Fixnum (в диапазоне между -(2**62) и 2**62 - 1 на 64-битных системах), числа с плавающей точкой (за исключением некоторых) и статические символы. Вторая группа объектов, heap allocated, целиком полагается на управление сборщиком мусора.</p> <p>Сколько бывает типов объектов в куче? В общей сложности существует 14 типов объектов.</p> <p>Какой тип у указателей на объекты Ruby? Тип VALUE.</p> <p>Из чего состоит куча? Куча состоит из страниц, каждая из которых имеет размер 64 КБ. Далее страницы делятся на слоты фиксированного размера. У каждой страницы слоты имеют единый размер в 40 байт. Если несколько объектов будут повторяться на одной странице памяти, то не потребуется не изменять размеры слотов.</p> <p>Как выделяется память? Сборщик памяти выделяет память постранично, а не отдельными слотами. Это обусловлено соображениями производительности. Выделять память реже, но большими порциями эффективнее, чем часто, но маленькими.</p> <p>Как работает Variable Width Allocation? Смысл этой функции в предоставлении разработчикам API для выделения объектов динамического размера. Также она добавляет поддержку слотов переменного размера внутри сборщика мусора. Жаль лишь, что функция не поддерживает все типы объектов.</p> <p>Что такое fast path? Термин fast path применим к распределению памяти для абстракций Ractor. Каждый экземпляр класса Ractor имеет кэш, который заранее содержит список связанных свободных слотов. Забронировав свободные слоты, несколько экземпляров Ractor могут выделять память объектам параллельно, без риска возникновения условий гонки. Это работает, пока свободные слоты в кэше не кончатся. Тогда придётся использовать slow path. </p> <p>Что такое slow path? Каждый Ractor поочерёдно блокирует виртуальную машину Ruby и начинает искать связанные списки свободных слотов по страницам кучи. Как только такая страница найдена, список перемещается в кэш Ractor. Если поиск успехом не увенчался, то система проверяет разрешение на выделение новой страницы кучи и если оно есть, то происходит выделение новой страницы. Отсутствие разрешение означает, что запущен цикл сборки мусора и надо подождать его окончания.</p> <p>Правда ли, что код Ruby перестаёт выполняться, когда выполняется цикл сборки мусора? Так и есть. Как в знаменитой рекламе «…и пусть весь мир подождёт». В больших приложениях это может вызывать длительные паузы. Чтобы избежать этого, в Ruby применяется два решения: инкрементная маркировка объектов и использование сборщика мусора на основе поколений.</p> <p>Надеемся, что такая маленькая Q&amp;A-сессия освежила ваши знания. Ну а если хотите узнать ещё подробностей про сборку мусора в Ruby, то советуем заглянуть в статью <a href="https://blog.peterzhu.ca/notes-on-ruby-gc/" rel=" noopener" target="_blank">Garbage Collection in Ruby</a>.</p> <h2>Jupiter notebooks для Ruby</h2> <p>Что приходит на ум, когда вы слышите словосочетание «Jupiter notebook»? Правильно — Python. Исторически так сложилось, что чаще всего функциональность платформы Jupiter notebook требуется специалистам по анализу данных. Те, в свою очередь, задействуют обширный инструментарий, вроде NumPy, pandas и Matplotlib. Все они написаны на Python, так что именно с ним чаще всего приходится иметь дело.</p> <p>На самом деле, Jupiter notebook отлично <a href="https://nithinbekal.com/posts/ruby-jupyter-notebooks/" rel=" noopener" target="_blank">сочетается</a> с Ruby. Есть крутой проект <a href="https://github.com/SciRuby/iruby">IRuby</a>, представляющий собой Ruby-ядро для проекта Jupiter. Вначале установим Jupiter с помощью инсталлятора Python:</p> pip install jupyter <p>IRuby поставляется в виде обычного gem. Устанавливаем:</p> gem install iruby <p>Регистрируем IRuby в качестве ядра для Jupiter:</p> iruby register --force <p>Открываем Jupiter Notebook:</p> jupyter notebook <p>Далее Kernel &gt; Change kernel. Там будет доступен Ruby. Осталось создать свой первый Ruby notebook и создать Hello World для проверки:</p> puts "Hello, world!" <p>Писать всё в одиночку скучно. Предлагаем привлечь к этому делу генеративную нейросеть от OpenAI. Для этого предусмотрен соответствующий gem. Его можно установить отдельно или указать в качестве зависимости. Можно написать прямо в notebook:</p> require "bundler/inline" gemfile do source "https://rubygems.org" gem "dotenv", require: "dotenv/load" gem "ruby-openai" end <p>Здесь помимо <a href="https://rubygems.org/gems/ruby-openai/versions/0.1.0" rel=" noopener" target="_blank">ruby-openai</a> указан <a href="https://rubygems.org/gems/dotenv/versions/2.1.1" rel=" noopener" target="_blank">dotenv</a>. Это нужно для удобного хранения токена OpenAI в файле с расширением .env. Его можно будет положить прямо в директорию c notebook. Содержимое файла будет выглядеть так:</p> OPENAI_ACCESS_TOKEN=your_key <p>Теперь можно инициализировать клиент строкой:</p> client = OpenAI::Client.new(access_token: ENV["OPENAI_ACCESS_TOKEN"]) <p>Для проверки возьмём пример из README проекта ruby-openai:</p> response = client.chat( parameters: { model: "gpt-3.5-turbo", messages: [{ role: "user", content: "Hello!"}], temperature: 0.7, } ) <p>Если у вас есть действующая подписка и валидный токен, то система в ответ на приветствие выдаст что-то вроде «Hello! How can I assist you today?». Удачных экспериментов!</p> <h2 id="article_title_7">Митапы</h2> Онлайн <h3>Ruby meetup</h3> <p>22 ноября 2023</p> <p>Осенью у нас состоится ещё один Ruby Meetup, который пройдёт в формате онлайн. Программа мероприятия формируется и скоро станет доступна <a href="https://meetups.evrone.ru/ruby-meetup-online?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_8_2023" rel=" noopener" target="_blank">на странице мероприятия</a>. Кстати, если вы не успели <a href="https://docs.google.com/forms/d/e/1FAIpQLSedDbmB8mAtDb5_bKgUnmh8f6apOYXDsdvtXt1JPz7VoWqX2A/viewform?usp=sf_link" rel=" noopener" target="_blank">подать доклад</a>, то можете это сделать прямо в режиме онлайн. Заявки на участие принимаются до 1 ноября.</p> <p>Теперь следить за митапами Evrone стало удобнее. В Telegram-канале <a href="https://t.me/meetups_evrone" rel=" noopener" target="_blank">Evrone meetups</a> мы выкладываем анонсы с подробными описаниями докладов, а также студийные записи после мероприятий. А ещё, у нас можно выступить, мы поможем оформить вашу экспертизу в яркое выступление. Подписывайтесь и пишите <a href="https://t.me/andrew_aquariuss" rel=" noopener" target="_blank">@andrew_aquariuss</a>, чтобы узнать подробности.</p> <p><a class="info-button" href="https://meetups.evrone.ru/ruby-meetup-online?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_8_2023" rel=" noopener" target="_blank">Регистрация</a></p> <h2 id="article_title_8">Вакансии</h2> <p>Удаленка / Офис</p> <h3>Evrone </h3> <p>Мы открыты для новых Ruby-разработчиков. В Evrone можно работать удалённо с первого дня, мы поддерживаем и оплачиваем участие в Open-source проектах и выступления на конференциях, а расти в грейдах можно с помощью честной системы проверки навыков и менторства.</p> <p><a class="info-button" href="https://jobs.evrone.ru/?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_8_2023" rel=" noopener" target="_blank">Подробнее</a></p> Thu, 03 Aug 2023 10:28:36 +0000 Evrone digest /20230803 Ещё парочку https://digest.evrone.ru/20230710 <img src="/sites/default/files/styles/card_m/public/2023-07/sfhnmgj%20%281%29.png?itok=BKZRKU24" width="960" height="540" alt="Ещё парочку" loading="lazy" typeof="Image" /> <br /> <p>Этот дайджест мы посвятили двум крутым фреймворкам: Glimmer и Camping. Первый позволяет создавать крутые GUI-приложения на Ruby, а второй — делать веб-приложения. Бонусом обсудили, являются ли векторы «новым JSON».</p> <h2>Строим GUI через Glimmer</h2> <img alt="AppExample" data-entity-type="file" data-entity-uuid="fe6bc600-1900-4447-99c1-5e7e11a74c3a" class="align-center lazyload" width="406" height="339" loading="lazy" data-src="/sites/default/files/inline-images/image3_5.png" /> <p>Если нужно создать нативное приложение с графическом интерфейсом на Ruby, советуем обратить внимание на DSL-фреймворк <a href="https://github.com/AndyObtiva/glimmer" rel=" noopener" target="_blank">Glimmer</a>. Название произошло от сокращённого выражения «Glimmer of Ruby in Graphical User Interfaces», то есть «проблеск Ruby в графических пользовательских интерфейсах». Аббревиатура DSL (Domain-specific language) в названии указывает на использование синтаксиса, «заточенного» под описание графических интерфейсов. Получается такой мини-язык внутри языка.</p> <p>Автор Glimmer, канадский программист Энди Малех (Andy Maleh) изначально задумывал Glimmer, как простую библиотеку, добавляющую графический интерфейс. Позже она превратилась в полноценный фреймворк. Идея <a href="https://andymaleh.blogspot.com/2007/12/data-shining-in-glimmer.html" rel=" noopener" target="_blank">родилась</a> в 2007 году, когда Энди со своим коллегой занимались парным программированием. Им обоим пришло в голову, что синтаксис связывания данных неплохо было бы упростить.</p> <p>На тот момент они частенько писали что-то вроде:</p> dbc.bind(nameTextBox, new Property(person, "name"), null); dbc.bind(ageSpinner, new Property(person, "age"), null); <p>В качестве идеи родился абстрактный вариант:</p> (nameTextBox, text) &lt;=&gt; (person, name) (ageSpinner, selection) &lt;=&gt; (person, age) <p>Который в Ruby выглядел вообще шикарно:</p> [@name_text_box, :text] &lt;=&gt; [@person, :name] [@age_spinner, :selection] &lt;=&gt; [@person, :age] <p>Оператор &lt;=&gt;, остроумно прозванный космическим кораблём, в DSL Glimmer означает двустороннее связывание данных. Одностороннее связывание обозначалось бы оператором &lt;=. С лёгкой руки этот синтаксис назвали <a href="https://github.com/AndyObtiva/glimmer#shine-data-binding-syntax" rel=" noopener" target="_blank">Shine</a>. </p> <p>Собственно, Glimmer состоит из двух частей: DSL-движка и библиотеки связывания данных. Его синтаксис ограничен mixin-классами, что позволяет не засорять пространство имён. Разработчик всё также пишет на обычном Ruby, но когда надо, использует синтаксис Glimmer. Вот, как выглядит Hello, world с использованием DSL Glimmer для LibUI:</p> require 'glimmer-dsl-libui' include Glimmer window('hello world').show <img alt="HelloWorldGlimmer" data-entity-type="file" data-entity-uuid="2156e297-9842-414a-8b57-ec22ed0491f7" class="align-center lazyload" width="300" height="228" loading="lazy" data-src="/sites/default/files/inline-images/image2_12.png" /> <p>Стоит ли повнимательнее изучить возможности Glimmer? На мероприятии Fukuoka Ruby Awards в 2022 Glimmer <a href="https://andymaleh.blogspot.com/2022/02/glimmer-dsl-for-libui-wins-fukuoka-ruby.html" rel=" noopener" target="_blank">заслужил</a> специальную награду и пожелание успеха от самого Матца. Отличный повод попробовать и сделать собственные выводы.</p> <h2>Микрофреймворк Camping</h2> <p>Давно ли вы ходили в поход? Главное правило сбора рюкзака — соблюсти баланс между пользой и весом. Каждая вещь может расширить ваши возможности, но увеличит вес. Если переборщить с весом, то до применения дело может вовсе не дойти. А если взять слишком мало, то нехватка возможностей также не даст шанс завершить поход успешно.</p> <p>Главная фича микрофреймворков: они предоставляют лишь базовые возможности, а весь дополнительный функционал приходится добавлять за счёт сторонних библиотек. Этим достигается компактность приложения и присутствие в нём только необходимых компонентов. Но даже с таким минималистичным каркасом приложения, можно делать крутые проекты.</p> <p><a href="https://github.com/camping/camping" rel=" noopener" target="_blank">Camping</a> изначально был создан легендарным Джонатаном Джиллеттом (Jonathan Gillette), более известным под псевдонимом <a href="https://en.wikipedia.org/wiki/Why_the_lucky_stiff" rel=" noopener" target="_blank">why the lucky stiff</a>. Наряду с <a href="https://evrone.ru/blog/interviews/yukihiro-matsumoto-interview-2021" rel=" noopener" target="_blank">Matz</a> и <a href="https://evrone.ru/blog/interviews/dhh-interview" rel=" noopener" target="_blank">DHH</a>, он одна из ключевых фигур сообщества Ruby, хоть и полностью исчезнувший из инфополя 11 лет назад (об этом можно узнать в статье <a href="https://www.slate.com/articles/technology/technology/2012/03/ruby_ruby_on_rails_and__why_the_disappearance_of_one_of_the_world_s_most_beloved_computer_programmers_.html" rel=" noopener" target="_blank">Where’s _why?</a>). Развитие Camping было продолжено сообществом разработчиков.</p> <p>Идея Camping в том, чтобы всё веб-приложение умещалось в один файл и при этом было построено по модели MVC (Model-View-Controller). Да и сам микрофреймворк получился крошечным, менее 5 килобайт. Чтобы «выезд на природу» был проще, у Camping есть структурированная документация: <a href="https://github.com/camping/camping/blob/main/book/01_introduction.md" rel=" noopener" target="_blank">The Camping Book</a>. В ней есть и общая информация, и простые примеры.</p> <p>Отдельно отметим, что запущенный camping-сервер не требуется перезапускать при изменениях файлов. Вы просто их редактируете, а camping автоматически выполнит перезапуск. Если у вас есть несколько веб-приложений на этом фреймворке со своими индивидуальными моделями, контроллерами и маршрутизацией, то из них можно сделать единое целое. Camping умеет обслуживать такой объединённый код и это делает его ещё интереснее.</p> <h2>Векторы — новый JSON</h2> <p>Именно так <a href="https://jkatz05.com/post/postgres/vectors-json-postgresql/" rel=" noopener" target="_blank">утверждает</a> программист из Нью-Йорка, Джонатан Кац (Jonathan S. Katz). Мнение достаточно странное, ведь векторы — это математическая структура, а JSON — формат обмена данными. Но бурное развитие генеративных нейронных сетей уже создаёт запрос на удобные способы хранения и выполнения запросов к ним. И удовлетворить такой запрос могут векторы, которые давным-давно используют массивы в качестве базовой структуры данных. Ну а для хранения векторов вполне можно использовать PostgreSQL.</p> <p>Всё это очень напоминает 2012 год. У многих тогда была необходимость где-то хранить данные в JSON. Релиз PostgreSQL 9.2, в котором впервые был добавлен текстовый тип JSON, стал очень ожидаемым событием. Изначальная поддержка была весьма скромной: встроенной индексации не было, система могла выполнять лишь простые действия по извлечению данных и гарантировала пользователю, что в базе находится только валидный JSON.</p> <p>Эксперимент получился успешным и уже в PostgreSQL 9.4 появился формат JSONB, представляющий бинарный, а не текстовый тип JSON. Это дало возможность создавать индексы GIN (Generalized Inverted Index) и поставило производительность PostgreSQL в один ряд со специализированными базами данных. Приятным бонусом становятся все преимущества хранения в реляционной БД.</p> <p>Современные AI и ML-системы на выходе дают векторы, которые можно хранить в PostgreSQL. Но есть целый ряд ограничений, которые мешают это делать. В частности, такой тип данных, как cube, позволяет хранить векторы максимум с 100 измерениями. По нынешним меркам этого мало. Расширение <a href="https://github.com/pgvector/pgvector" rel=" noopener" target="_blank">pgvector</a> добавляет новый тип данных vector и метод индексации <a href="https://github.com/facebookresearch/faiss/wiki/Faiss-indexes" rel=" noopener" target="_blank">IVF FLAT</a>. Этого уже вполне достаточно, чтобы хранить векторные данные в PostgreSQL и выполнять семантический поиск по ним.</p> <p>pgvector пока тоже сильно ограничен в возможностях, но интерес к его развитию растёт. Основной упор, по мнению Джонатана, следует сделать на добавлении большего параллелизма и аппаратном ускорении вычислений. Это позволит избавиться от множества ограничений и сделает выполнение операций быстрее. Ну а для PostgreSQL это станет очередным этапом развития, который даст ему гордое звание векторной базы данных.</p> <h2>Интересно посмотреть</h2> <p>Пропустили наш предыдущий митап? Не страшно! Видеозаписи докладов уже выложены на нашем <a href="https://www.youtube.com/@EvroneDevelopment/" rel=" noopener" target="_blank">YouTube-канале</a> в кинематографическом качестве 4K и со студийным звуком.</p> <p>Подписывайтесь и ставьте 🔔колокольчик, чтобы получать уведомления о будущих трансляциях и видео! А ещё присоединяйтесь к <a href="https://t.me/meetups_evrone" rel=" noopener" target="_blank">Telegram-каналу</a> и будьте в курсе всех наших будущих мероприятий.</p> <h2 id="article_title_7">Митапы</h2> Онлайн <h3>Ruby meetup</h3> <p>30 августа 2023</p> <p>В конце лета мы запланировали ещё один Ruby Meetup, который пройдёт в формате онлайн. Программа мероприятия формируется и скоро станет доступна <a href="https://meetups.evrone.ru/ruby-meetup-no22-online?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_7_2023" rel=" noopener" target="_blank">на странице мероприятия</a>. Кстати, если вы не успели <a href="https://docs.google.com/forms/d/e/1FAIpQLSedDbmB8mAtDb5_bKgUnmh8f6apOYXDsdvtXt1JPz7VoWqX2A/viewform?usp=sf_link" rel=" noopener" target="_blank">подать доклад</a>, то можете это сделать прямо в режиме онлайн. Заявки на участие принимаются до 10 августа.</p> <p>Теперь следить за митапами Evrone стало удобнее. В Telegram-канале <a href="https://t.me/meetups_evrone" rel=" noopener" target="_blank">Evrone meetups</a> мы выкладываем анонсы с подробными описаниями докладов, а также студийные записи после мероприятий. А ещё, у нас можно выступить, мы поможем оформить вашу экспертизу в яркое выступление. Подписывайтесь и пишите <a href="https://t.me/andrew_aquariuss" rel=" noopener" target="_blank">@andrew_aquariuss</a>, чтобы узнать подробности.</p> <p><a class="info-button" href="https://meetups.evrone.ru/ruby-meetup-no22-online?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_7_2023" rel=" noopener" target="_blank">Регистрация</a></p> <h2 id="article_title_8">Вакансии</h2> <p>Удаленка / Офис</p> <h3>Evrone </h3> <p>Мы открыты для новых Ruby-разработчиков. В Evrone можно работать удалённо с первого дня, мы поддерживаем и оплачиваем участие в Open-source проектах и выступления на конференциях, а расти в грейдах можно с помощью честной системы проверки навыков и менторства.</p> <p><a class="info-button" href="https://jobs.evrone.ru/?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_7_2023" rel=" noopener" target="_blank">Подробнее</a></p> Mon, 10 Jul 2023 14:08:45 +0000 Evrone digest /20230710 Монолит вместо микросервисов https://digest.evrone.ru/20230601 <img src="/sites/default/files/styles/card_m/public/2023-06/%D0%9C%D0%BE%D0%BD%D0%BE%D0%BB%D0%B8%D1%82%20%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%BE%20%D0%BC%D0%B8%D0%BA%D1%80%D0%BE%D1%81%D0%B5%D1%80%D0%B2%D0%B8%D1%81%D0%BE%D0%B2.png?itok=aKmnRDPw" width="960" height="540" alt="Монолит вместо микросервисов" loading="lazy" typeof="Image" /> <br /> <p>Идти против тренда непросто, но это иногда приносит хороший результат. Расскажем, почему в одном из сервисов Amazon полностью отказалась от микросервисной архитектуры в пользу монолита. Ещё обсудим обновления полезных библиотек и экспериментальный JIT-компилятор, включённый в первое превью Ruby 3.3.0.</p> <h2>Amazon отказывается от микросервисов?</h2> <p>В то время, как разные компании разбивают монолиты на микросервисы, некоторые делают полностью противоположные вещи. Если против тренда идёт никому не известная компания, то можно покрутить пальцем у виска и не брать это в расчёт. Но когда мы говорим об опыте Amazon, игнорировать его будет странно. Мы обратили внимание на этот кейс после публикации в блоге автора Ruby on Rails, Дэвида Хейнемейера Ханссона. Кстати, мы брали у него интервью, которое можно найти <a href="https://evrone.ru/blog/interviews/dhh-interview" rel=" noopener" target="_blank">на нашем сайте</a>.</p> <p>Команда сервиса <a href="https://www.primevideo.com/offers/nonprimehomepage/ref=dv_web_force_root" rel=" noopener" target="_blank">Amazon Prime Video</a> решила полностью <a href="https://www.primevideotech.com/video-streaming/scaling-up-the-prime-video-audio-video-monitoring-service-and-reducing-costs-by-90" rel=" noopener" target="_blank">отказаться от микросервисной архитектуры</a>, заменив её монолитом. И результат оказался ошеломляющим — удалось сэкономить почти 90% эксплуатационных расходов, а система стала проще. Звучит удивительно, поскольку их инфраструктура изначально проектировалась как serverless-решение с прицелом на независимое масштабирование каждого компонента. Но в процессе использования был достигнут жёсткий предел масштабирования, а стоимость компонентов была бы непомерно высока.</p> <p>Сам сервис, который они строили, состоял из трёх основных компонентов: медиаконвертера, анализаторов дефектов и оркестратора потоков для управления всем этим безобразием. Первая ошибка: отдать управление оркестровкой компоненту <a href="https://aws.amazon.com/step-functions/" rel=" noopener" target="_blank">AWS Step Function</a>. Последнему для эффективной работы требовалось каждую секунду потока выполнять по несколько переходов состояния. Так что вскоре они упёрлись в лимит аккаунта, а также высокую стоимость, так как за каждый такой переход взимается плата.</p> <p>Вторая проблема была ещё интереснее. Каждый анализатор дефектов был реализован, как микросервис, использующий <a href="https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html" rel=" noopener" target="_blank">AWS Lambda</a> для обработки кадров. Эти кадры, в свою очередь, готовились другим микросервисом, который разбивал видео и временно загружал полученные изображения в объектное хранилище <a href="https://aws.amazon.com/s3/" rel=" noopener" target="_blank">AWS S3</a>. Всё это отлично работало, но общее количество вызовов в соответствующий S3 bucket было огромным и стоило слишком дорого.</p> <p>Всё это, вкупе со сложностью построенной системы, заставило инженеров задуматься, что в их случае микросервисная инфраструктура избыточна и не даёт каких-либо особых преимуществ. Упаковав все упомянутые компоненты в один процесс, они получили высокую скорость, так как обмен данными происходил в памяти. Также это позволило отказаться от объектного S3-хранилища в роли временного буфера. Концептуально вся архитектура осталась прежней, вот только теперь оркестровка выполнялась всего лишь для одного экземпляра, а не множества.</p> <p>Такие существенные изменения, разумеется, сказались на возможностях масштабирования. Раньше каждый детектор работал отдельно и мог масштабироваться по горизонтали и по вертикали. При горизонтальном масштабировании создавался новый микросервис, который затем подключался к оркестрации. В новой системе такой вариант невозможен и остаётся только вертикальное масштабирование, которое для каждого экземпляра не бесконечно. Для этого случая было придумано решение с клонированием сервиса и выделением лишь нужных типов детекторов.</p> <p>По <a href="https://world.hey.com/dhh/even-amazon-can-t-make-sense-of-serverless-or-microservices-59625580" rel=" noopener" target="_blank">словам DHH</a>, во многих отношениях микросервисы — это зомби-архитектура, которая уже стала токсичной. Замена вызовов методов на сетевые вызовы с разделением сервисов в рамках единой согласованной команды почти всегда становится безумной затеей. Вовсе не факт, что микросервисы решат задачу клиента, а вот навредить могут.</p> <h2>Nokogiri 1.15 вышла в релиз</h2> <p>Если вы часто работаете с XML и HTML в Ruby, то наверняка пробовали <a href="https://nokogiri.org/" rel=" noopener" target="_blank">Nokogiri</a>. Эта библиотека предоставляет простой и понятный API, имея внутри себя нативные парсеры, такие как <a href="https://github.com/GNOME/libxml2" rel=" noopener" target="_blank">libxml2</a>, <a href="https://github.com/google/gumbo-parser" rel=" noopener" target="_blank">libgumbo</a> и <a href="https://xerces.apache.org/" rel=" noopener" target="_blank">xerces</a>. Она спроектирована быть безопасной по-умолчанию, а также создаёт удобную для разработки абстракцию, которая не пытается исправить поведенческие различия между парсерами. Удобства ради поставляется в виде предварительно скомпилированных гемов для различных платформ. Установка чаще всего выполняется одной командой:</p> gem install nokogiri <p>Если вы уже пользовались Nokogiri ранее, то вышедшее обновление должно вас приятно удивить. С момента выхода первой версии, парсер libxml2 использовал ruby_xmalloc для управления памятью. Это было хорошим решением на тот момент, поскольку давало гибкость. Платой за это была производительность. С версии 1.15 у разработчика появляется возможность выбирать. Теперь можно пожертвовать гибкостью в обмен на производительность, выбрав системный malloc вместо ruby_xmalloc. Делается это через переменную окружения:</p> # "default" here means "libxml2's default" which is system malloc NOKOGIRI_LIBXML_MEMORY_MANAGEMENT=default <p>Бенчмарки показывают, что такой финт ушами радикально улучшает производительность. Но при этом возникает риск появления других проблем, таких как раздутый размер кучи и создания условий для вызова OOM-киллера. Насколько такая рокировка может быть оправдана — сказать сложно. Вероятно, для некоторых приложений это будет отличным способом оптимизации.</p> <p>Помимо этого нововведения была исправлены ошибки в поведении libxml2 и libxslt, вызывающие segfault. Если в документе присутствуют пустые текстовые узлы с созданными для них объектами Ruby, то будет создаваться защитная копия документа. Небольшая потеря скорости, в данном случае, лучше небезопасного доступа к памяти и потенциальному segfault.</p> <h2>Будущий RJIT</h2> <p>12 мая был открыт доступ к первой превью-версии будущего релиза Ruby. Помимо штатного и официально рекомендуемого компилятора <a href="https://github.com/Shopify/yjit" rel=" noopener" target="_blank">YJIT</a>, в сборку добавлен экспериментальный компилятор RJIT. Существующие компиляторы MJIT и YJIT имеют общий недостаток — они оба полагаются на внешние компиляторы. MJIT зависит от компилятора языка C во время выполнения, а YJIT от компилятора Rust во время сборки. Ноу-хау RJIT в том, что он создан на чистом Ruby, что делает его полностью самодостаточным.</p> <p>Звучит отлично, но пока что RJIT не самый оптимальный вариант, даже с учётом того, что он генерирует код, аналогичный YJIT. Последний значительно быстрее и стабильнее, особенно после оптимизации в будущей версии 3.3.0. </p> <p>По словам Такаши Кокубуна (Takashi Kokubun), штатного Ruby-разработчика в Shopify, есть <a href="https://k0kubun.medium.com/rjit-a-pure-ruby-jit-for-ruby-f4084f0765" rel=" noopener" target="_blank">два ключевых фактора</a>, которые для RJIT пока недостижимы. Первый — время прогрева. Для JIT-компиляторов характерно следующее поведение: первое выполнение какой-либо функции будет медленнее, чем последующие вызовы этой же функции. Соответственно, программа со временем начинает работать значительно быстрее. Чем меньше время прогрева, тем лучше.</p> <p>Второй фактор — объём потребляемой памяти. Если Ruby-приложение развёртывается на небольшой инфраструктуре, то этот фактор не играет большой роли. Но когда речь идёт о крупномасштабных кластерах, то каждый лишний мегабайт начинает существенно влиять на стоимость эксплуатации. Достигнуть компромисса для RJIT будет сложнейшей задачей, так что не стоит пока списывать со счетов YJIT. Но сам факт того, что альтернативные варианты <a href="https://appmaster.io/ru/news/ruby-330-predstavliaet-kompiliator-rjit-pure-ruby-jit" rel=" noopener" target="_blank">включаются</a> в язык, заставляют задуматься, что в этом направлении есть потенциал для совершенствования.</p> <p>Ну и напоследок, раз уже начали говорить о грядущем обновлении Ruby, отметим, что нас ждут изменения некоторых гемов в стандартной библиотеке. В частности, будут затронуты <a href="https://github.com/rubygems/rubygems" rel=" noopener" target="_blank">RubyGems</a>, <a href="https://ruby-doc.org/stdlib-2.5.1/libdoc/bigdecimal/rdoc/BigDecimal.html" rel=" noopener" target="_blank">bigdecimal</a>, <a href="https://bundler.io/" rel=" noopener" target="_blank">bundler</a> и <a href="https://github.com/ruby/syntax_suggest" rel=" noopener" target="_blank">syntax_suggest</a>.</p> <h2>Экосистема TTY</h2> <p>Пару дней назад обновился парсер <a href="https://github.com/piotrmurach/tty-option" rel=" noopener" target="_blank">tty-option</a> из <a href="https://ttytoolkit.org/" rel=" noopener" target="_blank">TTY Toolkit</a>, это побудило нас написать об этом старом, но надёжном наборе инструментов. В нём 24 плагина для взаимодействия с командной строкой. Каждый из них максимально прост и годится для выполнения конкретной задачи. Но при необходимости их легко комбинировать между собой и получать достойный результат. Всё это очень напоминает бар, где каждый напиток хорош сам по себе, но смешайте их и получится что-то новое.</p> <p>Чтобы получить доступ сразу ко всем инструментам из набора введите следующую команду в терминале:</p> gem install ‘tty’ <p>Теперь вам станут доступны многие джедайские техники. Например, <a href="https://github.com/piotrmurach/tty-cursor" rel=" noopener" target="_blank">tty:cursor</a> позволяет перемещать курсор терминала в нужные вам координаты. Также есть простые методы, вроде перемещения курсора на строку вверх или вниз. Ещё есть прекрасная функция очистки экрана целиком, текущей строки или заданного количества строк.</p> <p>Отдельно отметим <a href="https://github.com/piotrmurach/tty-screen" rel=" noopener" target="_blank">tty:screen</a>, возвращающий значения высоты и ширины текущего экрана. Это сразу же позволяет использовать их для форматирования вывода, например:</p> require 'tty-cursor' require 'tty-screen' @cursor = TTY::Cursor @size = TTY::Screen.size # =&gt; [height, width] @height = @size[0] @width = @size[1] def move_cursor_to_required_coordinates(text) x = (@size[1] - text.length) / 2 y = (@size[0]) / 2 print @cursor.move_to(x, y) end def centered_text(text) move_cursor_to_required_coordinates(text) puts text end centered_text("welcome to the center") <p>Потребуется приличное количество времени, чтобы изучить каждый из 24 гемов, но это того стоит. Рекомендуем попробовать.</p> <p> </p> <p> </p> <h2 id="article_title_7">Митапы</h2> Онлайн <h3>Ruby meetup</h3> <p>05 июля 2023</p> <p>Летом мы встретимся с вами на замечательном Ruby Meetup, который пройдёт в Москве. Программа мероприятия формируется и скоро станет доступна <a href="https://meetups.evrone.ru/ruby-meetup-no21-online?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_6_2023" rel=" noopener" target="_blank">на странице мероприятия</a>. Кстати, если вы не успели <a href="https://docs.google.com/forms/d/e/1FAIpQLSedDbmB8mAtDb5_bKgUnmh8f6apOYXDsdvtXt1JPz7VoWqX2A/viewform?usp=sf_link">подать доклад</a>, то можете это сделать прямо в режиме онлайн. Заявки на участие принимаются до 20 июня.</p> <p>Теперь следить за митапами Evrone стало удобнее. В Telegram-канале <a href="https://t.me/meetups_evrone" rel=" noopener" target="_blank">Evrone meetups</a> мы выкладываем анонсы с подробными описаниями докладов, а также студийные записи после мероприятий. А ещё, у нас можно выступить, мы поможем оформить вашу экспертизу в яркое выступление. Подписывайтесь и пишите <a href="https://t.me/andrew_aquariuss" rel=" noopener" target="_blank">@andrew_aquariuss</a>, чтобы узнать подробности.</p> <p><a class="info-button" href="https://meetups.evrone.ru/ruby-meetup-no21-online?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_6_2023" rel=" noopener" target="_blank">Регистрация</a></p> <h2 id="article_title_8">Вакансии</h2> <p>Удаленка / Офис</p> <h3>Evrone </h3> <p>Мы открыты для новых Ruby-разработчиков. В Evrone можно работать удалённо с первого дня, мы поддерживаем и оплачиваем участие в Open-source проектах и выступления на конференциях, а расти в грейдах можно с помощью честной системы проверки навыков и менторства.</p> <p><a class="info-button" href="https://jobs.evrone.ru/?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_6_2023" rel=" noopener" target="_blank">Подробнее</a></p> Thu, 01 Jun 2023 11:38:23 +0000 Evrone digest /20230601 Сложнее, чем кажется https://digest.evrone.ru/20230502 <img src="/sites/default/files/styles/card_m/public/2023-05/Chngmhjhg.png?itok=y2hMn54Y" width="960" height="540" alt="Сложнее, чем кажется" loading="lazy" typeof="Image" /> <br /> <p>В майском выпуске мы разберёмся, почему тесты в CI глючат чаще, чем при локальных прогонах. Также расскажем о подводных камнях при парсинге CSV-файлов и предупреждениях об устаревании.</p> <h2>Почему тесты в CI глючат чаще</h2> <p>Один и тот же тест может проходить успешно или заканчиваться падением, даже если код не менялся. Такое случается, если в коде приложения или теста проявляется своеобразный недетерминизм. В таком коде 2 + 2 не всегда будет равно 4. Иногда это 3, а иногда 5. При запуске на тестовый прогон CI, ситуации с нестабильностью возникают значительно чаще, чем при локальном прогоне.</p> <p>Такое случается по причинам:</p> <ul> <li aria-level="1">утечки состояния, </li> <li aria-level="1">условий для состояния гонки, </li> <li aria-level="1">зависимости сетевой или от стороннего поставщика,</li> <li aria-level="1">зависимости с фиксированным временем,</li> <li aria-level="1">случайного стечения обстоятельств.</li> </ul> <p>Посмотрим на них с точки зрения выполнения тестов. Если один тест пропускает в глобальную среду какое-то состояние (поменявшийся файл или переменную окружения), то это влияет на последующие тесты. В CI это случается чаще, поскольку тесты запускаются «пачками», а не по одному, как при локальном прогоне. Это даёт тестам больше возможностей мешать друг другу, даже когда CI старается изолировать запуски.</p> <p>Состояние гонки часто вызвано платформами, на которых запускаются тесты. Другая ОС, тактовая частота процессора или характеристики памяти легко порождают условия, в которых поведение кода будет отличаться. Например, параллельные действия начнут происходить в другой последовательности.</p> <p>Это лишь пара примеров, объясняющих проблемы в процессе тестирования. Чтобы узнать больше деталей, советуем прочитать статью <a href="https://www.codewithjason.com/why-tests-flake-more-on-ci-than-locally/" rel=" noopener" target="_blank">Why tests flake more on CI than locally</a>, написанную американским программистом <a href="https://www.codewithjason.com/about/" rel=" noopener" target="_blank">Джейсоном Светтом</a> (Jason Swett).</p> <h2>Обманчиво простой парсинг CSV</h2> <p>CSV-формату «стукнуло» уже более полувека, но он всё ещё супер-популярный. Его используют для того, чтобы хранить таблицы в текстовом виде. Название CSV расшифровывается, как Comma-Separated Values, дословно «значения, разделённые запятыми». Но это уже немного лукавство, ведь CSV не полностью <a href="https://datatracker.ietf.org/doc/html/rfc4180" rel=" noopener" target="_blank">стандартизирован</a>, так что значения легко могут разделяться точками с запятой или вообще табуляцией.</p> <p>В идеальном мире CSV-файлы должны были упростить парсинг, но если вы разбираете файлы, загруженные пользователями, то столкнётесь с <a href="https://blog.rubyhero.dev/solving-problems-with-csv-parsing" rel=" noopener" target="_blank">проблемами</a>. Файлы могут иметь искажённый формат, иметь неправильную кодировку, повторяющиеся заголовки или пустые строки. Количество вариантов, при которых парсинг завершится провалом, способно удивить даже опытных разработчиков.</p> <p>Взглянем на симптомы, часто возникающие у приложения в процессе парсинга. </p> <p>Например, высокое использование памяти. Методы read и parse хорошо работают лишь до того момента, пока вам не попадётся действительно большой CSV-файл. Проблема в том, что оба этих метода полностью загружают все данные в память. Чтобы этого избежать, мы рекомендуем использовать метод foreach.</p> <p>Ещё часто встречаются дублированные заголовки, такие как:</p> first_name,last_name,email,first_name,age\n John,Doe,doe@gmail.com,Johnatan,30\n Tom,Doe,tom@gmail.com,Thomas,40\n <p>Когда нам нужен доступ только к первому вхождению значений заголовка, то проблем не возникает — стандартная библиотека это сделает автоматически. А вот если нужен доступ к второму значению дублированного заголовка, то можно применить следующий трюк: преобразуйте строку в массив, а затем в хэш. Благодаря этому вы получите хэш и искомое второе значение:</p> data = CSV.read("./duplications.csv", headers: true) transformed_data = Hash[data.first.to_a] transformed_data['first_name'] # =&gt; “Johnatan” <p>Если же повторяющиеся заголовки именно то, что вам нужно и вы хотите собрать все значения — метод inject из стандартной библиотеки вам в помощь.</p> <h2>Не привыкайте к deprecation warnings</h2> <p>Для новичка любой warning поначалу кажется чем-то из ряда вон выходящим и требующим внимания. У опытных разработчиков вырабатывается чёткая установка: warning — это всего лишь warning, рутинное предупреждение. Примерно так курильщики реагируют на предупреждающие надписи на пачках сигарет. </p> <p>Если мы говорим о deprecation warnings, то в большинстве случаев их можно спокойно игнорировать. Но при переходе на следующую версию того же Rails — это первое, на что стоит обратить внимание. В качестве примера приведём такое предупреждение из Rails 6.0:</p> Accessing hashes returned from config_for by non-symbol keys is deprecated and will be removed in Rails 6.1. Use symbols for access instead. <p>Казалось бы ничего страшного. Ну удалят в следующем обновлении возможность использования несимвольных ключей в config_for. Пока что их использовать можно и приложение будет работать. Но как только вы обновитесь до 6.1 — вангуем спецэффекты. </p> <p>Вариантов два:</p> <ol> <li aria-level="1">Проигнорировать и надеяться, что потенциальную проблему отловят тесты, и уже тогда с ней разбираться.</li> <li aria-level="1">Действовать проактивно и исправить это сразу, чтобы потом тратить время не на исправления, а на что-то более интересное.</li> </ol> <p>Выбор за вами, но всё же один из вариантов кажется более разумным. И для тех, кто решил пойти по этому сложному пути, мы можем дать интересную идею.</p> <p>Любой девопс вам скажет, что нет ничего хуже заспамленных логов, в которые попадает куча устаревших версий. Их практическая ценность будет околонулевой. Грамотная обработка — ключ к успеху, благо Rails это позволяет сделать с хирургической точностью. Идея в том, чтобы настроить приложение на :notify при устаревании:</p> module MyAwesomeApp &lt; Rails::Application config.active_support.deprecation = :notify end <p>Теперь, когда мы столкнёмся с устареванием, Rails отправит событие deprecation.rails через <a href="https://api.rubyonrails.org/v7.0/classes/ActiveSupport/Notifications.html" rel=" noopener" target="_blank">ActiveSupport::Notifications</a>. Теперь нужно подписаться на эти события и настроить их обработку. Все события устаревания можно условно разделить на допустимые, которые можно смело игнорировать и недопустимые, которые нужно рассматривать, как ошибку. Цель — составление документированного списка всех устаревших версий и дальнейшее распределение их по команде.</p> <p>Так, понемногу, мы будем заранее приближать приложение к запуску в следующей версии Rails без риска столкнуться с существенными сложностями. Ну а практическую реализацию такого обработчика вы найдёте в статье <a href="https://blog.testdouble.com/posts/2023-04-24-stop-ignoring-your-ruby-and-rails-deprecations/#deprecations-help-you-upgrade-your-app-before-you-upgrade-your-app" rel=" noopener" target="_blank">Stop ignoring your Rails (and Ruby) deprecations!</a></p> <h2>Интересно посмотреть</h2> <p>Если вы пропустили наш предыдущий Ruby meetup, ничего страшного. Мы уже выложили на наш <a href="https://www.youtube.com/@EvroneDevelopment" rel=" noopener" target="_blank">YouTube-канал</a> записи докладов. Рекомендуем налить себе чашечку кофе и посмотреть их в 4K качестве с отличным студийным звуком.</p> <p>Александр Елистратов из BGaming сравнил RoR и GoLang с точки зрения подхода к разработке, использования библиотек и фреймворков, основываясь на личном опыте использования в коммерческих проектах:</p> <p>Артем Соломатин из Samokat.tech рассказал в докладе, что такое distroless-образы, зачем они нужны. Детально разобрал их плюсы и минусы, а также привёл примеры реального использования:</p> <h2 id="article_title_7">Митапы</h2> Онлайн <h3>Ruby meetup №20</h3> <p>05 июля 2023</p> <p>Летом мы встретимся с вами на замечательном Ruby Meetup, который пройдёт в Москве. Программа мероприятия формируется и скоро станет доступна <a href="https://meetups.evrone.ru/ruby-meetup-no21-online?utm_source=digest_site&amp;utm_medium=digest&amp;utm_campaign=ruby_digest_5_2023" rel=" noopener" target="_blank">на странице мероприятия</a>. Кстати, если вы не успели <a href="https://docs.google.com/forms/d/e/1FAIpQLScqo6uBwS7sGnF5cp-AjZj4541fIVXWUNlA1A-05vE5Eg3YQA/viewform?usp=sf_link" rel=" noopener" target="_blank">подать доклад</a>, то можете это сделать прямо в режиме онлайн.</p> <p>Теперь следить за митапами Evrone стало удобнее. В Telegram-канале <a href="https://t.me/meetups_evrone" rel=" noopener" target="_blank">Evrone meetups</a> мы выкладываем анонсы с подробными описаниями докладов, а также студийные записи после мероприятий. А ещё, у нас можно выступить, мы поможем оформить вашу экспертизу в яркое выступление. Подписывайтесь и пишите <a href="https://t.me/andrew_aquariuss" rel=" noopener" target="_blank">@andrew_aquariuss</a>, чтобы узнать подробности.</p> <p><a class="info-button" href="https://meetups.evrone.ru/ruby-meetup-no21-online?utm_source=digest_site&amp;utm_medium=digest&amp;utm_campaign=ruby_digest_5_2023" rel=" noopener" target="_blank">Регистрация</a></p> <h2 id="article_title_8">Вакансии</h2> <p>Удаленка / Офис</p> <h3>Evrone </h3> <p>Мы открыты для новых Ruby-разработчиков. В Evrone можно работать удалённо с первого дня, мы поддерживаем и оплачиваем участие в Open-source проектах и выступления на конференциях, а расти в грейдах можно с помощью честной системы проверки навыков и менторства.</p> <p><a class="info-button" href="https://jobs.evrone.ru/?utm_source=digest_site&amp;utm_medium=digest&amp;utm_campaign=ruby_digest_5_2023" rel=" noopener" target="_blank">Подробнее</a></p> Tue, 02 May 2023 11:16:21 +0000 Evrone digest /20230502 Стучим по рельсам https://digest.evrone.ru/20230403 <img src="/sites/default/files/styles/card_m/public/2023-04/image2.png?itok=sotds0r8" width="960" height="540" alt="Стучим по рельсам" loading="lazy" typeof="Image" /> <br /> <p>Наш апрельский дайджест мы посвящаем Ruby on Rails. В фокусе патч для пользовательских пространств имён и руководство по развёртыванию RoR на современной OpenBSD. В качестве «изюминки» вспомнили забавный баг GitHub десятилетней давности. Приятного чтения!</p> <h2>Улучшение custom namespaces в Rails 7.1</h2> <p>Что происходит во время автоматической загрузки веб-приложения на Rails? Вначале система сканирует и загружает все поддиректории, находящиеся в директории приложения. Исключение составляют лишь assets, views и javascript. Если мы создадим там любую поддиректорию, то Rails обеспечит её автоматическую загрузку.</p> <p>При этом стоит помнить, что директории работают в роли пространств имён, а имена файлов должны соответствовать константам, которые они определяют. Если у нас есть файл app/services/users/signup.rb, то внутри него должна быть определена константа Users::Signup. Предположим, что мы хотим, чтобы все сервисы в app/services были в пространстве имён Services.</p> <p>Получается, надо определить константу Services::Users::Signup. Фактически придётся создать директорию app/services/services. Помещаем туда все наши сервисы и тогда они будут в пространстве имён Services. Не самое элегантное решение. Хотелось бы иметь способ сразу прописать константу вида Services::Users::Signup в файле app/services/users/signup.rb.</p> <p>С выходом патча <a href="https://github.com/rails/rails/pull/47583/files" rel=" noopener" target="_blank">#47583</a> такая возможность появится. Никакой магии, исключительно настройка автозагрузчика:</p> # config/initializers/autoloading.rb module Services; end Rails.autoloaders.main.push_dir("#{Rails.root}/app/services", Services) <p>В предыдущих версиях (до Rails 7.1) требовалось дополнительно удалять директорию app/services из ActiveSupport::Dependencies.autoload_paths. Теперь же этого делать не придётся. За дополнительной информацией советуем заглянуть в комментарии к PR <a href="https://github.com/rails/rails/pull/47583" rel=" noopener" target="_blank">Improve support for custom namespaces</a>.</p> <h2>Байка про nil</h2> <p>Новое — это хорошо забытое старое. Предлагаем отправиться в 2013 год и вспомнить любопытный баг GitHub. Тогда каждому пользователю без аватарки присваивалась аватарка из профиля разработчика Yehuda Katz. У этого разработчика идентификатор пользователя был равен 4. Ruby же присваивал идентификатору 4 значение nil, то есть отсутствие значения. Таким образом все пользователи без аватарки приравнивались к пользователю с идентификатором 4. Баг пофиксили, но вот для многих так и осталось тайной, почему так произошло.</p> <p>Разгадка кроется в правильном понимании NIL. В мире Ruby nil представляет собой объект и имеет соответствующий идентификатор (ID). Если два объекта имеют один и тот же ID, значит они оба являются одним и тем же объектом в памяти. Это очень полезно для сравнения объектов, а также позволяет сборщику мусора лучше управлять выделением памяти. Вы можете самостоятельно взглянуть на некоторые зарезервированные ID через интерактивную оболочку irb: </p> <p><img alt="В мире Ruby nil " data-entity-type="file" data-entity-uuid="09efa1b3-a635-4c47-ae5d-c39d452970a5" width="691" height="438" loading="lazy" class="lazyload" data-src="/sites/default/files/inline-images/image1_5.png" /></p> <p>В Matz Ruby Interpreter (MRI) значения true/false/nil (а также значения типа Fixnum) обрабатываются особым образом и реально для всех этих объектов в памяти нет структуры с данным. ID, в этом случае, будет указателем на место в памяти, который не будет пересекаться с кучей. За подробностями советуем заглянуть в <a href="https://www.dsdev.in/blog/github-bug-nil-id" rel=" noopener" target="_blank">статью</a> Дхавала Сингха (Dhaval Singh).</p> <h2>Rails и Falcon на OpenBSD</h2> <p>OpenBSD существует уже 28 лет и до сих пор сохраняет статус свободной операционной системы. Концепция OpenBSD подразумевает полное отсутствие несвободных компонентов. Никакой код не может попасть в систему случайным образом, а любая неточность в документации немедленно исправляется. Несмотря на свою «самобытность» этот дистрибутив остаётся хорошим выбором для тех, у кого надёжность и безопасность стоит на первом месте.</p> <p><img alt="OpenBSD " data-entity-type="file" data-entity-uuid="132f9ccd-eafe-4cb2-a3c0-ccef63617d3d" width="855" height="502" loading="lazy" class="lazyload" data-src="/sites/default/files/inline-images/image3_4.png" /></p> <p>Пару недель назад на Reddit <a href="https://www.reddit.com/r/rails/comments/11rvxba/openbsd_ruby_on_rails_falcon/" rel=" noopener" target="_blank">появилась</a> любопытная заметка, что в сервисе GitHub Gists (сервис наподобие Pastebin) выложили подробное <a href="https://gist.github.com/basicfeatures/7d1448aad6fadc0def4ffee443f26dab" rel=" noopener" target="_blank">руководство</a> по развёртыванию Ruby on Rails + Falcon на OpenBSD. Автор руководства утверждает, что это самое чистое ядро, пользовательское пространство и синтаксис конфигурации. Предложенный им способ позволяет запускать веб-приложения от имени непривилегированного пользователя myappy. Такой пользователь владеет исключительно директориями tmp/ и log/ и не сможет сломать основную систему в случае взлома приложения.</p> <p>Мы не поленились развернуть инстанс по этому руководству и нашли несколько проблемных моментов. Чтобы не подпортить впечатление, вспомните, что UNIX-системы чувствительны к регистру. Поэтому USER стоит заменить на user.</p> root# adduser -group user -batch myappy <p>Проверить, что непривилегированному пользователю назначены верные группы можно командой:</p> root# id -Gn myappy myappy user <p>Точно также вторая команда с созданием привилегированного пользователя dev закончится ошибкой, если не поправить регистр (WHEEL заменить на wheel):</p> root# adduser -group wheel -batch dev <p>Когда мы ставим Ruby on Rails, система выдаст предупреждение, которое не стоит игнорировать, а именно создать символьные ссылки. Для версии 3.1.X эти команды выглядят так:</p> ln -sf /usr/local/bin/ruby31 /usr/local/bin/ruby ln -sf /usr/local/bin/bundle31 /usr/local/bin/bundle ln -sf /usr/local/bin/bundler31 /usr/local/bin/bundler ln -sf /usr/local/bin/erb31 /usr/local/bin/erb ln -sf /usr/local/bin/gem31 /usr/local/bin/gem ln -sf /usr/local/bin/irb31 /usr/local/bin/irb ln -sf /usr/local/bin/rdoc31 /usr/local/bin/racc ln -sf /usr/local/bin/rake31 /usr/local/bin/rake ln -sf /usr/local/bin/rdoc31 /usr/local/bin/rbs ln -sf /usr/local/bin/rdoc31 /usr/local/bin/rdbg ln -sf /usr/local/bin/rdoc31 /usr/local/bin/rdoc ln -sf /usr/local/bin/ri31 /usr/local/bin/ri ln -sf /usr/local/bin/typeprof31 /usr/local/bin/typeprof <p>Выделяем всю эту цепочку команд и вставляем в консоль. Без этого последующие команды просто не сработают. Перед тем, как выполнять множество команд, начинающихся на gem git_install, надо не забыть поставить git:</p> root# pkg_add git <p>Дальнейшие шаги по клонированию объёмного репозитория и установки гемов могут занять некоторое время, так что наберитесь терпения. Для удобства создания конфигов можно поставить привычный по Linux-системам редактор nano:</p> root# pkg_add nano <p>Подытожим: способ рабочий и результатом станет безопасный инстанс на базе OpenBSD. Возможно, кто-то однажды найдёт ваш сервер на OpenBSD с 30-летним аптаймом и приложением, которое будет работать, как ни в чём не бывало.</p> <h2>Интересно посмотреть</h2> <p>Мы рады делиться экспертизой наших специалистов. На YouTube-канале Evrone Academy появилось много новых обучающих видео по Ruby и Rails. Даже если вы давно пишете на Ruby, иногда полезно освежить знания и сверить их с другими экспертами. Ну а для новичков эти видео могут стать отличной отправной точкой.</p> <p>Короткий и ёмкий формат занятий в 15-20 минут отлично подойдёт даже тем, у кого не слишком много свободного времени. Текстовые версии лекций доступны в нашем <a href="https://habr.com/ru/users/Evrone/posts" rel=" noopener" target="_blank">блоге</a> на Хабре. Подписывайтесь и отправляйте эти видео своим друзьям, которые хотят начать изучать Ruby. И не забывайте нажать на «колокольчик», чтобы не пропустить новые видео.</p> <h2 id="article_title_7">Митапы</h2> Онлайн <h3>Ruby meetup №20</h3> <p>19 апреля 2023</p> <p>Скоро мы встретимся с вами на замечательном Ruby Meetup. Программа мероприятия формируется и скоро станет доступна <a href="https://meetups.evrone.ru/ruby-meetup-no20-online?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_4_2023" rel=" noopener" target="_blank">на странице мероприятия</a>. Кстати, если вы не успели <a href="https://forms.gle/svgvCCxM7cRTL3wX7" rel=" noopener" target="_blank">подать доклад</a>, то можете это сделать прямо в режиме онлайн. </p> <p>Теперь следить за митапами Evrone стало удобнее. В Telegram-канале <a href="https://t.me/meetups_evrone" rel=" noopener" target="_blank">Evrone meetups</a> мы выкладываем анонсы с подробными описаниями докладов, а также студийные записи после мероприятий. А ещё, у нас можно выступить, мы поможем оформить вашу экспертизу в яркое выступление. Подписывайтесь и пишите <a href="https://t.me/andrew_aquariuss" rel=" noopener" target="_blank">@andrew_aquariuss</a>, чтобы узнать подробности.</p> <p><a class="info-button" href="https://meetups.evrone.ru/ruby-meetup-no20-online?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_4_2023" rel=" noopener" target="_blank">Регистрация</a></p> <h2 id="article_title_8">Вакансии</h2> <p>Удаленка / Офис</p> <h3>Evrone </h3> <p>Мы открыты для новых Ruby-разработчиков. В Evrone можно работать удалённо с первого дня, мы поддерживаем и оплачиваем участие в Open-source проектах и выступления на конференциях, а расти в грейдах можно с помощью честной системы проверки навыков и менторства.</p> <p><a class="info-button" href="https://jobs.evrone.ru/?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_4_2023" rel=" noopener" target="_blank">Подробнее</a></p> Mon, 03 Apr 2023 13:11:06 +0000 Evrone digest /20230403 Speeding Things Up https://digest.evrone.ru/20230303 <img src="/sites/default/files/styles/card_m/public/2023-03/image1.png?itok=yjFLq0Qg" width="960" height="540" alt="Speeding Things Up" loading="lazy" typeof="Image" /> <br /> <p>Наш мартовский Ruby-дайджест предлагает поразмышлять над консервативным подходом к внедрению новых функций в язык. Также подумаем, как предупредить возникновение memory bloat в Rails и узнаем, как Джереми Эванс решил ускорить запуск процессов.</p> <h2>Консерватизм в Ruby</h2> <p>Разработчики часто сталкиваются с дилеммой: применять консервативный подход и годами отточенные решения или же отслеживать и применять самые новые функции. Первое не требует каких-либо усилий и уменьшает вероятность того, что приложение станет работать нестабильно. Второе даёт разработчику значительно больше способов решения тех или иных задач.</p> <p>Изучение новых функций языка программирования очень похоже на процесс изучения иностранного языка. Чем больше слов, конструкций и устойчивых выражений вы узнаёте, тем лучше и точнее вы можете выразить свою мысль. Если глобально взглянуть на программирование, то его можно сравнить с моделированием реальности при помощи ограниченного набора слов и конструкций. Чем больше у вас слов в запасе, тем проще будет описывать сложные алгоритмы и структуры данных.</p> <p>Ruby в 2007 году с точки зрения разработчика на С/С++, Java или PHP явно казался странным и обладал очень непривычным синтаксисом. Здесь уместно вспомнить о гипотезе лингвистической относительности, утверждающей, что структура языка влияет на мировосприятие и воззрения его носителей, а также на их когнитивные процессы. Роман «Вавилон-17», вдохновивший Юкихиро Мацумото на создание Ruby, как раз был основан на этой гипотезе. Кстати, мы брали у него <a href="https://evrone.ru/blog/interviews/yukihiro-matsumoto-interview" rel=" noopener" target="_blank">интервью</a> — советуем взглянуть, если ещё не читали.</p> <p>Время шло и язык неуловимо менялся. Приоритет был сделан на стабильность и надёжность, а скорость внедрения новых функций существенно снизилась. Для некоторых разработчиков, таких как <a href="https://ghinda.com/" rel=" noopener" target="_blank">Lucian Ghinda</a>, это стало индикатором замедления развития языка. В своей статье «<a href="https://allaboutcoding.ghinda.com/we-should-adopt-and-use-new-ruby-features" rel=" noopener" target="_blank">We should adopt and use new Ruby features</a>» он высказал мнение, что желание оградить язык от нововведений может негативно сказаться на его развитии.</p> <p>В качестве доказательства он приводит вышеупомянутую гипотезу лингвистической относительности и гипотезу лингвистического детерминизма. Все элементы языка программирования могут влиять на то, как разработчики определяют проблему и решение, которое они выбирают для этой проблемы. Так что основным пожеланием к дальнейшему развитию языка будет экспериментирование с новыми функциями. Это расширит границы языка и даст разработчикам больше инструментов для решения повседневных задач.</p> <h2>Борьба с memory bloat в Rails</h2> <p>В одном из прошлых дайджестов мы уже <a href="https://digest.evrone.ru/20220602" rel=" noopener" target="_blank">упоминали</a> раздувание памяти в Ruby. Вполне рядовая ситуация с извлечением данных из БД или Redis, чтением файлов с диска или выполнением сетевого запроса может привести к резкому выделению большого объема памяти. Если это происходит на локальной машине разработчика, то не приводит к каким-либо проблемам. Но вот в больших Rails-инсталляциях раздувание памяти может обойтись очень дорого.</p> <p>Наглядно можно увидеть разницу между Transaction.sum(:amount) (где SELECT SUM(amount) FROM transactions) и Transaction.all.sum(&amp;:amount) (где SELECT * FROM transactions с последующей отправкой по сети) на следующем примере. Для инстансов с Active Record это может выглядеть так:</p> ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(Module.new do def build_result(*args, **kwargs, &amp;block) io_bytesize = kwargs[:rows].sum(0) do |row| row.sum(0) do |val| ((String === val) ? val : val.to_s).bytesize end end Aggregator.instance.increment(io_bytesize) super end end) <p>Подписываемся на уведомления от контроллера:</p> ActiveSupport::Notifications.subscribe("process_action.action_controller") do |*args| io_bytesize = Aggregator.instance.io_bytesize body_bytesize = args.last[:response].body.bytesize ratio = body_bytesize.to_f / io_bytesize Rails.logger.info "Loaded from I/O #{io_bytesize}, response bytesize #{body_bytesize}, I/O to response ratio #{ratio}" end <p>Завершающим штрихом создадим контроллер с нашей парой действий:</p> class App &lt; Rails::Application routes.append do get '/slow', to: 'app#slow' get '/fast', to: 'app#fast' end end class AppController &lt; ActionController::Base def slow render json: {sum: Transaction.all.sum(&amp;:amount)} end def fast render json: {sum: Transaction.sum(:amount)} end end <p>Запускаем и разница будет очевидна:</p> Started GET "/slow" for 127.0.0.1 at 2023-02-04 23:01:27 +0300 Processing by AppController#slow as */* Transaction Load (4.5ms) SELECT "transactions".* FROM "transactions" Completed 200 OK in 43ms (Views: 0.1ms | ActiveRecord: 11.7ms | Allocations: 95836) Loaded from I/O 69899, response bytesize 15, I/O to response ratio 4659.933333333333 Started GET "/fast" for 127.0.0.1 at 2023-02-04 23:01:35 +0300 Processing by AppController#fast as */* Transaction Sum (3.1ms) SELECT SUM("transactions"."amount") FROM "transactions" Completed 200 OK in 4ms (Views: 0.1ms | ActiveRecord: 3.1ms | Allocations: 336) Loaded from I/O 7, response bytesize 15, I/O to response ratio 0.4666666666666667 <p>Понятно, что когда проблема возникла, поправить ситуацию не слишком сложно. Можно загружать данные партиями или перенести вычисления куда-либо в другое место. Но было бы здорово не ликвидировать последствия катастрофы, а заранее <a href="https://dmitrytsepelev.dev/finding-rails-memory-bloats-early" rel=" noopener" target="_blank">предсказать</a> её появление. Идеальный вариант — прикинуть сколько данных будет загружено из I/O и сравнить с размером потенциального ответа. Соотношение станет тем показателем, которое может выступить тревожным звоночком и которое стоит отслеживать.</p> <p>Чтобы проверить такой подход на реальном приложении, не нужно писать код с нуля. Тот же <a href="https://github.com/DmitryTsepelev/io_monitor" rel=" noopener" target="_blank">io_monitor</a> отлично упрощает жизнь. После установки его нужно сконфигурировать:</p> IoMonitor.configure do |config| config.publish = [:logs, :notifications, :prometheus] # defaults to :logs config.warn_threshold = 0.8 # defaults to 0 config.adapters = [:active_record, :net_http, :redis] # defaults to [:active_record] end <p>И включить те контроллеры, которые нужно будет проверить:</p> class MyController &lt; ApplicationController include IoMonitor::Controller end <p>Если проблем не найдено, то в логах будет чисто. О наличии проблем будет сигнализировать строчка вида:</p> ActiveRecord I/O to response payload ratio is 0.1, while threshold is 0.8 <h2>Ruby-by-by</h2> <p>При разработке больших приложений скорость запуска процессов часто становится бутылочным горлышком и требуется много усилий, чтобы приложение соответствовало требованиям заказчика. Анализируя профиль быстродействия можно заметить, что причиной часто становится загрузка библиотек. С этим можно успешно бороться.</p> <p>Каждый раз, когда процесс запускается, Ruby ищет нужные библиотеки и анализирует файлы в каждой из них. Чем больше библиотек, тем дольше загрузка. Сократить время можно, сделав их предварительную загрузку. Если у нас в памяти уже будут заранее подгружены все необходимые библиотеки, то Ruby не станет тратить на это драгоценное время. Это стало ключевой идеей создания предварительного загрузчика библиотек, разработанного Джереми Эвансом. <a href="https://evrone.ru/blog/interviews/jeremy-evans-interview" rel=" noopener" target="_blank">Интервью</a> с ним также есть у нас на сайте.</p> <p>Новый загрузчик называется by. Подразумевается, что это Ruby, в котором первые две буквы уже предварительно загружены. Использована клиент-серверная архитектура, где сервер занимается предзагрузкой библиотек и слушает Unix-сокет. Клиент использует этот сокет для подключения и запуска процесса, после чего отсылает необходимые аргументы и ждёт exit code. В ответ сервер создаёт процесс, использующий текущий каталог, stdin/stdout/stderr и среду клиентского процесса. Присланные клиентом аргументы обрабатываются и финально сервер возвращает exit code 0, если всё хорошо или exit code 1 в случае ошибки.</p> <p>Установить by можно одной командой:</p> gem install by <p>Использовать тоже легко. Вначале стартуем сервер с указанием библиотек, требующих предзагрузки, например:</p> $ by-server sequel roda capybara <p>Теперь запускаем Ruby через by, указывая предзагруженные библиотеки:</p> $ by -e 'p [Sequel, Roda, Capybara]' [Sequel, Roda, Capybara] <p>Теперь Ruby не станет искать их и заниматься бесполезным парсингом. Выигрыш в скорости получается значительный:</p> $ /usr/bin/time ruby -e 'require "sequel"; require "roda"; require "capybara"' 1.67 real 0.93 user 0.66 sys $ /usr/bin/time by -e 'require "sequel"; require "roda"; require "capybara"' 0.37 real 0.20 user 0.15 sys <p>Те, кому этого мало, могут заняться экстримом и «вырезать» загрузку Rubygems. Детальная инструкция есть в <a href="https://github.com/jeremyevans/by" rel=" noopener" target="_blank">репозитории проекта</a>.</p> <h2 id="article_title_7">Митапы</h2> Онлайн <h3>Ruby meetup №20</h3> <p>19 апреля 2023</p> <p>Весной мы соберёмся на замечательный Ruby Meetup. Программа мероприятия формируется, но регистрация уже открыта. Кстати, вы уже можете <a href="https://forms.gle/svgvCCxM7cRTL3wX7" rel=" noopener" target="_blank">подать доклад</a> прямо в режиме онлайн. Заявки на участие принимаются до 1 апреля!</p> <p>Теперь следить за митапами Evrone стало удобнее. В Telegram-канале <a href="https://t.me/meetups_evrone">Evrone meetups</a> мы выкладываем анонсы с подробными описаниями докладов, а также студийные записи после мероприятий. А ещё, у нас можно выступить, мы поможем оформить вашу экспертизу в яркое выступление. Подписывайтесь и пишите <a href="https://t.me/andrew_aquariuss">@andrew_aquariuss</a>, чтобы узнать подробности.</p> <p><a class="info-button" href="https://meetups.evrone.ru/ruby-meetup-no20-online?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_3_2023" rel=" noopener" target="_blank">Регистрация</a></p> <h2 id="article_title_8">Вакансии</h2> <p>Удаленка / Офис</p> <h3>Evrone </h3> <p>Мы открыты для новых Ruby-разработчиков. В Evrone можно работать удалённо с первого дня, мы поддерживаем и оплачиваем участие в Open-source проектах и выступления на конференциях, а расти в грейдах можно с помощью честной системы проверки навыков и менторства.</p> <p><a class="info-button" href="https://jobs.evrone.ru/?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_3_2023" rel=" noopener" target="_blank">Подробнее</a></p> Fri, 03 Mar 2023 12:10:19 +0000 Evrone digest /20230303 Самокритика, RubyMine и графики https://digest.evrone.ru/20230201 <img src="/sites/default/files/styles/card_m/public/2023-02/image4.png?itok=kuwUuodA" width="960" height="540" alt="Самокритика, RubyMine и графики" loading="lazy" typeof="Image" /> <br /> <p>В этом дайджесте обсудим инструмент rubycritic, взглянем на новый интерфейс RubyMine и некоторые утилиты визуализации данных. Мы стремимся сделать ваш опыт работы с Ruby интересным!</p> <h2>Конструктивная критика</h2> <p>Все любят, когда их работу хвалят. А вот к критике большинство относится с негативом. Даже если она конструктивная, правильно её обработать бывает трудно. Разработчики вынуждены показывать свой код коллегам и сталкиваться с оценочными суждениями. С одной стороны, это даёт возможность для улучшения качества и процесса разработки. А с другой, может создавать дискомфорт.</p> <p>Между «показать код человеку» и «показать код программе» есть разница. Разработчики постоянно читают код и делают это чаще, чем пишут. Поэтому показать свой код коллеге и попросить оставить комментарии — отличная идея. Ну а на постоянной основе «причёсыванием» кода занимаются приложения-линтеры. Кроме них в мире Ruby-разработки существуют такие утилиты, как <a href="https://github.com/troessner/reek" rel=" noopener" target="_blank">Reek</a>, <a href="https://github.com/seattlerb/flay" rel=" noopener" target="_blank">Flay</a> и <a href="https://github.com/seattlerb/flog" rel=" noopener" target="_blank">Flog</a>.</p> <p>Первая ищет «код с душком» — потенциально проблемные участки кода. Вторая анализирует код на предмет наличия структурно похожих участков, выявляя потенциальных кандидатов на рефакторинг. А третья анализирует метрику сложности. Достаточно воспользоваться каждой из этих утилит, чтобы получить однозначный вывод о качестве кода.</p> <p>Разработчик <a href="https://github.com/guilhermesimoes" rel=" noopener" target="_blank">Guilherme Simões</a> значительно упростил процесс самостоятельной проверки, создав проект <a href="https://github.com/whitesmith/rubycritic" rel=" noopener" target="_blank">rubycritic</a>. В нём он объединил возможности вышеперечисленных утилит, добавив удобный интерфейс для просмотра результатов. Инструмент проверяет код и формирует подробный отчёт по всем найденным проблемам. Разработчику остаётся его просмотреть и наметить области, требующие улучшения или полной переработки. В большинстве случаев это поможет получить объективную оценку без участия человека.</p> <h2>Новый UI в RubyMine</h2> <p>JetBrains стремительно готовятся переводить RubyMine на новый UI. Нет ничего хуже неизвестности, поэтому из обновления не стали делать секрета. Новый интерфейс кардинально отличается от предыдущего. Для сравнения главное окно RubyMine со старым и новым интерфейсом:</p> <p><img alt="RubyMine на новый UI" data-entity-type="file" data-entity-uuid="609a0293-e0df-4826-9292-91e37d85e73d" width="1277" height="732" loading="lazy" class="lazyload" data-src="/sites/default/files/inline-images/image2_6.png" /></p> <p><img alt="new_interface" data-entity-type="file" data-entity-uuid="07801a74-76dd-40be-a7ff-9fa094c9e298" width="1277" height="732" loading="lazy" class="lazyload" data-src="/sites/default/files/inline-images/image1_3.png" /></p> <p>Заголовок окна стал значительно проще. Разработчики убрали все разделы меню под единую кнопку с пиктограммой «гамбургера». На их место встали виджеты проекта, контроля версий и запуска. Панель навигации переехала вниз, в строку состояния.</p> <p>Большая часть значков была перерисована для улучшения визуального баланса и удобочитаемости. Шрифт заменили на Inter, уже активно применяющийся в других продуктах JetBrains. Визуально интерфейс стал менее перегруженным. Отличный повод сконцентрироваться на коде, а не отвлекаться на лишние и редко используемые элементы.</p> <h2>Рисуем графики</h2> <p>Если нужно отрисовать график с использованием Python, то в большинстве случаев разработчики и дата-аналитики обращаются к библиотеке <a href="https://matplotlib.org/" rel=" noopener" target="_blank">matplotlib</a>. Даже далёким от программирования людям обычно не составит труда построить простой график из готовых данных буквально за 8 строчек кода. Профессионалы же получают обширный набор инструментов, который способен визуализировать любые данные. А как с этим обстоят дела в мире Ruby-разработки?</p> <p>Здесь есть любопытные утилиты. Например, <a href="https://github.com/red-data-tools/YouPlot)" rel=" noopener" target="_blank">YouPlot</a>. В его основе лежит библиотека <a href="https://github.com/red-data-tools/unicode_plot.rb" rel=" noopener" target="_blank">unicode_plot</a>. Это отличный вариант, если нужно одной командой загрузить файл с данными, отсортировать их и вывести в виде красивого графика или диаграммы прямо в консоль:</p> <p><img alt="YouPlot " data-entity-type="file" data-entity-uuid="8c2cce45-d5e5-4eaa-b35f-5a05bd24b23b" width="631" height="383" loading="lazy" class="lazyload" data-src="/sites/default/files/inline-images/image5_0.png" /></p> <p>Rails может похвастаться инструментом <a href="https://chartkick.com/" rel=" noopener" target="_blank">Chartkick</a>. Он позволяет буквально одной строкой на Ruby вывести нужный график. Это может быть линейный график, круговая или столбчатая диаграмма — вариантов множество. Упор сделан на то, чтобы перестать тратить время разработчиков на борьбу с библиотеками визуализации. Большинство задач решается без необходимости вдумчиво изучать документацию. Каждый график и диаграмма при этом может быть настроена десятком различных опций.</p> <p>После установки gem и простой настройки окружения можно протестировать инструмент:</p> &lt;%= line_chart User.group_by_day(:created_at).count %&gt; <p>Обратите внимание, что для метода group_by_day надо поставить ещё один gem — <a href="https://github.com/ankane/groupdate" rel=" noopener" target="_blank">groupdate</a>. </p> <p>Выводом будет красивый линейный график:</p> <p><img alt="Выводом будет красивый линейный график:" data-entity-type="file" data-entity-uuid="ead53db3-2840-4007-a03e-090e395c5415" width="1162" height="375" loading="lazy" class="lazyload" data-src="/sites/default/files/inline-images/image3_3.png" /></p> <h2 id="article_title_7">Митапы</h2> Онлайн <h3>Ruby meetup №18</h3> <p>22 февраля 2023</p> <p>Совсем скоро мы соберёмся на замечательный Ruby Meetup. Программа мероприятия формируется, но регистрация уже открыта. Кстати, вы уже можете <a href="https://forms.gle/svgvCCxM7cRTL3wX7" rel=" noopener" target="_blank">подать доклад</a> прямо в режиме онлайн. Заявки на участие принимаются до 10 февраля!</p> <p>Теперь следить за митапами Evrone стало удобнее. В Telegram-канале <a href="https://t.me/meetups_evrone">Evrone meetups</a> мы выкладываем анонсы с подробными описаниями докладов, а также студийные записи после мероприятий. А ещё, у нас можно выступить, мы поможем оформить вашу экспертизу в яркое выступление. Подписывайтесь и пишите <a href="https://t.me/andrew_aquariuss">@andrew_aquariuss</a>, чтобы узнать подробности.</p> <p><a class="info-button" href="https://meetups.evrone.ru/ruby-meetup-no20-online?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_2_2023" rel=" noopener" target="_blank">Регистрация</a></p> <h2 id="article_title_8">Вакансии</h2> <p>Удаленка / Офис</p> <h3>Evrone </h3> <p>Мы открыты для новых Ruby-разработчиков. В Evrone можно работать удалённо с первого дня, мы поддерживаем и оплачиваем участие в Open-source проектах и выступления на конференциях, а расти в грейдах можно с помощью честной системы проверки навыков и менторства.</p> <p><a class="info-button" href="https://jobs.evrone.ru/?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_2_2023" rel=" noopener" target="_blank">Подробнее</a></p> Wed, 01 Feb 2023 14:12:53 +0000 Evrone digest /20230201 Подарки под ёлочкой https://digest.evrone.ru/20230104 <img src="/sites/default/files/styles/card_m/public/2023-01/ruby%201.png?itok=yI6G3jiB" width="960" height="540" alt="Подарки под ёлочкой" loading="lazy" typeof="Image" /> <br /> <p>2023 год уже здесь. Пора рассказать о самых интересных новостях Ruby-разработки. Доставайте из холодильника заботливо спрятанные салатики и закуски. Наливайте чаю или чего покрепче. Приятного чтения!</p> <h2>Вышел Ruby 3.2.0</h2> <p>Стабильность — признак мастерства. 25 декабря 2022, спустя год после релиза Ruby 3.1.0, вышла <a href="https://www.ruby-lang.org/en/downloads/" rel=" noopener" target="_blank">новая версия 3.2.0</a>. Изменений много, расскажем лишь об основных. В <a href="https://digest.evrone.ru/20221205" rel=" noopener" target="_blank">предыдущем дайджесте</a> мы упоминали про Opal. Идея изоморфной разработки нашла своё отражение в начале интеграции WebAssembly и системного интерфейса WASI непосредственно в язык. </p> <p>Цель в том, чтобы запускать <a href="https://evrone.ru/" rel=" noopener" target="_blank">Ruby-приложения</a> прямо из браузера или обособленных runtime, таких как <a href="https://github.com/bytecodealliance/wasmtime" rel=" noopener" target="_blank">wasmtime</a>. Интерпретатор CRuby теперь умеет компилироваться в промежуточный код WASM, а для прямого взаимодействия с ОС задействуется API WASI. Стоит учитывать, что этот функционал находится в активной разработке и часть возможностей пока отсутствует. Так что там пока нет исключений, файберов и сборщика мусора.</p> <p>В рамках этих же изменений была представлена <a href="https://github.com/kateinoigakukun/wasi-vfs/wiki/Getting-Started-with-CRuby" rel=" noopener" target="_blank">wasi-vfs обвязка</a>, необходимая, чтобы всё Ruby-приложение можно было упаковать в единый wasm-файл.</p> <p>Разработчики из Shopify, <a href="https://shopify.engineering/yjit-just-in-time-compiler-cruby" rel=" noopener" target="_blank">объявившие</a> осенью 2021 года о начале работы над собственным JIT-компилятором для CRuby, наконец-то могут выдохнуть. Шалость удалась, <a href="https://github.com/Shopify/yjit" rel=" noopener" target="_blank">YJIT</a> получил статус stable. Он работает на процессорах Apple Silicon, кастомных ARM-чипах AWS Graviton и даже одноплатниках Raspberry Pi 4. Написан на Rust, так что ему нужен <a href="https://bugs.ruby-lang.org/issues/18481" rel=" noopener" target="_blank">rustc 1.58.0+</a>. По этой причине YJIT не включен по-умолчанию.</p> <p>Тем не менее, если его всё же включить, то ускорение будет значительным — до 41% по сравнению с обычным интерпретатором, если верить данным бенчмарка <a href="https://github.com/Shopify/yjit-bench" rel=" noopener" target="_blank">yjit-bench</a>. Бонусом будет и на треть меньший расход оперативной памяти. Также в Ruby 3.2.0 появилось два механизма защиты от DoS-атак на приложение через Regexp, так называемый <a href="https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS" rel=" noopener" target="_blank">ReDoS</a>. Первый улучшает алгоритм сопоставления, а второй вводит понятие тайм-аута для регулярных выражений.</p> <p>Выход новой версии Ruby стал прекрасным поводом заглянуть в будущее языка и понять чего ожидать в следующие годы. По нашему мнению, выбранный путь вполне соответствует текущему тренду на развитие смежных технологий. Так что Ruby живее всех живых.</p> <h2>Dockerfiles в комплекте</h2> <p>Древнее китайское проклятие «Чтоб ты жил в эпоху перемен» актуально, как никогда. К счастью, некоторые перемены положительные. Реагируя на современные тренды, <a href="https://evrone.ru/" rel=" noopener" target="_blank">веб-фреймворк Ruby on Rails</a> получил интересное изменение. Оно предназначено не для разработчиков, а для DevOps-инженеров.</p> <p>Сейчас все предпочитают развёртывать приложения в контейнерах, выполняя оркестрацию с помощью Kubernetes. Чтобы создать образ контейнера, требуется Dockerfile. Это простой текстовый файл, содержащий инструкции для сборки образа. Если копнуть чуть глубже, то в нём будет указано какой базовый образ использовать для контейнера. Также там дано описание всех команд, которые к этому образу будут применены.</p> <p>Задача автоматического создания Dockerfile для дальнейшей сборки и развёртывания обычно решалась с помощью сторонних инструментов. Но теперь RoR научился сам генерировать Dockerfile, а также вспомогательные .dockerignore и bin/docker-entrypoint. Они будут автоматически добавляться во все новые приложения и тем самым упрощать работу DevOps.</p> <h2>Рецепт Ruby-однострочников</h2> <p>Командная строка всегда была суперсилой Unix-систем. В отличие от графического интерфейса, через командную строку можно проделывать потрясающие трюки. Комбинирование внешних команд, таких как grep, sed, awk, sort и т.д. даёт возможность получать результат, не прибегая к сторонним инструментам. Это круто, но давайте задумаемся: способен ли Ruby заменить внешние команды консоли и их сочетания? Ответ однозначен — да, способен. Предположим, что есть некий текстовый файл:</p> $ cat table.txt brown bread mat hair 42 blue cake mug shirt -7 yellow banana window shoes 3.14 <p>Чтобы отфильтровать только целые числа, мы можем использовать grep:</p> $ grep -oE '[0-9]+$' table.txt 42 7 14 <p>А вот как это выглядит на Ruby:</p> $ ruby -ne 'puts $&amp; if /\d+$/' table.txt 42 7 14 <p>Теперь стоит задуматься над ещё одним интересным вопросом: а зачем тут вообще использовать Ruby? Тот же sed или awk мощные утилиты, способные свернуть горы. Но есть один нюанс — их синтаксис и идиомы нужно учить. Если вы Ruby-разработчик, то второй способ вам будет привычнее. Сами операции будут выполняться медленнее, зато написание станет быстрее и легче. Для многих ситуаций такой подход будет более продуктивным.</p> <p>Из плюсов можно отметить, что применение Ruby даёт два крутых баффа — использование стандартной библиотеки и подключение сторонних библиотек. Даже в однострочнике это будет востребовано для выполнения каких-либо действий над файлами. Примечательно, что при этом вы не теряете возможность использовать системные команды, вызывая их методом system:</p> $ ruby -e 'system("echo Hello World")' Hello World <p>Получается, что если вы хорошо знаете Ruby, то обойдётесь без знания синтаксиса внешних команд. А если они для вас привычны и знакомы, то их комбинирование с Ruby даст превосходный буст при работе в командной строке. Детали есть в отличном обучающем материале с примерами — <a href="https://learnbyexample.github.io/learn_ruby_oneliners/one-liner-introduction.html" rel=" noopener" target="_blank">Ruby one-liners cookbook</a>.</p> <h2>Фейки во благо</h2> <p>Успешная разработка приложений так или иначе требует тестовых данных. Реальные базы данных хороши, но часто требуют предварительной подготовки перед использованием. Создавать эти данные вручную долго и непродуктивно. Нужен способ эффективно генерировать их таким образом, чтобы они напоминали реальные. Но прежде чем придумывать велосипед советуем присмотреться к библиотеке <a href="https://github.com/faker-ruby/faker" rel=" noopener" target="_blank">faker-ruby</a>.</p> <p>Эта библиотека — порт расширения <a href="https://metacpan.org/dist/Data-Faker" rel=" noopener" target="_blank">Data::Faker</a> для Perl. В оригинале было всего лишь несколько простых генераторов, способных заполнить поля вроде Name / Phone number / Company / Address и так далее, но faker-ruby пошёл дальше. Здесь более сотни различных генераторов — от простых до экзотических, вроде <a href="https://github.com/faker-ruby/faker/blob/main/doc/tv_shows/big_bang_theory.md" rel=" noopener" target="_blank">генератора цитат из ситкома «Теория Большого Взрыва»</a>.</p> <p>У faker-ruby есть несколько полезных опций, которые можно подкрутить, исходя из поставленной задачи. Так, например, можно заставить его генерировать только уникальные значения или же повлиять на генератор псевдослучайных чисел, чтобы получить воспроизводимые результаты генерации. Для некоторых типов данных созданы дополнительные проверки. Например, международный банковский номер счета IBAN валидируется на корректность перед генерацией, а сгенерированные VIN-коды автомобилей будут иметь правильную контрольную цифру.</p> <p>Вот такой простой инструмент поможет заполнить вашу тестовую базу любым количеством данных, максимально похожих на настоящие. Это будет полезно для визуального тестирования и отлова багов. При этом проект активно развивается и постоянно улучшается. Рекомендуем!</p> <h2 id="article_title_8">Вакансии</h2> <p>Удаленка / Офис</p> <h3>Evrone </h3> <p>Мы открыты для новых Ruby-разработчиков. В Evrone можно работать удалённо с первого дня, мы поддерживаем и оплачиваем участие в Open-source проектах и выступления на конференциях, а расти в грейдах можно с помощью честной системы проверки навыков и менторства.</p> <p><a class="info-button" href="https://jobs.evrone.ru/?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_digest_1_2023" rel=" noopener" target="_blank">Подробнее</a></p> Wed, 04 Jan 2023 12:54:48 +0000 Evrone digest /20230104 Не Rails единым https://digest.evrone.ru/20221205 <img src="/sites/default/files/styles/card_m/public/2022-12/image1.png?itok=cAyE1eWD" width="960" height="540" alt="Не Rails единым" loading="lazy" typeof="Image" /> <br /> <p>Заключительный Ruby-дайджест этого года мы посвящаем обновлённому фреймворку Hanami, компилятору Opal и инструменту автоматизации ruby-nmap. Надеемся, что каждый наш читатель узнает для себя что-то новое.</p> <h2>Вышел в релиз Hanami 2.0</h2> <p>Увидела свет вторая версия fullstack-фреймворка Hanami. Мы уже упоминали о нём в одном из <a href="https://digest.evrone.ru/20220804" rel=" noopener" target="_blank">прошлых дайджестов</a>. Его создавали, как полноценный легковесный аналог Rails, добиваясь меньшего потребления памяти и компактности. Немалую роль в этом играет модульность. Разработчик может взять лишь необходимые части фреймворка и не раздувать кодовую базу.</p> <p>Новая версия принесла кардинальные изменения. Ядро приложения теперь включает иной подход к загрузке кода. Фишка в том, что можно не загружать сразу всё приложение, а выбрать подготовку отдельных компонентов к загрузке. Это гарантирует, что даже когда этих компонентов станет несколько сотен или тысяч, консоль Hanami всё равно будет запускаться мгновенно.</p> <p>Полностью переписанная HTTP-маршрутизация теперь значительно превосходит по скорости <a href="https://rubyonrails.org/" rel=" noopener" target="_blank">Rails</a>, <a href="https://sinatrarb.com/" rel=" noopener" target="_blank">Sinatra</a> и <a href="https://cuba.is/" rel=" noopener" target="_blank">Cuba</a>. Также была реализована <a href="https://guides.hanamirb.org/v2.0/app/autoloading/" rel=" noopener" target="_blank">автозагрузка кода</a>. Все ваши классы и модули будут доступны без необходимости прямого вызова через require.</p> <p>Разработчики фреймворка не стали изобретать велосипед и воспользовались давно проверенным решением <a href="https://github.com/fxn/zeitwerk" rel=" noopener" target="_blank">zeitwerk</a>. Правда, это наложило небольшое ограничение в виде необходимости соблюдать требования Zeitwerk к файловой структуре. Ничего необычного для тех, кто знаком с Rails 7.</p> <p>Наверняка вы слышали про коллекцию библиотек <a href="https://dry-rb.org/" rel=" noopener" target="_blank">dry-rb</a>. 24 гема, предназначенные решать задачи, которые не под силу стандартной библиотеке (детальнее мы рассказывали о ней в <a href="https://habr.com/ru/company/evrone/blog/697964/" rel=" noopener" target="_blank">статье на Хабре</a>). Разработка Hanami 2.0 ознаменовалась объединением усилий двух независимых команд. В октябре 2022 разработчики dry-rb закончили работу над тем, чтобы все библиотеки могли <a href="https://dry-rb.org/news/2022/10/17/dry-rb-adopts-zeitwerk-for-code-loading/" rel=" noopener" target="_blank">автоматически подгружаться</a> через Zeitwerk. На наш взгляд отличная коллаборация и замечательный результат. Об остальных изменениях можно узнать из <a href="https://hanamirb.org/blog/2022/11/22/announcing-hanami-200/" rel=" noopener" target="_blank">анонса</a> на официальном сайте проекта.</p> <h2>Драгоценный камень Opal</h2> <p>Ещё одно обновление в мире Ruby-разработки. Инструмент Opal для прямой компиляции Ruby в JavaScript получил порцию изменений, призванных увеличить производительность и убрать ошибки. Новая версия 1.6 стала гораздо удобнее для разработчиков. Но для начала напомним, как это вообще работает.</p> <p>Одной из попыток улучшить читаемость кода и уменьшить его размер был CoffeeScript. Тонна синтаксического сахара в стиле Ruby/Python/Haskell/Erlang действительно позволяла писать более компактно. При этом выдаваемый после трансляции код полностью проходил проверку <a href="https://www.javascriptlint.com/" rel=" noopener" target="_blank">JS Lint</a>.</p> <p>Удачным экспериментом стал и Opal, открыв новые возможности изоморфной разработки. Это же прекрасно, когда и клиент, и сервер созданы на одном языке. Забудьте про все эти страшные точки с запятой и пишите в привычном стиле. Перед тем, как пускаться «во все тяжкие» советуем <a href="https://opalrb.com/try/" rel=" noopener" target="_blank">попробовать Opal</a> в действии прямо из браузера.</p> <p>Если посмотреть пристальнее, то становится ясно, что Opal <span><span><span><span><span><span>—</span></span></span></span></span></span> серьёзная и полноценная реализация Ruby (как corelib, так и stdlib). Но вот с поддержкой нативных JS-библиотек не всё так радужно. Да, кое-какие штуки, например jQuery, заработают без дополнительных танцев с бубном. Но вот для поддержки других библиотек вероятно придётся написать собственную оболочку или же интегрировать чистый код на JS. Принцип такой же, как и у вызова внешних консольных команд из Ruby.</p> <p>Авторы проекта не забрасывают своё детище и и прикладывают значительные усилия по его доработке. Новая версия стала дружелюбнее к RSpec, перестав обрезать трейсы до 15 строк, а async/await вышла из разряда экспериментальной фичи. Показательным примером стал <a href="https://github.com/opal/opal/pull/2449" rel=" noopener" target="_blank">PR</a>, демонстрирующий, как изменение всего лишь одной строчки кода способно повлиять на производительность и компиляции, и в рантайме.</p> <h2>Автоматизация nmap с помощью Ruby</h2> <p>Даже если вы никогда не использовали nmap, то наверняка что-то о нём слышали или видели в фильмах. Этот инструмент для сканирования IP-cетей настолько полюбился киношникам, что они его пихают везде, где только можно. Об этом даже была забавная <a href="https://habr.com/ru/post/399375/" rel=" noopener" target="_blank">статья</a> на Хабре. Но что если нам нужно будет использовать возможности nmap в Ruby-приложении или же мы хотим автоматизировать процесс сканирования?</p> <p>В этом нам поможет пакет <a href="https://github.com/postmodern/ruby-nmap" rel=" noopener" target="_blank">ruby-nmap</a>. Он предоставляет Ruby-интерфейс для взаимодействия с nmap, а также парсер для сгенерированных nmap XML-файлов. Работает он предельно просто, вот пример из официального репозитория проекта:</p> require 'nmap/command' Nmap::Command.run do |nmap| nmap.connect_scan = true nmap.service_scan = true nmap.output_xml = 'scan.xml' nmap.verbose = true nmap.ports = [20, 21, 22, 23, 25, 80, 110, 443, 512, 522, 8080, 1080] nmap.targets = '192.168.1.*' end <p>При помощи ruby-nmap можно за короткое время создать собственное приложение или даже целый веб-сервис, обладающий всей мощью сканера. Так, например, родился небольшой проект <a href="https://github.com/Michael-Meade/Rmap" rel=" noopener" target="_blank">Rmap</a>, задействующий возможности <a href="https://nmap.org/book/nse.html" rel=" noopener" target="_blank">Nmap Scripting Engine</a>. Получившийся инструмент умеет выполнять сканирование на уязвимости, такие как SMTP-брутфорс или XSS-атака на движок WordPress. Полный перечень того, что умеет Rmap вы найдёте в <a href="https://medium.com/@michaelmeade3/rmap-nmap-scanning-in-ruby-412a8337c8a9" rel=" noopener" target="_blank">статье</a> автора проекта.</p> <p>Раз речь зашла о скучном инструменте, давайте глянем нет ли в нём немного веселья. «Пасхальное яйцо» тут есть в виде вывода лога в «хакерском стиле». Делается это так, добавлением параметров -oS -</p> $ nmap -oS - scanme.nmap.org STart1Ng nmAp 7.92 ( https://nmap.org ) at 2022-12-05 07:27 E3T NmAp $can R3pOrt f0R $canm3.nmap.0rg (45.33.32.156) H0St iz up (0.22s latency). 0th3r aDdrE$s3z F0r $caNm3.nmap.0rg (N0T Scann3d): 2600:3c01::F03C:91ff:f318:bb2f NoT sH0Wn: 988 cl0$Ed tcP pOrtz (cONn-r3FUs3d) P0RT STAtE $eRvIC3 22/tcp 0pen $sH 25/tcp Filter3d $mtp 80/tcp 0p3n http 726/tcP f!lt3r3d uNkn0wn 1027/tcp f|ltered !Is 2005/tcp F!ltered DEsl0g1n 2605/tcp f!lTered bgPd 9929/tcp OpEn nP1NG-3cho 12174/tcp filtErEd unkn0wN 16993/tcp fIlt3red aMt-soap-https 31337/tcp 0p3N Elit3 32772/tcp f!lter3D s0m3T1Mes-rpc7 NmAp done: 1 IP aDDresz (1 h0st up) scann3d !n 20.48 $3condz <h2>Интересно посмотреть</h2> <p>Наш осенний Ruby-митап был богат на интересные доклады. Если вам не удалось принять участие, то ничего страшного. Все записи уже есть на нашем <a href="https://www.youtube.com/@EvroneDevelopment" rel=" noopener" target="_blank">Youtube-канале</a>.</p> <p>Если монады Either или Maybe для вас не простой звук, то вам явно будет интересно узнать ещё об одной абстракции <span><span><span><span><span><span>—</span></span></span></span></span></span> аппликативных функторах:</p> <p>Опыт можно получать разными способами. Если внимательно посмотреть на процесс разработки популярных библиотек, то можно почерпнуть немало полезной информации:</p> <p>Портирование доставшегося в наследство кода иногда превращается в историю. Одну из таких мы услышали на нашем ноябрьском митапе:</p> <p>Если паттернов проектирования превеликое множество, то базовых принципов написания не слишком много. Проблема лишь в их абстрактности. Так что предлагаем послушать о практическом использовании базовых принципов в реальной жизни:</p> <p>Теперь следить за митапами Evrone стало удобнее. В Telegram-канале <a href="https://t.me/meetups_evrone" rel=" noopener" target="_blank">Evrone meetups</a> мы выкладываем анонсы с подробными описаниями докладов, а также студийные записи после мероприятий. А ещё, у нас можно выступить, мы поможем оформить вашу экспертизу в яркое выступление. Подписывайтесь и пишите <a href="https://t.me/andrew_aquariuss" rel=" noopener" target="_blank">@andrew_aquariuss</a>, чтобы узнать подробности.</p> <h2 id="article_title_8">Вакансии</h2> <p>Удаленка / Офис</p> <h3>Evrone </h3> <p>Мы открыты для новых Ruby-разработчиков. В Evrone можно работать удалённо с первого дня, мы поддерживаем и оплачиваем участие в Open-source проектах и выступления на конференциях, а расти в грейдах можно с помощью честной системы проверки навыков и менторства.</p> <p><a class="info-button" href="https://jobs.evrone.ru/?utm_source=digest_site&amp;utm_medium=transition&amp;utm_campaign=ruby_12_22" rel=" noopener" target="_blank">Подробнее</a></p> Mon, 05 Dec 2022 11:12:24 +0000 Evrone digest /20221205