こんにちは、hachi8833です。今夜沖縄に到着します。
RubyKaigi 2024で話題になりそうな技術的トピックの予習メモをまとめた https://t.co/IjadL2Y7XE
— osyoyu (@osyoyu) May 11, 2024
お知らせ: 来週の週刊Railsウォッチはお休みし、通常記事を公開します🙇。
🔗 Rails: 先週の改修(続き)(Rails公式ニュースより)
🔗 Active Recordで再帰的CTEをサポート
- PR: Add support for recursive CTEs in ActiveRecord by ClearlyClaire · Pull Request #51601 · rails/rails
Active Recordで再帰的CTEのサポートを追加。
Post.with_recursive( post_and_replies: [ Post.where(id: 42), Post.joins('JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id'), ] )
上によって以下のSQLが生成される。
WITH RECURSIVE "post_and_replies" AS ( (SELECT "posts".* FROM "posts" WHERE "posts"."id" = 42) UNION ALL (SELECT "posts".* FROM "posts" JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id) ) SELECT "posts".* FROM "posts"
ClearlyClaire
同Changelogより
動機/背景
Rails 7.1でCTE(Common Table Expressions)がサポートされるようになったが、再帰的なCTEまでは拡張されていない。
このプルリクは、再帰的なCTEを有効にできる
QueryMethods#with_recursive
を追加する。さらに、この機能によってクエリの配列を
with
ハッシュ値として渡せるようになる(再帰的なCTEが有用なのは一般にUNION
やUNION ALL
で使われる場合だけであるため)。自分の知る限り、これはArelの外部には公開されていない。詳細
このプルリクでは、
where.not
の方式でWithChain
プレースホルダオブジェクトをwith
やrecursive
のチェインに追加する。QueryMethods#with_recursive
を呼び出すと、with_is_recursive
フラグがオンになったスコープが返される(これはその後build_with
でArelに:recursive
を渡すために使われる)。また、このプルリクは
build_with_value_from_hash
を変更して、値の配列をクエリとして扱えるようにする。このときサブクエリはUNION ALL
でjoinされる。これで、以下のようなクエリを書けるようになる。
Post.with_recursive( post_and_replies: [ Post.where(id: 42), Post.joins('JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id'), ] )
上のコードで以下のSQLが生成される。
WITH RECURSIVE "post_and_replies" AS ( (SELECT "posts".* FROM "posts" WHERE "posts"."id" = 42) UNION ALL (SELECT "posts".* FROM "posts" JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id) ) SELECT "posts".* FROM "posts"
追加情報
unionのときと同様に、再帰的なCTEをサポートするためにさまざまな構文が議論された。
- Common Table Expression support added "out-of-the-box" #37944 (comment) では
.with(:recursive, ...)
が提案された- Common Table Expression support added "out-of-the-box" #37944 (comment)でも
.with.recursive(...)
(これはこのプルリクで実装されている)と.with_recursive(...)
が提案された- Recursive + 7.1 vlado/activerecord-cte#16 (comment)では
.with_recursive(cte_name, base_case, recursive_case)
が提案された。自分は最後のモンキーパッチから多くのインスピレーションを得たが、このAPIは既存の
QueryMethods#with
との相性が非常によくないと思えたので、.with_recursive
方式を採用し、そのインターフェイスを拡張してUNION ALL
のサブクエリに提供することにした。また、配列のサポートと
.with_recursive
を同じプルリクに入れることにした。これらは密接に関連しているが、必要なら2つのプルリクに分けてもよい。テストもこの変更に応じて更新したが、これにふさわしい既存のフィクスチャがあるかどうかわからなかったため、本質的に再帰的なクエリはテストしていない。
同PRより
つっつきボイス:「WITH RECURSIVE
は普通に使うので、with_recursive
で書けるのはよさそう👍」「WITH RECURSIVE
はCTEでないと1回のクエリで良い感じに取得できないので、CTEを使う大きな動機になりますよね」
参考: 第181回 SQLの共通テーブル式(CTE)を使ってみよう | gihyo.jp
参考: 7.8. WITH問い合わせ(共通テーブル式)
「ところで自分の場合、欲しいSQLを最初に思い浮かべてからActive RecordやArelのコードに置き換えて書くみたいな形になることが多いこともあって、SQLを書いたらそれに対応するActive Recordのクエリコードを自動生成できたらいいのにという気持ちになることが割とありますね」「そういうエンジンが欲しくなるのわかる」
「Active Recordで書くクエリ呼び出しは、シンプルなものならともかく、ある程度以上に複雑になってくると、もうその時点で欲しいSQLは自分の中でだいたい決まっているので、その意味ではActive Recordのクエリコードとして書き直す作業は自分にとって完全に二度手間ではあるんですよね」「Active RecordのようなORMとSQLは違う言語ですもんね」「自分はクエリが複雑になってきたら生SQLで書きたい派」「最近だとチャットAIと相談しながら複雑なSQLを書けるので便利」「少なくとも構文レベルでは有効なSQLを生成してくれるので、ゼロから生SQLを書くよりは全然楽ですよね」
🔗 PostgreSQLアダプタにDate
へのデコード機能が追加
PostgreSQLAdapter
がdate型のカラムを文字列ではなくDate
にデコードするようになった。例:
ActiveRecord::Base.connection .select_value("select '2024-01-01'::date").class #=> Date
Joé Dupuis
同Changelogより
修正: #51448
コネクションレベルでdateを型キャストする
Date
デコーダーをpgアダプタに追加。これにより、
ActiveRecord::Base.connection.select_all
経由で生クエリを実行するときに、date
型のカラムがRubyのDate
型にキャストされるようになる。改修前:
ActiveRecord::Base.connection.select_value("select '2024-01-01'::date").class #=> String
改修後:
ActiveRecord::Base.connection.select_value("select '2024-01-01'::date").class #=> Date
(すべてのアダプタにdateがあるわけではないので)このレベルで型キャストのexpectationを設定することが求められるとは思わないが、これによってpgアダプタが(dateに関しては)mysql2アダプタと同等になる。
既にタイムスタンプも変換しているのだから、dateを変換してもよさそう。
テストを追加すべきかどうかわからなかった。テストではアダプタレベルでの型キャストに関するexpectationを設定するのだろうと考えている。
これによってpublic APIが変更されるが、かなり低レベルの機能とはいえ、他の型でもテストはされていない様子。
タイムスタンプとfloat型/numericを削除したが、引き続きテストはすべてパスする。他の人がヤケドしないよう、changelogエントリも更新しておいた。
このプルリクはマージされていいものかどうかわからない。影響範囲は大きいが、このあたりを掘り下げてテストを実行してみたところかなり良さそうに見える。
同PRより
つっつきボイス:「コネクションレベルでPostgreSQLのdate型をRubyのDate
にキャストしてするようになったのはいい👍」「MySQLのアダプタでは前からできてたんですね」
🔗 番外: 非推奨化済みのコードを削除
- 非推奨の
config.active_storage.silence_invalid_content_types_warning
を削除Rafael Mendonça França
- 非推奨の
config.active_storage.replace_on_assign_to_many
を削除Rafael Mendonça França
同Changelogより、他多数
つっつきボイス:「Rafaelさんによる恒例の大掃除ですね」「Changelogのエントリ数がすごい」
🔗Rails
🔗 Rails World 2024のチケット、早くもソールドアウト(Rails公式ニュースより)
つっつきボイス:「The Rails Foundationが主催するRails World 2024のチケット販売が4/30に始まっていたんですね」「公式サイトを見に行ったらもう売り切れでした↓」「チケット代500ドルは円安日本的には厳しいけど、この規模の国際的なカンファレンスとしては普通の価格でしょうね」
🔗 Autotuner: Rails・アプリでRubyのガベージコレクションのチューニング方法を提案するツール
つっつきボイス:「AutotunerはShopifyが作ったツールで、以下の記事↓を翻訳していて知りました(近日公開予定)」「Rubyのガベージコレクション(GC)がRailsアプリのどこでボトルネックになっているかを測定してチューニング方法を提案するんですね: GCが遅さの原因になるようなレベルになってくるとこういうツールはありがたい👍」
参考: Autotuner: How to Speed Up Your Rails App | Rails at Scale
🔗 sidekiq-fair_tenant: マルチテナントRailsアプリでSidekiqジョブの優先順位付けを公平にする
つっつきボイス:「こちらはEvil Martiansが公開しているgemで、一足先に公開した以下の記事↓でフィーチャーされています」「RailsやSidekiqに限らず、不特定多数のユーザーがジョブキューを使うようになってくるとこの種の制御が必要になってきますね: 重たいジョブをたくさんエンキューするユーザーにはキューを分けたプレミア料金プランをおすすめするとか」「記事でもこのツールのおかげでヘビーユーザー向けの別プランを作れるようになったと書かれていますね」「こういう一般的な問題を扱いやすくできるのはよさそう👍」
Rails: マルチテナントでSidekiqジョブを公平に優先順位付けするsidekiq-fair-tenant gem(翻訳)
🔗 Ruby/Railsのアップグレード情報をscrapboxに集約
つっつきボイス:「少し前に有志がRubyとRailsのアップグレード情報をScrapboxに集約したとのことで(今後も継続)、さらにRailsガイドのアップグレードガイドにもscrapboxへのリンクが末尾に掲載されました」「同じような箇所でつまづく人は多いので、アップグレード時に情報をかき集めなくて済むのはいいですね👍」「TechRachoの記事もあちこちにある」「ありがたい🙏」
現時点のアップグレード情報
- 総合目次: アップグレード - ruby-jp
- Rubyアップグレード目次: Rubyアップグレードガイド - ruby-jp
- Railsアップグレード目次: Railsアップグレードガイド - ruby-jp
追記(2024/05/16)
アップグレードガイドでは、Railsバージョンごとにもリンクが追加されています↓。
2 Rails 7.0からRails 7.1へのアップグレード -- Rails アップグレードガイド - Railsガイドより
🔗Ruby
🔗 スライド『ふつうのWebサービス開発者がRubyKaigiを楽しむためのRubyの知識』
つっつきボイス:「タイトルどおりRugyKaigiの予習に便利なスライドです」「RubyKaigiは何も予習しないで参加すると発表内容を理解できなくなるので、どんな話題が登場するかだけでも見ておくとよいと思います👍」
🔗 参考: RubyKaigi予習用TechRacho記事
RubyKaigiの発表内容と関連の深い記事を以下に貼っておきます。
🔗 Ruby LSPで"Go to definition"をサポート(Ruby Weeklyより)
We just released v0.16.5 of the Ruby LSP with the first iteration of method support for go to definition!
We will continue iterating to handle more method types and improve accuracy as much as possible. Please let us know if you have feedback and happy coding! pic.twitter.com/UAR1y0KSmm
— Vinicius Stock (@vinistock) April 24, 2024
つっつきボイス:「Rubyのlanguage serverであるruby-lspで"Go to definition"をサポートしたそうです」「JetBrainsのRubyMineではずっと前から"Go to definition"が使えていますけどね」「さすがお金を払う価値のあるIDEですね」「そのあたりを考えると、自分はVSCodeでないと作業できない場合でなければ当分RubyMineを使いたいかな」
参考: JetBrains の Ruby on Rails IDE
「ところで自分は未だに素のVimとgrep
でRails開発しています」「あら、RubyMineやVSCodeにもVimプラグインありますよ」「いえ、Vimにこだわりがあるわけではないので、RubyMineを使うなら素のセットアップにすると思いますけど」「IDEのキーバインドとかはなるべくカスタマイズしない方が後で困ることが少なくなりますよね」「JetBrains IDEのマクロはVimとかと比べるとめちゃくちゃ遅いですし、まあそれを承知で使っていますけど」
参考: Use Vim Editor in RubyMine (IdeaVim) | RubyMine Documentation
参考: Vim - Visual Studio Marketplace
🔗言語/ツール/OS/CPU
🔗 GitHub Copilot Workspaceがテクニカルプレビューに(Publickeyより)
つっつきボイス:「自分もとりあえずテクニカルプレビュー申し込んで承認待ちしてます」
参考: GitHub Next | Copilot Workspace
「最近の生成AIが要件定義のような上流工程で必要なものを文章化してくれるというのはやっぱり作業上のメリットは大きいでしょうね: 今も昔も上流工程をやる人が一番人手が足りていないし、こういう部分のコストは大きいので」
「記事のこのあたり↓にあるように、Issueを元に仕様案を作成するのをAIが支援できるようになると、たとえばIssueを書く人にそういった仕様案や実装計画の素案生成までやってもらうようにすれば、その通りにするにしろ変えるにしろ、それを元に進められるようになるので作業上大きいと思いますね」「なるほど」「実装計画は実際にはそこまで細かく立てないと思いますが、上流工程の作業全般のAI支援は期待したい👍」
Copilot Workspaceは、自然言語で書かれたIssue(課題)を基に、Copilotが仕様案と実装計画を示し、コーディングや既存のコードの修正を行い、ビルドをしてエラーがあればデバッグも行うという、プログラミングのほとんど全ての工程をCopilotが自動的に実行してくれる、というものです。
GitHub、「Copilot Workspace」テクニカルプレビューを開始。ほとんど全ての開発工程をAIで自動化 - Publickeyより
後編は以上です。
バックナンバー(2024年度第2四半期)
週刊Railsウォッチ: Railsコンソールが最新のIRB APIに移行、assertionless_tests_behaviorほか(20240513前編)
- 20240426後編 Prismの歴史と現況を振り返る、Steepの"narrowing"実装の内部ドキュメントほか
- 20240425前編 RailsからOpenStructを削除、Playwrightベストプラクティスほか
- 20240423後編 Kamalはゲームチェンジャーになるか、Solid Queueで使われているfugitほか
- 20240416前編 ジョブのエンキューをトランザクション完了時まで自動先延ばしほか
- 20240410後編 SeleniumでRubyの全クラスとモジュールにRBSが追加ほか
- 20240409前編 Rails公式の"rails-new"ツールでRailsプロジェクトをセットアップほか
- 20240402 solid_queueとmission_control-jobsが正式にRailsのgemに、Rubyの"チルド"文字列ほか
ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。
週刊Railsウォッチについて
TechRachoではRubyやRailsなどの最新情報記事を平日に公開しています。TechRacho記事をいち早くお読みになりたい方はTwitterにて@techrachoのフォローをお願いします。また、タグやカテゴリごとにRSSフィードを購読することもできます(例:週刊Railsウォッチタグ)