«Новая жизнь» Fullstack-разработки

Можно ли всерьёз обсуждать Fullstack-разработку? Если смотреть в сторону громоздких фреймворков для frontend и backend, то разговор про fullstack выглядит сомнительно — требования к специалисту при таких условиях слишком обширны. Означает ли это, что тему нужно закрыть? — думаю нет и предлагаю посмотреть на термин Fullstack с точки зрения Ruby on Rails и прежних более простых принципов.

Ruby on Rails — MVC-фреймворк, сосредоточенный на быстрой разработке, уделяющий много внимания поддержанию устроенности внутри проекта (чтобы «быстро» не оказалось «как попало»). В нём широкие возможности как для backend, так и для frontend разработки. Чем удобен Ruby on Rails для Fullstack? Представляю вашему вниманию обзор frontend-возможностей, предусмотренных во фреймворке Ruby on Rails или связанных с ним, и делюсь своим впечатлением от знакомства с ними.

image

Мне нравится заниматься fullstack-разработкой: работать и с беком, и с фронтом, использовать проверенный фреймворк на полную, обсуждать разные темы с широким кругом коллег, а не обосабливаться в отделе backend-разработчиков. Хочется быть уверенным, что хорошо ориентируюсь в разных сторонах проекта, что хватит компетенций сделать решение от «А» до «Я», что мои возможности зависят только от меня, а не от формальных границ. Лично знаю несколько человек, которые солидарны с этим мнением, и думаю, что таких людей не мало. Моментным упущением fullstack-направления считаю неосведомленность о его развитии и заблуждение об отставании и доисторических инструментах. Задача этой статьи осветить каким путем развивается fullstack-подход и сколько интересных возможностей может предложить.

Webpacker

Webpacker — гем, который идет в поставке с Ruby on Rails.

Webpacker предоставляет обертку над Webpack: команды для подключения и стартовые конфигурации для работы. Webpacker де-факто устанавливает стандарт работы с frontend в Ruby on Rails, способствует использованию последних возможностей языка JavaScript и современных принципов работы с кодом (структурированность, модульность, сборка и многое другое).

Webpacker задает общие конфигурации, необходимые для начала работы, и структуру приложения, что повышает определенность и упрощает понимание проекта разными разработчиками. Для JavaScript-кода отводится папка app/javascript/ с первичным файлом app/javascript/packs/application.js.

Файлы и папки, добавленные Webpacker

config/webpacker.yml
config/webpack/
config/webpack/development.js
config/webpack/environment.js
config/webpack/production.js
config/webpack/test.js
package.json
postcss.config.js
babel.config.js
.browserslistrc
node_modules/
bin/webpack
bin/webpack-dev-server
app/javascript/
app/javascript/packs/
app/javascript/packs/application.js

Webpacker запускается по умолчанию в процессе создания нового приложения и выполняет свои настройки. Создать приложение можно сразу вместе с дополнительными конфигурациями для Stimulus, Vue, Typescript или другого из списка предусмотренных:

rails new myapp --webpack=stimulus

Или установить дополнительные конфигурации после создания приложения:

bundle exec rails webpacker:install:stimulus

Разрабатывать Frontend с фреймворком Ruby on Rails = использовать самые актуальные подходы к разработке на JavaScript. Все удобства от использования современных стандартов JavaScript хорошо интегрированны с Ruby on Rails. Предусмотрены необходимые конфигурации для работы с Webpack, что позволяет меньше отвлекаться на правильную организацию проекта и сосредоточиться на решении востребованных задач, используя привычное окружение.

Turbolinks

Turbolinks — JavaScript-библиотека, которая поставляется вместе с Ruby on Rails.

Приоритетная задача Turbolinks — облегчение нагрузки на сервер и сокращение «швов» при переходе по url-адресам приложения. Эту возможность часто сравнивают с SPA, так как создается впечатление перерендеринга содержимого в браузере вместо неказистых стандартных переходов между страницами.

Принцип работы Turbolinks: осуществлять навигацию между страницами не путем стандартного перехода на новый адрес, а за счет выполнения запроса на этот адрес «в фоне» через ajax, загрузки ответа в JavaScript, и замены содержимого страницы на новое. Этот процесс сопровождается специальными эвентами, которые позволяют добавить функциональность на переход между страницами и на возврат к предыдущим страницам. Например,

  • на запуск перехода к другому адресу: turbolinks:click, turbolinks:before-visit, turbolinks:visit;
  • или на обработку запроса к новой странице: turbolinks:request-start, turbolinks:request-end;
  • или на процесс отображения новой страницы: turbolinks:before-render, turbolinks:render, turbolinks:load.

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

