Заметки о релизе Ruby on Rails 5.1

Ключевые новинки в Rails 5.1:

  • Поддержка Yarn
  • Опциональная поддержка Webpack
  • jQuery больше не является зависимостью по умолчанию
  • Системные тесты
  • Шифруемые секреты
  • Параметризованные рассыльщики
  • Направленные и вычисляемые маршруты
  • Объединение form_for и form_tag в form_with

Эти заметки о релизе покрывают только основные изменения. Чтобы узнать о других обновлениях, различных исправлениях программных ошибок и изменениях, обратитесь к логам изменений или к списку коммитов в главном репозитории Rails на GitHub.


1. Апгрейд до Rails 5.1

Прежде чем апгрейднуть существующее приложение, было бы хорошо иметь перед этим покрытие тестами. Также, до попытки обновиться до Rails 5.1, необходимо сначала произвести апгрейд до Rails 5.0 и убедиться, что приложение все еще выполняется так, как нужно. Список вещей, которые нужно выполнить для апгрейда доступен в руководстве Апгрейд Ruby on Rails.

2. Основные особенности

2.1. Поддержка Yarn

Pull Request

Rails 5.1 позволяет управлять зависимостями JavaScript из npm с помощью Yarn. Это облегчает использование библиотек, таких как React, VueJS и любых других из мира npm. Поддержка Yarn интегрирована с файлопроводом, поэтому все зависимости будут без проблем работать с приложением Rails 5.1.

2.2. Опциональная поддержка Webpack

Pull Request

Приложения Rails можно интегрировать с Webpack, пакетированием ассетов JavaScript, используя новый стандартный гем Webpacker. Укажите флажок --webpack при генерации новых приложений, чтобы включить интеграцию с Webpack.

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

2.3. jQuery больше не является зависимостью по умолчанию

Pull Request

jQuery требовался по умолчанию в ранних версиях Rails для предоставления особенностей, таких как data-remote, data-confirm и других частей, предлагаемых Unobtrusive JavaScript в Rails. Он больше не требуется, так как UJS был переписан с использованием чистого JavaScript. Этот код теперь находится внутри Action View как rails-ujs.

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

2.4. Системные тесты

Pull Request

Rails 5.1 имеет встроенную поддержку для написания тестов Capybara в форме системных тестов. Больше не нужно беспокоиться о настройке Capybara и стратегиях очистки базы данных для таких тестов. Rails 5.1 предоставляет обертку для запусков тестов в Chrome с дополнительными особенностями, такими как скриншоты при падении.

2.5. Шифруемые секреты

Pull Request

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

Запустите bin/rails secrets:setup для настройки нового зашифрованного файла с секретами. Это также сгенерирует мастер-ключ, который должен храниться вне репозитория. Тогда сами секреты могут безопасно добавляться в систему контроля версий в зашифрованной форме.

Секреты будут дешифрованы в production с помощью ключа, либо хранящегося в переменной окружения RAILS_MASTER_KEY, либо в файле с ключом.

2.6. Параметризованные рассыльщики

Pull Request

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

class InvitationsMailer < ApplicationMailer
  before_action { @inviter, @invitee = params[:inviter], params[:invitee] }
  before_action { @account = params[:inviter].account }

  def account_invitation
    mail subject: "#{@inviter.name} invited you to their Basecamp (#{@account.name})"
  end
end
InvitationsMailer.with(inviter: person_a, invitee: person_b)
                 .account_invitation.deliver_later

2.7. Направленные и вычисляемые маршруты

Pull Request

Rails 5.1 добавляет в DSL роутинга два новых метода, resolve и direct. Метод resolve позволяет настроить полиморфное сопоставление моделей.

resource :basket

resolve("Basket") { [:basket] }
<%= form_for @basket do |form| %>
  <!-- basket form -->
<% end %>

Это сгенерирует одиночный URL /basket вместо обычного /baskets/:id.

Метод direct позволяет создавать хелперы для произвольного URL.

direct(:homepage) { "http://rubyonrails.org" }

homepage_url # => "http://rubyonrails.org"

Возвращаемое из блока значение должно быть валидным аргументом для метода url_for. Поэтому можно передать валидные строковый URL, Hash, Array, экземпляр Active Model или класс Active Model.

