Формирование HTML-документов и обработка форм в Roda

Андрей Васильев

2020

Система тестирования студентов

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

Система должна поддерживать следующие действия:

  • Отображать список проверочных работ
  • Позволять фильтровать список проверочных работ по дате и по тексту описания
  • Позволять добавлять новый элемент в список проверочных работ

Roda — модульная система

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

Модульная система позволяет проводить кардинальные изменения в API путём добавления нового модуля и помечая старый модуль как устаревший

Для подключения модулей внутри RODA-приложения используется метод plugin

class App < Roda
  plugin :csrf
end

Доступные модули

Список модулей, поставляемых вместе с джемом roda, описан в официальной документации

Модули разделены на следующие группы:

  • Routing — маршрутизация, обработка запросов от клиентов
  • Rendering/View — формирование ответа от сервера, обычно для HTML-контента
  • Request/Responce — обработка запросов и ответов на уровне HTTP-протокола
  • Matchers — расширение возможностей дерева маршрутизации (route do |r|)
  • Mail — формирование e-mail из приложения
  • Middleware — работа приложения с Rack-стеком промежуточных слоёв
  • Other — различные модули, не вошедшие в предыдущие категории

Стандартная структура небольшого RODA-проекта

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

Структура такого приложения описана в конвенциях

Формирование вида, HTML-страниц

При показе HTML-страниц с точки зрения веб-сервера мы должны разделять

  • Статические документы, не зависящие от состояния сервера (данных им управляемых). В нашем случае это CSS-документы, JavaScript-документы, они не изменяются, когда пользователь взаимодействует с сервером.
  • Динамические документы, зависящие от состояния сервера. В нашем случае это HTML-страницы, формируемые в ответ на запрос от пользователя.

При развёртывании приложения для большого количества пользователей (больше 1) статические файлы должен раздавать специализированный веб-сервер (Nginx, Apache и т.п.)

При локальной разработке удобно раздавать файлы единым сервером

Раздача статических файлов

Для раздачи статических файлов будем использовать модуль Public

opts[:root] = __dir__
plugin :public

route do |r|
  r.public
end

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

Работа в режиме разработчика

Для ограничения раздачи файлов только в окружении для разработки будем использовать модуль Environments

configure :development do
  plugin :public
  opts[:serve_static] = true
end

route do |r|
  r.public if opts.key?(:serve_static)
end

Формирование динамических страниц

Для формирования HTML-документов используется библиотека tilt, которая поддерживает множество шаблонизаторов. В рамках курса мы будем использовать уже знакомые вам ERB-шаблоны. Данную библиотеку нужно добавить в зависимости приложения

На стороне Roda мы будем использовать следующие модули:

  • Render — базовый модуль для формирования HTML-страниц
  • Partials — модуль для отображения шаблонов, начинающихся с подчёркивания

Render предлагает 2 метода для отображения информации:

  • view - отображение шаблона страницы внутри раскладки
  • render - отображение шаблона без раскладки

Partials добавляет метод partial, работающий как render

Система раскладок, layout

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

Файл раскладки называется layout.erb и располагается рядом с шаблонами страницы в каталоге views

r.on 'tests' do
  r.get do
    view('tests')
  end
end

Передача данных для отображения

Существует два варианта передачи информации:

  • Локальные переменные

    view('tests', locals: { info: 'Some' })
    Данные: <%= info %>
  • Переменные экземпляра

    @info = 'Some info'
    view('tests')
    <%= @info %>

Что лучше использовать?

  • Синтаксис локальных переменных сложнее переменных экземпляра
  • Если не будет найдена локальная переменная, то работа шаблонизатора завершится с исключением
  • Если не будет найдена переменная экземпляра, то данные на странице не отобразятся
  • Скорость работы шаблонизаторов выше, если используются переменные экземпляра

Полезные элементы языка Ruby

Эти классы и библиотеки входят в стандартную поставку языка Ruby

Создание классов для хранения данных

Класс Struct предоставляет возможности для быстрого создания классов для хранения данных

Он входит в состав языка, не надо подключать никаких библиотек

Customer = Struct.new(:name, :address)
dave = Customer.new("Dave", "123 Main")
dave.name     #=> "Dave"

Делегирование методов

Модуль Forwardable предоставляет инструменты для делегирования методов переменной экземпляра

require 'forwardable'

class RecordCollection
  extend Forwardable
  def_delegator :@records, :[], :record_number
  def initialize
    @records = [1, 5, 10]
  end
end

collection = RecordCollection.new
collection.record_number(0)  # => 4

Фильтрация данных

Фильтрация данных

Комментарии к схеме взаимодействия

  • Фильтрация не изменяет состояние данных на сервере, поэтому используется GET-запрос
  • Условия обработки фильтров определяются на сервере
  • Все фильтрационные формы приложения должны работать одинаково
  • При выполнении фильтра не надо терять данные, введённые пользователям
  • Если в результате запроса данные не найдены, тогда надо явно об этом сообщить пользователю

Изменение данных на сервере

Изменение данных

Комментарии к схеме взаимодействия

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