ActionCable

ActionCable — часть фреймворка Ruby on Rails. ActionCable обустраивает работу с веб-сокетами. Для перечисления каналов на сервере предусмотрена папка app/channels/ с первичными файлами channel.rb и connection.rb. Для реализации подключения к этим каналам — папка app/javascript/channels/ с файлами index.js и consumer.js.

Знакомиться с возможностями ActionCable лучше сразу на примере. Простейшее подключение к веб-сокетам с его помощью можно реализовать всего за пару шагов.

  1. Создать файл с каналом
    app/channels/hello_channel.rb

    # app/channels/hello_channel.rb class HelloChannel < ApplicationCable::Channel   def subscribed     stream_from "hello_1"   end end 

  2. Создать подписку на этот канал
    app/javascript/channels/hello_channel.js

    // app/javascript/channels/hello_channel.js import consumer from "./consumer"  consumer.subscriptions.create({ channel: "HelloChannel" }, {   received(data) {     console.log("Data received", data);     document.body.innerText += `\nHello, ${data.name}!`   } }) 

И подключение к веб-сокетам готово.

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

app/controllers/hello_controller.rb

# app/controllers/hello_controller.rb class HelloController < ApplicationController   def index     render :html => "Hello page...", :layout => true   end    def new     ActionCable.server.broadcast("hello_1", name: params[:name])     head 200   end end 

config/routes.rb

# config/routes.rb get "/hello" => "hello#index" get "/hello/new" => "hello#new" 

Запускаем приложение, заходим по адресу 127.0.0.1:3000/hello и открываем консоль браузера, в ней можно будет видеть логирование сообщений, которые приходят от сервера через веб-сокеты.

Далее отправляем запрос на экшн рассылки:

curl http://127.0.0.1:3000/hello/new?name=World

И смотрим на страницу /hello и на вывод в её консоли.

Хелперы форм и Rails-ujs

Заслуживают внимания и некоторые не новые, но хорошо зарекомендовавшие себя возможности фреймворка Ruby on Rails. Среди них хелперы для представлений и форм. Изначальное удобство хелперов в том, что они облегчают интеграцию разметки с моделями, конфигурациями и другими backend-компонентами. Преимущество хелперов форм над обычной разметкой состоит в предоставлении возможности быстро перечислить поля формы не вдаваясь в подробности их привязки к атрибутам модели — с помощью хелперов связь между ними сформируется автоматически. Фрагмент, показывающий пример связывания полей формы с параметрами контроллера и атрибутами модели:

app/views/articles/new.html.erb

<%# app/views/articles/new.html.erb %> <%# Перечисляем в форме поля модели %> <%= form_with(model: Article.new) do |f| %>   <div>     <%= f.label :title %>     <%= f.text_field :title %>   </div>   <div>     <%= f.label :text %>     <%= f.text_area :text %>   </div>   <%= f.submit %> <% end %> 

config/locales/ru.yml

# config/locales/ru.yml # текст для лейблов автоматически связывается с локализацией ru:   activerecord:     attributes:       article:         title: Название статьи         text: Текст статьи 

config/application.rb

# config/application.rb # для активации нашей локализации добавим строчку в config/application.rb config.i18n.default_locale = :ru 

app/controllers/articles_controller.rb

# app/controllers/articles_controller.rb class ArticlesController < ApplicationController    def new     # метод new можно оставить пустым     # rails по-умолчанию перейдёт к рендерингу соответствующего представления   end    def create     # используем значения полей формы для создания нового объекта     @article = Article.create(article_params)      # редирект на модель - еще один удобный хелпер,     # он автоматически подставит url нужного ресурса     # для отображения страницы, соответствующей объекту модели     redirect_to @article   end    private    def article_params     # для безопасности необходимо указать структуру параметров     params.require(:article).permit(:title, :text)   end end 

Подробнее с этим примером можно познакомиться здесь и здесь.

Rails-ujs

Rails-ujs — базовая часть фреймворка Ruby on Rails, отвечающая за ненавязчивый JavaScript.
Rails-ujs предоставляет несколько дополнительных опций для элементов страницы, которые меняют или расширяют работу с ними.

