HOOK(フック)
HOOK とは、
&fml8; の内部にあらかじめ用意されている
「小さな perl のプログラムを実行する」
機能です(専門用語でいえば HOOK を eval() するということです)。
HOOK は要所要所に用意されています。
もちろん、HOOK は、そのコンテキストの中で実行されます。
その意味で本質的に危険な機能です。
また、
ある程度 &fml8; の中身について理解していないと HOOK を書くことはできません。
その一方、
HOOK をうまく使うことで、
少しの手間で複雑な動作を実現することが可能になります。
ものは使い用です。
概要もしくは HOOK における諸問題
HOOK は微妙な修正、
もしくは複雑なカスタマイズのために是非必要な機能ですが、
実装においては、いくつかの問題があります。
まず、
&fml4; の $START_HOOK のような曖昧な名称では、
プログラムごとに異なる動作をさせることが難しいです。
たとえば $START_HOOK を設定すると、
配送システムとコマンドメールシステムで同じ $START_HOOK を実行します。
それが便利な場合もあるのですが、
プログラムの役割ごとに異なる動作をさせようとすると、
面倒で分かりにくい設定になります
(HOOK 内に if else ... が必要になります)。
そのため HOOK の名前に一定の基準が必要でしょう。
&fml8; では、
ある関数の最初と最後に HOOK を用意する場合、
次のようなスタイルの HOOK 名称としています。
$役割_関数_start_hook
$役割_関数_end_hook
これにより、
配送システムとコマンドメールシステムで共通の HOOK などというものはなくなり、
別々の HOOK をしかけることができるようになります。
逆に、
共通のものをしかけようとするなら、一杯書いて下さい;)ということです。
まぁコピーすればいいだけなので難しくないですよね?(と思いたい)
$distribute_XXX_start_hook = q{ ... };
$YYY_XXX_start_hook = $distribute_XXX_start_hook;
また、HOOK の書き方が少々よくなくても動かないと困るでしょう。
というのは「少しだけ修整する」というケースでは、
Perl の素人さんが行なうことも多いからです。
現場では、
欲しい機能を実現したい場合、
美しいコードを書くことよりも、少しでも早く実現することが求められます。
そこは「美しさ」にこだわるべきではない点と考えます。
というわけで、通常、各モジュールでは
use strict;
になっていますが、
HOOK を評価する lexical scope では無視するようにしてあります。
実際には、HOOK を評価する際に、つねに
no strict;
HOOK の内容
という構文に変換した上で eval() しています。
&fml4; と &fml8; の大きな違いの一つは変数・関数のスコープです。
注意して下さい。
&fml4; の関数は全てグローバル関数なので、どんな関数でも使えますが、
&fml8; では、そうはいきません。
とはいえ、まったく手がかりがないのも困るので、
HOOK が利用できる環境では
$curproc オブジェクトだけは常にアクセス可能であることを保証しています。
つまり HOOK を書く際には、
$curproc オブジェクトのアクセスメソッドだけを使って書くようにして下さい。
HOOK の命名規則
HOOK の名前の標準形式は
役割名_メソッド名_start_hook
役割名_メソッド名_end_hook
です。
ここで役割名は
「use_XXX_function」(例: use_article_post_function)の
XXX 部分です。
もっと粒度の細かい HOOK については、この形式でないものもあります。
たとえば、fmlconf コマンドのメインメソッド run() の HOOK の名前は
fmlconf_run_start_hook
fmlconf_run_end_hook
となり、run() の中での hook 呼び出しは次のようなコードになっています。
sub run
{
my ($curproc, $args) = @_;
my $config = $curproc->{ config };
my $eval = $config->get_hook( 'fmlconf_run_start_hook' );
if ($eval) {
eval qq{ $eval; };
print STDERR $@ if $@;
}
$curproc->_fmlconf($args);
$eval = $config->get_hook( 'fmlconf_run_end_hook' );
if ($eval) {
eval qq{ $eval; };
print STDERR $@ if $@;
}
}
レシピ’s
§.hook.recipes;