8

内部に次のようなコードを含む宝石があります。

def read(file)
    @file = File.new file, "r"
end

問題は、次のようなディレクトリ構造があるとします。

app/main.rb
app/templates/example.txt

main.rb次のコードがあります。

require 'mygem'

example = MyGem.read('templates/example.txt')

それは思いつきFile Not Found: templates/example.txtます。example.txt同じディレクトリにある場合は機能しますが、ディレクトリにある場合は機能しmain.rbません。この問題を解決するために、relative_toinというオプションの引数を追加しましたread()。これは絶対パスを取るため、上記は次のようにする必要があります。

require 'mygem'

example = MyGem.read('templates/example.txt', File.dirname(__FILE__))

それはうまくいきますが、少し醜いと思います。クラスread()が呼び出されているファイルを認識し、それに基づいてパスを計算するようにする方法はありますか?

4

5 に答える 5

4

興味深い図書館があります -私はそれがプライベートだと言いました. メソッドを外部から呼び出されないように保護できます。このコードは、呼び出し元メソッドのファイルを見つけて削除します。違反者は、次の行を使用して検出されます。

offender = caller[0].split(':')[0]

MyGem.read コードで使用できると思います。

def read( file )
  fpath = Pathname.new(file)
  if fpath.relative?
    offender = caller[0].split(':')[0]
    fpath = File.join( File.dirname( offender ), file )
  end
  @file = File.new( fpath, "r" )
end

このようにして、pwd ではなく、Mygem 呼び出し元に相対的なパスを使用できます。まさにあなたが試した方法app/main.rb

于 2013-02-16T09:57:48.213 に答える
2

さて、あなたは発信者を使うことができ、他の人が言ったことよりもはるかに確実に使うことができます。

クラスまたはモジュールの外部のgemファイルに、次のように記述します。

c = caller
req_file = nil
c.each do |s|
  if(s =~ /(require|require_relative)/)
    req_file = File.dirname(File.expand_path(s.split(':')[0])) #Does not work for filepaths with colons!
    break
  end
end
REQUIRING_FILE_PATH = req_file

必要なスクリプトがDir.chdirを実行しない限り、これは90%の時間で機能します。File.expand_pathはそれに依存します。あなたの要求者が彼らを渡さない限り、__FILE__彼らが作業ディレクトリを変更した場合、あなたができることは何もありません。

于 2013-02-17T22:23:01.843 に答える
1

また、呼び出し元を確認することもできます:

def read(file)
  if /^(?<file>.+?):.*?/ =~ caller(1).first
    caller_dir, caller_file = Pathname.new(Regexp.last_match[:file]).split
    file_with_path = File.join caller_dir, file
    @file = File.new "#{file_with_path}", "r"
  end
end

そうすることはお勧めしません (上記のコードは間接的に呼び出されると機能しなくなります。 のcaller(1)ドキュメントへの参照を参照してくださいcaller)。さらに、呼び出し元のパスにコロンを含めることを意図している場合は、上記の正規表現をより正確に調整する必要があります。

于 2013-02-08T15:41:56.460 に答える
1

これは典型的な用途で機能するはずです(上記の madusobwaで述べたように、間接的な使用に対する耐性がどの程度かはわかりません):

def read_relative(file)
  @file = File.new File.join(File.dirname(caller.first), file)
end

補足として、生成後にファイルを閉じるメソッドのブロック形式を追加することを検討してください。現在の形式では、クライアントが宝石の使用をensure.

于 2013-02-13T05:12:16.663 に答える
1

ファイル パス文字列を引数として受け入れます。Pathname オブジェクトに変換します。パスが相対かどうかを確認します。はいの場合は、絶対値に変換します。

def read(file)
  fpath = Pathname.new(file)
  if fpath.relative?
    fpath = File.expand_path(File.join(File.dirname(__FILE__),file))
  end
  @file = File.new(fpath,"r")
end

このコードをより簡潔にする (冗長性を低くする) ことができます。

于 2013-02-14T07:47:18.020 に答える