質問: rubyのloadとrequireの違いは、ドキュメントでは以下のように説明されています。 > > Kernel.#require は同じファイルは一度だけしかロードしませんが、 Kernel.#load は無条件にロードします。 > > また、require は拡張子.rb や .so を自動的に補完しますが、 load は行いません。 > > > しかし、以下のプログラムではloadもrequireも正しく動作するにもかかわらず、 ``` #! ruby253 -EWindows-31J # -*- mode:ruby; coding:Windows-31J -*- $:.push("c:/test/test") load "sub.rb" require "sub.rb" ``` 以下のプログラムではrequireのみが失敗します。loadは成功します。 ``` #! ruby253 -EWindows-31J # -*- mode:ruby; coding:Windows-31J -*- $:.push("c:/テスト/test") load "sub.rb" require "sub.rb" ``` 動作環境はWindows7 + Ruby2.5.3です。この挙動はrubyの仕様上正しいのか、二番目の例でrequireを成功させるにはどのように記述すべきなのか、教えていただけるとたいへん助かります。よろしくお願いします。
回答: Ruby自体の不具合と思われます。関係がありそうなIssueとして下記がありますが、未解決です。 Bug #9737: Non-ASCII characters in the path to ruby executable break require paths - Ruby trunk - Ruby Issue Tracking System ライブラリのファイルへの絶対パス(ルートからの各ディレクトリ名及びファイル名)にASCII以外の文字が含まれていない場合は、何らかの不具合が他にも発生する可能性が高いです。これらを回避したい場合は、**ASCII文字のみのディレクトリ名およびファイル名を使用してください。** どうしても日本語のディレクトリ名を使いたいというのであれば、RubyのIssueへ問題を報告し、修正して貰う、または、自分でパッチを作って送りつけるしかないでしょう。 以下、私が手元で調べた詳細です。 手元のWin 10, ruby 2.5.3p105 [x64-mingw32] (Ruby Instller)で確認する限り、`load`でも`require`でも"stack level too deep (SystemStackError)"が発生しました。また、読み込みすらしていない次のコードでもSystemStackErrorが発生しました。 ``` # coding: Windows-31J $:.push("テスト") puts $: ``` ソースファイルをUTF-8にした場合は正常に動作し、`require`や`load`も問題なく動作しました。また、WSLのUbuntu環境(UTF-8)でRuby 2.5.3および2.6.0で試したところ、`pust $:`は問題ないですが、`require`や`load`を用いた場合は次のような結果になりました。 * ソースファイルがUTF-8の場合はSystemStackErrorで落ちます。 * ソースファイルがWindows-31Jの場合はSystemStackErrorで落ちませんが、ディレクトリを辿れずライブラリファイルを見つけられません。 となりました。 ソースコードを見ると`$:`自体は`rb_ary_new()`で作成された普通のArrayであり、`vm->load_path`としてC上では存在します。しかし、詳しくはソースを追っていませんが、何かのタイミングでパスを展開し、キャッシュを持つようになっているようです。そこら辺の処理でASCII以外の文字の処理が再帰してSystemStackErrorになっていると思われます。 OS、Rubyのバージョン、環境、パスの書き方によって異なる結果になると思われますので、質問者さんが出くわした現象では無いかも知れません。いずれにしても、ASCII以外は使わないことぐらいしか、確実な回避策は無いと思います。