direct :commentable do |model|
  [ model, anchor: model.dom_id ]
end

direct :main do
  { controller: 'pages', action: 'index', subdomain: 'www' }
end

2.8. Объединение form_for и form_tag в form_with

Pull Request

До Rails 5.1 было два интерфейса для обработки форм HTML: form_for для экземпляров моделей и form_tag для произвольных URL.

Rails 5.1 объединяет оба этих интерфейса с помощью form_with и может генерировать теги формы, основанные на URL, скоупах или моделях.

Используя просто URL:

<%= form_with url: posts_path do |form| %>
  <%= form.text_field :title %>
<% end %>

<%# Сгенерирует %>

<form action="/posts" method="post" data-remote="true">
  <input type="text" name="title">
</form>

Добавление скоупа добавляет префикс для имен полей ввода:

<%= form_with scope: :post, url: posts_path do |form| %>
  <%= form.text_field :title %>
<% end %>

<%# Сгенерирует %>

<form action="/posts" method="post" data-remote="true">
  <input type="text" name="post[title]">
</form>

URL и скоуп на основе используемой модели:

<%= form_with model: Post.new do |form| %>
  <%= form.text_field :title %>
<% end %>

<%# Сгенерирует %>

<form action="/posts" method="post" data-remote="true">
  <input type="text" name="post[title]">
</form>

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

<%= form_with model: Post.first do |form| %>
  <%= form.text_field :title %>
<% end %>

<%# Сгенерирует %>

<form action="/posts/1" method="post" data-remote="true">
  <input type="hidden" name="_method" value="patch">
  <input type="text" name="post[title]" value="<the title of the post>">
</form>

3. Несовместимости

Следующие изменения могут потребовать немедленных действий после апгрейде.

3.1. Транзакционные тесты с несколькими соединениями

Сейчас транзакционные тесты оборачивают все соединения Active Record в транзакции базы данных.

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

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

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

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

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

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

4. Railties

За подробностями обратитесь к Changelog.

4.1. Удалено

  • Удалена устаревшая config.static_cache_control. (commit)

  • Удалена устаревшая config.serve_static_files. (commit)

  • Удален устаревший файл rails/rack/debugger. (commit)

  • Удалены устаревшие задачи: rails:update, rails:template, rails:template:copy, rails:update:configs и rails:update:bin. (commit)

  • Удалена устаревшая переменная среды CONTROLLER для задачи routes. (commit)

  • Удалена опция -j (--javascript) для команды rails new. (Pull Request)

4.2. Значимые изменения

  • Добавлена общий раздел в config/secrets.yml, которая будет загружена для всех сред. (commit)

  • Конфигурационный файл config/secrets.yml теперь загружается со всеми ключами в качестве символов. (Pull Request)

  • Убран jquery-rails из стека по умолчанию. rails-ujs, который теперь встроен в Action View, включен в качестве адаптера UJS по умолчанию. (Pull Request)

  • Добавлена поддержка Yarn для новых приложений с помощью бинстаба yarn и package.json. (Pull Request)

  • В новых приложениях добавлена поддержка Webpack с помощью опции --webpack, которая делегируется в гем rails/webpacker. (Pull Request)

  • При генерации нового приложения инициализируется репозиторий Git, если не предоставлена опция --skip-git. (Pull Request)

  • Добавлены зашифрованные секреты в config/secrets.yml.enc. (Pull Request)

  • Отображается имя класса railtie в rails initializers. (Pull Request)

5. Action Cable

За подробностями обратитесь к Changelog.

5.1. Значимые изменения

  • Добавлена поддержка channel_prefix к Redis и событийным адаптерам Redis в cable.yml, чтобы избежать коллизии имен при использовании одного и того же сервера Redis с несколькими приложениями. (Pull Request)

  • Для данных трансляции добавлен хук ActiveSupport::Notifications. (Pull Request)

6. Action Pack

За подробностями обратитесь к Changelog.

