2009-01-09 83 views
5

我用一個小模塊封裝了Perl的Net::SSH::Expect,以減少編寫新配置腳本所需的樣板代碼,以便與我們的HP iLO卡配合使用。儘管一方面我希望這個包裝儘可能精簡,所以非程序員的同事可以使用它,我也希望它儘可能地寫得很好。我應該如何處理Perl方法中的錯誤,以及我應該從方法返回什麼?

它用於像這樣:

my $ilo = iLO->new(host => $host, password => $password); 
$ilo->login; 

$ilo->command("cd /system1"); 
$ilo->command("set oemhp_server_name=$system_name", 'status=0'); 

,這是iLO::command()

sub command { 
    my ($self, $cmd, $response) = @_; 

    $response = 'hpiLO-> ' unless defined($response); 

    # $self->{ssh} is a Net::SSH::Expect object 
    croak "Not logged in!\n" unless ($self->{ssh}); 

    $self->{ssh}->send($cmd); 
    if ($self->{ssh}->waitfor($response, $self->{CMD_TIMEOUT}, '-re')) { 
     return { 
      before => $self->{ssh}->before(), 
      match => $self->{ssh}->match(), 
      after => $self->{ssh}->after(), 
     }; 
    } else { 
     carp "ERROR: '$cmd' response did not match /$response/:\n\n", 
      $self->{ssh}->before()), 
      "\n"; 
     return undef; 
    } 
} 

我有兩個相關的查詢。首先,我應該如何處理與預期響應不匹配的響應?我想我現在正在做的是令人滿意的 - 通過返回undef我發現某些信息已經損壞,我的croak()將輸出一個錯誤(儘管幾乎沒有優雅)。但感覺就像是一種代碼味道。如果Perl有異常,我會引發一個,並讓調用代碼決定是否忽略它/退出/打印警告,但它不會(在5.8中)。也許我應該返回一些其他對象(iLO::response,或其他東西),其中包含錯誤消息和$ilo->before()(這只是Net :: SSH :: Expect的before())的內容?但是如果我這樣做 - 並且必須在測試中包裝每一個$ilo->command以捕捉它 - 我的腳本將再次充滿樣板。其次,我該如何回報成功?同樣,我的散列包含或多或少來自Net :: SSH :: Expect的響應,但是它並不覺得「正確」。雖然這個例子在Perl中,但我用其他語言的代碼發出了相同的熟悉的氣味:我不知道如何或從某個方法返回什麼。你能告訴我什麼?

+0

您不應該`返回undef()`,因爲它會在列表上下文中創建一個元素列表。只要「迴歸」,在任何情況下都會做正確的事情。 :) – 2009-01-10 02:23:22

回答

5

如果你熟悉的,如Java語言異常,然後想die作爲throwevaltrycatch。而不是返回undef的,你可以做這樣的事情:

if ($self->{ssh}->waitfor($response, $self->{CMD_TIMEOUT}, '-re')) { 
    return { 
     before => $self->{ssh}->before(), 
     match => $self->{ssh}->match(), 
     after => $self->{ssh}->after(), 
    }; 
} 

die "ERROR: '$cmd' response did not match /$response/:\n\n" 
. $self->{ssh}->before(); 

然後,在你調用代碼:

eval { 
    $ilo->command("set oemhp_server_name=$system_name", 'status=0'); 
}; 

if (my $error = [email protected]) { 
    # handle $error here 
} 

就像在其他語言中的例外,這可以讓你在拯救一個子方法的任何一點都不用擔心在調用堆棧上傳播返回值。他們會被發現它們的第一個eval塊捕獲。此外,您可以再使用die重新拋出您無法處理的異常備份堆棧。

更好的是,您可以使用die來引發異常處理程序可以查詢有用信息和錯誤消息的對象。我喜歡爲此使用Exception::ClassError模塊也提供了一些用於執行類似Java的try/catch塊的語法糖。

5

在Perl中引發異常的常用方法是使用die。捕捉它們的常用方法是使用eval並將塊作爲參數,並在eval結束後測試$ @。

0

除了使用「死」作爲例外,你還可以添加另一種方法:

if (!$ilo->commandSucceeded("set oemhp_server_name=$system_name", 'status=0')) { 
    #recover here 
} 

當然,內部實行命令的()成爲

die ... if !commandSucceeded; 
4

你會發現關於Google空間中這類事情的討論很多。無論您決定什麼,最佳實踐是不要重載任何值,所以返回值意味着不同的事情。它應該始終是一個錯誤代碼,或者它不應該是錯誤代碼。人們不應該看實際值來決定它是否是錯誤代碼。

查看CPAN上流行的Perl模塊(或者您已經使用過的模塊),看看它們的功能。我甚至在Mastering Perl中談論這一點,但我不給黑白答案。與所有真實的代碼一樣,真正的答案是「它取決於」。

有很多不同的方法來做到這一點。不幸的是,這意味着人們以各種方式進行。既然如此,我調用一致性作爲最重要的規則。大部分代碼已經做了什麼(不算錯誤的方式)?如果我必須適應現有的代碼庫,我嘗試使用大多數代碼已經使用的相同類型的接口。

如果沒有明確的贏家,請使用幾種不同的樣式編寫用例。哪一個更適合這個問題,或者更自然地表達了大多數用戶將採取的步驟?這並不總是隻讀爲dieeval。使用未實現的接口編寫示例腳本。你打算使用哪種風格?我發現,在實現界面之前實際編寫腳本顯示了我比我想象的更多的東西。如果我正在爲其他人寫東西,我會用不同的風格向他們展示腳本,並詢問他們更喜歡哪一個。

而且,如果所有的失敗,達到2d6。 :)

相關問題