はじめに
Rails7.2の新機能について気になったので調べてみました。
Rails 7.2の新機能は、Ruby on Rails Guidesを参考にしていきす。
現時点だと、7.2.0.beta2までしか出ていないですが、この時点までのでまずは書いてきます。
rails new myapp --devcontainer
rails newに --devcontainer
オプションが追加されました。
devcontainerを用意できるようになりました。
コードで言うとこの辺りです。
class_option :devcontainer, type: :boolean, default: false,
desc: "Generate devcontainer files"
このdevcontainer
オプションがtrue
になったとき動くのがこの辺りです。
devcontainer_options
を定義して、DevcontainerGenerator
クラスに渡し、 invoke_all
でDevcontainerGenerator
のPublicメソッドを実行させていますね。
def devcontainer
devcontainer_options = {
database: options[:database],
redis: !(options[:skip_action_cable] && options[:skip_active_job]),
system_test: depends_on_system_test?,
active_storage: !options[:skip_active_storage],
dev: options[:dev],
node: using_node?,
app_name: app_name
}
Rails::Generators::DevcontainerGenerator.new([], devcontainer_options).invoke_all
end
DevcontainerGenerator
はこんな感じになっています。
class DevcontainerGenerator < Base # :nodoc:
# 省略
# ~~
def create_devcontainer
empty_directory ".devcontainer"
template "devcontainer/devcontainer.json", ".devcontainer/devcontainer.json"
template "devcontainer/Dockerfile", ".devcontainer/Dockerfile"
template "devcontainer/compose.yaml", ".devcontainer/compose.yaml"
end
# ~~
# 省略
以下の3つのファイルを作成しています。
.devcontainer/devcontainer.json
.devcontainer/Dockerfile
.devcontainer/compose.yaml
上記のファイルの元となっているテンプレートはここです。
allow_browser
Railsの各Actionにアクセスできるブラウザを制限する機能です。
使い方はこんな感じ。
class ApplicationController < ActionController::Base
# allow_browser versions: :modern
allow_browser versions: { safari: 16.4, firefox: 121, ie: false }, only: :show
end
AllowBrowser
Moduleが追加されていて、ActionController
でModuleを読み込んでいます。こちら
module ActionController
extend ActiveSupport::Autoload
# ~~ 省略
autoload_under "metal" do
autoload :AllowBrowser
# ~~省略
end
end
end
AllowBrowser module
に allow_browser
メソッドが定義されています。こちら
before_action
でallow_browser
メソッドを実行しているようです。
def allow_browser(versions:, block: -> { render file: Rails.root.join("public/406-unsupported-browser.html"), layout: false, status: :not_acceptable }, **options)
before_action -> { allow_browser(versions: versions, block: block) }, **options
end
AllowBrowser#allow_browser
メソッド内で実行されているallow_browser
は以下のように定義されています。こちら
BrowserBlocker
クラスでblocked?
で判定していますね。
def allow_browser(versions:, block:)
require "useragent"
if BrowserBlocker.new(request, versions: versions).blocked?
ActiveSupport::Notifications.instrument("browser_block.action_controller", request: request, versions: versions) do
instance_exec(&block)
end
end
end
blocked?
の判定の仕方はこんな感じです。
「UserAgent
が存在している」 かつ 「サポートされていないブラウザである」 場合は blocked?
がtrue
になるようです。
def blocked?
user_agent_version_reported? && unsupported_browser?
end
Ruby3.1以上の必要がある
以下のような感じで3.1.0以上である必要があるように定義されていました。こちら
spec.required_ruby_version = ">= 3.1.0"
デフォルトでpwa用のファイルが生成されるようになった
pwa_controllerが定義されています。こちら
class Rails::PwaController < Rails::ApplicationController # :nodoc:
skip_forgery_protection
def service_worker
render template: "pwa/service-worker", layout: false
end
def manifest
render template: "pwa/manifest", layout: false
end
end
pwa/service-worker,pwa/manifestファイルが生成されます。
テンプレートはこちらにあります。
デフォルトでrubocop-rails-omakaseが用意されます
gemはこちら
rails new . --skip_rubocop
でinstallをskipすることもできます。
rails new --devcontainer
のときと同様に以下のようにオプションが定義されています。こちら
class_option :skip_rubocop, type: :boolean, default: nil,
desc: "Skip RuboCop setup"
このオプションが以下の3箇所でrubocopは関係しています。
1つは.rubocop.ymlの生成 こちら
def rubocop
template "rubocop.yml", ".rubocop.yml"
end
もう1つはbin/rubocop
の生成 こちら
def bin
exclude_pattern = Regexp.union([(/rubocop/ if skip_rubocop?), (/brakeman/ if skip_brakeman?)].compact)
directory "bin", { exclude_pattern: exclude_pattern } do |content|
"#{shebang}\n" + content
end
chmod "bin", 0755 & ~File.umask, verbose: false
end
もう1つは、CI上でrubocopのjobの生成もあります。こちら
<%- unless skip_rubocop? -%>
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
- name: Lint code for consistent style
run: bin/rubocop -f github
デフォルトでGitHub ActionsのCIが用意されます。
rails new . --skip_ci
でinstallをskipすることもできます。
rails new . --skip_rubocop
のときと同様に以下のようにオプションが定義されています。こちら
class_option :skip_ci, type: :boolean, default: nil,
desc: "Skip GitHub CI files"
このオプションがfalse、つまりciを生成するときはこちらのように動きます。
-
.github/workflows
フォルダ作成 -
.github/workflows/ci.yml
ファイル作成 -
.github/dependabot.yml
ファイル作成
def cifiles
empty_directory ".github/workflows"
template "github/ci.yml", ".github/workflows/ci.yml"
template "github/dependabot.yml", ".github/dependabot.yml"
end
デフォルトでbrakemanが用意されます
rails new . --skip_brakeman
でinstallをskipすることもできます。
rails new --skip_rubocop
のときと同様に以下のようにオプションが定義されています。こちら
class_option :skip_brakeman, type: :boolean, default: nil,
desc: "Skip brakeman setup"
このオプションによって、bin/brakeman
コマンドの生成がされます。
def bin
exclude_pattern = Regexp.union([(/rubocop/ if skip_rubocop?), (/brakeman/ if skip_brakeman?)].compact)
directory "bin", { exclude_pattern: exclude_pattern } do |content|
"#{shebang}\n" + content
end
chmod "bin", 0755 & ~File.umask, verbose: false
end
またCIでbrakemanのJobの生成もされます。こちら
<%- if options[:javascript] == "importmap" -%>
scan_js:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
- name: Scan for security vulnerabilities in JavaScript dependencies
run: bin/importmap audit
デフォルトのpumaのスレッド数が5から3に変更
この変更は、デフォルトのスレッド数がいくつが相応しいのかというこのissueで話題になっていました。
結局3つ
になったようです。
puma.rb
のテンプレートにあるthreads_count
が3になっていました。こちら
threads_count = ENV.fetch("RAILS_MAX_THREADS", 3)
threads threads_count, threads_count
enqueue_after_transaction_commit オプション
トランザクションの最中にエンキューするか、Commit後にエンキューするかを設定できるオプションです。
Topic.transaction do
topic = Topic.create
NewTopicNotificationJob.perform_later(topic)
end
上記のコードのようなコードを書いたとき、今まではTransaction
がCommitされる前にJobがキューに登録され、Jobが動く可能性がありました。
enqueue_after_transaction_commit
オプションを使用すると、Transaction
内にJobをキューするコードを書いても、commit後にキューさせることができます。
このオプションがある場所はこちら
# It can be set on a per job basis:
# - `:always` forces the job to be deferred.
# - `:never` forces the job to be queued immediately.
# - `:default` lets the queue adapter define the behavior (recommended).
class_attribute :enqueue_after_transaction_commit, instance_accessor: false, instance_predicate: false, default: :never
- always
- never
- default
が選択できます。
この選択によって、以下のように、after_transaction
がtrue
/false
になり、Transaction
のCommit後にエンキューするかどうかに影響します。こちら
def raw_enqueue
after_transaction = case self.class.enqueue_after_transaction_commit
when :always
true
when :never
false
else # :default
queue_adapter.enqueue_after_transaction_commit?
end
if after_transaction
self.successfully_enqueued = true
ActiveRecord.after_all_transactions_commit do
self.successfully_enqueued = false
super
end
self
else
super
end
end
Ruby3.3+の場合デフォルトでYJITが有効化
Configuration
クラスのinitialize
でyjit
が追加されています。こちら
def initialize(*)
super
# 省略
@yjit = false
# 省略
end
def load_defaults(target_version)
# 省略
case target_version.to_s
when "7.2"
load_defaults "7.1"
self.yjit = true
if respond_to?(:active_job)
active_job.enqueue_after_transaction_commit = :default
end
# 省略
end
load_defaults
内でRailsのバージョンが7.2だったら、yjit
をtrueに変更しているのが分かります。
デフォルトのDockerfileにjemallocを設定してメモリ割り当てを最適化
jamellocについてはこちらが分かりやすいかもしれません。
コードだとこの辺りです。こちら
def dockerfile_base_packages
# Add curl to work with the default healthcheck strategy in Kamal
packages = ["curl"]
# ActiveRecord databases
packages << database.base_package unless skip_active_record?
# ActiveStorage preview support
packages << "libvips" unless skip_active_storage?
# jemalloc for memory optimization
packages << "libjemalloc2"
packages.compact.sort
end
packages
という配列の変数に、libjemalloc2
を入れています。
それを以下のDockerfileのテンプレートで展開して、apt install
されるようです。こちら
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y <%= dockerfile_base_packages.join(" ") %> && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
bin/setupにpuma-devの設定の提案を追加
puma-devはDockerを使わない場合の、開発環境のRailsアプリの開発用サーバーの設定を楽にするものです。
その提案がbin/setup
にコメントで追加されています。こちら
# puts "\n== Configuring puma-dev =="
# system "ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}"
# system "curl -Is https://#{APP_NAME}.test/up | head -n 1"
おわりに
以上で、Rails Guideに載っている7.2の新機能については、ざっと紹介できたと思います。
Railsリポジトリの中身のコードをたくさん見れて楽しい機会でした。
今後、追加されたりしたら、追記していきます!