Что нового в Rails 7.1

Что нового в Rails 7.1

Содержание
  1. Новый API для асинхронных запросов в Rails
  2. Сброс сингулярных ассоциаций
  3. Отключение методов, генерируемых с помощью ActiveRecord#enum
  4. Поддержка общих табличных выражений
  5. Поддержка асинхронного массового уничтожения записей
  6. Другие обновления Rails 7.1
  7. Активная запись regroup
  8. Новый метод stub_const для тестирования
  9. Вызов пароля с помощью has_secure_password
  10. Сохранение вложений Возврат блоба
  11. Хранение CSRF-токенов вне сессий
  12. Проверка валидности индексов PostgreSQL
  13. Dockerfiles по умолчанию для новых Rails-приложений
  14. Контроллер здоровья по умолчанию
  15. Новый Rails.env.local для проверки окружения
  16. Завершение

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

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

Новый API для асинхронных запросов в Rails

Опираясь на более раннюю функцию из Rails 7, Rails 7.1 позволит выполнять некоторые запросы асинхронно. В Rails 7 появилась функция ActiveRecord::Relation#load_async, которая планирует выполнение запроса в фоновом пуле потоков, позволяя нам делать такие вещи, как Post.where(published: true).load_async.

В Rails 7.1 мы сможем выполнять гораздо больше запросов в фоновых потоках. Агрегатные методы будут выполняться параллельно. Если вы выполняете два или более независимых запроса в задании или контроллере, результаты запросов могут возвращаться быстрее, если ваше приложение настроено соответствующим образом.

Чтобы все работало как надо, стоит обратить внимание на два параметра конфигурации:

config.active_record.async_query_executor config.active_record.global_executor_concurrency.

Среди методов, которые можно запускать асинхронно в Rails 7.1, - async_sum, async_pluck и async_count_by_sql.

Сброс сингулярных ассоциаций

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

class Teacher < ActiveRecord::Base has_many :students has_one :classroom end teacher = Teacher.first.

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

В Rails 7.1 мы получим метод reset для ассоциации has_one. Используя наш пример класса выше, Rails 7.1 позволит нам сделать teacher.classroom.reset_teacher, чтобы очистить кэш для ассоциаций между teacher и classroom.

Отключение методов, генерируемых с помощью ActiveRecord#enum

ActiveRecord#enum генерирует кучу методов, если вы создаете перечисление Rails. В Rails 7.1 появится возможность отказаться от использования этих генерируемых методов. Вот простой пример:

class Payment < ActiveRecord::Base enum :status, %i[succeeded failed], instance_methods: false end.

Rails не будет генерировать вспомогательные методы с instance_methods: false. В настоящее время мы ожидаем, что у нас будут такие методы, как:

payment = Payment.first payment.succeeded? payment.failed? payment.succeeded! payment.failed!.

Поддержка общих табличных выражений

В Rails 7.1 будет встроена поддержка общих табличных выражений (CTE). Это гарантирует, что код будет более лаконичным, но, что более важно, нам не придется использовать Arel::Nodes для сложных запросов.

В Rails 7.1 у нас появится новый метод .with для написания запросов, подобных приведенному ниже:

Post.with( posts_with_comments: Post.where("comments_count > ?", 0), posts_with_tags: Post.where("tags_count > ?", 0) ).

Поддержка асинхронного массового уничтожения записей

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

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

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

Другие обновления Rails 7.1

ActiveRecord::Relation#explain принимает опции

Rails 7.1 позволит передавать в ActiveRecord::Relation#explain системы баз данных, поддерживающие опции EXPLAIN. Пример запроса может выглядеть следующим образом:

Customer.where(id: 1).joins(:orders).explain(:analyze, :verbose).

Активная запись regroup

Active Record позволит ”перегруппировать” запросы с помощью нового метода regroup, который можно использовать следующим образом: Post.group(:title).regroup(:author). Это генерирует SQL, эквивалентный SELECT posts.* FROM posts GROUP BY posts.author.

Того же самого можно добиться и в текущих версиях Rails с помощью более подробного кода:

Post.group(:title)..unscope(:group).group(:author).

Новый метод stub_const для тестирования

Будет добавлен новый метод stub_const для ActiveSupport::TestCase, который вставляет константу для продолжительности yield.

Например:

# World::List::Import::LARGE_IMPORT_THRESHOLD = 5000
stub_const(World::List::Import, :LARGE_IMPORT_THRESHOLD, 1) do
  assert_equal 1, World::List::Import::LARGE_IMPORT_THRESHOLD
end
assert_equal 5000, World::List::Import::LARGE_IMPORT_THRESHOLD = 5000

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

Вызов пароля с помощью has_secure_password

В Rails 7.1 улучшена функциональность has_secure_password за счет добавления аксессора password_challenge и соответствующей проверки. Проверка будет проверять, что password_challenge соответствует хранимому password_digest.

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

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

password_params = params.require(:password).permit(:password_challenge, :password, :password_confirmation).with_defaults(password_challenge: "")
if current_user.update(password_params)
  # выполнить некоторую работу
end

Сохранение вложений Возврат блоба

В Rails 7.1 при сохранении вложений к записи метод attach будет возвращать вложенный блоб или блобы. Это позволяет напрямую использовать методы blob для вложений. Однако если запись не удастся сохранить, attach вернет false.

Вот пример, демонстрирующий его использование:

@user = User.create!(name: "Josh")
avatar = @user.avatar.attach(params[:avatar])
# Теперь вы можете напрямую вызывать методы blob следующим образом:
avatar.download
avatar.url
avatar.variant(:thumb)

Хранение CSRF-токенов вне сессий

В Rails появилась новая опция конфигурации для решения проблемы чрезмерного создания и удаления миллионов сессий для простого хранения CSRF-токена, когда сессии не хранятся в cookies.

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

Вы также можете создать пользовательские классы стратегий для хранения CSRF-токенов.

class CustomStore
  def fetch(request)
    # Возвращаем токен из пользовательского места
  end

  def store(request, csrf_token)
    # Храним токен в пользовательском месте
  end

  def reset(request)
    # Удаляем сохраненный токен сессии
  end
end

class ApplicationController < ActionController:x:Base
  protect_from_forgery store: CustomStore.new
end

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

Создание индексов, как показано ниже, может привести к появлению недействительного индекса:

add_index :account, :active, algorithm: :concurrently.

В Rails 7.1 вы можете проверить валидность индекса, как показано здесь:

connection.index_exists?(:users, :email, valid: true) connection.indexes(:users).select(&:valid?).

ActiveRecord::QueryMethods#select принимает хэш

ActiveRecord::QueryMethods#select в Rails 7.1 теперь принимает хэш опций. Лучше всего это продемонстрировать на примере:

# Теперь вы можете писать селекты следующим образом:
Post.joins(:comments).select(
  posts: { id: :post_id, title: :post_title },
  comments: { id: :comment_id, body: :comment_body}
)
# Вместо этого:
Post.joins(:comments).select(
  "posts.id as post_id, posts.title as post_title, comments.id as comment_id, comments.body as comment_body"
)

Количество процессоров соответствует количеству рабочих Puma

По умолчанию вновь созданные Rails-приложения будут иметь рабочих Puma, количество которых ограничено общим числом физических процессоров на хост-машине. Эту настройку по умолчанию всегда можно изменить в файле puma.rb.

Теперь файл puma.rb для вновь созданных Rails-приложений будет выглядеть следующим образом:

if ENV["RAILS_ENV"] == "production"
  worker_count = ENV.fetch("WEB_CONCURRENCY") { Concurrent.physical_processor_count }
  workers worker worker_count if worker_count > 1
end

Ассоциации preload и eager_load должны быть разгруппированы

В Rails 7.1 будет добавлена возможность разгруппировки предварительно загруженных и нетерпеливо загруженных ассоциаций, подобно тому, как работают методы includes, select и joins в Active Record.

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

Пример использования может выглядеть следующим образом:

query.unscope(:eager_load, :preload).group(:id).select(:id).

Dockerfiles по умолчанию для новых Rails-приложений

Файлы Docker должны быть добавлены в качестве опции по умолчанию для новых приложений Rails. К ним относятся:

Dockerfile .dockerignore bin/docker-entrypoint

Эти файлы служат отправной точкой для развертывания приложения в производственной среде и не предназначены для использования во время разработки. Однако, при желании, вы можете пропустить эти файлы, используя опцию --skip-docker.

Контроллер здоровья по умолчанию

В Rails 7.1 появилась новая конечная точка для балансировщиков нагрузки и мониторов времени работоспособности. Конечная точка, названная Rails::HealthController#show, сопоставляется с путем ”/up” во вновь создаваемых Rails-приложениях. Это позволяет балансировщикам нагрузки и мониторам времени работы легко отслеживать доступность приложения.

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

Новый Rails.env.local для проверки окружения

В Rails 7.1 появился новый параметрМетод local? можно использовать для упрощения проверки окружения.

Вы сможете заменить код типа:

if Rails.env.development? || Rails.env.test? end.

С:

если Rails.env.local? end

Новый метод ActiveRecord::Persistence#update_attribute!.

В Rails добавлен новый метод ActiveRecord::Persistence#update_attribute!, который функционирует аналогично update_attribute, но использует save! вместо save.

Вот как можно использовать этот новый метод:

class Apm < ActiveRecord::Base
  before_save :check_name

  def check_name
    throw(:abort) if name == "abort"
  end
end

monitor = Apm.create(name: "App Signal") # => #<Apm name: "App Signal">
monitor.update_attribute!(:name, "AppSignal") # => #<Apm name: "AppSignal">
monitor.update_attribute!(:name, "abort") # повышается ActiveRecord::RecordNotSaved

Шаблоны, способные определять принимаемые локали

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

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

Это улучшение предоставляет больше возможностей для контроля и настройки поведения и функциональности шаблонов.

Теперь частичный шаблон в Rails может выглядеть следующим образом:

<%# locals: (title: "Заголовок по умолчанию", comment_count: 0) %>
<h2><%= title %></h2>
<span class="comment-count"><%= comment_count %></span>

Вместо:

<% title = local_assigns[:title] || "Default title" %> <% comment_count = local_assigns[:comment_count] || 0 %> <h2><%= title %></h2> <span class="comment-count"><%= comment_count %></span>

Завершение

Как видите, Rails 7.1 обещает много дальнейших улучшений в Rails 7.

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

Счастливого кодинга!

P.S. Если вы хотите читать посты Ruby Magic сразу после их выхода из печати, подпишитесь на нашу рассылку Ruby Magic и не пропустите ни одного поста!

Источник