/usr/share/tiarra/main/Hook.pm is in tiarra 20100212+r39209-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | # -----------------------------------------------------------------------------
# $Id: Hook.pm 13831 2008-06-13 14:01:33Z topia $
# -----------------------------------------------------------------------------
# Hook: あらゆるフックのベースクラス
# HookTarget: あらゆるフック先のベースクラス
# -----------------------------------------------------------------------------
# Hookの使い方:
#
# パッケージ変数 $HOOK_TARGET_NAME, @HOOK_NAME_CANDIDATES,
# $HOOK_NAME_DEFAULT, $HOOK_TARGET_DEFAULT を定義する
# 各変数の意味は次の通り
#
# $HOOK_TARGET_NAME:
# このフックをかける先のパッケージ名。
#
# @HOOK_NAME_CANDIDATES:
# フック名として許される名前の候補。
#
# $HOOK_NAME_DEFAULT:
# フック名が省略された場合のデフォルト値。
# これは省略可能で、省略した場合はフック名の候補の個数が
# 2つ以上である場合に限り、フック名の省略が不可能になる。
#
# $HOOK_TARGET_DEFAULT:
# フックを掛ける対象のオブジェクトが省略された場合のデフォルト値。
# これは省略可能で、省略した場合はinstall時にターゲットの省略が出来なくなる。
#
# これらの変数を定義し、Hookを@ISAに入れたパッケージを作る。
# -----------------------------------------------------------------------------
# HookTargetの使い方:
#
# HookTargetを@ISAに入れたクラスを作る。コンストラクタでの配慮は不要。
# $obj->call_hooks($hook_name)で、インストールされた全てのフックを呼ぶ。
# $obj->call_hooks($hook_name, $foo, $bar, $baz)のように任意の個数の引数を
# 渡す事が可能で、その場合はそれらを引数としてフック関数が呼ばれる。
#
# 現在の実装では、HookTargetはオブジェクトをハッシュで持つクラスでのみ使用可能。
# また、`installed-hooks'と云うキーを勝手に使う。
# -----------------------------------------------------------------------------
package Hook;
use strict;
use warnings;
use Carp;
use UNIVERSAL;
use Tiarra::Utils;
utils->define_attr_getter(0, qw(name));
sub new {
my $class = shift;
#my ($class, $code) = @_;
my $name = shift;
my $code = shift;
if (!defined $name) {
croak $class."->new, Arg[0] was undef.\n";
}
if (ref($name) eq 'CODE' && !defined($code)) {
$code = $name;
$name = utils->simple_caller_formatter($class.' registered');
}
my $this = {
target => undef,
target_package_name => undef,
hook_name => undef,
name => $name,
code => $code,
};
if (ref($code) ne 'CODE') {
croak $class."->new, Arg[0] was bad type.\n";
}
do {
no strict;
no warnings;
local %symtable = %{$class.'::'};
if (defined ${$symtable{HOOK_TARGET_NAME}}) {
$this->{target_package_name} = ${$symtable{HOOK_TARGET_NAME}};
}
else {
croak "${class}->new, \$${class}::HOOK_TARGET_NAME undefined.\n";
}
if (@{$symtable{HOOK_NAME_CANDIDATES}} == 0) {
croak "${class}->new, \@${class}::HOOK_NAME_CANDIDATES undefined.\n";
}
};
bless $this, $class;
}
sub install {
my ($this, $hook_name, $target) = @_;
if (defined $this->{target}) {
croak ref($this)."->install, this hook is already installed.\n";
}
do {
no strict;
my %symtable = %{ref($this).'::'};
if (!defined $hook_name) {
# @HOOK_NAME_CANDIDATESの個数は1つか?
# それとも$HOOK_NAME_DEFAULTは定義されているか?
if (@{$symtable{HOOK_NAME_CANDIDATES}} == 1) {
$hook_name = $symtable{HOOK_NAME_CANDIDATES}->[0];
}
elsif (defined ${$symtable{HOOK_NAME_DEFAULT}}) {
$hook_name = ${$symtable{HOOK_NAME_DEFAULT}};
}
else {
croak ref($this)."->install, you can't omit the hook name.\n";
}
}
# $hook_nameは本当にフック名として許されているか?
if (!{map {$_ => 1} @{$symtable{HOOK_NAME_CANDIDATES}}}->{$hook_name}) {
croak ref($this)."->install, hook `$hook_name' is not available.\n";
}
if (!defined $target) {
# $HOOK_TARGET_DEFAULTは定義されているか?
if (defined ${$symtable{HOOK_TARGET_DEFAULT}}) {
$target = ${$symtable{HOOK_TARGET_DEFAULT}};
}
else {
croak ref($this)."->install, you can't omit the hook target.\n";
}
}
};
# $targetは本当にHookTargetを継承したオブジェクトか?
if (!UNIVERSAL::isa($target, 'HookTarget')) {
croak ref($this)."->install, target is not a subclass of HookTarget: ".
ref($target)."\n";
}
# $targetは本当に$HOOK_TARGET_NAMEのオブジェクトか?
if (!UNIVERSAL::isa($target, $this->{target_package_name})) {
croak ref($this)."->install, target is not a subclass of $this->{target_package_name}: ".
ref($target)."\n";
}
$this->{target} = $target;
$this->{hook_name} = $hook_name;
$target->install_hook($hook_name, $this);
$this;
}
sub uninstall {
my $this = shift;
$this->{target}->uninstall_hook($this->{hook_name}, $this);
$this->{target} = undef;
$this->{hook_name} = undef;
$this;
}
sub call {
my ($this, @args) = @_;
my ($caller_pkg) = caller(2);
if ($caller_pkg->isa(ref $this->{target})) {
utils->do_with_errmsg("Hook: $this->{target}/$this->{hook_name}($this->{name})",
sub {
$this->{code}->($this, @args);
});
}
else {
croak "Only ${\ref($this->{target})} can call ${\ref($this)}->call\n".
"$caller_pkg is not allowed to do so.\n";
}
}
sub module_destruct {
my ($this, $module) = @_;
$this->uninstall if $this->{target};
undef $this->{code};
}
# -----------------------------------------------------------------------------
package HookTarget;
sub _get_hooks_hash {
my $this = shift;
my $ih = $this->{'installed-hooks'};
if (defined $ih) {
$ih;
}
else {
$this->{'installed-hooks'} = {};
}
}
sub _get_hooks_array {
my ($this, $hook_name) = @_;
my $installed_hooks = $this->_get_hooks_hash;
my $ar = $installed_hooks->{$hook_name};
if (defined $ar) {
$ar;
}
else {
$installed_hooks->{$hook_name} = [];
}
}
sub install_hook {
my ($this, $hook_name, $hook) = @_;
my $array = $this->_get_hooks_array($hook_name);
push @$array, $hook;
$this;
}
sub uninstall_hook {
my ($this, $hook_name, $hook) = @_;
my $array = $this->_get_hooks_array($hook_name);
@$array = grep {
$_ != $hook;
} @$array;
$this;
}
sub call_hooks {
my ($this, $hook_name, @args) = @_;
my $array = $this->_get_hooks_array($hook_name);
foreach my $hook (@$array) {
eval {
$hook->call(@args);
}; if ($@) {
my $msg = ref($this)."->call_hooks, exception occured:\n".
" Hook: ".$hook->name."\n".
"$@";
if (require RunLoop) {
RunLoop->notify_error($msg);
} else {
die $msg;
}
}
}
}
1;
|