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

週刊Railsウォッチ: bundlerのbinstub生成を廃止、RubyのZJITほか(20250319)

こんにちは、hachi8833です。

週刊Railsウォッチについて

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

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

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

🔗 bundler-auditのコンフィグがデフォルトで追加された

アプリケーションのGemfileに既知のセキュリティ問題があるかどうかの探索はすべてのCIフローに組み込むべき。

同PRより


つっつきボイス:「DHH自らのプルリクです」「お、bundler-auditがデフォルトで追加されたのか」「これがbinstubですね↓」「bundler-auditはgemのセキュリティチェックの定番として継続的にCIとかで動かすものだし、コードを直接書き換えるものでもないので追加しておいていいと思う👍」

# railties/lib/rails/generators/rails/app/templates/bin/bundler-audit.tt
require_relative "../config/boot"
require "bundler/audit/cli"

ARGV.concat %w[ --config config/bundler-audit.yaml ] if ARGV.empty? || ARGV.include?("check")
Bundler::Audit::CLI.start

rubysec/bundler-audit - GitHub

🔗 RuboCopがRuby 2.7より新しいRuby構文をチェックできるようになった→取り消し

RuboCopの対象となるRubyバージョンが、デフォルトの2.7から利用中のRubyバージョンに変更された。

Jeremy Daer
同Changelogより


つっつきボイス:「そうそう、RuboCopにはTargetRubyVersionでRubyバージョンを指定できるけど、そのバージョンを環境変数などから拾うようになったんですね」

# railties/lib/rails/generators/rails/plugin/templates/rubocop.yml.tt
# Omakase Ruby styling for Rails
inherit_gem: { rubocop-rails-omakase: rubocop.yml }

+AllCops:
+ TargetRubyVersion: <%= ENV["RBENV_VERSION"] || ENV["rvm_ruby_string"] || RUBY_ENGINE_VERSION %>

「あれ、直後にcad2196でこのプルリクが取り消されてる」「#54692のコメントを見ると、Rubyバージョンをチェックするコードは前からあったけど、それに気づかずに作ったプルリクがマージされてしまったので後から取り消したのね: こういうこともありそう」

RuboCopはデフォルトで現在のRubyバージョンを使うが、特定の状況(親ディレクトリの.ruby-versionに依存するプラグイン内のtest/dummyアプリなど)ではバージョンを推測できない。

コミットcad2196より

rubocop/rubocop - GitHub

🔗 bundleのbinstubを生成しないようになった

Railsの新規アプリケーション作成でbin/bundleというbinstubを生成しなくなった。

bin/bundle binstubは、適切なバージョンのbundlerを有効にするために使割れていたが、このメカニズムはRubygem自体に組み込まれたため不要になった。

Edouard Chin

同Changelogより

動機/背景

詳細

Bundlerは今後のバージョンで、bundle binstub bundlerを実行してもbin/bundle binstubを生成しないようになった。Bundler側でこの変更が行われた理由は、rubygems/rubygems#8345に詳しい。

問題

Bundlerの変更がリリースに組み込まれると、新しいRailsアプリケーションを生成したときにBundlerが警告を発して、bin/bundleを使うAuthenticationGeneratorが動かなくなる。

解決方法

Bundlerのbinstubの生成を停止し、ジェネレーターを変更してコードベースの残りの部分と同じBundlerコマンドを使うようになった。
同PRより


つっつきボイス:「お、bundlerのbinstabが新しいbundlerで動かなくなったという問題なのね: 現在はそもそもRubygemsでbundlerのbinstubを生成しなくなったし、bundlerのバージョンが適切に選ばれるようにRubygemが改善されたので、bundlerのbinstubを生成しないことになったという流れなのか」「binstubはRailsのbin/ディレクトリに置かれる実行ファイルのラッパースクリプトでしたね↓」

binstubをしっかり理解する: RubyGems、rbenv、bundlerの挙動(翻訳+解説)

このプルリクの元になったエンドユーザーや開発者の問題

Bundlerが生成したbinstubが厳しすぎて、直接エラーが発生してBundlerの自動インストールと自動切り替えメカニズムがトリガーされないことがあった。

