こんにちは、hachi8833です。
今年は現地参加のみで、録画は後日公開ですね。https://t.co/AUga53JSRL
> RubyKaigi 2024 is an in-person only conference
RubyKaigi 2024 doesn't offer remote attendance option and live streams. But as usual, we'll record all sessions and will upload to YouTube after the conference.— NAITOH Jun (@naitoh) March 6, 2024
つっつきボイス:「今年のRubyKaigi 2024はライブ中継ないのか〜」「Super Earlybirdはもう売り切れなんですね」
お知らせ: 来週の週刊Railsウォッチはお休みをいただき、通常記事を公開いたします🙇
🔗Rails: 先週の改修(Rails公式ニュースより)
🔗 汎用のfixtureアクセサを追加
minitestと競合する可能性のあるfixture名に対応するため、汎用の
fixtureアクセサを公開する。assert_equal "Ruby on Rails", web_sites(:rubyonrails).name assert_equal "Ruby on Rails", fixture(:web_sites, :rubyonrails).nameJean Boussier
同Changelogより
assert_equal "Ruby on Rails", web_sites(:rubyonrails).name assert_equal "Ruby on Rails", fixture(:web_sites, :rubyonrails).nameこれは
Metadataモデルを持っている某氏から自分に提供されたもの。fixtureアクセサはmetadataだが、Minitestに最近追加されたmetadataメソッドと競合する。cc: @zk-1
同PRより
つっつきボイス:「たしかにfixture名のような自動生成される名前が既存のものとかぶるのはよくある話」「factoryなんかもそうですね」「fixture()で囲うことで名前衝突を回避できるようになるのはいいですね👍」
# activerecord/lib/active_record/test_fixtures.rb#L256
def method_missing(method, ...)
- if fs_name = fixture_sets[method.name]
- access_fixture(fs_name, ...)
+ if fixture_sets.key?(method.name)
+ fixture(method, ...)
else
super
end
end
...
+ def fixture(fixture_set_name, *fixture_names)
+ if fs_name = fixture_sets[fixture_set_name.name]
+ access_fixture(fs_name, *fixture_names)
+ else
+ raise StandardError, "No fixture set named '#{fixture_set_name.inspect}'"
+ end
+ end
「minitestの以下の変更でMinitestモジュールにmetadataというメソッドが入ったのがたまたまぶつかっていたことでこの問題に気づいたのね↓」
参考: + Add metadata lazy accessor to Runnable / Result. (matteeyah) · minitest/minitest@de80282
# lib/minitest.rb#L460
def metadata
@metadata ||= {}
end
🔗 assert_initializerを追加
Rails::Generators::Testing::Assertions#assert_initializerを導入。既存の
initializerジェネレータアクションを補完する。assert_initializer "mail_interceptors.rb"Steve Polito
同Changelogより
つっつきボイス:「assert_initializerは新しいアサーションですね」「config/initializers/ディレクトリの下に指定のイニシャライザが存在しているかとか中身をチェックしたりするアサーションということなのか」「内容はassert_fileのショートハンドというシンプルなものですね↓」「アプリのイニシャライザがいつの間にか書き換えられていないかどうかのチェックに使う感じなのかな」「Railsフレームワークのジェネレータテストでも今後使うかも?」
# railties/lib/rails/generators/testing/assertions.rb#L141
+ def assert_initializer(name, *contents)
+ assert_file("config/initializers/#{name}", *contents)
+ end
参考: § 5 イニシャライザファイルを使う -- Rails アプリケーションの設定項目 - Railsガイド
参考: Rails API assert_file -- Rails::Generators::Testing::Assertions
🔗 development/test環境でdefault_url_optionsをデフォルトで設定するよう修正
development環境とtest環境で
action_mailer.default_url_options値を設定する。このコミットが入る前は、新規Railsアプリケーションのメーラーに
*_pathhelperでビルドしたURLが含まれているとActionView::Template::Errorエラーが発生していた。Steve Polito
同Changelogより
つっつきボイス:「default_url_optionsコンフィグが未設定だと、開発中にAction Mailerのテンプレートで*_urlヘルパーとかを書いたときにエラーになっちゃうので、この修正が欲しい気持ちわかる: 設定を自分で入れれば解決できるけど、そのひと手間を解消してくれるのは地味にありがたい👍」「Rails開発の長い人なら一度は経験しているでしょうね」「修正ではlocalhost:3000がdeveloper環境とtest環境のデフォルトになるんですね」
# railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt#L46
+
+ config.action_mailer.default_url_options = { host: "localhost", port: 3000 }
参考: §4.5 default_url_options --Action Controller の概要 - Railsガイド
コントローラと異なり、メーラーのインスタンスは受信リクエストに関するコンテキストをまったく持たないので、
:hostパラメータは自分で設定する必要がある。
config.action_mailer.default_url_options = { host: "www.example.com" }
railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt#L47より
🔗 主キーでない単一のカラムでModel.query_constraintsのエラーメッセージを修正
主キーでない単一のカラムで
Model.query_constraintsをraiseされるのは期待通りだが、そのエラーメッセージが正しくなかった。適切なエラーメッセージを表示するよう修正した。Joshua Young
同Changelogより
つっつきボイス:「単一主キー(いわゆるサロゲートキー)がないカラムでquery_constraintsがエラーをraiseするときの条件が適切でなかったのね↓」「複合主キー(composite primary key)を使っているときに起きる可能性があるエラーなんですね」
# activerecord/lib/active_record/reflection.rb#L786
def derive_fk_query_constraints(foreign_key)
primary_query_constraints = active_record.query_constraints_list
owner_pk = active_record.primary_key
- if primary_query_constraints.size != 2
+ if primary_query_constraints.size > 2
raise ArgumentError, <<~MSG.squish
The query constraints list on the `#{active_record}` model has more than 2
attributes. Active Record is unable to derive the query constraints
for the association. You need to explicitly define the query constraints
for this association.
MSG
end
「本当はこのメッセージが表示されるべきだったということですね↓」
# activerecord/test/cases/associations_test.rb#L381
assert_equal "The query constraints on the `Sharded::BlogPost` model does not include the primary key so Active Record is unable to derive the foreign key constraints for the association. You need to explicitly define the query constraints for this association.", error.message
Sharded::BlogPostモデルのquery constraintsには主キーが含まれていないので、Active Recordはこの関連付けで外部キー制約を導出できない。この関連付けのquery constraintsを明示的に定義する必要がある。
🔗 RailsのテストスイートにあるBase.connectionをwith_connectionやlease_connectionに置き換えた
振る舞いをより適切に反映するため
#lease_connectionによって置き換えられる。
ActiveRecord::Base.connectionも同様に非推奨化されるが、削除猶予期間や非推奨化警告の表示は行わない。今後内部で使われることのないようにするため、Active Recordのテストスイート内部から
Base.connectionを削除する。一部の呼び出し側(callsites)では
with_connectionを利用する形に変換され、他の呼び出し側の一部についてもよりシンプルな形でlease_connectionに移行された。このプルリクは、#50793に基づいて変換される呼び出し側リストとして使える。
同PRより
つっつきボイス:「これと次はプルリクリストから見繕ったものです」「前回見た#51083の続きということですね(ウォッチ20240228)」「修正量は多いけど、基本的にconnectionをwith_connectionやlease_connectionに置き換えている↓: 特定のコネクションでコネクションプールを直接使うのをやめて、トランザクション内で同じコネクションプールに再接続できるwith_connectionやlease_connectionに変えたいというような話でしたね」
# actionmailbox/test/migrations_test.rb#L6
class ActionMailbox::MigrationsTest < ActiveSupport::TestCase
setup do
@original_verbose = ActiveRecord::Migration.verbose
ActiveRecord::Migration.verbose = false
- @connection = ActiveRecord::Base.connection
+ @connection = ActiveRecord::Base.lease_connection
@original_options = Rails.configuration.generators.options.deep_dup
end
「この修正で従来のBase.connectionは非推奨になったということだけど、プルリクメッセージにも書かれているように、Base.connectionを非推奨化サイクルに乗せて今後削除することはしないという点がポイントですね: Base.connectionはとても広範囲に使われている定番のメソッドなので、仮に非推奨化アラートを表示すると、ありとあらゆるgemからものすごい量のアラートが発せられることになるでしょうね」「そうなったらつらいヤツですね」
「Base.connectionは今後も外部のgemなどでは引き続き使えるけど、Railsフレームワーク内からは呼び出さないようにした: これは最も適切な処置だと思います👍」
「非推奨化されるけどアラートも出さず今後削除されることもないというのはかなり珍しいパターンかも: これに気づかないまま使い続ける人もたくさんいそうですし、実際そういう対応でもいける内容なんでしょうね」
前方互換性維持のために残されているレガシー機能といえば、以下の記事で取り上げられているcontent_tagを思い出しました↓。こちらは非推奨ではありませんが。
🔗 uncachedメソッドにdirtiesオプションを追加
このプルリクは、
ActiveRecord::Base.uncachedとActiveRecord::ConnectionAdapters::ConnectionPool#uncachedにdirtiesオプションを追加する。
true(デフォルト)に設定すると、書き込みで現在のスレッドに属するすべてのクエリキャッシュがクリアされる。
falseに設定すると、影響を受けるコネクションプールへの書き込みでクエリキャッシュがクリアされなくなる。これは、Solid Cacheでキャッシュ書き込みによってクエリキャッシュがクリアされないようにするために必要。
Donal McBreen
同Changelogより
つっつきボイス:「これもActiveRecord::Baseとかに関連しているので上の#51240と関連しているのかなと思ったら、Solid Cache向けの別の改修のようですね」
参考: Rails API uncached -- ActiveRecord::QueryCache::ClassMethods
🔗 Solid CacheもRails 8に入る
「この間からSolid Queueが何度も話題になっていますけど、DBをエンジンにしたクエリキャッシュであるSolid CacheもRails 8でデフォルトになる流れになっていますね↓」「Solid QueueとSolid Cacheの字面が似ててSolid Cacheの方に気づいてなかった...」「 データベースをキャッシュとして使うこと自体はキャッシュ用テーブルを作れば可能なので、そうした実装としてSolid Cacheを作ったということでしょうね」
参考: Solid Cache should be the default caching backend for Rails 8 · Issue #50443 · rails/rails
参考: Rails API ActiveRecord::ConnectionAdapters::QueryCache
「最近の動きを見ると、Rails 8ではrails newしたときにジョブキューやクエリキャッシュのためのインフラをさしあたって決めなくても済むような方向に進めている感じはしますね」「自分もそんな感じがしています」「前回話したように(ウォッチ20240306)、アプリケーションの負荷が大きいときはRDBの仕事をあまり増やしたくないけど、アプリを新規作成するときにジョブキューやクエリキャッシュをどれにするかみたいな、決める必要のある項目が減るというのは新規アプリ開発プロジェクトで嬉しいことであるのはたしか👍」
「Railsチュートリアルのような教育用途でもジョブキューやクエリキャッシュを学びやすくなりそうですね」「インフラのセットアップが減るのは教育目的でももちろん有用だと思いますが、おそらくDHHの頭にあるのは主に業務プロジェクトで使う新規アプリのセットアップ軽減と期間短縮なんじゃないかなと想像しています」「なるほど」
参考: Ruby on Rails チュートリアル:プロダクト開発の0→1を学ぼう
「なお、クエリキャッシュについてはファイルシステム上(FileStore)やメモリ上(MemoryStore)のキャッシュなら現状でも追加インフラなしでできますけど、異なる環境同士でキャッシュを共有できないという問題があるんですよ」「あ、そうでしたか」「ファイルベースのものはコンテナと相性があまりよくないし、メモリベースのものもコンテナに割り当てられるメモリはそんなに多くないので、そのまま本番のコンテナにデプロイしたときなんかにメモリ上のクエリキャッシュが増えるとメモリを使い切ってしまうといった懸念はありますね」「たしかに」「MemoryStoreはプロセスが違うと共有できませんし、FileStoreはコンテナ(ホスト)が違うと共有できませんけど、Solid Cacheはコンテナ間でも共有が可能な点が大きなメリットだと思います👍」
前編は以上です。
バックナンバー(2024年度第1四半期)
週刊Railsウォッチ: method_missingの引数を'...'に置き換え、JRuby Prism、Sidekiqのしくみほか(20240306)
- 20240228 Rails 8でSprocketsがPropshaftに置き換わる、devcontainerサポートほか
- 20240227後編 Turbo Nativeアプリ、書籍『Everyday Rails Testing with RSpec』新版執筆開始ほか
- 20240221前編 form_withのmodelオプションへのnil渡しが非推奨化、Dockerfileでjemallocが有効にほか
- 20240207後編 aws-sdk-rubyの全gemにRBSファイルが追加ほか
- 20240206前編 Pumaのデフォルトスレッド数変更、Rails 1.0をRuby 3.3で動かすほ
- 20240125後編 RailsコントローラのparamsはHashではない、ruby-enumほか
- 20240123前編 Railsの必須Rubyバージョンが3.1.0以上に変更ほか
- 20240119後編 Ruby 3.3でYJITを有効にすべき理由、Turbo 8の注意点8つほか
- 20240117前編 Rails 8マイルストーン、2023年のRails振り返り、Solid Queueほか
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。

週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)