2013-02-08 17 views
8

をインスタンス化したファイルのディレクトリを取得します私は、この内部のようなコードを持っている宝石を持っているクラスルビー

app/main.rb 
app/templates/example.txt 
それは File Not Found: templates/example.txtを思い付く

require 'mygem' 

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

main.rbは、次のコードを持っています。 example.txtmain.rbと同じディレクトリにあったとしても、それがディレクトリにある場合は機能しません。この問題を解決するために、私はread()relative_toというオプションの引数を追加しました。正常に動作しますが、私はそれは少し醜いだと思う

require 'mygem' 

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

:上記のことが必要となることができるようにこれは絶対パスを取ります。とにかくそれを作るクラスは、ファイルread()が呼び出されていることを知っているとそれに基づいてパスを知っているようにするには?

+2

main.rbのディレクトリは無関係です。重要なのは 'Dir.pwd'の値です。 –

+0

ああ、それは私が必要なものです。問題は 'rspec'があなたが期待するディレクトリから実行されていないように見えるということでした。 'spec /'の 'templates'ディレクトリを参照することができませんでした。 – andy

答えて

1

また、あなたはcallerをチェックすることがあります。

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上のドキュメントへの参照を参照してください、なぜならcaller(1)の、間接的に呼び出されて壊れる)そうするためにあなたを示唆しています。さらに、呼び出し元パスがコロンを含むことを意図している場合、上記の正規表現はより正確に調整されるべきです。

1

この典型的な用途(私はmadusobwa aboveで述べたように、それは、間接的な使用にどれだけ耐性がわからない)のために働く必要があります:あなたのメソッドのブロック形式を追加することを検討し、サイドノートで

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

降伏後にファイルを閉じる。現在のフォームでは、クライアントはensureであなたの宝石の使用を包むように強制しています。

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 

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

4

興味深い図書館 - i told you it was privateがあります。外部から呼び出されることからその方法を保護することができます。コードは、呼び出し元メソッドのファイルを見つけ出し、削除します。 犯罪者は、このラインを使用して発見された:

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 

あなたがパスを使用することができますこの方法では、あなたのMygem呼び出し元に相対し、 pwdではありません。まさにあなたが試したやり方app/main.rb

+0

非常に興味深い宝石です。 – Speed

+1

これは必ずしも機能しません。メソッドが間接的に呼び出され、メソッドが別のライブラリによって呼び出された場合は、ブレークします。私の解決策を見てください。 – Linuxios

2

まあ、あなたは、他の人が言ったよりもずっと信頼できる発信者を使うことができます。あなたの宝石のファイルで

、任意のクラスまたはモジュールの外部で、これを置く:

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__を渡さない限り、彼らが作業ディレクトリを変更する場合は何もできないことに気づいています。

関連する問題