古いJRuby(9.2未満)向けワークアラウンドの一掃

rspec/rspec

JRuby 9.2未満のEOLバージョンを対象に追加されていたワークアラウンドとハックを削除し、コードベースの保守性を高めました。#323でJRuby 10.1のサポートが追加されたことを受け、不要になった負債を整理しています。

背景

rspecのコードベースには、長年にわたりJRuby固有のバグへの対処コードが蓄積されていました。今回の整理が可能になったのは、対象となるJRubyバージョンがすべてEOLとなったためです。#323でJRuby 10.1(CRuby 4.0互換)のCI対応が完了し、現在サポートされるJRubyはすべて9.2以降となっています。

PRが参照するワークアラウンドの根拠となっていたJRubyのバグは以下のものです:

これらのバグはいずれも修正済みであり、対象バージョンのEOLと合わせてワークアラウンドを維持する意義がなくなっていました。

技術的な変更

複数のライブラリにまたがり、バージョン条件分岐・エイリアス定義・代替実装が削除されました。

bisect/shell_command.rb: open3_safe_escapeの廃止

rspec-core のビセクト機能では、JRubyのOpen3.popen3がシェルエスケープ済み引数を正しく処理しないバグ(jruby/jruby#2767)に対処するため、open3_safe_escapeメソッドをJRubyではquoteに、それ以外ではescapeにエイリアスしていました。このバグが解消されたため、open3_safe_escapeを廃止し、すべての呼び出し箇所を直接escapeに置き換えています。

変更前:

if RSpec::Support::Ruby.jruby?
  # :nocov:
  alias open3_safe_escape quote
  # :nocov:
else
  alias open3_safe_escape escape
end

# ...
parts << open3_safe_escape(RSpec::Core.path_to_executable)
parts.concat(locations.map { |l| open3_safe_escape(l) })
@load_path ||= "-I#{$LOAD_PATH.map { |p| open3_safe_escape(p) }.join(':')}"

変更後:

parts << escape(RSpec::Core.path_to_executable)
parts.concat(locations.map { |l| escape(l) })
@load_path ||= "-I#{$LOAD_PATH.map { |p| escape(p) }.join(':')}"

テストコード側でも、uses_quoting_for_escaping?の条件からJRubyの判定が除かれ、RSpec::Support::OS.windows?のみの判定となりました。

example.rb: rescue節の=> _ハック削除

JRuby固有のバグ(jruby/jruby#4467)を回避するため、rescue Pending::SkipDeclaredInExample => _ と変数代入形式を使っていた箇所を、通常の rescue Pending::SkipDeclaredInExample に戻しています。

変更前:

rescue Pending::SkipDeclaredInExample => _
  # The "=> _" is normally useless but on JRuby it is a workaround
  # for a bug that prevents us from getting backtraces:
  # https://github.com/jruby/jruby/issues/4467
  #
  # no-op, required metadata has already been set by the `skip`
  # method.

変更後:

rescue Pending::SkipDeclaredInExample
  # no-op, required metadata has already been set by the `skip`
  # method.

failure_aggregator.rb: assign_backtraceの統一

rspec-expectations では、JRuby 9.1以前でcallerraiseが生成するバックトレースのフレームが異なる問題に対し、バージョン条件付きで2種類のassign_backtrace実装を切り替えていました。9.2未満のサポート終了に伴い、パフォーマンス面で優れるシンプルなcallerベースの実装のみが残されています。

変更前:

if RSpec::Support::Ruby.jruby? && RSpec::Support::Ruby.jruby_version < '9.2.0.0'
  # :nocov:
  def assign_backtrace(failure)
    raise failure
  rescue failure.class => e
    failure.set_backtrace(e.backtrace)
  end
  # :nocov:
else
  def assign_backtrace(failure)
    failure.set_backtrace(caller)
  end
end

変更後:

# Using `caller` performs better (and is simpler) than `raise` on most Rubies.
def assign_backtrace(failure)
  failure.set_backtrace(caller)
end

method_signature_verifier.rb: Javaプロキシメソッド向け回避策の削除

rspec-support では、JRubyのUnboundMethod#parametersが空を返すバグと、Javaプロキシメソッドのarityが常に-1を返すバグに対処するため、MethodSignatureクラスを実行時に動的に再定義するコードが存在していました。これらのバグの修正状況をJava::JavaLang::String.instance_method(:char_at).parameters等で検出してクラスを差し替えるアプローチは、44行のコードを要していましたが、今回すべて削除されています。

ruby_features_spec.rb: Ripper検証ロジックの削除

ripper_works_correctly?メソッドと、それを構成する3つのJRuby固有バグ検証メソッド(ripper_reports_correct_line_number?ripper_can_parse_source_including_keywordish_symbol?ripper_can_parse_source_referencing_keyword_arguments?)が削除されました。ripper_supported?の判定はripper_is_implemented?のみに簡略化されています。

その他の変更

  • spec_file_load_errors_spec.rb: defined?(JRUBY_VERSION) && !JRUBY_VERSION.empty?による手動JRuby判定をRSpec::Support::Ruby.jruby?に統一
  • library_wide_checks.rb: --disable=gemオプションをMRIのみに適用していた条件を削除し、全Ruby実装に適用
  • backtrace_exclusion_patterns.feature: バックトレース表示の正規表現パターンをJRuby 9.2以降のフォーマットに合わせて更新

設計判断

バージョン条件分岐の代わりに実装を1本化する方針が一貫して採られています。JRuby固有のパスは:nocov:タグで収支カバレッジから除外されていたものが多く、実際には動作確認が困難な状態になっていました。EOLバージョンのサポート終了というタイミングを活用し、こうした「テスト不能なコード」を一掃しています。

test_double.rbの変更は特徴的で、JRubyバグ回避として導入したraise_non_public_errorによる独自エラーが、結果的にCRubyと共通で有益な動作(エラーメッセージに追加コンテキストを含む)をもたらしていたため、実装自体は維持しつつコメントのみ更新されています。バグ回避が意図せず機能改善になっていたケースとして、コードの意図を正確に記録した変更です。

まとめ

本PRは、EOLとなったJRuby 9.2未満を対象としたワークアラウンドを取り除くことで、コードベースから「歴史的経緯によるノイズ」を削減した変更です。条件分岐・動的クラス再定義・エイリアス定義といった保守コストの高いパターンが消え、現行のJRuby(9.4以降)に対してもシンプルな実装パスを通るようになっています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
73899d1f

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)」という3部構成が明確に適用されており、非常に分かりやすい記事構成です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```言語:ファイルパス)やGitHubのPR/Issueリンク記法が正しく使用されており、技術記事としての可読性を高めています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

JRubyのバージョンや内部実装に関する知識を前提としており、専門知識を持つエンジニアという対象読者に完全に適合した内容と表現になっています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成で、各段落がトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が徹底されており、論理的で読みやすい文章です。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

Diffからコードブロックを正確に引用し、変更の前後を的確に示しています。削除されたコードについても、その内容を要約して説明しており、Diffとの整合性は完璧です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

EOL、ワークアラウンド、ビセクト機能、UnboundMethod#parametersなど、技術用語を文脈に沿って正確に使用しています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

各コード変更が「なぜ行われたのか」という理由(JRubyの特定バージョンのバグ回避など)を、Diff内のコメントやPRの文脈から正確に読み解き、論理的に説明できています。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内の主張はすべてPRのタイトル、Description、Diff内のコードやコメントによって裏付けられており、ハルシネーション(捏造)は一切見られません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#324, #323)、JRubyのIssue番号、バージョン番号(9.2など)といった数値や固有名詞はすべて正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトル「古いJRuby(9.2未満)向けワークアラウンドの一掃」は、PRのタイトル「chore: remove old JRuby < 9.2 workarounds/hacks」の内容を正確に反映しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

JRubyのバージョンごとのEOL状況など、PRのDescriptionに記載されている情報を適切に参照しており、外部知識の捏造はありません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「EOLとなった」「修正済みであり」など、PRの趣旨である「既に不要になったコードの削除」を正確に反映した時間表現が使われています。