適切なバージョンのBundlerを有効にするためのメカニズムは2つあるが、1つを除けばほとんど同じ。

  • 1: RubyGemsで行う場合。
    これにより、最も適切なBundlerバージョンがリストの先頭に配置されるが、存在しない場合は他のバージョンにフォールバックする。
    このメカニズムはBundlerのコードでもバックアップされており、ロックされたバージョンのBundlerを自動的にインストールし、最初に最適なバージョンが有効になっていない場合にそのバージョンでBundlerを再起動する。
  • 2: Bundler binstubsで行う場合。
    これは実際にはBundlerのバージョンに~> x.y制約を設定するため、エラーが発生する可能性がある。

特に、vendor/bundle/が設定済みで、ロックされたバージョンのBundlerと一致するマイナーバージョンのBundlerが実行されている場合、Bundlerのbinstubsはエラーで終了する。Bundlerのbinstubsが配置されていなければ、Bundlerの内部メカニズムはvendor/bundle/以下にあるBundler自体のロックされたバージョンで自動的に再起動し、すべて正常に動作する。

このプルリクで実装されている問題の修正方法

サポートしているすべてのRubyGemsバージョンに同じ(かつ厳しくない)メカニズムが存在するため、Bundler自体のbinstubは不要になった。このため、binstubの生成を停止することにした。
rubygems/rubygems#8345より

🔗 before_validationafter_validationexcept_onオプションが追加された

動機/背景

このプルリクは、before_validationコールバックとafter_validationコールバックにexcept_on:オプションを追加する。

詳細

before_validationコールバックとafter_validationコールバックは、on: :createon: :updateの他に、on: :some_custom_valueなどのカスタムバリデーションのコンテキストをサポートする唯一のコールバックである。

except_on:オプションは、既に#43495validatesなどのバリデーションに追加された。
このプルリクは、before_validationコールバックとafter_validationコールバックにも同じオプションを追加することで、オプションを一貫させる(ところでexcept_on:は本当に素晴らしい!@DRBraggに感謝😅)
同PRより


つっつきボイス:「お、今まではどんな場合にバリデーションするかをバリデーションコンテキストのon:オプションで指定できたけど、このexcept_on:オプションでも指定できるようになったんですね: これはよさそう👍」「言われてみればon:が使えるなら逆の条件も指定できる方がいいですね」

# activemodel/test/cases/validations/callbacks_test.rb#L64
  before_validation :set_before_validation_marker_on_context_b, on: :context_b
+ before_validation :set_before_validation_marker_except_on_context_a, except_on: :context_a
  after_validation :set_after_validation_marker_on_context_a, on: :context_a
  after_validation :set_after_validation_marker_on_context_b, on: :context_b
+ after_validation :set_after_validation_marker_except_on_context_a, except_on: :context_a

🔗 YJITを有効にするときにハッシュ形式でオプションを渡せるようになった

動機/背景

現在のRailsでは config.yjit = true でYJITを有効にできるが、この設定ではYJITオプションの設定まではサポートされていなかった。

RUBYOPT="--yjit-log"を設定することで統計やログなどのオプションを有効にできるが、これらのオプションを config.yjit経由で直接構成する方法が提供されていない。

YJIT自体 では、RubyVM::YJIT.enable(stats: false, log: false)という方法でこれらのオプションを有効にできるので、Railsの設定レイヤ内で管理する方が便利。

詳細

このプルリクは、config.yjitを拡張してハッシュを受け取れるようにし、ユーザーがYJITオプションを指定可能にする。

config.yjit = true            # デフォルト設定でYJITを有効にする
config.yjit = { stats: true } # YJITをカスタムオプションで有効にする(新しくサポート)
config.yjit = false           # YJIT を無効にする

同PRより


つっつきボイス:「細かな改良だけど、ハッシュ形式でオプションを渡せるようにすることで、YJITをオン/オフすることに加えて今後オプションを増やすことも可能になりますね👍」

🔗 CSPのnonceをサードパーティのビューにも設定可能になった

動機/背景

コンテンツセキュリティポリシー(CSP)のコンテキストでnonceを利用する場合、現在のビューで使われるタグにnonce属性を自動的に追加できない。

つまり、nonce属性は、それを必要とする各タグ(javascript_tagjavascript_include_tagstylesheet_link_tag)に手動で追加しなければならない。

