autodie::hints - 提供使用者子常式提示給 autodie
package Your::Module;
our %DOES = ( 'autodie::hints::provider' => 1 );
sub AUTODIE_HINTS {
return {
foo => { scalar => HINTS, list => SOME_HINTS },
bar => { scalar => HINTS, list => MORE_HINTS },
}
}
# Later, in your main program...
use Your::Module qw(foo bar);
use autodie qw(:default foo bar);
foo(); # succeeds or dies based on scalar hints
# Alternatively, hints can be set on subroutines we've
# imported.
use autodie::hints;
use Some::Module qw(think_positive);
BEGIN {
autodie::hints->set_hints_for(
\&think_positive,
{
fail => sub { $_[0] <= 0 }
}
)
}
use autodie qw(think_positive);
think_positive(...); # Returns positive or dies.
autodie pragma 在處理 Perl 內建函式時非常聰明。這些函式的行為是固定的,而 autodie
確切知道它們如何嘗試發出失敗訊號。
但模組中的使用者自訂子常式呢?如果你對使用者自訂子常式使用 autodie
,它會假設以下行為來顯示失敗
在純量內容中為 false 值
在清單內容中為空清單
在清單內容中包含單一未定義值
所有其他傳回值(包括單一零的清單,以及包含單一空字串的清單)都視為成功。然而,實際世界的程式碼並不總是這麼簡單。也許你正在處理的程式碼會在失敗時傳回包含「FAIL」字樣的字串,或包含 (undef, "human error message")
的二元素清單。若要讓 autodie 與這些類型的子常式一起使用,我們有提示介面。
提示介面允許向 autodie
提供提示,說明它應該如何從使用者自訂子常式偵測失敗。雖然這些提示可以由 autodie
的最終使用者提供,但理想情況下會寫入模組本身,或寫入 autodie
本身的輔助模組或子類別。
提示是針對自動死亡子常式的傳回值檢查的子常式或值。如果比對傳回 true,autodie
會認為子常式已失敗。
如果提供的提示是子常式,則 autodie
會將完整的傳回值傳遞給該子常式。如果提示是任何其他值,則 autodie
會針對提供的值進行智慧比對。在 Perl 5.8.x 中沒有智慧比對運算子,因此這些版本僅支援子常式提示。
提示可以提供給純量和清單內容。請注意,自動死亡子常式永遠不會看到空內容,因為 autodie
始終需要擷取傳回值以進行檢查。在空內容中呼叫的自動死亡子常式會像在純量內容中呼叫一樣,但在檢查其傳回值後會將其捨棄。
提示可能包含子常式參照、重載智慧比對的物件、正規表示式,以及視 Perl 版本而定的其他內容。你可以指定不同的提示,說明如何在純量和清單內容中識別失敗。
這些範例適用於在 AUTODIE_HINTS
子常式中使用,以及在呼叫 autodie::hints->set_hints_for()
時使用。
最常見的特定於內容的提示為
# Scalar failures always return undef:
{ scalar => sub { !defined($_[0]) } }
# Scalar failures return any false value [default expectation]:
{ scalar => sub { ! $_[0] } }
# Scalar failures always return zero explicitly:
{ scalar => sub { defined($_[0]) && $_[0] eq '0' } }
# List failures always return an empty list:
{ list => sub { !@_ } }
# List failures return () or (undef) [default expectation]:
{ list => sub { ! @_ || @_ == 1 && !defined $_[0] } }
# List failures return () or a single false value:
{ list => sub { ! @_ || @_ == 1 && !$_[0] } }
# List failures return (undef, "some string")
{ list => sub { @_ == 2 && !defined $_[0] } }
# Unsuccessful foo() returns 'FAIL' or '_FAIL' in scalar context,
# returns (-1) in list context...
autodie::hints->set_hints_for(
\&foo,
{
scalar => qr/^ _? FAIL $/xms,
list => sub { @_ == 1 && $_[0] eq -1 },
}
);
# Unsuccessful foo() returns 0 in all contexts...
autodie::hints->set_hints_for(
\&foo,
{
scalar => sub { defined($_[0]) && $_[0] == 0 },
list => sub { @_ == 1 && defined($_[0]) && $_[0] == 0 },
}
);
這個「在所有內容中」的建構非常常見,可以使用「失敗」鍵進行縮寫。這會將 scalar
和 list
提示都設定為相同的值
# Unsuccessful foo() returns 0 in all contexts...
autodie::hints->set_hints_for(
\&foo,
{
fail => sub { @_ == 1 and defined $_[0] and $_[0] == 0 }
}
);
# Unsuccessful think_positive() returns negative number on failure...
autodie::hints->set_hints_for(
\&think_positive,
{
fail => sub { $_[0] < 0 }
}
);
# Unsuccessful my_system() returns non-zero on failure...
autodie::hints->set_hints_for(
\&my_system,
{
fail => sub { $_[0] != 0 }
}
);
如果您使用的是在失敗時傳回特殊內容的模組,則您可以手動為每個所需的子常式建立提示。一旦指定提示,它們就會提供給之後載入的所有檔案和模組,因此您可以將這項工作移至模組中,它仍然會運作。
use Some::Module qw(foo bar);
use autodie::hints;
autodie::hints->set_hints_for(
\&foo,
{
scalar => SCALAR_HINT,
list => LIST_HINT,
}
);
autodie::hints->set_hints_for(
\&bar,
{ fail => SOME_HINT, }
);
可以傳遞子常式參考(建議)或完全限定的子常式名稱作為第一個引數。這表示您可以設定可能會載入的模組提示
use autodie::hints;
autodie::hints->set_hints_for(
'Some::Module:bar', { fail => SCALAR_HINT, }
);
當您有一個使用許多第三方模組的專案時,此技術最為有用。您可以在一個地方定義所有可能的提示。這甚至可以在 autodie 的子類別中。例如
package my::autodie;
use parent qw(autodie);
use autodie::hints;
autodie::hints->set_hints_for(...);
1;
您現在可以使用 my::autodie
,它將像標準的 autodie
一樣運作,但現在會知道您設定的任何提示。
autodie
提供一個被動介面,讓您可以宣告模組的提示。如果載入 autodie
,它會找到並使用這些提示,但如果沒有載入,則這些提示不會產生任何效果(或依賴關係)。若要設定這些提示,您的模組需要宣告它執行 autodie::hints::provider
角色。您可以撰寫自己的 DOES
方法,使用 Class::DOES
等系統為您處理繁重的工作,或宣告一個 %DOES
套件變數,其中包含 autodie::hints::provider
鍵和對應的 true 值。
請注意,檢查 %DOES
hash 是 autodie
專用的捷徑。其他模組不會使用此機制來檢查角色,不過您可以使用 CPAN 中的 Class::DOES
模組來允許它。
此外,您必須定義一個 AUTODIE_HINTS
子常式,它會傳回一個包含子常式提示的 hash 參考。
package Your::Module;
# We can use the Class::DOES from the CPAN to declare adherence
# to a role.
use Class::DOES 'autodie::hints::provider' => 1;
# Alternatively, we can declare the role in %DOES. Note that
# this is an autodie specific optimisation, although Class::DOES
# can be used to promote this to a true role declaration.
our %DOES = ( 'autodie::hints::provider' => 1 );
# Finally, we must define the hints themselves.
sub AUTODIE_HINTS {
return {
foo => { scalar => HINTS, list => SOME_HINTS },
bar => { scalar => HINTS, list => MORE_HINTS },
baz => { fail => HINTS },
}
}
這樣您的程式碼就可以設定提示,而不需要依賴載入或甚至安裝 autodie
和 autodie::hints
。如此一來,您的程式碼在安裝 autodie
時可以執行正確的動作,但不需要依賴它才能運作。
當使用者定義的子常式被 autodie
包裝時,它會在提示可用時使用提示,否則會回復到此文件引言中所述的預設行為。如果我們預期提示存在,但(出於任何原因)它尚未載入,這可能會造成問題。
我們可以要求 autodie 堅持使用提示,方法是在子常式名稱的開頭加上驚嘆號。單獨的驚嘆號表示所有在其後的子常式都必須宣告提示。
# foo() and bar() must have their hints defined
use autodie qw( !foo !bar baz );
# Everything must have hints (recommended).
use autodie qw( ! foo bar baz );
# bar() and baz() must have their hints defined
use autodie qw( foo ! bar baz );
# Enable autodie for all of Perl's supported built-ins,
# as well as for foo(), bar() and baz(). Everything must
# have hints.
use autodie qw( ! :all foo bar baz );
如果指定的子常式沒有提示可用,這將導致編譯時期錯誤。強烈建議堅持使用 Perl 內建函式的提示(例如,open
和 close
)。
強烈建議堅持提示。
您使用子常式參考呼叫 autodie::hints->set_hints_for()
,但無法將該參考解析回子常式名稱。它可能是匿名子常式(無法使其自動執行),或由於其他原因而沒有名稱。
如果您收到這個錯誤,而子常式具有真實名稱,那麼您可能在 autodie 中發現一個錯誤。請參閱 autodie 中的「BUGS」 以了解如何報告此錯誤。
在定義提示時,您可以提供 list
和 scalar
關鍵字,或您可以提供一個 fail
關鍵字。您不能混合搭配它們。
您提供了沒有提供 list
提示的 scalar
提示,反之亦然。您必須同時提供 scalar
和 list
提示,或提供一個 fail
提示。
Damian Conway 博士建議提示介面並提供範例使用。
Jacinta Richardson 將我的許多想法翻譯成這個文件。
版權所有 2009,Paul Fenwick <pjf@perltraining.com.au>
這個模組是免費軟體。您可以根據 Perl 本身的條款來散布它。