File Coverage

blib/lib/RPC/ExtDirect/API/Hook.pm
Criterion Covered Total %
statement 54 54 100.0
branch 10 12 83.3
condition 5 9 55.5
subroutine 10 10 100.0
pod 3 3 100.0
total 82 88 93.1


line stmt bran cond sub pod time code
1             package RPC::ExtDirect::API::Hook;
2              
3 27     27   87 use strict;
  27         29  
  27         583  
4 27     27   73 use warnings;
  27         27  
  27         519  
5 27     27   76 no warnings 'uninitialized'; ## no critic
  27         21  
  27         621  
6              
7 27     27   83 use B;
  27         28  
  27         911  
8              
9 27     27   93 use RPC::ExtDirect::Util::Accessor;
  27         47  
  27         11542  
10              
11             ### PUBLIC CLASS METHOD (ACCESSOR) ###
12             #
13             # Return the list of supported hook types
14             #
15              
16 55     55 1 98 sub HOOK_TYPES { qw/ before instead after / }
17              
18             ### PUBLIC CLASS METHOD (CONSTRUCTOR) ###
19             #
20             # Instantiate a new Hook object
21             #
22              
23             sub new {
24 136     136 1 323 my ($class, %arg) = @_;
25            
26 136         184 my ($type, $coderef) = @arg{qw/ type code /};
27            
28             # If we're passed an undef or 'NONE' instead of a coderef,
29             # then the hook is not runnable. Otherwise, try resolving
30             # package if we have a coderef.
31 136   66     524 my $runnable = !('NONE' eq $coderef || !defined $coderef);
32            
33 136         99 my ($package, $sub_name);
34            
35 136 100       238 if ( 'CODE' eq ref $coderef ) {
36 126         189 $package = _package_from_coderef($coderef);
37             }
38             else {
39 10         32 my @parts = split /::/, $coderef;
40            
41 10         12 $sub_name = pop @parts;
42 10         21 $package = join '::', @parts;
43            
44             # We've got to have at least the sub_name part
45 10 50       25 die "Can't resolve '$type' hook $coderef" unless $sub_name;
46             }
47            
48 136         427 my $self = bless {
49             package => $package,
50             type => $type,
51             code => $coderef,
52             sub_name => $sub_name,
53             runnable => $runnable,
54             }, $class;
55            
56 136         376 return $self;
57             }
58              
59             ### PUBLIC INSTANCE METHOD ###
60             #
61             # Run the hook
62             #
63              
64             sub run {
65 55     55 1 135 my ($self, %args) = @_;
66            
67 55         61 my $method_ref = $args{method_ref};
68 55         1028 my $action_name = $method_ref->action;
69 55         986 my $method_name = $method_ref->name;
70 55         1000 my $method_pkg = $method_ref->package;
71            
72 55         130 my %hook_arg = $method_ref->get_api_definition_compat();
73            
74 55         96 $hook_arg{method_ref} = $method_ref;
75 55         115 $hook_arg{code} = $method_ref->code;
76              
77             @hook_arg{qw/arg env metadata aux_data/}
78 55         170 = @args{qw/arg env metadata aux_data/};
79              
80             # Result and exception are passed to "after" hook only
81 55 100       1039 if ( $self->type eq 'after' ) {
82             @hook_arg{ qw/result exception method_called/ }
83 26         71 = @args{ qw/result exception callee/ }
84             }
85              
86 55         97 for my $type ( $self->HOOK_TYPES ) {
87             my $hook = $args{api}->get_hook(
88 165         350 action => $action_name,
89             method => $method_name,
90             type => $type,
91             );
92            
93 165         319 $hook_arg{ $type.'_ref' } = $hook;
94 165 100       2198 $hook_arg{ $type } = $hook ? $hook->code : undef;
95             }
96            
97 55         67 my $arg = $args{arg};
98              
99             # A drop of sugar
100 55     1   211 $hook_arg{orig} = sub { $method_pkg->$method_name(@$arg) };
  1         3  
101              
102 55         1265 my $hook_coderef = $self->code;
103 55         1018 my $hook_sub_name = $self->sub_name;
104 55         1005 my $hook_pkg = $self->package;
105              
106             # By convention, hooks are called as class methods. If we were passed
107             # a method name instead of a coderef, call it indirectly on the package
108             # so that inheritance works properly
109 55 100 66     471 return $hook_pkg && $hook_sub_name ? $hook_pkg->$hook_sub_name(%hook_arg)
110             : $hook_coderef->($hook_pkg, %hook_arg)
111             ;
112             }
113              
114             ### PUBLIC INSTANCE METHODS ###
115             #
116             # Simple read-write accessors
117             #
118              
119             RPC::ExtDirect::Util::Accessor::mk_accessors(
120             simple => [qw/ type code package sub_name runnable /],
121             );
122              
123             ############## PRIVATE METHODS BELOW ##############
124              
125             ### PRIVATE PACKAGE SUBROUTINE ###
126             #
127             # Return package name from coderef
128             #
129              
130             sub _package_from_coderef {
131 126     126   106 my ($code) = @_;
132              
133 126         341 my $pkg = eval { B::svref_2object($code)->GV->STASH->NAME };
  126         834  
134              
135 126 50 33     922 return defined $pkg && $pkg ne '' ? $pkg : undef;
136             }
137              
138             1;