自分の理解では、現時点ではこれらのnonceをタグに自動的に追加する方法は存在しない。

  • 自分が特定した現状のソリューションは以下のとおり。

  • javascript_include_tagおよびstylesheet_link_tagヘルパーをオーバーライドして、nonce属性を明示的に追加する。

  • 各タグに手動でnonce: trueを追加する。この場合、サードパーティから直接取得したビュー内のタグには反映されないため、アプリケーションがそれらを信頼しているにもかかわらず、CSPによってブロックされる可能性が生じることになる。

必要なタグでnonce属性を自動的に有効にする設定オプションを利用可能にするのが理想的。

もっとよい方法があるかどうか、およびそうしない方がよい理由があるかどうかについては、自分にはわからない。

詳細

このプルリクの主な変更は、content_security_policy_nonce_auto設定オプションの追加である。
これにより、content_security_policy_nonce_directives設定オプションで指定されたディレクティブの影響を受けるタグにnonceが自動で含まれるようになる。現在の振る舞いを維持するために、新しい設定オプションはデフォルトでfalseに設定される。

追加情報

これにより、開発者は各タグに手動で追加することなく、必要なタグのnonce属性を手軽に有効にできる。また、ビューがサードパーティgemの内部にある場合でも、アプリケーションによって信頼され使われているgemは自動的にnonce属性の恩恵を受けられるようになる。

繰り返すが、もっとよい方法があるかどうか、およびそうしない方がよい理由があるかどうかについては、自分にはわからない。

このプルリクエストを改善する方法についてのフィードバックや提案を求む。
同PRより


つっつきボイス:「これは、content_security_policy_nonce_autoというコンフィグを追加して、これが有効な場合はサードパーティgemのビューも含めてCSP(コンテンツセキュリティポリシー)のnonce属性を自動でJSやCSSのタグに設定してくれるようになった: 自動でやってくれるのはありがたい👍」

参考: nonce - HTML: ハイパーテキストマークアップ言語 | MDN
参考: §9.3.2 nonceを追加する -- Rails セキュリティガイド - Railsガイド

🔗 スキーマ形式をデータベース単位で指定可能になった

動機/背景

このプルリクによって、マルチデータベースのアプリケーションで、データベースごとにschema_formatを個別に設定可能になる。

個人的には、レガシーアプリケーションを変換してRails標準の手法に1つずつ移行するときに、この機能が必要だと感じた。また、(特定の機能を使うために)データベースをstructure.sqlに切り替える必要があるが、すべてのデータベースでそうする必要がない場合にも有用。

詳細

データベース設定ファイルでschema_formatrubyまたはsqlを設定可能になった。この項目が存在しない場合は振る舞いは変更されず、アプリケーション全体の設定が使われる。または、適切な環境変数が存在する場合はその環境変数が使われる。
同PRより


つっつきボイス:「Railsのデータベーススキーマは標準のRuby形式の他にSQL形式も選べますけど、データベースごとに指定する方法が意外にもなかったんですね」「これまで必要に迫られたことがなかったけど、特に古い外部データベースでスキーマ形式をSQLにしたくなることはありそうなので、確かにスキーマ形式はデータベースごとにも指定可能にすべきですね👍」

データベース設定ファイルでschema_formatを設定できるようになった。

primary:
  schema_format: ruby

マルチデータベースのセットアップでデータベースごとにフォーマットを変えたい場合に有用。

T S Vallender
同Changelogより

参考: § 6.2 スキーマダンプの種類 -- Active Record マイグレーション - Railsガイド

🔗 RailsガイドにdevcontainerでPodmanを使う方法を追加


つっつきボイス:「これはRailsガイドの更新です」「devcontainerでPodmanを使えるようにする機能自体は既に追加されているのね↓」

containers/podman - GitHub

「Podmanを知らなかったので調べてみたところ、Dockerとは別のコンテナシステムなんですね」「そうそう、PodmanはRed Hatがかなり昔からやっているDockerのオルタナ的なソフトウェアで、Dockerと同じくOCIというコンテナ標準規格にも沿っているはず↓」「Podmanはroot権限がなくても動かせるのが売りだけど、その後Dockerも似たようなことができるようになったみたいですね」

