こんにちは、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).name
Jean 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アプリケーションのメーラーに
*_path
helperでビルドした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ウォッチタグ)