Опция remote — предназначена для элементов, которые выполняют обращение к серверу (ссылки, формы), чтобы сделать запросы асинхронными. Пример ссылки:

<%= link_to "Добавить статью", new_article_path, remote: true %> 

Для отображения результатов такого запроса требуются дополнительные манипуляции, например, добавление обработчика к remote-событиям: ajax:success, ajax:error, ajax:complete.

Опция confirm — позволяет запросить подтверждение действия перед его выполнением.

<%= link_to "Удалить", article_path(article), method: :delete,   data: { confirm: 'Вы уверены, что хотите удалить эту статью?' } %> 

Опция disable_with — позволяет деактивировать элемент после действия

<%= form.submit data: { disable_with: "Сохранение..." } %> 

Дополнительно в Rails-ujs есть несколько удобных функций. Вот некоторые из них:

Rails.fire(obj, name, data) — функция вызова события
Rails.ajax(options) — обертка над XMLHttpRequest
Rails.isCrossDomain(url) — проверка принадлежности url другому домену
Rails.$(selector) — обертка над document.querySelectorAll

Подключить их к вашему коду можно командой

import Rails from "@rails/ujs" 

Stimulus

Stimulus — JavaScript-фреймворк от разработчиков Ruby on Rails.

Stimulus один из редких фреймворков и по своему уникальный, поскольку обустраивает frontend-разработку с применением новых подходов к JavaScript, при этом не стремится управлять всеми вашими действиями и не навязывает отделять frontend от backend.

Базовая задача Stimulus — привязка обработчиков к событиям. Согласно Stimulus исходный код нужно располагать по классам-контроллерам, а в роли обработчиков использовать их методы. По умолчанию для stimulus-контроллеров в проекте отводится папка app/javascript/controllers/ с первичным файлом index.js. Здесь мы можем добавлять наши контроллеры, для этого нужно создать файл с суффиксом _controller.js, например, articles_controller.js. Далее загрузчик Stimulus импортирует такие файлы и подключит контроллеры к соответствующим блокам на наших страницах.

У контроллеров в Stimulus предусмотрены дополнительные оснащения: инициализация объекта контроллера (initialize), хелперы для обращения к элементам внутри блока (targets, таргеты), присоединение объекта контроллера к блоку (connect) и отсоединение от него (disconnect), обращение к дата-аттирибутам блока (this.data.get). Ниже приведен пример блока с переключением состояния активен/неактивен, написанный на Stimulus.

app/views/home/show.html.erb

<%# app/views/home/show.html.erb %>  <%# назначаем контроллер home нашему блоку %> <%# и дополняем данными его атрибуты (для полноты примера) %> <div data-controller="home"     data-home-active-text="Activated" data-home-deactive-text="Deactivated">    <%# назначаем параграфу таргет text контроллера home  %>   <p data-target="home.text"></p>    <%# назначаем кнопке action click контроллера home %>   <button data-action="home#click">Кнопка</button>  </div> 

app/javascript/controllers/home_controller.js

// app/javascript/controllers/home_controller.js import { Controller } from "stimulus"  export default class extends Controller {   // объявляем таргеты   static targets = [ "text" ]    initialize() {     // загружаем текст активного и неактивного состояния     this.activeText = this.data.get("active-text");     this.deactiveText = this.data.get("deactive-text");   }    connect() {     // загружаем состояние активен/неактивен     this.active = this.data.get("active") == "true";     // обновляем отображение     this.refresh();   }    disconnect() {     // сохраняем состояние перед отключением контроллера     this.element.setAttribute("data-home-active", !!this.active);   }    click() {     // переключаем активность     this.active = !this.active;     // обновляем отображение     this.refresh();   }    // функция обновления внешнего вида   refresh(){     // меняем цвет блока самого контроллера     this.element.style.background =   this.active ? "none" : "#EEE";     // меняем текст     this.textTarget.innerHTML =   this.active ? this.activeText : this.deactiveText;     // меняем цвет текста     this.textTarget.style.color = this.active ? "black" : "#777";   } } 

Несмотря на сохранение прежних fullstack-принципов, подход к разработке со Stimulus значительно улучшается: по новому устроена структура исходного кода, изменяется привязка обработчиков к событиям, предусмотрены дополнительные оснащения. Благодаря этим удобствам и своей простоте фреймворк Stimulus позволяет быстро и грамотно структурировать даже большой frontend.