6.1. Удалено

  • Удалена поддержка аргументов, не являющихся ключами, в #process, #get, #post, #patch, #put, #delete и #head для классов ActionDispatch::IntegrationTest и ActionController::TestCase. (Commit, Commit)

  • Удалены устаревшие ActionDispatch::Callbacks.to_prepare и ActionDispatch::Callbacks.to_cleanup. (Commit)

  • Удалены устаревшие методы, относящиеся к фильтрам контроллера. (Commit)

  • Удалена устаревшая поддержка :text и :nothing в render. (Commit, Commit)

  • Удалена устаревшая поддержка для вызова метода HashWithIndifferentAccess на ActionController::Parameters. (Commit)

6.2. Устарело

  • Устарел config.action_controller.raise_on_unfiltered_parameters. Он ничего не делает в Rails 5.1. (Commit)

6.3. Значимые изменения

  • Добавлены методы direct и resolve в DSL роутинга. (Pull Request)

  • Добавлен новый класс ActionDispatch::SystemTestCase для написания системных тестов вашего приложения. (Pull Request)

7. Action View

За подробностями обратитесь к Changelog.

7.1. Удалено

  • Удален устаревший #original_exception в ActionView::Template::Error. (commit)

  • Удалена неправильно названная опция encode_special_chars из strip_tags. (Pull Request)

7.2. Устарело

  • Устаревший обработчик ERB Erubis заменен в пользу Erubi. (Pull Request)

7.3. Значимые изменения

  • Обработчик raw шаблонов (обработчик шаблонов по умолчанию в Rails 5) теперь выводит HTML-безопасные строки. (commit)

  • Изменены datetime_field и datetime_field_tag, чтобы они генерировали поле datetime-local. (Pull Request)

  • Новый синтаксис в стиле Builder для тегов HTML (tag.div, tag.br и т.д.) (Pull Request)

  • Добавлен form_with, объединяющий использование form_tag и form_for. (Pull Request)

  • Добавлена опция check_parameters в current_page?. (Pull Request)

8. Action Mailer

За подробностями обратитесь к Changelog.

8.1. Значимые изменения

  • Разрешена установка произвольного типа содержимого, когда включены прикрепленные файлы и тело установлено как встроенное. (Pull Request)

  • Разрешена передача lambda в качестве значений в метод default. (Commit)

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

  • Входящие аргументы передаются в экшн рассыльщика в событии process.action_mailer в ключе args. (Pull Request)

9. Active Record

За подробностями обратитесь к Changelog.

9.1. Удалено

  • Удалена поддержка одновременной передачи аргументов и блока в ActiveRecord::QueryMethods#select. (Commit)

  • Удалены устаревшие скоупы i18n activerecord.errors.messages.restrict_dependent_destroy.one и activerecord.errors.messages.restrict_dependent_destroy.many. (Commit)

  • Удален устаревший аргумент принудительной перезагрузки для методов чтения одиночной и множественной связи. (Commit)

  • Удалена устаревшая поддержка передачи столбца в #quote. (Commit)

  • Удалены устаревшие аргументы name из #tables. (Commit)

  • Удалено устаревшее поведение #tables, и #table_exists?, которое возвращало таблицы и представления, чтобы теперь возвращало только таблицы, но не представления. (Commit)

  • Удален устаревший аргумент original_exception в ActiveRecord::StatementInvalid#initialize и ActiveRecord::StatementInvalid#original_exception. (Commit)

  • Удалена устаревшая поддержка передачи класса в качестве значения в запрос. (Commit)

  • Удалена устаревшая поддержка запросов с использованием запятых в LIMIT. (Commit)

  • Удален устаревший параметр conditions из #destroy_all. (Commit)

  • Удален устаревший параметр conditions из #delete_all. (Commit)

  • Удален устаревший метод #load_schema_for в пользу #load_schema. (Commit)

  • Удалена устаревшая конфигурация #raise_in_transactional_callbacks. (Commit)

  • Удалена устаревшая конфигурация #use_transactional_fixtures. (Commit)

9.2. Устарело

  • Устаревший флажок error_on_ignored_order_or_limit заменен в пользу error_on_ignored_order. (Commit)

  • Устаревший sanitize_conditions заменен в пользу sanitize_sql. (Pull Request)

  • Устарел supports_migrations? в адаптерах соединения. (Pull Request)

  • Устарел Migrator.schema_migrations_table_name, вместо него используйте SchemaMigration.table_name. (Pull Request)

  • Устарело использование #quoted_id в квотировании и приведении типов. (Pull Request)

  • Устарела передача аргумента default в #index_name_exists?. (Pull Request)

