Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

週刊Railsウォッチ: Ruby/Railsのアップグレード情報をscrapboxに集約ほか(20240514後編)

こんにちは、hachi8833です。今夜沖縄に到着します。

週刊Railsウォッチについて

  • 各記事冒頭には🔗でパーマリンクを置いてあります: 社内やTwitterでの議論などにどうぞ
  • 「つっつきボイス」はRailsウォッチ公開前ドラフトを(鍋のように)社内有志でつっついたときの会話の再構成です👄
  • お気づきの点がありましたら@hachi8833までメンションをいただければ確認・対応いたします🙏

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

お知らせ: 来週の週刊Railsウォッチはお休みし、通常記事を公開します🙇。

🔗 Rails: 先週の改修(続き)(Rails公式ニュースより)

🔗 Active Recordで再帰的CTEをサポート

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が有用なのは一般にUNIONUNION ALLで使われる場合だけであるため)。自分の知る限り、これはArelの外部には公開されていない。

詳細

このプルリクでは、where.notの方式でWithChainプレースホルダオブジェクトをwithrecursiveのチェインに追加する。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をサポートするためにさまざまな構文が議論された。

自分は最後のモンキーパッチから多くのインスピレーションを得たが、この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ドルは円安日本的には厳しいけど、この規模の国際的なカンファレンスとしては普通の価格でしょうね」

参考: Rails World - 2024

🔗 Autotuner: Rails・アプリでRubyのガベージコレクションのチューニング方法を提案するツール

Shopify/autotuner - GitHub


つっつきボイス:「AutotunerはShopifyが作ったツールで、以下の記事↓を翻訳していて知りました(近日公開予定)」「Rubyのガベージコレクション(GC)がRailsアプリのどこでボトルネックになっているかを測定してチューニング方法を提案するんですね: GCが遅さの原因になるようなレベルになってくるとこういうツールはありがたい👍」

参考: Autotuner: How to Speed Up Your Rails App | Rails at Scale

🔗 sidekiq-fair_tenant: マルチテナントRailsアプリでSidekiqジョブの優先順位付けを公平にする

Envek/sidekiq-fair_tenant - GitHub


つっつきボイス:「こちらはEvil Martiansが公開しているgemで、一足先に公開した以下の記事↓でフィーチャーされています」「RailsやSidekiqに限らず、不特定多数のユーザーがジョブキューを使うようになってくるとこの種の制御が必要になってきますね: 重たいジョブをたくさんエンキューするユーザーにはキューを分けたプレミア料金プランをおすすめするとか」「記事でもこのツールのおかげでヘビーユーザー向けの別プランを作れるようになったと書かれていますね」「こういう一般的な問題を扱いやすくできるのはよさそう👍」

Rails: マルチテナントでSidekiqジョブを公平に優先順位付けするsidekiq-fair-tenant gem(翻訳)

🔗 Ruby/Railsのアップグレード情報をscrapboxに集約


つっつきボイス:「少し前に有志がRubyとRailsのアップグレード情報をScrapboxに集約したとのことで(今後も継続)、さらにRailsガイドのアップグレードガイドにもscrapboxへのリンクが末尾に掲載されました」「同じような箇所でつまづく人は多いので、アップグレード時に情報をかき集めなくて済むのはいいですね👍」「TechRachoの記事もあちこちにある」「ありがたい🙏」


14 参考資料(日本語) -- Rails アップグレードガイド - Railsガイドより

追記(2024/05/16)

アップグレードガイドでは、Railsバージョンごとにもリンクが追加されています↓。


2 Rails 7.0からRails 7.1へのアップグレード -- Rails アップグレードガイド - Railsガイドより

🔗Ruby

🔗 スライド『ふつうのWebサービス開発者がRubyKaigiを楽しむためのRubyの知識』


つっつきボイス:「タイトルどおりRugyKaigiの予習に便利なスライドです」「RubyKaigiは何も予習しないで参加すると発表内容を理解できなくなるので、どんな話題が登場するかだけでも見ておくとよいと思います👍」

🔗 参考: RubyKaigi予習用TechRacho記事

RubyKaigiの発表内容と関連の深い記事を以下に貼っておきます。

Rubyパーサーを一新するprism(旧YARP)プロジェクトの全容と将来(翻訳)

YJIT: CRuby向けの新しいJITコンパイラを構築する(翻訳)

Ruby 3.3で大幅に強化されたIRBの解説(翻訳)

Ruby: byebugからruby/debugへの移行ガイド(翻訳)

ruby.wasmはじめの一歩: Ruby Next Playground構築の舞台裏(翻訳)

Ruby: mallocでマルチスレッドプログラムのメモリが倍増する理由(翻訳)

Rubyのメモリ管理方法1: 基本概念(翻訳)

Rubyのヒープをビジュアル表示する(翻訳)

🔗 Ruby LSPで"Go to definition"をサポート(Ruby Weeklyより)

Shopify/ruby-lsp - GitHub


つっつきボイス:「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前編)

ソースの表記されていない項目は独自ルート(TwitterやはてブやRSSやruby-jp SlackやRedditなど)です。

Rails公式ニュース

Ruby Weekly

Publickey

publickey_banner_captured


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。