2016-04-07 68 views
0

我有這個代碼,可以在生產中觸發「非本地出口檢測!」科。我無法理解如何發生,因爲即使返回也會觸發NonLocalExit異常。即使拋出也會引發異常。從紅寶石檢測和處理NonLocalExit

有什麼辦法可以讓exception_raised和yield_returned都是假的?

def transaction 
    yield_returned = exception_raised = nil 

    begin 
    if block_given? 
     result = yield 
     yield_returned = true 
     puts 'yield returned!' 
     result 
    end 

    rescue Exception => exc 
     exception_raised = exc 
    ensure 
     if block_given? 
     unless yield_returned or exception_raised 
      puts 'Non local exit detected!' 
     end 
     end 
    end 

end 


transaction do 
    puts 'good block!' 
end 

transaction do 
    puts 'starting transaction with block with return' 
    return 
    puts 'this will not show' 
end 

輸出:

good block! 
yield returned! 
starting transaction with block with return 

我想以某種方式輸出 '檢測非本地退出!'。我知道這發生在生產中,但我無法在開發中實現。試着用一個回報和一個投擲,但他們都提出了一個例外。任何其他方式?

回答

1

問題出在我的翻版上,從Ruby的最頂層返回。你可以從一個有堆棧的方法返回,但是如果你從Kernel返回,你會得到一個LocalJumpError。

假設前面的方法transaction()。在什麼情況下,你的問題致電回報:

def lol 
    transaction do 
    puts 'Block in method: starting transaction with block with return' 
    return 
    puts 'this will not show' 
    end 
end 


lol() 

transaction do 
    puts 'block in Kernel: starting transaction with block with return' 
    return 
    puts 'this will not show' 
end 

輸出:

$ ruby local_jump_error.rb 
# Running from method: 
Block in method: starting transaction with block with return 
yield_returned=nil, exception_raised=nil 
Non local exit detected! 
# Running without method: 
block in Kernel: starting transaction with block with return 
yield_returned=nil, exception_raised=#<LocalJumpError: unexpected return> 
local_jump_error.rb:45: unexpected return (LocalJumpError) 
      from local_jump_error.rb:6:in `transaction' 
     from local_jump_error.rb:43 
0

我不知道你的水平與Ruby,但要退出一個塊,你必須使用break而不是return。在這種情況下,break也以與return相同的方式接受值,這意味着break將在概念上將變量result分配爲值break

如果發生拋出,它會引發異常,所以它總是會展開調用堆棧,直到找到一條救援語句,因此它將運行代碼exception_raised = exc

您可以微調救援以使用LocalJumpError而不是Exception來僅捕獲其中包含return的塊。所有其他異常類型將不會停在那個救援。

+0

這是我想了解遺留代碼。我需要了解它是如何達到'非本地出口檢測!' – Costi

+0

那麼,現在唯一發生在我身上的是'callcc',這看起來很牽強。你能夠改變代碼嗎?添加一個參數'&block'並用'block.source_location'檢查確保塊內的原點。 –

0

我不知道你的意思是寫:

if block_given? 
    if yield_returned.nil? && exception_raised 
    puts 'Non local exit detected!' 
    end 
end 

如果你做出這種改變,代碼會產生「檢測非本地退出!」,與返回的#transaction第二個方法調用。

當您編寫unless yield_returned or expection_raised時,if子句僅在兩個變量都爲false時纔會被計算。據我所知,這是不可能的。

而作爲一個方面的說明,正如在另一個答案中建議,one should not rescue Exception,LocalJumpError應該是足夠的。

+0

關於此代碼的祕密就在於它會觸及'檢測到非本地出口!' 如果我改變代碼以使其以其他方式達到目的,我不解決這個謎團。 – Costi

+0

啊,現在我明白了。這似乎是一個棘手的情況。唯一出現在我腦海裏的是,可能還有別的東西,那就是拯救回報?如果被屏蔽的塊會在後臺設置某些值,那麼通常返回,然後就是當我們分配yield_returned = true後,後臺作業會拋出一個異常並被解救。雖然這個變化很小...... –