9.3. Значимые изменения

  • Изменены первичные ключи по умолчанию на BIGINT. (Pull Request)

  • Поддержка виртуальных/генерированных столбцов для MySQL 5.7.5+ и MariaDB 5.2.0+. (Commit)

  • Добавлена поддержка лимитов в обработке пакетами. (Commit)

  • Транзакционные тесты теперь оборачивают все соединения Active Record в транзакцию базы данных. (Pull Request)

  • По умолчанию опускаются комментарии в выводе команды mysqldump. (Pull Request)

  • Починен ActiveRecord::Relation#count, чтобы использовался Enumerable#count из Ruby для подсчета записей, когда передан блок, вместо игнорирования переданного блока. (Pull Request)

  • Передача флажка "-v ON_ERROR_STOP=1" команде psql не подавляет ошибки SQL. (Pull Request)

  • Добавлен ActiveRecord::Base.connection_pool.stat. (Pull Request)

  • Наследование непосредственно от ActiveRecord::Migration вызывает ошибку. Необходимо указывать версию Rails, для которой была написана миграция. (Commit)

  • Вызывается ошибка, когда у связи through имеется избыточное имя противоположной связи. (Commit)

10. Active Model

За подробностями обратитесь к Changelog.

10.1. Удалено

  • Удалены устаревшие методы в ActiveModel::Errors. (commit)

  • Удалена устаревшая опция :tokenizer в валидаторе длины. (commit)

  • Удалено устаревшее поведение, прерывающее колбэки, когда возвращаемое значение равно false. (commit)

10.2. Значимые изменения

  • Оригинальная строка, назначенная атрибуту модели, больше не замораживается неправильно. (Pull Request)

11. Active Job

За подробностями обратитесь к Changelog.

11.1. Удалено

  • Удалена устаревшая поддержка передачи класса адаптера в .queue_adapter. (commit)

  • Удален устаревший #original_exception в ActiveJob::DeserializationError. (commit)

11.2. Значимые изменения

  • Добавлена декларативная обработка исключений с помощью ActiveJob::Base.retry_on и ActiveJob::Base.discard_on. (Pull Request)

  • После того, как все попытки провалятся, передается экземпляр задания, поэтому у вас будет доступ к таким вещам, как job.arguments, для реализации собственной логики. (commit)

12. Active Support

За подробностями обратитесь к Changelog.

12.1. Удалено

  • Удален класс ActiveSupport::Concurrency::Latch. (Commit)

  • Удалена halt_callback_chains_on_return_false. (Commit)

  • Удалено устаревшее поведение, прерывающее колбэки, когда возвращаемое значение равно false. (Commit)

12.2. Устарело

  • Верхнеуровневый класс HashWithIndifferentAccess устарел в пользу ActiveSupport::HashWithIndifferentAccess. (Pull Request)

  • Устарела передача строк в опции условий :if и :unless для методов set_callback и skip_callback. (Commit)

12.3. Значимые изменения

  • Починен парсинг продолжительности и перемещения во времени, теперь он более последователен при смене DST. (Commit, Pull Request)

  • Unicode обновлен до версии 9.0.0. (Pull Request)

  • Добавлены Duration#before и #after в качестве псевдонимов для #ago и #since. (Pull Request)

  • Добавлен Module#delegate_missing_to для делегирования вызовов метода, не определенного для текущего объекта, на прокси-объект. (Pull Request)

  • Добавлен Date#all_day, возвращающий интервал, представляющий целый день для текущих даты и времени. (Pull Request)

  • Представлены методы assert_changes и assert_no_changes для тестов. (Pull Request)

  • Методы travel и travel_to теперь вызывают ошибку на вложенных вызовах. (Pull Request)

  • Обновлен DateTime#change для поддержки usec и nsec. (Pull Request)

13. Благодарности

Взгляните на полный список контрибьюторов Rails, на людей, которые потратили много часов, сделав Rails стабильнее и надёжнее. Спасибо им всем.