2012-01-29 62 views
21

我愛the autoload functionality of Ruby;然而,它是going away in future versions of Ruby,因爲它從來沒有線程安全。自動加載類在Ruby中沒有它的`autoload`

所以現在我想假裝它已經走了,沒有它寫我的代碼,由實現我自己的延遲加載機制。我想以最簡單的方式實現它(我現在不關心線程安全性)。 Ruby應該允許我們這樣做。

讓我們開始通過增加一類的const_missing

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

將Ruby當我們嘗試引用在‘虛擬’是不可少的,例如,如果我們嘗試引用「虛擬恆定稱這種特殊方法: :你好「,它會撥打const_missing與符號:Hello。這正是我們需要的,所以我們把它進一步:

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的」,這將需要預計將定義「的「虛擬/ oauth.rb」文件Dummy :: OAuth「常量。還有當我們調用const_get(因爲它可以調用const_missing內部),但防範是這個問題的範圍無限循環的可能性。

最大的問題是,這種整體解決方案打破了,如果存在一個在頂級命名空間名爲「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 2012-01-29 14:48:41

+0

這是一個選項。 – mislav 2012-01-29 14:54:10

+0

聽起來像一個蠻力的事情,但你不能''remove_const'上的頂級類? – phoet 2012-01-29 15:34:54

回答

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 2012-01-29 21:31:35

0

如果我在const_missing內使用const_get,但在使用::時沒有問題,請在ree 1.8.7上(您不提具體版本)解決問題。我不喜歡使用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 

我希望Module::方法,所以你可以做self.send :"::", const

+0

抱歉沒有提及具體版本。我的問題存在於1.8.7,1.9.2,1.9.3,魯比尼烏斯(穩定和邊緣)和其他。我會嘗試你的建議,但從我在測試中得到的'const_missing'方法根本沒有被調用。因此,我如何實施它並不重要。 – mislav 2012-01-29 18:03:06

+0

這取決於您是否在'const_missing'方法之外使用'Dummy.const_get:Hello'或'Dummy :: Hello' * *。只有':: Hello'會觸發'const_missing' - 或者至少在我嘗試的一個Ruby實現中是這樣。 – 2012-01-29 18:32:07

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 
相關問題