2012-01-29 8 views
21

私は大好きですthe autoload functionality of Ruby;ただし、スレッドセーフではないため、going away in future versions of Rubyです。Rubyでオートロードせずにクラスをオートロードする

だから私はすでにで遅延ロードメカニズムを実装しているので、すでにそれがなくなって自分のコードを書いたふりをしたいと思います。私は可能な限り簡単な方法で実装したいと思っています(今はスレッドの安全性について気にしません)。 Rubyはこれを可能にするはずです。

クラスのconst_missingを増強することによって開始するのをしてみましょう:

class Dummy 
    def self.const_missing(const) 
    puts "const_missing(#{const.inspect})" 
    super(const) 
    end 
end 

我々が欠けている 『ダミー』の下で定数を参照しようとすると、私たちは「ダミーを参照しようとした場合のRubyは、例えば、この特別なメソッドを呼び出します。 :こんにちは "、:Helloconst_missingを呼び出します。これは、私たちが必要とする正確に何である、それでは、さらにそれをみましょう:私たちは「ダミー:: OAuthの」参照する場合

class Dummy 
    def self.const_missing(const) 
    if :OAuth == const 
     require 'dummy/oauth' 
     const_get(const)  # warning: possible endless loop! 
    else 
     super(const) 
    end 
    end 
end 

を今、それは "を定義することが期待される「ダミー/ oauth.rb」ファイルが必要になりますダミー:: OAuth "定数。 const_get(内部ではを呼び出すことができるので)という無限ループが発生する可能性がありますが、それを守ることはこの質問の範囲外です。

トップレベルの名前空間に "OAuth"という名前のモジュールが存在すると、大きな問題が発生します。「Dummy :: OAuth」を参照すると、const_missingがスキップされ、トップレベルから「OAuth」が返されます。ほとんどのRuby実装もこのことについて警告行います:

warning: toplevel constant OAuth referenced by Dummy::OAuth 

This was reported as a problem way back in 2003を私はRubyのコアチームはこのことについて、これまで心配していたという証拠を見つけることができませんでした。今日、最も人気のあるRubyの実装は同じ動作をしています。

問題は、const_missingは、最上位の名前空間の定数のために黙ってスキップされるということです。これはRubyのautoload機能で "Dummy :: OAuth"が宣言された場合には発生しません。これを回避する方法は?

+0

これは愚かな提案のように思えるが、あなたは、 'autoload'のCソースを見ることができますか?私はあなたがRubyソースのどこかでそれを見つけることができると確信しています。あなたがまっすぐなRubyでそれをすることができないなら、(通訳者の下腹にアクセスできる)C拡張を作成するオプションがあります。 – Linuxios

+0

オプションです。 – mislav

+0

はブルートフォースのように聞こえるが、トップレベルクラスでは 'remove_const'できないだろうか? – phoet

答えて

5

これは以前のRailsチケットで育ったものですが、私が調査したところ、それはまったく同じように見えませんでした。問題は、Rubyが先祖を検索してからconst_missingを呼び出し、すべてのクラスが祖先としてObjectを持つため、常にトップレベルの定数が見つかることです。あなたが名前空間を用モジュールのみを使用して自分自身を制限することができた場合、それは動作します彼らは祖先としてObjectを持っていないので、例えば:

>> class A; end 
>> class B; end 
>> B::A 
(irb):3: warning: toplevel constant A referenced by B::A 

>> B.ancestors 
=> [B, Object, Kernel, BasicObject] 

>> module C; end 
>> module D; end 
>> D::C 
NameError: uninitialized constant D::C 

>> D.ancestors 
=> [D] 
+0

ええ、クラスで動作させることができないのは残念ですが、少なくともこれはモジュールで動作することが分かりました。ありがとう! – mislav

0

私はREE 1.8.7(あなたが特定のバージョンを言及していない)私はconst_missing内部const_getを使用している場合、私は::を使用しない場合に、あなたの問題を取得します。私はevalを使用して愛していないが、それはここで働いん:

class Dummy 
    def self.const_missing(const) 
    if :OAuth == const 
     require 'dummy/oauth' 
     eval "self::#{const}" 
    else 
     super(const) 
    end 
    end 
end 

module Hello 
end 

Dummy.const_get :Hello # => ::Hello 
Dummy::Hello   # => Dummy::Hello 

私はあなたがself.send :"::", constを行うことができるようにModule::方法があればいいのに。

+0

特定のバージョンについてはごめんなさい。私の問題は、1.8.7,1.9.2,1.9.3、Rubinius(安定したものと縁のもの)、およびその他のものに存在します。私はあなたの提案を試みますが、私がテストで得たものから、 'const_missing'メソッドは全く呼び出されませんでした。だから私はそれを実装する方法は関係ありませんでした。 – mislav

+0

これは、 'Dummy.const_get:Hello'か' Dummy :: Hello' *を 'const_missing'メソッドの外で使うかどうかによって決まります。 ':: Hello'だけが' const_missing'を引き起こします - 少なくとも、私が試した1つのRubyの実装には当てはまります。 –

0

レイジーローディングは非常に一般的なデザインパターンである、あなたは多くの方法でそれを実装することができます。以下のような:

class Object 
    def bind(key, &block) 
    @hooks ||= Hash.new{|h,k|h[k]=[]} 
    @hooks[key.to_sym] << [self,block] 
    end 

    def trigger(key) 
    @hooks[key.to_sym].each { |context,block| block.call(context) } 
    end 
end 

次にあなたがすることができます

bind :json do 
    require 'json' 
end 

begin 
    JSON.parse("[1,2]") 
rescue 
    trigger :json 
    retry 
end 
関連する問題