参考: オープン・コンテナ・イニシアティブ(OCI)仕様の謎解き | Docker
参考: Podman使ってみた & Dockerとの違いは? #Docker - Qiita
参考: Podman とは?をわかりやすく解説

Podman が他のコンテナエンジンと異なるのは、デーモンを使用しないという点です。つまり、コンテナを実行するのに、root 権限を持つプロセスを必要としません。

Podman とは?をわかりやすく解説より

「RailsでPodmanを使いたい人ってどのぐらいいるんでしょう?」「Red Hat Enterprise Linux(RHEL)で動かしているプロジェクトだとRed Hadが公式にサポートするソフトウェアを使うことになってPodmanが必要になるとかはありそうですね」「なるほど」

参考: Red Hat Enterprise Linux operating system

なおDockerコンテナのビルドの高速化も行われたそうです↓。

🔗 SQLiteアダプタにActiveRecord::Resultcolumn_typesを追加

これにより、ActiveRecord::Result#cast_valuesでPostgreSQLの場合と同様に、SQLiteで日付や時刻をキャスト可能になる。これは、ActiveRecord::Base.lease_connection.select_allを利用するクエリで有効。カラム型は、PostgreSQLの場合と同様にカラム名または位置で参照可能。

関連するPostgreSQLアダプタコード: rails/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb at v8.0.1 · rails/rails
同PRより


つっつきボイス:「PostgreSQLで使えていたActiveRecord::Resultcolumn_typesをSQLiteでも使えるようにしたそうです」「このあたりを見ると、従来はSQLiteの場合はEMPTY_HASHにされてたんでしょうね↓」「なるほど」「SQLiteアダプタの改修だけど、PostgreSQLアダプタも少し変更されてるのが面白い」

# activerecord/lib/active_record/result.rb#L102
    def initialize(columns, rows, column_types = nil)
      # We freeze the strings to prevent them getting duped when
      # used as keys in ActiveRecord::Base's @attributes hash
      @columns      = columns.each(&:-@).freeze
      @rows         = rows
      @hash_rows    = nil
-     @column_types = column_types || EMPTY_HASH
+     @column_types = column_types.freeze
+     @types_hash   = nil
      @column_indexes = nil
    end

参考: Rails API ActiveRecord::Result

🔗 RailsのルーティングをGTGで最適化

RailsフレームワークのJourneyで使われているGTG(Generalized Transition Graph)は、ルーティング処理を高速化するために使用される技術。GTGは以下の特徴を持つ。

  1. 非決定性有限オートマトン(NFA)を用いてルート定義を表現する。

  2. ルート定義をトークンに分解し、抽象構文木(AST)を構築した後、NFAに変換する。

  3. 状態遷移表を使用して、URLのマッチングを効率的に行います。

  4. 複数のルート定義を単一のNFAで表現することができ、ORノードを使用して結合します。

  5. 文字列による遷移(string_states)と正規表現による遷移(regexp_states)を組み合わせて、柔軟なルーティングを実現します。

JourneyでGTGを使うことで、大規模なアプリケーションでのルーティング処理を高速化し、効率的なURLマッチングを実現する。


Perplexity の Eliot より: pplx.ai/share


つっつきボイス:「こちらはルーティングの最適化です」「GTGの詳細はわからないけど、状態遷移図があることからもルーティングのツリーを高速化する技術ですね↓」

参考: Generalized transition graph (GTG) definition with Example | Engineer's Portal

🔗Rails

🔗 Rails World 2025のCFP募集(Rails公式ニュースより)


つっつきボイス:「今年のRails World 2025は9月にアムステルダムで開催だそうです」「4/10とあるのはRails WorldのCFPの締め切りなんですね」

🔗Ruby

🔗 RubyKaigi 2025とZJIT


つっつきボイス:「今回のRubyKaigi 2025は型の話題が多いようです」「今度はZJITが登場か: 今のところRubyKaigi 2025のプロポーザル以上の情報がなさそうだけど」「発表者がYJITと同じMaxime Chevalierですけど、もしかしてYの次だからZなんでしょうか?」「MatzもZの次はαかもってツイートに書いてる😆」

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


今回は以上です。

バックナンバー(2025年度第1四半期)

週刊Railsウォッチ: RailsガイドがRails 8.0.1に対応ほか(20250123)

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

Rails公式ニュース

Ruby Weekly


CONTACT

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