[rails/thor] マージツール実行時のエンコーディングエラーを修正
背景
Thorでファイル操作を行う際、既存ファイルとの衝突が発生した場合にマージツールを起動できる機能があります。しかし、UTF-8文字を含むファイルに対してマージツールを実行すると、エンコーディングエラーが発生する問題がありました。この問題は #898 で報告されていた類似の問題と同じ根本原因によるものです。
技術的な変更内容
問題の原因
Thor::Shell::Basic#merge メソッドでは、衝突が発生した際に一時ファイルを作成してマージツールに渡していました。しかし、Tempfile.open の呼び出し時に binmode: true オプションが指定されていなかったため、Rubyのデフォルトエンコーディング設定によってはバイナリモードで開かれず、UTF-8などの特定の文字コードを含むファイルの処理時にエンコーディングエラーが発生していました。
修正内容
変更前:
Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination)) do |temp|
temp.write content
temp.rewind
system(*merge_tool, temp.path, destination)
end
変更後:
Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination), binmode: true) do |temp|
temp.write content
temp.rewind
system(*merge_tool, temp.path, destination)
end
binmode: true オプションを追加することで、一時ファイルが常にバイナリモードで開かれるようになり、エンコーディングの変換処理が行われなくなりました。これにより、元のファイル内容がそのまま保持され、UTF-8文字を含むファイルでもマージツールが正常に動作するようになります。
テストの追加
この修正を検証するため、UTF-8文字を含むファイルに対してマージツールを実行する新しいテストケースが追加されました。
it "launches the merge tool when there is a collision and source has utf-8 characters" do
previous_internal = Encoding.default_internal
silence_warnings do
Encoding.default_internal = Encoding::UTF_8
end
destination = File.join(destination_root, "encoding_with_utf8.thor")
FileUtils.mkdir_p(destination_root)
File.write(destination, "blabla")
allow(runner.shell).to receive(:merge_tool).and_return("meld")
expect(Thor::LineEditor).to receive(:readline).and_return("m")
expect(runner.shell).to receive(:system).with("meld", /encoding_with_utf8.thor/, /encoding_with_utf8.thor/)
action :copy_file, "encoding_with_utf8.thor"
ensure
silence_warnings do
Encoding.default_internal = previous_internal
end
end
このテストでは、Encoding.default_internal を一時的にUTF-8に設定し、UTF-8文字を含むファイルに対してマージツールが正常に呼び出されることを確認しています。
影響範囲
この変更により、以下のような状況でThorが正常に動作するようになります:
- UTF-8などの非ASCII文字を含むファイルに対してマージツールを使用する場合
-
Encoding.default_internalが設定されている環境でのファイル操作 - 国際化されたプロジェクトでのジェネレーター実行時
既存の動作に対する破壊的変更はなく、バイナリモードでの一時ファイル作成により、より安全で予測可能な動作が保証されます。