Дополнительно стоит подчеркнуть, что Stimulus хорошо сочетается с другими возможностями Ruby on Rails — почти в каждой связке возникает полезная эмерджентность.

Stimulus и Webpacker

В Webpacker предусмотрены команды для создания приложения с подключенным Stimulus:

rails new myapp --webpack=stimulus

Или для подключения его в уже созданный проект:

bundle exec rails webpacker:install:stimulus

Stimulus и JavaScript

Stimulus способствует применению современных принципов разработки на JavaScript для реализации интерактивности на ваших страницах. Со Stimulus frontend-решение собирается модульно, для обработчиков событий используется ООП, продуманно структурируется код. По средством таргетов с помощью stimulus-контроллеров удобно управлять подключением к элементам блока сложных графических компонентов, взятых из сторонних библиотек или написанных самостоятельно (календари, автокомплитеры, списки, деревья и другие). Благодаря этому, Stimulus — один из простейших способов перейти от устаревших frontend-инструментов и получить необходимую продуктивность от применения современного чистого JavaScript.

Stimulus и Ruby on Rails

Со структурой кода, рекомендуемой Stimulus, вы продолжаете писать на JavaScript по той же схеме, как писали бы на Ruby on Rails. Так же объявляете контроллеры, так же привязываете экшены к методам. Со Stimulus frontend-разработка становится схожа с backend, что облегчает работу и там, и там.

Stimulus и ActionCable

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

Stimulus и Turbolinks

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

Turbolinks не только облегчает загрузку страниц, но и кеширует их содержимое при переходе. При возврате на закешированную страницу из истории Stimulus активируется автоматически, как при загрузке новой страницы. Если требуется сохранить какие-то значения перед отключением контроллера от блока, то можно воспользоваться методом disconnect — и тогда, при возврате и подключении контроллера, он сможет восстановить своё последнее состояние. В коде первого примера работы со Stimulus можно увидеть как при отключении (disconnect) в data-атрибуте блока контроллера фиксируется значение this.active, а при подключении (connect) извлекается обратно.

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

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

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

Stimulus и Хелперы форм

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

app/views/articles/show.html.erb

<%# пример элемента с данными в представлении app/views/articles/show.html.erb %>  <%# извлекаем необходимые данные из модели %> <% article_data = article.attributes       .slice("id", "created_at", "updated_at", "author", "title").to_json %>  <%# объявляем тег div с помощью хелпера content_tag %> <%= content_tag :div,     :data => { controller: :articles },     "data-articles-attributes" => article_data do %>   <%# ... %> <% end %> 

app/javascript/controllers/articles_controller.js (фрагмент)

// затем распарсить данные // в методе initialize контроллера app/javascript/controllers/articles_controller.js initialize() {   this.attributes = JSON.parse(this.data.get("attributes"));   // другие действи инициализации   // ... } 

Stimulus и Rails-ujs

С помощью Stimulus и remote-опций можно напрямую подключать контроллеры к ajax-событиям и обрабатывать результаты запросов. Объявим ссылку с использованием Rails-ujs и привяжем к ней stimulus-обработчик.

ссылка с опцией remote и stimulus-обработчиком

<%= link_to "Открыть статью",   article_path(article, format: :html),   data: { remote: true, action: "ajax:success->articles#showArticle" } %> 

При нажатии на эту ссылку произойдёт асинхронный Ajax-запрос в rails-контроллер articles_controller.rb на экшен show. При получении положительного ответа сработает событие ajax:success и вызовется метод showArticle из контроллера app/javascript/controllers/articles_controller.js

метод showArticle контроллера app/javascript/controllers/articles_controller.js

showArticle(e) {    // берем ответ сервера из объекта события   const xhr = e.detail[2];    // обновляем содержимое формы просмотра   this.showFormTarget.innerHTML = xhr.responseText;    // показываем форму   this.showFormTarget.style.display = "block"; } 

Что дальше?

Перечисленные средства вместе с фреймворком Ruby on Rails открывают новые горизонты для fullstack-разработки. При этом описанные инструменты относительно просты и не требуют длительных погружений — всё необходимое для успешного проекта находится на поверхности.

Создавайте веб-приложения с помощью современных и быстрых fullstack-инструментов разработки с фреймворком Ruby on Rails и получайте от этого удовольствие!

FavoriteLoadingДобавить в избранное
Posted in Без рубрики

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *