今日(2026/10/07)時点でまだRails8.2はリリースされていませんが、今日開催のGinza.rb 第99回 でRails8.2特集をするので、これまでにRails8.2用に入った変更をまとめました。
これまでは節ごとにリンクが付与されるのが便利でgistに書いていたのですが、どうやら最近からはてなブログでも節のリンクが付与されるようになったので今回からはてなブログに書いていこうと思います。
自分が気になったものだけを書いているので抜け漏れ等あると思います。間違いもあるかもしれないので気づいたらコメントください。
今日以降で新しい機能が追加されたり変更されたりしたら追記や編集などする予定ですが、ベストエフォートでやるつもりなのでご了承ください (( ⁰⊖⁰)/)
過去のまとめ
- 「Rails 6.1で新しく入る機能について」iCARE Dev Meetup #12 の登壇内容 https://icare.connpass.com/event/183716/
- Rails7.2のマイナーフィーチャーで気になったところ
- Rails8.0のマイナーフィーチャーで気になったところ
- Rails8.1のマイナーフィーチャーで気になったもの
Railties
存在しない環境名でrails sしたときにエラーになる
- Add environment config file existence check by daniel-niknam · Pull Request #55813 · rails/rails
- これまでは環境名をtypoしても
config/environments/を読まずにそのまま起動する、だったので気づかずに運用して事故る可能性があったが、これからはconfig/environments/配下にない環境名でrails sするとエラーになる
ローカルCIでfast failできるようになった
- Fast failure mode for local ci · rails/rails@673bfc4
bin/ci --fail-fastもしくはbin/ci -fでfast fail(一つ失敗したらテスト全体を止める)ができるようになった
ローカルCIで並列実行できるようになる
- Add parallel step groups to CI runner by djmb · Pull Request #56774 · rails/rails
- ローカルCIにgroupというDSLが追加された
- parallelというオプションがあり、ここで並列数を書く
- 並列数で書いた数のスレッドでgroup内のステップを実行していく
- 各ステップはQueueに入れられ、一つのスレッドが一つずつQueueからステップをpopして実行、という流れ
- なので↓は3スレッドでそれぞれのstepを実行する
- parallelというオプションがあり、ここで並列数を書く
group "Checks", parallel: 3 do step "Style: Ruby", "bin/rubocop" step "Security: Brakeman", "bin/brakeman --quiet" step "Security: Gem audit", "bin/bundler-audit" end
- groupはネストすることができる
- parallelを書けるのは一番外側のgroupだけ
- 内側のgroupは並列に処理されない
- ↓はTestsの二つのstepは順番通りに実行される
group "Checks", parallel: 2 do group "Tests" do step "Tests: Rails", "bin/rails test" step "Tests: Seeds", "env RAILS_ENV=test bin/rails db:seed:replant" end step "Style: Ruby", "bin/rubocop" step "Security: Brakeman", "bin/brakeman --quiet" step "Security: Gem audit", "bin/bundler-audit" end
deprecation情報がStructured Eventに通知されるようになった
- Add structured event for Rails deprecations, when config.active_supp… · rails/rails@7e8fa10
config.active_support.deprecation = :notifyのとき、ActiveSupport::InstrumentationだけじゃなくてStructured Event越しにログに出力することもできるようになった
boot時にroutes.rbに対応するものがない時に警告してくれるようになる
- Remove support for dynamic controller/action in routes by tenderlove · Pull Request #58893 · rails/rails
config.eager_loadが有効なときに、ルーティングに対応するコントローラがない時にないことをログ出力するようになった- アクションがないところまではみてない
":controller(/:action(/:id))"形式のルーティングを廃止したことでこれが可能になった
CI実行時に rake_eager_loadが効くようになる
- ブログに書いたので詳しくはこちら
- フォローアップのPR
- Make config.rake_eager_load more explicit by fxn · Pull Request #57796 · rails/rails
- rails newした時に生成される
config/environments/production.rbにconfig.rake_eager_load = falseが追加されてこの項目が目立つようになった - ハマりどころなので目立つのは良いんじゃないでしょうか
Chromiumのワークスペース自動検出用のエンドポイントがRailsに追加された
- Introduce DevToolsController for chrome workspaces by coorasse · Pull Request #56245 · rails/rails
- chromiumのワークスペース機能を使うとchromiumのdevtoolでローカルの開発ができる
- それの設定は
.well-known/appspecific/com.chrome.devtools.jsonでできる .well-known/appspecific/com.chrome.devtools.jsonのルーティングを追加して、ワークスペース用のjsonを返すようになった- すると次のようにchrome devtoolsのSourcesからローカルのコードを編集できる

- フロントエンドまわりのコードを編集してすぐ内容確認したいようなケースでは便利そうだなと思いました
rails statsで統計を取りたいファイルの拡張子を一つずつ追加できるようになった
- Add
Rails::CodeStatistics#register_extensionto register file extensions forrails statsby taketo1113 · Pull Request #56017 · rails/rails - Rails8.1で拡張子を追加したい場合は次のように全体を変更する必要があった
Rails::CodeStatistics.pattern = /^(?!\.).*?\.(rb|js|ts|css|scss|coffee|rake|erb|slim)$/
- ライブラリ(例: slim)が動的に拡張子を足したいときにこれだと困る(他にも追加があるかもしれない)ので、拡張子を一つ追加するメソッド
register_extensionが追加された
Rails::CodeStatistics.register_extension("slim")
- どちらかというとライブラリ作者向けの変更
Rails.applicationをRails.appと短縮してかけるようになった
- Add Rails.app alias for Rails.application by dhh · Pull Request #56403 · rails/rails
- Rails.application長いなとずっと思っていたので朗報
- 実態は
alias :app :applicationとしているだけ
Rails.app.revision の導入
- Add Rails.app.revision by byroot · Pull Request #56490 · rails/rails
- Remove proc support in config.revision by byroot · Pull Request #56508 · rails/rails
- Read from
ENV['REVISION']if present by cocoahero · Pull Request #56742 · rails/rails Rails.app.revisionで現状のリビジョンを返すようになったENV['REVISION']があればそれを最優先するREVISIONというファイルがあればそれを、なければgitを使って最新のコミットハッシュを得るconfig.revision = ENV["GIT_SHA"]のように手動で設定することもできる
Useful in many context, deployment tracking, error reporting, monitoring, even sometimes in cache keys.
- ↑とのことで将来的に結構使われるんじゃないかなという気がします
rails consoleがexecutorでwrapされるようになった
- Wrap console command with an executor by default by zzak · Pull Request #56297 · rails/rails
- 前提: executorとは
- Rails のスレッドとコード実行 - Railsガイド に書いてある
- これまでrails consoleを実行したときは、executorを通っていなかった
- このため、console中で
User.limit(1).load_asyncのようなコードが使えない問題があった - ActiveRecord: async query methods not working in console · Issue #56277 · rails/rails
- load_async
- load_async系のメソッドは実行状況を監視する目的でexecutorの利用を必須にしている
- このため、console中で
- そこでrails consoleをexecutorでwrapするようにした
- 以下は余談
- この変更により、consoleでクエリキャッシュが意図せず有効になってしまった
- Rails edge is enabling query_cache by default for console sessions. I wonder if it is intentional or a side effect · Issue #56473 · rails/rails
- executorはクエリキャッシュの管理にも使われているのだった
- 本番環境でrails consoleを利用してバグの調査などをするとき、最新のDBの状態が返ってくる想定で実行するけどクエリキャッシュが効いているとおかしなことになってしまう
- これを解消するためにまずreload!でexecutorをresetするようになった
- そしてconsole中ではデフォルトでクエリキャッシュが無効になった
- Disable console query cache by default by camallen · Pull Request #56678 · rails/rails
rails console -qもしくはrails console --query-cacheとするとrails consoleでクエリキャッシュを有効にできる
Rack::Sendfileがデフォルトから外れた
- Don't add Rack::Sendfile to the stack if
x_sendfile_headerisnilby byroot · Pull Request #56915 · rails/rails config.action_dispatch.x_sendfile_headerがnilのとき外れる- デフォルト値がnilなのでデフォルトから外れると言って良さそう
- Improper handling of proxy headers in
Rack::Sendfilemay allow prox… · rack/rack@917c161 でほとんどのアプリケーションでRack::Sendfileの脆弱性については対応された印象はあるけど、設定によっては穴を開けられないわけではないので使っているアプリケーションがある場合は設定を見直したいですね - 手動で
config.middleware.delete Rack::Sendfileする必要がなくなるのは良い
rails queryコマンドが追加された
- Query command for read-only database queries by lewispb · Pull Request #57156 · rails/rails
- railsのサブコマンドとしてqueryが追加された
rails query "Account.where(plan: 'premium').limit(2)"のように使うと、リードレプリカなDBに対してクエリを発行し、結果をjsonで返してくる- リードレプリカに相当するコネクションがない時も
while_preventing_writesで更新系のクエリを防ぐようにはなっている
- リードレプリカに相当するコネクションがない時も
rails query --sql "SELECT id, name FROM accounts LIMIT 2"のようにARを通さずに直接クエリを投げることもできる- 他にも
rails query schemaでschema全体の情報を取得する、とかrails query schema acountsのようにして特定のテーブルの定義を見る、とかrails query modelsで全モデルのメタ情報を見るとかrails queyr explainでexplainの情報を見るとかできる - 実際はrails runnerで実行したクエリをJSONで返すのを簡単にしたくらいの機能ではある
- Rails公式で使えるようになることでAIエージェントが本番のデータを見て何かする、がやりやすくなるのが良い
アプリケーションコード限定でfrozen string literalをデフォルトにする
- Enable frozen string literal by default by byroot · Pull Request #57252 · rails/rails
- frozen string literalは古いgemではまだ対応しきれていないものがあるので、
--enable-frozen-string-literalとするのはなかなか難しい - アプリケーションコードだけなら自分たちが管理しているので切り替えやすい
- bootsnapに「アプリケーションコードだけfrozen string literalにする」を可能にするオプションが追加された
- Rails8.2でrails newするとこれが設定されたコードが出力される
- 合わせてrubocopの"StringLiteralsFrozenByDefault"が有効になる
- これは
Style/FrozenStringLiteralCommentとは違って、rubocopにfrozen string literalが有効になってますよ、というのを伝えるフラグ - 例えば
Style/MutableConstantはCONST = 'hoge'に対して.freezeをつけろ、と言ってくるけど"StringLiteralsFrozenByDefault"なら不要
- これは
- だいたいのプロジェクトは
Style/FrozenStringLiteralCommentによって# frozen_string_literal: trueがついていると思うのだけど、これが有効になればマジックコメントを削除できる- Rails8.2を待たずともbootsnapをアップグレードして設定をするだけで有効にできる
rails plugin new以外のサブコマンドを明示的にエラーにした
- fix: validate subcommand in
rails plugincommand · rails/rails@adc0f4a - rails pluginはnew以外のコマンドは受け付けないが、new以外を入れても何も起こらなかった
- 何も起きないよりはエラーになった方がAIがすぐ気づけて良いですね
i18nファイルの存在確認を遅延させてRailsの起動を早くした
- Stop filtering i18n paths on initialize by gmcgibbon · Pull Request #58199 · rails/rails
- 起動時に、i18n関連のファイルの存在チェックをしていた
- i18nなファイルが数千あると遅いので、起動時は存在チェックをしないようにした
- おそらくShopifyのアプリケーションのi18nのローディングが400msから250msに高速化されたとのこと
rails cした時にバナーが出るようになった
- Show bin/console startup banner based on IRB by byroot · Pull Request #58455 · rails/rails
- irbでバナーが出力されるようになった のを受けて、rails cでも出すようになった

rails newしたときに生成されるGitHub Actionsの各ジョブにデフォルト15分制限が追加された
- Set timeout-minutes for ci.yml jobs by t27duck · Pull Request #58595 · rails/rails
- rails newで生成されるci.ymlの各ジョブに
timeout-minutes: 15を追加しただけ - GitHubの調子が悪いせいでパッケージの取得がめっちゃ遅くなり、普段4分で済むジョブが6時間かかった、という事例が一緒に紹介されている
- 真似しておくと良さそう
publicディレクトリ配下のエラーページがGitHubの言語統計に含まれないようになった
- Exclude generated error pages from GitHub language stats by irinanazarova · Pull Request #58693 · rails/rails
- rails newしたときにできる
.gitattributesでpublic配下にあるエラーページを言語統計に含めないような指示が追加された - public配下のエラーページの容量が結構大きいので、小さいRailsアプリケーションを作ったときにHTMLの方がRubyよりも割合が高くてHTMLのリポジトリだと判定されてしまう問題があった
- 小さいけど個人的には嬉しい変更
Active Support
SecureRandom.base32が追加された
- Add SecureRandom.base32 by monorkin · Pull Request #56288 · rails/rails
- base32は読みやすさを重視して5ビットをそれぞれの文字列に変換するもの
- base32の定義はいくつかあるみたいだけど、ここではCrockfordのBase32(I, L, O, Uを除く)を採用している
- Base 32
- 数字の方(0, 1, 8, 9)を省略するパターンもあるみたい
- これを利用してランダムで視認しやすい文字列を生成するメソッドSecureRandom.base32が追加された
- ワンタイムで入力するコードなどに便利
Rails標準のテストで並列にテストを実行したときの順番を固定できるようになった&テスト実行時間の平準化ができるようになった
- Make flaky parallel tests easier to diagnose by deterministically assigning tests to workers by jeremy · Pull Request #56175 · rails/rails
--seedが同じで並列実行数が同じなら同じ順番で同じワーカでテストを再現することができるようになった- ↑をするとテストが偏っているときに遅くなるので、
work_stealing: trueというオプションを付けると暇なワーカが忙しいワーカからテストを取得して実行することができるようになるのでべんり
this_week?, this_month?, this_year?メソッドが追加された
- Introduce
this_week?,this_month?, andthis_year?to Date/Time by MatheusRich · Pull Request #55770 · rails/rails - これ系はもう追加されないんじゃないの、と思ってたけど比較的あっさり追加された
- 名前から推測できるように、DateやTimeが今週なのか今月なのか今年なのかをbooleanで返す
this_week?はデフォルト月曜始まりの模様Date.current.all_week => Mon, 16 Feb 2026..Sun, 22 Feb 2026
- Merge pull request #56872 from 55728/add-start-day-to-this-week · rails/rails@0602c5c で
date.this_week?(:sunday)のように始まりの曜日を指定できるようになった
RedisCacheStoreでキャッシュを読み込むのと同時に消す、がやりやすくなった
- Rails.cache.read: add support to new option delete: true by glaucocustodio · Pull Request #56807 · rails/rails
Rails.cache.read("greeting", delete: true)のようにdelete: trueをつけると、redisに対してGETDELという名前の通りのコマンドを発行するようになる- atomicに実行されるので普通にgetしてdelするより良い
- onetimeな何かを扱う時には便利そう
TimeやDateのRails用変換フォーマットが追加された
- Introduce
ActiveSupport::TimeFormatsandActiveSupport::DateFormatsfor registering custom date formats by paracycle · Pull Request #57345 · rails/rails Time.current.to_fs(:db)などとしたときに使われるフォーマットのハッシュが格納されているTime::DATE_FORMATSとDate::DATE_FORMATSがdeprecatedになった- これらの定数にはハッシュが入っていて、これまではそのハッシュを破壊的に変更してフォーマットの追加を行なっていたが、それが良くないやろとなった
- 定数はimmutableであるべき
- なので定数で保持するやり方をdeprecatedにして、新しいAPIである
ActiveSupport::TimeFormats.registerおよびActiveSupport::DateFormats.registerを通じてフォーマットの追加を行えるようにする - 定数に依存している後方互換性を考えて、8.2では両方使えるがどこかのバージョンで定数は消される予定
Date#this_quarter?とTime#this_quater?メソッドの追加
- Add
this_quarter?to Date/Time by 55728 · Pull Request #57963 · rails/rails - 名前の通り四半期内だったらtrueが変える
- B2B向けのサービスだったら使うんですかね
- そもそもthis_quarter?以外はquarterが完備されていたのでPRになった模様
ActiveSupport::Ractors.on_mainメソッドが追加された
- Add ActiveSupport::Ractors.on_main to proxy work to the main Ractor by gmcgibbon · Pull Request #57662 · rails/rails
- non mainなRactorでクラスインスタンス変数などを変更することはできない、という制約がある
- 処理をmain Ractorに委譲するActiveSupport::Ractors.on_mainメソッドを追加することで、Ractor環境でクラスレベルのメモ化を可能にした、というのがこのPR
- ↓をnon main Ractor環境で実行するとエラーになる
class Something def self.something @ivar ||= :a end end
- こう書けばOK
class Something def self.something @ivar || ActiveSupport::Ractors.on_main { @ivar ||= :a } end end
- on_mainメソッドは内部で jhawthorn/ractor-dispatch gemを利用している
ActiveSupport::TimeZone#parseでエラーになっていなかった引数をエラーにできるようになった
- Add config to raise on invalid TimeZone#parse strings by Saidbek · Pull Request #57211 · rails/rails
- "foobar"のような文言を引数にするとエラーにならず
nilが返っていた - "9000"のような文字列を引数にするとArgumentError
ActiveSupport.raise_on_invalid_time_zone_parse = trueで"foobar"も例外にできるようになるconfig.load_defaults 8.2に含まれている
Action Pack
routes.rbで:controllerと:actionは使えなくなる
- Remove support for dynamic controller/action in routes by tenderlove · Pull Request #58893 · rails/rails
- Rails5.0からdeprecatedだった
get ":controller(/:action(/:id))"形式のルーティングはArgumentErrorになる - ↓のように明示的に書きましょう
get "photos", to: "photos#index" get "photos/:id", to: "photos#show"
- 他にも色々細かい差分がある
- 過去には
get "show", to: PhotosControllerという書き方もできたらしいのですがこれからはget "show", to: PhotosController, action: "show"と書く必要があるとのこと controller: /photos|users/という書き方が過去にできたみたいだけどこれも無効- routingをロードする前やRackアプリケーションをマウントしている時に
request.controller_classを呼ぶとActionDispatch::MissingControllerを返すようになる
- 過去には
paramsをパターンマッチできるようになる
ActionController::Parameters#deconstruct_keysfor pattern matching by seanpdoyle · Pull Request #55789 · rails/rails- ActionController::Parametersにdeconstruct_keysメソッドが追加された
- つまりparamsに対してパターンマッチがかける
- べんり
if params in { search:, page: } Model.search(search).limit(page) else … end case (value = params[:primitive_or_nested]) in String # do something with a String `value`… in { nested_key: } # do something with `nested_key` or `value` else # … end
Accept: text/markdownでリクエストを投げたときのエラーページをテキスト形式にする
- Support text/markdown format in DebugExceptions middleware · rails/rails@6f73129
- Railsアプリケーション開発時、AIエージェントなどがmarkdownをリクエストしたときにエラーになったらHTML形式で返すよりテキストで返したほうが良かろうということでそうなった
- トークン量が減らせそうで良さそう
CSRFトークンを使わずにCSRFを防げるようになる
- Rails8.2ではCSRFトークンを使わずにCSRFを防げるようになりそう - おもしろwebサービス開発日記
- 関連: RailsでCSRFトークンを使うことで防ぐことのできる攻撃について - おもしろwebサービス開発日記
rails routes --expandedでアクションメソッドの位置も見れるようになる
- Add controller action source location to routes inspector by guilleiguaran · Pull Request #56361 · rails/rails
- 次のようにAction Locationが追加された
--[ Route 1 ]---------------------------------------------------------------------------------------------------------------------------- Prefix | users Verb | GET URI | /users(.:format) Controller#Action | users#index Source Location | /Users/shinichi.maeshima/tmp/2025/12/17/mainhoge/config/routes.rb:2 Action Location | /Users/shinichi.maeshima/tmp/2025/12/17/mainhoge/app/controllers/users_controller.rb:5 --[ Route 2 ]----------------------------------------------------------------------------------------------------------------------------
さらにEDITOR環境変数が設定されているときにルーティングエラーページを表示すると次のようにペンのアイコンが表示されて、クリックするとエディタで対応するページが開くようになる。

ActionController::Liveで一部のコンテキストをコピーしないようにできる
- Imeplement exclude keys for ActionController::Live execution sharing · rails/rails@c66a762
- 前提として、ActionController::Liveというものがある
- ↓はAction Controller の概要 - Railsガイドから引用
- このように書くことで、ストリーミング送信(データができたらできた分だけ送信する)ができる
- Railsガイドだとカラオケシステムを作るのが例に出されている
- 例えば超大きいCSVを動的に作ってから送るようなケースでも使える
- 一行ずつできた順に送るとCSVファイル全部のメモリを確保しなくても良い
class MyController < ActionController::Base include ActionController::Live def stream response.headers['Content-Type'] = 'text/event-stream' 100.times { response.stream.write "hello world\n" sleep 1 } ensure response.stream.close end end
- 実際にストリーミング送信は新規にスレッドを生成して実行している
- このとき親となるスレッドからコンテキストをコピーして実行する、という仕組み
- 状況によっては全部親のをコピーすると都合が悪い
- 例えば複数DBでレプリカを使う、というコンテキストでストリーミングするけどストリーミング処理中ではDB書き込みをしたいというケース
- そんなのあるの?という気もするけどこれを踏んでおかしくなっている人自体入る模様
ActionController::LiveandActiveRecord::Base.connected_tostale db state · Issue #52906 · rails/rails
config.action_controller.live.streaming_excluded_keys = [:active_record_connected_to_stack]のようにするとコピーしないように修正できる
docker環境でも開発時エラー画面からエディタを開けるようになる
- Fix editor URL translation and add tests for path translation logic · rails/rails@1858ded
- Rails8.1でエラーページからエディタを開けるようになった のだけど、docker環境だとホストとパスが違うので使えない…という状況だった
- RAILS_HOST_APP_PATH環境変数でこれを調整できるようになった
- 最高べんり
.devcontainer/devcontainer.jsonに次のように書くと良い
{ "containerEnv": { "EDITOR": "code", "RAILS_HOST_APP_PATH": "${localWorkspaceFolder}" } }
401レスポンス時のconent-typeを変更できるようになった
- Add
content_typeparameter tohttp_authenticationmethods by ilianah · Pull Request #56887 · rails/rails - 次のように
http_basic_authenticate_with!メソッドなどにcontent_typeを引数で渡せるようになった- JSONを返したい時などに便利
http_basic_authenticate_with( name: "admin", password: "secret", message: '{"error":"Access denied"}', content_type: "application/json" )
リクエストが安全なメソッドを利用しているかどうかを表すメソッドが追加された
- Add request.safe_method? and request.unsafe_method? · rails/rails@59942bd
request.safe_method?とrequest.unsafe_method?が追加された- RFC 9110にそのような話があるので
- リソースを変更させないメソッドとしてGET, HEAD, OPTIONS, TRACEがあり、これらをsafe_methodとしている
ActionController::Parameters#fetch_valuesが追加された
- Add fetch_values method to ActionController::Parameters · rails/rails@c628635
- Hash#fetch_valuesにもあるし、paramsでも使いたい人がいるのはわからなくない
params = ActionController::Parameters.new(name: "Francesco", age: 22) params.fetch_values(:name, :age) # => ["Francesco", 22] params.fetch_values(:name, :none) # => ActionController::ParameterMissing: param is missing or the value is empty or invalid: none params.fetch_values(:name, :none) { |key| key } # => ["Francesco", :none]
ActionController::Parameters#deep_transform_valuesが追加された
- Add ActionController::Parameters#deep_transform_values by Edilbek · Pull Request #57340 · rails/rails
- ActiveSupport内のHashでも同名メソッドがある
- これまででも一回Hashに変換すれば↓似たようなことは可能だけど、これだとpermitされた、されていないという情報が失われてしまうのが困るとのこと
params.to_unsafe_h.deep_transform_values { ... }
ユーザごとにrate limitをかける、がやりやすくなった
- Rate limiting calls
cache_keyonby:if the object responds to it by daniel-sabourin · Pull Request #55555 · rails/rails - rate_limitを定義したときのデフォルトではipアドレスで区別をする
- これだと例えば法人などを相手したときに困ることがある(オフィス全体で同じIPアドレスになったりしますよね)
- そこで、cache_keyメソッドを持っているオブジェクトをbyオプションの戻り値にすると、それを使ってrate limitのキーを作るようになった
- べんりでは
class CommentsController < ActionController::API # Cache key in the store would be `rate-limit:comments:user/1` rate_limit to: 2, within: 2.seconds, by: -> { current_user } private # Substitute however you load your user object (potentially using Devise) def current_user User.find(1) end end
Acceptヘッダに*/*が含まれていた時に必ずHTMLを返す、をやめた
- Add RFC 9110 compliant Accept header content negotiation opt-in by williantenfen · Pull Request #57579 · rails/rails
- これまでIE7対応由来で「Acceptヘッダに
*/*が含まれていた時に必ずHTMLを返す」という仕様になっていた - でも今はそんなブラウザはいないのでRFC準拠のやり方に変わった
Accept: application/json, */*な時にHTMLじゃなくてJSONを返すようになる
- ただし非互換な変更なので
config.action_dispatch.strict_accept_header = trueとした時に有効になる(config.load_defaults 8.2)
トークン認証時に認証スキーム情報を渡せるようになった
- Pass scheme to token authentication block in http_atthentication by chad-cole · Pull Request #58211 · rails/rails
- トークンベースで認証させるとき、Authorization ヘッダーを利用することが多い
- Railsでは
authenticate_or_request_with_http_tokenなどを利用する - 渡されてきたトークンを検証するときに、トークンがなんのスキームで渡されてきたかという情報がなかった
- DPoPというOAuth2.0の拡張仕様を利用するときはスキームの検証が必要
- DPoPはクライアントの秘密鍵の署名をつけて送る手法で、トークンが盗まれても(秘密鍵がバレない限り)悪用がしづらい手法
- というわけで
authenticate_or_request_with_http_tokenなどのブロック引数にschemeが追加された(optional) authenticate_or_request_with_http_token(scheme: :dpop) do ...のようにすると受け付けるschemeをdpopに限定できる- 何も指定しないとtoken, bearer, dpopに限定される
QUERYメソッド対応が入った
- Add support for the HTTP QUERY method by magnogouveia · Pull Request #57973 · rails/rails
- BODYを伴ったGETメソッドであるところのQUERYメソッドがRailsアプリケーションで使えるようになった
- 検索フォームで、クエリ文字列が長すぎるのでPOSTにせざるを得ない、というケースで便利に使える
- QUERYメソッドを使うには、fetch APIで送る
- formタグによるサポートは議論中
- Support QUERY method in form submissions · Issue #12594 · whatwg/html
Active Model
Errorsオブジェクトに引数に渡したエラー種別に関するメッセージを返すメソッドが追加された
person.errors.where(:name, :invalid).map(&:message) person.errors.where(:name, :invalid).map(&:full_message)
もともと↑のように書けていたのが↓のようにかけるようになる
person.errors.messages_for(:name, :invalid) person.errors.full_messages_for(:name, :invalid)
allow_nilやallow_blankに条件をつけることが簡単にできるようになった
- Add callable support to allow_nil and allow_blank by rjlynch · Pull Request #55309 · rails/rails
- これまでvalidation時の
allow_nilやallow_blankにはtrueを値として渡すことしかなかったけど、↓のようにシンボル(もしくはProcなどのcallableなオブジェクト)を渡して、allow_nilやallow_blankを有効にする条件を指定できるようになった
validates( :course_id, inclusion: { in: -> (form) { form.permitted_courses.map(&:id) } }, allow_nil: :saving_progress? )
- ifやunlessで同様のことはできるが、こっちのほうが簡潔にかけるぞという主張
- バリデーション条件が複雑になるのは微妙だけど、選択肢が増えること自体はいいんじゃないでしょうか
has_secure_passwordでbcrypt以外にargon2も使えるようになった
- Add
:algorithmoption tohas_secure_passwordby lucasmazza · Pull Request #56041 · rails/rails でbcrypt以外のアルゴリズムを追加できるようになり - Add built-in Argon2 support for
has_secure_password. by guilleiguaran · Pull Request #56057 · rails/rails でargon2がビルトインで使えるようになった - いまどきはargon2が推奨されているので、新しいサービスならこっちを指定するのが良さそう
json型のカラムのスキーマを定義できるようになった
- Add schematized json for has_json by dhh · Pull Request #56258 · rails/rails
- DHH作
- 次のような
has_jsonメソッドとhas_delegated_jsonメソッドが追加された
class Account < ApplicationRecord has_json :settings, restrict_creation_to_admins: true, max_invites: 10, greeting: "Hello!" has_delegated_json :flags, beta: false, staff: :boolean end a = Account.new a.settings.restrict_creation_to_admins? # => true a.settings.max_invites = "100" # => Set to integer 100 a.settings = { "restrict_creation_to_admins" => "false", "max_invites" => "500", "greeting" => "goodbye" } a.settings.greeting # => "goodbye" a.staff # => nil a.staff = true a.staff? # => true
- 次のようにしてjson型のカラムのスキーマを定義することができる
- シンボルでプロパティの型を定義する(boolean, integer, string)か、デフォルト値を決めることができる
- 決めている型以外の値がアサインされたらArgumentError
- ネストは不可
- シンボルでプロパティの型を定義する(boolean, integer, string)か、デフォルト値を決めることができる
- デフォルト値はインスタンスを作るときとbefore_saveのタイミングで設定される
has_delegated_jsonは名前からわかるように各値へのアクセスを簡単にするためのメソッド- べんりな気もするけど、スキーマ決まってるならjsonじゃなくて普通にカラムを定義したらいいんじゃん?という気もしますね
- サンプルコードのように設定値やフラグのようなものを一つずつカラムを定義して管理するのが面倒というケースを想定しているのかな
validatesのトップレベルの条件とvalidatorレベルの条件が両方とも効くようになった
- Combine per-validator and top-level :if/:unless/:on in validates by Mordorreal · Pull Request #57050 · rails/rails · GitHub
- これまで次のようなコードはglobal?だけが実行されてlocal?は実行されなかった
validates :title, presence: { if: :local? }, if: :global?
- 理由は
Hash#mergeでオプションのマージをしていたので、上の例だとifキーで衝突してglobalが勝つ - これを両方の条件を実行してANDで評価するような修正が入った
- これはバグフィックスで良いと思うけどこのバグの振る舞いに依存しているコードとかあったりしないかやや心配
validates_numericality_of ... in: にprocやシンボルを渡せるようになった
- Support proc and symbol for
NumericalityValidator's:inoption by ThomasSevestre · Pull Request #46262 · rails/rails - 元々less_thanなどの他のオプションはprocやシンボルを受け付けていたけど、inは受け付けられていなかった
validates_numericality_of :price, in: ->(o) { 0..o.max_price } validates_numericality_of :price, in: :price_range def price_range 0..max_price end
Active Record
enumのnegative scopeにnullなレコードも含むようになった
- Fix negative scopes for enums to include records with
nilvalues by fatkodima · Pull Request #55912 · rails/rails - enumを指定したときにデフォルトでscopeが定義される
- 例えば↓のとき、proposed, written, publishedという名前のscopeができる
class Book < ApplicationRecord enum :status, [:proposed, :written, :published] end
not_をつけるとnegative scopeとして、通常のscopeの否定のscopeができていた- not_proposed
- not_written
- not_published
- enumが指定するカラムがNULLを含むときに、negative scopeでNULLを含むレコードが対象にならないのはおかしい、ということでNULLのカラムも対象になるようにnegative scopeが修正された
- 非互換な変更なので設定で切り替えるのかな?と思いきやアップグレードガイドに変わるぞ、と追記されて完了
- これ設定で切り替えるほうがいいんじゃないかな
explainのフォーマットを指定できるようになった
- Make explain accept hash format syntax by gmcgibbon · Pull Request #56198 · rails/rails
- MySQLやPostgreSQLではEXPLAINを打つときにフォーマットの指定ができる
- 余談だけどMySQL9.5.0からはデフォルトでTREEという形式になるらしいMySQL 9.5.0 Innovation changed
explain_formatdefault value fromTRADITIONALtoTREE· Issue #55958 · rails/rails
- 余談だけどMySQL9.5.0からはデフォルトでTREEという形式になるらしいMySQL 9.5.0 Innovation changed
- これまでもexplainの引数として文字列で"FORMAT=JSON"(MySQLの場合)のようにフォーマット指定をすることができたが、PostgreSQLのとき("FORMAT JSON")と指定の仕方が違うのが微妙だった
- これを
explain(format: :json)とできるようにしたのがこのPR
- これを
マイグレーションの仕方をDBごとにカスタマイズすることができるようになった
- Add support for configuring migration strategy on a per-adapter basis. by adrianna-chang-shopify · Pull Request #56204 · rails/rails
- マイグレーションのやりかたを設定する箇所(migration strategy)があり、これを利用するとカスタマイズしたオレオレマイグレーションをつくることができる
- しかしこれはグローバルな設定であり、複数DB(例えばMySQLとPostgreSQLを併用する)のときにDBごとに別のmigration strategyを設定したいときに困る
- そこでDBアダプタごとにmigration strategyを設定できるようにしたのがこのPR
- DBごとにカスタマイズする、というユースケースはそれほどなさそうだけど、全体のmigrationのstrategyを変更できるとこういう便利さがあるぞという例を紹介してくれているブログがあるので参考までにリンクを載せておきます
- Customizing Rails Migrations with Execution Strategies | Saeloun Blog
- 証跡を残せる
- dry-run的なことができる
- migration実行時に承認させるができる
- Customizing Rails Migrations with Execution Strategies | Saeloun Blog
with_lockのブロック引数にTransactionオブジェクトが渡されるようになった
- Yield the transaction for Model#with_lock by ngan · Pull Request #56334 · rails/rails
- with_lock は内部で
transaction do ... endしている - 次のようにwith_lockの中でafter_commitを設定できるようになる。べんり
person.with_lock do |transaction| transaction.after_commit { puts "hello" } end
query_log_tagsに追加できるコンテキストにsqlが追加された
- Pass sql query to query log tags by fatkodima · Pull Request #56683 · rails/rails
- 前提
- query_log_tagsはSQLクエリにコメントの形で、どこでクエリが発行されたかを記述する
- Debugging Rails Applications — Ruby on Rails Guides
- slow query logなどを見るときに便利
verbose_query_logsはクエリの発行したファイル名や行数をRailsのログに追加する
- query_log_tagsはSQLクエリにコメントの形で、どこでクエリが発行されたかを記述する
- 今回、query_log_tagsの情報としてsqlそのものを追加できるようになった
- そのまま使うとセンシティブな情報がそのまま出る(しそもそもクエリのコメントとして出力されるので無駄な)ので、例のようにクエリの長さとか、何か加工したものを出力する想定
- どういうのがコメントとして出力されていると便利なんでしょうねえ
- PR中ではIN句に指定されたものの個数とか書いてある
トランザクションにフックして何かする、が簡単にできるようになった
- Add extension point to customize transaction for persistence methods by ipvalverde · Pull Request #56732 · rails/rails
- モデルのトランザクション処理にフックを仕掛けられる口として、
implicit_persistence_transactionメソッドが使えるようになった- ↓のように各モデルクラス内でオーバライドすると、transaction時に呼ばれる
class Account < ApplicationRecord private def implicit_persistence_transaction(connection, &block) if connection.transaction_open? yield # avoid opening a transaction if one is already open else super end end end
- この例だとトランザクションをすでに開いていたらなにもせずに実行するというようなコードになっている
- Shopifyではこれを使って「特定のモデルであれば決められた分離レベルで実行する」がやりたいみたい
create_table時に発行されるクエリをまとめて実行するようになった
- Batch SQL statements when creating tables. by andrewn617 · Pull Request #57000 · rails/rails · GitHub
create_tableを即時実行するのではなくて、create_table時に実行されるクエリ(例:add_index,change_column_comment)をまとめて処理することでschema.rbの実行を高速にした- PRコメントでは1000以上のPostgreSQLのテーブルをロードするのに最大120秒かかっていたのが最大25秒まで削減されたとのこと
add_indexに配列を渡せるようになった
- Fix #55099 allowing array syntax for expression indexes in add_index by AlexandreCamillo · Pull Request #55100 · rails/rails
- これまで、式を書きたい時は次のように文字列で書く必要があった
t.index 'lower(email), status'
- これが次のように配列で渡すことができるようになる
t.index [ "lower(email)", :status ] t.index [ "lower(email)" ]
- ちょっとした変更だけど、式じゃなければ元々配列でかけたので一貫性としてはいいんじゃないでしょうか
in_order_ofの引数をグループ化できるようになった
- Allow to pass array values to
.in_order_ofby doits · Pull Request #52871 · rails/rails - 次のように、
:publishedと:canceledは同一の優先順位で、その次に:archivedが来るようにorder byを設定できるようになった
Posts.in_order_of(:state, [[:published, :canceled], :archived]).order(created_at: :desc) # => # SELECT "posts".* FROM "posts" WHERE "posts"."state" IN (1, 2, 3) # ORDER BY CASE # WHEN "posts"."state" IN (1, 2) THEN 1 # WHEN "posts"."state" = 3 THEN 2 # END ASC, "posts"."created_at" DESC
insert_allとupsert_allでキーが揃っていない引数を渡した時のエラーメッセージが改善された
- Improve insert_all error message to show key mismatch · rails/rails@a0491b5
- insert_allやupsert_allの引数となるハッシュの配列でキーが揃ってない時のエラーメッセージが詳細になった
Book.insert_all [ { name: "Rework", author_id: 1 }, { name: "Remote", author_id: 1, isbn: "..." } ] # Before: # ArgumentError: All objects being inserted must have the same keys # # After: # ArgumentError: All objects being inserted must have the same keys (extra: ["isbn"])
- このAI時代、エラーメッセージが改善されるとトークンが節約できるので、エラーメッセージが今までよりも大事になっている気がします
enumでinvalidな値を指定したときのエラーメッセージの内容がより詳細になった
Book.new(status: "bogus") # Before: # ArgumentError: 'bogus' is not a valid status # # After: # ArgumentError: 'bogus' is not a valid status. Valid values are: "proposed", "written", "published"
デフォルトのorderを設定できるようになった
- Add support for default order by udan11 · Pull Request #39525 · rails/rails
default_orderメソッドが追加された- 他にorderが指定されなかったときに適用されるorder
- 次のように、関連に対してデフォルトのorderを設定する時にとても有用
class Post < ApplicationRecord has_many :comments, default_order: :likes # .. or .. has_many :comments, -> { default_order(:likes) } end post = Post.first post.comments # comments ordered by `likes` post.comments.order(:created_at) # comments ordered by `created_at`
query log tagがコネクションプールごとに設定できるようになった
- Enable per-pool query log tags prepend config by hmcguire-shopify · Pull Request #57641 · rails/rails
- 複数DBを利用している場合で便利なときもあるかもしれない
- 分析用のDBではquery_log_tagsをオフにしたい
- 特定のDBではコメントのフォーマットを変えたい
- 特定のDBではコメントの挿入位置を変えたい
- SQLプロキシやAPMツールなどは先頭にコメントがあると嬉しいらしい
- などの要件を満たすことができる
db/schema.rb にこれまで実行したmigrationのリストを追加できるようになった
- Support dumping schema_migrations in db/schema.rb by fxn · Pull Request #58134 · rails/rails
- これまでのdb/schema.rbは次のように、適用済みmigration最新のバージョンが
versionに記載されていた
ActiveRecord::Schema[8.2].define(version: 2026_04_16_011000) do # ... end
- これは次のようなエッジケースで困る
- 自分のブランチでまだ実行していないmigrationファイルが存在する状態でmainブランチをmergeする
- versionが、自分のmigrationファイルよりも新しくなる
bin/rails db:resetする- config/schema.rbが適用される
- このとき、versionより古いマイグレーションは全て適用済み扱いになる(!!)
- よって、この後bin/rails db:migrateしても自分のブランチでまだ実行していないmigrationファイルは実行されない
- 自分のブランチでまだ実行していないmigrationファイルが存在する状態でmainブランチをmergeする
- エッジケースの方はそれほど踏んだ人は多くなさそうだけど、versionのところでコンフリクトするのはよくある
- これらの問題を解決するために、実行したmigrationのバージョンを全部schema.rbに書くようにする、というのがこのPR
config.active_record.dump_schema_migrations = trueとしたときに有効になる(デフォルトはfalse)- 次のように、schema.rbの末尾に適用済みマイグレーションのバージョンが追記される
ActiveRecord::Schema[8.2].define do # ... end ActiveRecord::Schema.load_schema_migrations(__FILE__) __END__ 20260723024750 20260723024804
- バージョンの記述順は、バージョンをto_sしてreverseしたもの順
- バージョンを追加する場所が重複するとコンフリクトするので、reverseすることである程度ランダムな箇所にバージョン情報が挿入されることになりコンフリクトの確率を下げている
- ソート順を明示的に指定することもできる
config.active_record.dump_schema_migrations_sort_by = ->(version) { Digest::MD5.hexdigest(version) }
query log tagsとprepared statementsを同時に使えるようになった
- Allow prepared statements with query log tags by bschrag620 · Pull Request #58274 · rails/rails
- prepared statementsは、DBのクエリを先に登録しておく仕組みで、SQLインジェクションを防いだり何度も実行するクエリの実行が速くなったりする
- query log tagsは、クエリの発行元をクエリのコメントとして追加する設定
- prepqred statementsはクエリのコメント部分も見て同一のクエリなのかどうかを判定するのでquery log tagsと相性が悪く、query_log_tagsを使う時は必ずprepared statementsはオフになっていた
- でもあえて両方使いたい時もあるんや、ということで設定で両方使えるようになった
config.active_record.disable_prepared_statements = falseとすると両方使える- 両方使いたい理由は特にPRに書いてないんだけど、コメントの種類が限られている時はデメリットが少ないので使ってもいいんじゃんということなのかなあ
スキーマキャッシュから特定のテーブルを除外する設定が統合された
- Add config.active_record.schema_ignored_tables by edcarvio · Pull Request #58554 · rails/rails
- スキーマキャッシュは、Railsが起動するときにスキーマ情報を取得する際のクエリを投げずに済ませるために使えるキャッシュ
- スキーマキャッシュなしでたくさんのRailsアプリケーションプロセスが一斉に起動すると、DBに無駄に負荷がかかる
- 特定のテーブルをスキーマキャッシュから除外するときに、これまで二つの設定があった
ActiveRecord::SchemaDumper.ignore_tables: DBスキーマの出力(schema.rb)から除外する設定config.active_record.schema_cache_ignored_tables: スキーマキャッシュの生成から除外する設定
- 二つに分かれてる意味ないやろ、ということで
config.active_record.schema_ignored_tablesに統合するぞ、というのがこのPR - 従来の二つの設定はdeprecatedになっています
「SELECTでorderがない場合はshuffleして返す」ができるようになった
activerecord: add an option to shuffle unorderedSELECTresults by viralpraxis · Pull Request #58548 · rails/rails- SELECTでorderをつけない時の順序は不定だけど大体同じ順番で返ってきてしまうので、テストでは気付けないことが多い
- これに気づきやすくするための設定が追加された
- ↓の設定を書くと、orderなしのクエリでDBから複数の結果が返ってきた場合、必ずshuffleをするようになる
- DBでshuffleしているわけではなくて、あくまでArray#shuffle!を利用している
- なので
firstやlastなどのメソッドで一つのレコードだけ取得する場合は対象外 - arelでorderがあるかないか判別しているので、find_by_sqlなどでSQL文字列を直接渡しているような場合は対象外
- orderの指定の仕方によっては不定にケースがあるけどorderがついていれば対象外
config.active_record.shuffle_unordered_selects = true
- 100%気付けるわけではないにしろ、テスト環境でオンにしておくと便利じゃないかと思います
PostgreSQLのエラーメッセージの詳細度をdatabase.ymlから指定できるようになった
- Add PostgreSQL adapter support for configuring error_verbosity by beauraF · Pull Request #58638 · rails/rails
- PostgreSQLで、例えばユニーク制約のかかっているカラムで制約違反のエラーになったときに、そのメールアドレスがエラーメッセージとしてかえってくる
ERROR: duplicate key value violates unique constraint "index_users_on_email" DETAIL: Key (email)=(alice@example.com) already exists.
- 個人情報をログやSentryに出したくない!!!
- PostgreSQLにはメッセージの詳細度を変更するための関数PQsetErrorVerbosityがある
- これをdatabase.yml経由で設定できるようになった
- ↓のようにすると、エラーメッセージにメールアドレスが含まれなくなる
production: adapter: postgresql error_verbosity: <%= PG::PQERRORS_TERSE %>
- べんり
Action Mailer
ActionMailerのassertionの追加
- Extend
ActionMailer::TestCasewith multi-part assertions by seanpdoyle · Pull Request #55348 · rails/rails - assert_partというアサーションが追加された
- 引数で指定されたcontent typeをメールが持っているかどうかをチェックする
- ブロック引数はHTMLであればNokogiriでパースされた状態になっている
- multipartなメールのとき、HTML部分を選んでNokogiriでパースして…というのを手動でやる必要があったのが簡単にできるようになった
mail = UsersMailer.create(user) assert_part :text, mail do |text| assert_includes text, "Welcome, #{user.email}" end assert_part :html, mail do |html| assert_dom html.root, "h1", text: "Welcome, #{user.email}" end assert_no_part :html assert_no_part :text
Action View
collectionをrenderするときにブロックを渡せるようになった
- Add ability to use a block when rendering a collection by genezys · Pull Request #56044 · rails/rails
- 次のようにブロックに渡したテンプレートをyieldで評価することができるようになった
- collectionを使わないことで同じ事はできるけど、collectionのほうがパフォーマンスが優れているのでなるべくこっちを使いたいですね
# _post.html.erb <article class="post"> <h1><%= post.title %></h1> <div class="post__body"><%= post.body %></div> <%# Render additional content %> <%= yield post if block_given? %> </article> # index.html.erb <%= render @posts do |post| %> <%= link_to "Edit", [:edit, post] %> <% end %>
ビューヘルパで属性をネストした時にハイフンでつなぐようになる
- Tag Builders: render keywords as dasherized HTML attributes by seanpdoyle · Pull Request #50320 · rails/rails
- これまでは
dataやariaだけが特別扱いで、ネストした時にキー名をハイフンでつなぐようになっていたのが、他の属性でも同様の振る舞いになった- Alpine.jsや htmx - high power tools for html では
x-やhx-を使っているのでそれを簡単に使いたいというのがそもそものモチベーション
- Alpine.jsや htmx - high power tools for html では
- dataやaria以外でネストする意味はないのでやっているアプリケーションはほぼないとは思うんだけど、でも破壊的変更なので一応Rails8.2で気をつけた方が良いポイントだと思います
before
tag.button "POST to /clicked", hx: { post: "/clicked", swap: :outerHTML, data: { json: true } } => "<button hx=\"post /clicked swap outerHTML data {json: true}\">POST to /clicked</button>"
after
tag.button "POST to /clicked", hx: { post: "/clicked", swap: :outerHTML, data: { json: true } } # => <button hx-post="/clicked" hx-swap="outerHTML" hx-data="{"json":true}">POST to /clicked</button>
number_fileld_tagとrange_field_tagにendless、beginlessのrangeを渡せるようになる
- Support endless and beginless ranges in
number_field_tag/range_field_tagby 55728 · Pull Request #58039 · rails/rails · GitHub - number_field_tag と range_field_tag にはinというオプションがあって受け付ける値の上限下限をRangeで指定できる
- endlessやbeginlessのRangeを渡すとこれまでエラーになっていたのだけどそれを解消してendlessなら下限だけ、beginlessなら上限だけを設定するようになった
<input type="range">なんてあるんですね- input range
- input number
color_fieldにvalue: nilを渡すとnilが無視されるのが無視されなくなった
- Honor an explicit
value: nilincolor_fieldby 55728 · Pull Request #58023 · rails/rails · GitHub - color_fieldで明示的に
value: nilをしても、元々のモデルの値が表示されてしまうという不具合があったのをなおした <input type="color">もあるんですね- input color
ERBの設定をする口が公式にできた
- Allow to configure ERB options through
ActionView::Baseby Edouard-chin · Pull Request #58359 · rails/rails - 元々コミッタとしてはprivate APIのつもりだったのだけれど、publicなAPIとしてドキュメントに載っていた時期があり、これに依存しているアプリケーションもあるとのことでpublicな口を作った、という流れらしい
- 元々publicだったerb_trim_mode=に加えて三つの設定が追加されている
- erb_implementation=
- escape_ignore_list=
- strip_trailing_newlines=
Rails8.2ではherbがデフォルトのテンプレートエンジンになる
- Add Herb as an HTML-aware ERB implementation by marcoroth · Pull Request #58552 · rails/rails
- Add
herb:checktask to verify Herb compatibility · rails/rails@563d9a0 - Add a framework default to compile HTML+ERB templates through Herb by marcoroth · Pull Request #58721 · rails/rails
config.load_defaults 8.2もしくはconfig.action_view.erb_implementation = :herbで有効になる- herbがテンプレートエンジンになると、HTML的におかしいERB(例: divが閉じられていない)というのをコンパイル時に検出できるようになって便利
rails herb:checkでherbでコンパイルできないビューがあるかどうかをチェックできるようになる- ここでdivが閉じられていないようなERBを検出できる
Active Storage
Active Storageでサムネイル変換を即時で行えるような選択肢が追加された
ActiveStorageimmediate variants by tomrossi7 · Pull Request #51951 · rails/rails- これまでActiveStorageのサムネイル作成は、サムネイルが呼ばれたタイミングで作るもしくはActiveJob経由でバックグラウンドで作成することができた
- 作ってすぐ大量にサムネイルへのアクセスがあるときに困る
- 同時に大量の同一サムネイル生成が必要になって競合する
- そこで新しいオプションが追加されて、次のように
immidiate: trueをオプションとして付与することで、同期的にサムネイルを作ることができるようになった - 実装はjobで作られていてperform_nowとperform_laterを切り替えるだけにしている
has_one_attached :avatar_with_immediate do |attachable| attachable.variant :thumb, resize_to_limit: [4, 4], immediate: true end
ActiveStorageで即時サムネイル生成するときに解析もやるようになった
- Active Storage: eager analysis and local file processing for immediate variants by jeremy · Pull Request #56327 · rails/rails
- もともとActiveStorageでアップロードしたファイルは
ActiveStorage::AnalyzeJobで非同期に解析処理が行われていた- AnalyzeJobでは例えば画像ならwidthとheightをメタデータとしてDBに保存する
- 即時サムネイル生成を有効にしたときに解析処理も即時で行うようになった
- ついでに
after_uploadができるようになった- これまでは
after_create_commitが使われていたが、すでに存在するblobについてはcommitしないのでうまく機能しなかったっぽい - 恐らくblobは同じでもattachmentが違って、それぞれ別々のサムネイルが作られる、というユースケースを考えているのかな(PRのコメントが簡潔なのでこのあたり推測)
- これまでは
ActiveStorage::Attachment.after_upload do # Your custom logic here end
アップロードファイルの解析は遅延処理ではなくバリデーション前に行うのがデフォルトになる
- Analyze attachments before validation by jeremy · Pull Request #56335 · rails/rails
config.load_defaults 8.2でデフォルトになる
Active StorageがAPIモードで使いやすくなった
- Allow configuring Active Storage base controller parent class by zzak · Pull Request #54189 · rails/rails
- これまでActiveStorageの親クラスは
ActionController::Base決め打ちであり、APIモードを利用しているときのActionController::APIではないので実質的にActive Strorageの機能はAPIモードではない、となっていた - ↑のPRで可変になり、APIモードであれば
ActionController::APIが親クラスとして設定されるようになった
ActiveStorageにattach!が追加された
- ActiveStorage: implement attach! by quentindemetz · Pull Request #54416 · rails/rails
- attachメソッドは対象モデルがDBに永続化されていて、変更されていなければ引数のファイルを即時saveするメソッド
- attach!はsaveとsave!の関係と同じで、バリデーションに失敗するなどで保存に失敗をしたら例外(
ActiveRecord::RecordNotSaved)を発生させる - トランザクション中でattachさせて、失敗したらロールバックしたいぞ、みたいな時には便利かと
has_many_attachedで関連づけられているファイルの容量合計を返すヘルパーメソッドbyte_sizeが追加された
- Add a helper to get the combined byte size of blobs attached via rails/rails@7376be3
- 合計でn MBまでアップロード可能、みたいな制限をかけることがあるはずなので便利そう
document.images.byte_size # => 2048
ImageProcessing gemのメジャーバージョンアップ
- Active Storageの裏側で利用されているImageProcessing gemがメジャーバージョンアップした
- Update Active Storage for ImageProcessing 2.0 by janko · Pull Request #57403 · rails/rails · GitHub
- ImageProcessingはruby-vipsとmini_magickを透過的に使えるgem
- 1.xでは両方を依存関係に含んでいたけど、2.0からは明示的な依存関係がなくなり、利用者が明示的に使う方のライブラリをGemfileに含める運用になった
- なのでImageProcessing2.0にするタイミングでruby-vipsを明示的にGemfileに追加する必要がある
- ここでの注意点はruby-vipsをrequireするときにlibvipsがないとエラーになるという点
- CIなどでもしlibvipsがない環境だとRails初期化の時にエラーになる
- もしこれに引っ掛かるようであれば
gem 'ruby-vips', require: falseのようにしておく- Rails8.2でrails newした時のGemfileはそうなる
- Make
rails newwork again on systems that do not have vips by jeromedalbert · Pull Request #57613 · rails/rails
variant生成に独自のクラスを指定できるようになった
- Allow
config.active_storage.variant_processorto be set to a class … · rails/rails@5ea765e - ActiveStorageでvariantsを作る手段としては、mini_magick, vips, disabledの三つしか選べなかった
- 今回の変更で独自のクラスを指定できるようになった
- ASにはanalyzeとpreviewという機能もあるのだけれど、こちらはすでにクラスを指定できたのでそれに合わせた形
ActiveStorage::Transformers::Transformerのインタフェースを備えているクラスを指定する- サンドボックス環境で画像変換したい、というときに便利そう
- basecamp/hotcell: ☢️HotCell☢️ is an unprivileged sidecar container for handling untrusted inputs, purpose-built for Rails applications. をActiveStorageで使うための前提になっている
ffmpegとffprobeに対してオプションを渡せるようになった
- Allow ffmpeg and ffprobe input arguments to be configured by flavorjones · Pull Request #58461 · rails/rails
- ActiveStorageの機能に、動画のプレビュー生成やメタデータの解析などがある
- これはffmpegやffprobeを使っているけど、オプションを渡す口がなかったので追加した、というPR
- オプションで許可するコーデックを絞り込む、ができると悪意のある人からの攻撃箇所を減らすことができて便利
marcelのバージョンが2にアップグレードされた
- Upgrade to Marcel 2 for Active Storage content type detection by jeremy · Pull Request #58549 · rails/rails
- marcelはActive Storageで利用されている、ファイルの形式を判定するためのgem
- rails/marcel: Find the mime type of files, examining file, filename and declared type
- v2になるにあたって、返すMIME typeの表記を正式なものに揃える、という変更が入った
- 非互換変更なので、もしmarcelを使ってMIME typeを見ている場合は影響を受けるところがないか確認すると良いと思います
ActiveStorageのダイレクトアップロードに関するルーティングを設定しない、ができるようになった
- Introduce
config.active_storage.draw_direct_upload_routeby codergeek121 · Pull Request #58377 · rails/rails config.active_storage.draw_direct_upload_route = falseにするとダイレクトアップロードに関するルーティングが設定されなくなる- 先日のActiveStorageに関する脆弱性がダイレクトアップロードを利用するものだったので、ダイレクトアップロードを使っていないなら機能をオフにしたいぞ、という機運が高まった模様
Active Job
一回deprecatedになったconfig.active_job.enqueue_after_transaction_commitの設定が復活した
- Activejob Enable enqueue_after_transaction_commit by default with ActiveRecord and validate invalid configurations by mugitti9 · Pull Request #55788 · rails/rails
- Rails8.2からは
config.active_job.enqueue_after_transaction_commit = trueがデフォルトになる - そもそもの経緯
- Rails7.2で
config.active_job.enqueue_after_transaction_commitの設定が追加された- Automatically delay Active Job enqueues to after commit by casperisfine · Pull Request #51426 · rails/rails
- トランザクション中にエンキューを試みても、トランザクション後にエンキューを遅延させるための設定
- 設定は3値
:always:never:default- Rails7.2でのデフォルト値(
config.load_defaults 7.2のとき)は:default:default時の振る舞いはActiveJobのアダプタに任せるという振る舞いになる- アダプタを切り替えても(例: SolidQueueで同一DB→sidekiqに移行)いい感じに動くようにこのような設定が作られた
- しかしその後
:default設定時の振る舞いが分かりづらい、という話が出た - これを受けて、設定をbooleanにするのとグローバルな設定をdeprecatedにするという変更が入った
- Deprecate
config.active_job.enqueue_after_transaction_commitconfig · Issue #52675 · rails/rails - Deprecate setting
config.active_job.enqueue_after_transaction_commitby rafaelfranca · Pull Request #53375 · rails/rails - ここの流れがややこしい
- とにかくこれらの設定は8.0でdeprecatedで8.1で削除された
- ジョブごとの設定は残された
- Deprecate
- そして今回グローバルでtrueにすべきでしょ、という人が現れてなんとdeprecatedが撤回された
- Activejob Enable enqueue_after_transaction_commit by default with ActiveRecord and validate invalid configurations by mugitti9 · Pull Request #55788 · rails/rails
- deprecatedのときの議論が込み入っていて分かりづらいのだけど、DHHは理想的にはtrueが良いと言っていて、rafaelは互換性のためにRails8.0ではとりあえずfalseをデフォルトにしておくと言っているのでその流れを汲んだっぽい
- 個人的にもデフォルトtrueがRailsアプリケーションエンジニアの幸福の総量は増えると思うけど、Railsを7.2->8.1->8.2とアップグレードするごとに設定が変わるのは大変ですね
- Rails7.2で
retry_onのwaitに渡すprocの第二引数にエラーオブジェクトが追加された
- Allow retry_on wait procs to accept error as a second argument by jpcamara · Pull Request #56601 · rails/rails
- エラーオブジェクトに持たせている属性を見て、振る舞いを変えることができるのは便利
- PRのコメントでは、rate limitエラーな時に、次に実行するタイミングを調整したい、というユースケースを主張していたけどなるほどなあとなった
class RemoteServiceJob < ActiveJob::Base retry_on CustomError, wait: ->(executions, error) { error.retry_after || executions * 2 } def perform # ... end end
Active Jobにattributeメソッドが追加されて、Active Job Continuation利用時に途中経過を保存するのがやりやすくなった
- Add
ActiveJob::Attributesto persist data between steps by bdewater-thatch · Pull Request #57208 · rails/rails - 次のように、ActiveModelのattributeをActiveJobでも使えるようになった
- これはActiveJob::Continuationの機能の一部
- デプロイ時など、途中でジョブを終了する時にどこまで終了したかをジョブの情報として残す
- さらにattributeとして指定した属性も一緒にジョブの情報として残すことができる
- これまではActiveRecordなどを利用して途中経過を保存する必要があったけど、そこまででもないものはここに保存すると良さそう
include ActiveJob::Continuableとせずにinclude ActiveJob::Attributesとしてattributeだけを使うこともできる- でもattributeだけ使えてもあんまり有効活用はできなさそう
class SubmitEnrollmentJob < ApplicationJob include ActiveJob::Continuable attribute :payment_token, :string attribute :billing_profile_id, :integer def perform(enrollment) step(:tokenize_payment_instrument) do self.payment_token = PaymentGateway.tokenize(enrollment.user.payment_instrument) end step(:create_billing_profile) do self.billing_profile_id = BillingProfileApi.create(customer_id: enrollment.user_id) end step(:submit_enrollment) do submission_id = EnrollmentApi.submit(enrollment, billing_profile_id) enrollment.update!(status: 'processing', submission_id: submission_id) end end end
特定のキューを中断する時にActive Job Continuationの中断ができるようになる
- Active Job: pass job to
stopping?for fine-grained control over interruption by bdewater-thatch · Pull Request #57472 · rails/rails - ActiveJob Continuationは、デプロイをする際にジョブを実行中だったら「ここまでやった」という情報を持ちつつ再エンキューする機能
- ActiveJobのアダプタによっては特定のキューを一時停止する機能がある
- このPRによって、アダプタ側で特定のキューを一時中止するときに実行中のジョブキューの情報を見て止めるができるようになる
- もちろんアダプタ側がそれに対応しているのが前提
retry_onのwaitオプションにfloatを渡せるようになった
- Use the float value for
:waitif passed toActiveJob.retry_onby p8 · Pull Request #57817 · rails/rails - retry_onでリトライするまでの間隔をwaitオプションで指定できる
- これまで
ActiveJob.set(wait: 4.5)のようにsetではfloatが使えていたのにretryではto_iで整数秒に丸められていたのを丸めないようになった - あんまりユースケースなさそうだけど利用できる型が一致しているのはいいんじゃないでしょうか
DBの接続が不調でエラーな時はリトライしたい
- Introduce
ActiveJob::DeserializationError::RecordNotFoundby rosa · Pull Request #57742 · rails/rails - HeyでDB接続が不調で、引数のデシリアライズ中にエラーになりジョブの実行が失敗する、ということがあった
- この時のエラーはActiveJob::DeserializationErrorという広い意味でのエラーになる
discard_on ActiveJob::DeserializationErrorとしているとリトライができない
- DBへのコミットからジョブ実行までに該当レコードが削除されているような時はdiscard_onで良いのだけど、DB接続が不調なときはリトライさせたい
- というわけで
ActiveJob::DeserializationErrorのサブクラスとしてActiveJob::DeserializationError::RecordNotFoundが新設された discard_on ActiveJob::DeserializationError::RecordNotFoundとすれば「DBへのコミットからジョブ実行までに該当レコードが削除されている」時だけdiscardできる
Action Text
Action Textにto_markdownが追加された
- Add
to_markdownto Action Text, mirroringto_plain_textby flavorjones · Pull Request #56858 · rails/rails - Action TextのコンテンツをAIに渡す時なんかに便利なんじゃないでしょうか
複数のコンポーネントにまたがる変更
Railsの出力する標準のログ出力がStructured Event経由に変更になった
- Make framework log subscribers to consume structured events by gmcgibbon · Pull Request #55900 · rails/rails
- 標準のログ出力が、ActiveSupport::NotificationからRuby on Rails 8.1の構造化イベントレポート に変更された
- これにより「Rails標準のログ出力をJSONベースにする」がgem不要でできるようになるはず
- ただし
config.structured_logging.format = :jsonみたいな設定はないので現状では自前でゴニョゴニョする必要がある
- ただし
Credentialsと環境変数をまとめて取り扱う仕組みができた
- Add Rails.app.creds to provide combined credentials lookup in ENV and encrypted file by dhh · Pull Request #56404 · rails/rails
- Allow Rails.app.creds to access .env values in dev by dhh · Pull Request #56455 · rails/rails
Rails.app.creds.require(:db_password)のようなメソッドが追加された- ↑のような書き方だと最初
ENV['DB_PASSWORD']をみてあればそれを使い、なければcredentialsにあるキーを探す。なければエラーENV.fetch("DB_HOST") || Rails.app.credentials.require(:db_host)と同じ
- 他にも値がなかったときにデフォルト値を返すためのoptionメソッドもある
Rails.app.creds.require(:db_host) # ENV["DB_HOST"] || Rails.app.credentials.require(:db_host) Rails.app.creds.require(:dataase, :host) # ENV["DATABASE__HOST"] || Rails.app.credentials.require(:database, :host) Rails.app.creds.option(:cache_host, default: "cache-host-1") # ENV["CACHE_HOST"] || Rails.app.credentials.option(:cache_host) Rails.app.creds.option(:cache_host, default: -> { "cache-host-1" }) # ENV["CACHE_HOST"] || Rails.app.credentials.option(:cache_host) || "cache-host-1"
- 環境変数だけを対象にしたenvsメソッドも同じように扱える
Rails.app.envs.require(:db_password) # ENV,fetch("DB_PASSWORD") Rails.app.envs.require(:aws, :access_key_id) # ENV.fetch("AWS__ACCESS_KEY_ID") Rails.app.envs.option(:cache_host) # ENV["CACHE_HOST"] Rails.app.envs.option(:cache_host, default: "cache-host-1") # ENV.fetch("CACHE_HOST", "cache-host-1") Rails.app.envs.option(:cache_host, default: -> { HostProvider.cache }) # ENV.fetch("CACHE_HOST") { HostProvider.cache }
- 開発環境の時だけ
.envも読む- デフォルト設定だと
.env決め打ちで、.env.developmentみたいなファイルは考慮していない
- デフォルト設定だと
- 環境変数と.env、Credentials以外に対象となる要素を追加することもできるので、config/application.yml なども追加したら全て
Rails.app.credsで賄うこともできそう- そのときはcredsからリネームしたほうが良いかもしれない
Railsのお作法に則っていないgemを検出する機能がRails公式に入る
- 注意: まだこの機能はちゃんと動いてない
- Load hook guard by gmcgibbon · Pull Request #56201 · rails/rails · GitHub
- Railsの各コンポーネント(例: ActiveRecord)を拡張するgemは
ActiveSupport.on_load do ... endを利用して拡張を遅延させる必要がある- Railsはコンポーネントのロードをアプリケーション起動の最後に行われる前提で実装しているため、このお作法に則っていないと設定されるはずのものが設定されていない、ということが起こりうる
- 去年これに関するgemを作り発表をした
- 作ったgemに近い機能が公式に入った
- デフォルトで、お作法に則っていないgemがあると起動時に次のようなログが出力される
#{component.inspect} was loaded before appliction initialization.
Prematurely executing load hooks will slow down your boot time
and could cause conflicts with the load order of your application.
Please wrap your code with an on_load hook:
ActiveSupport.on_load(#{component.inspect}) do
# your code here
end
次のように設定するとログ出力の代わりに例外にすることができる
config.action_on_eary_load_hook = :raise
tメソッドのscopeを現在のアクション/テンプレートの相対パスで表現できるようになった
- Allow
translate'sscope:argument to resolve relative i18n keys with leading period by bensheldon · Pull Request #58504 · rails/rails - t(transrate)メソッドは現在のアクションもしくはテンプレートのパスを利用して相対的に辞書の場所を示すことができたけど、
scopeオプションを利用したときは使えなかった
# always works t(".foo.bar") # => posts.index.foo.bar # before this PR t("bar", scope: ".foo") #=> foo.bar # after this PR t("bar", scope: ".foo") #=> posts.index.foo.bar


