2011-10-31 73 views
3

我們的團隊正在開發一個新的應用程序,我們從Rails 3.1和Ruby 1.9.2開始 - 昨天晚上我把它帶到Ruby 1.9.3。在Rails中跟蹤依賴加載

一個我們正在使用的依賴鏈(css_parser)的寶石最後不得不在它需要「的iconv」,引發1.9.3 a deprecation warning看起來像這樣:

.../gems/activesupport-3.1.1/lib/active_support/dependencies.rb:240:in `block in require': iconv will be deprecated in the future, use String#encode instead. 

起初我天真地指責,沒有更好的跟蹤軌道上,直到我沒有找到一個要求'iconv'在任何地方。

我跟蹤這件事的唯一方法是我開始在我的Gemfile中評論事情,然後我終於明白了加載irb並開始需要每個庫的輪流。我也可以在gems目錄下完成一個文件系統grep,但我不確定「需要'iconv'」是什麼引發了錯誤。

什麼是PITA。必須有一個更好的方法 - 只是在rake任務中執行加載軌道的操作並沒有削減它。是否有某種方式/任何方式觸發此跟蹤,這將顯示我在相對較長的庫依賴關係列表中的哪一行導致了棄用?

回答

6

因此,它可能有點沒有實際意義,因爲我不可能再次遇到問題(並且css_parser gem是我當前Rails 3.1/Ruby 1.9.3項目中唯一需要iconv的gem)。

但它一個難題,所以我想找到解決方法。

在這種情況下,問題是非常iconv特定的。還有其他ruby deprecations,但大多數情況下,它們似乎都經過了kernel#warn(如果是ruby)或者rb_warn()(如果C) - 但是warning in iconv.c is a little different比其他的 - 無論如何它都是rb_stderr的一個提示。

所以也許我可以調用原有的內核#需要

  • 引發一個異常如果消息找到後請執行下列操作

    1. 覆蓋內核#需要捕捉標準錯誤
    2. 檢查的的iconv消息,從而獲得跟蹤
    3. 在bundler運行之前,如果可能的話,請執行此操作。

    事實證明,我不能做#4 - 因爲Bundler直接調用Kernel.require - 但我可以使用Bundler來解析Gemfile給我一個需要我自己的事情列表。

    原來這就是我得到的 - 感謝this stack overflow post的指針上捕捉標準錯誤 - 併爲理念RubyGems的源aliasing the original Kernel#require

    # override Kernel#require to intercept stderr messages on require 
    # and raise a custom error if we find one mentioning 'iconv' 
    require "stringio" 
    
    class RequireError < StandardError 
    end 
    
    module Kernel 
    
        alias :the_original_require require 
        private :the_original_require 
    
        def capture_stderr 
        # The output stream must be an IO-like object. In this case we capture it in 
        # an in-memory IO object so we can return the string value. You can assign any 
        # IO object here. 
        previous_stderr, $stderr = $stderr, StringIO.new 
        yield 
        $stderr.string 
        ensure 
        # Restore the previous value of stderr (typically equal to STDERR). 
        $stderr = previous_stderr 
        end 
    
        def require(name) 
        captured_output = capture_stderr do 
         the_original_require(name) 
        end 
    
        if(captured_output =~ %r{iconv}) 
         raise RequireError, 'iconv requirement found' 
        end 
        end 
    end 
    
    require 'bundler' 
    
    # load the list of Bundler requirements from the Gemfile 
    required_libraries = Bundler.definition.dependencies.map(&:name) 
    
    # loop through and require each, ignoring all errors other than 
    # our custom error 
    
    required_libraries.each do |requirement| 
        begin 
        require(requirement) 
        rescue Exception => e 
        if(e.class == RequireError) 
         raise e 
        end 
        end 
    end 
    

    瞧!跟蹤消息有助於追蹤iconv要求的位置。最後,可能只是搜索「require」iconv'「仍然是最好的(一旦明確是導致錯誤的原因)。

    但是,就像在生活中一樣。 Some Yaks Must Be Shaved.

  • +0

    您可能會對http://stackoverflow.com/questions/660737/can-you-ask-ruby-to-treat-warnings-as-errors感興趣,它會詢問如何在提供警告時引發異常。 –

    +0

    是的,我開始了重寫Kernel#warn的想法,當我開始搜索iconv錯誤的ruby代碼時,它很快就被打破了。我希望有更好的方法。 – jay

    0

    您可以查看Gemfile.lock文件,該文件將所有依賴關係保存在分層樹中,並指示每個gem所需的版本。這可能有助於確定需要它的寶石。

    +0

    事實證明,將它縮小到子依賴關係是有幫助的,因爲我在Gemfile中註釋了(或需要)每個命名庫 - 但它仍然很好,可以獲得特定的跟蹤源代碼行觸發棄用。 – jay

    +0

    是的。我個人不知道任何方法來做到這一點。等待答案。 ;) – murphyslaw