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   93 use strict;
  27         28  
  27         643  
4 27     27   88 use warnings;
  27         25  
  27         562  
5 27     27   75 no warnings 'uninitialized'; ## no critic
  27         28  
  27         656  
6              
7 27     27   89 use B;
  27         28  
  27         1053  
8              
9 27     27   108 use RPC::ExtDirect::Util::Accessor;
  27         25  
  27         12101  
10              
11             ### PUBLIC CLASS METHOD (ACCESSOR) ###
12             #
13             # Return the list of supported hook types
14             #
15              
16 55     55 1 91 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 565 my ($class, %arg) = @_;
25            
26 136         189 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     538 my $runnable = !('NONE' eq $coderef || !defined $coderef);
32            
33 136         99 my ($package, $sub_name);
34            
35 136 100       234 if ( 'CODE' eq ref $coderef ) {
36 126         188 $package = _package_from_coderef($coderef);
37             }
38             else {
39 10         35 my @parts = split /::/, $coderef;
40            
41 10         19 $sub_name = pop @parts;
42 10         20 $package = join '::', @parts;
43            
44             # We've got to have at least the sub_name part
45 10 50       40 die "Can't resolve '$type' hook $coderef" unless $sub_name;
46             }
47            
48 136         457 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         388 return $self;
57             }
58              
59             ### PUBLIC INSTANCE METHOD ###
60             #
61             # Run the hook
62             #
63              
64             sub run {
65 55     55 1 129 my ($self, %args) = @_;
66            
67 55         61 my $method_ref = $args{method_ref};
68 55         950 my $action_name = $method_ref->action;
69 55         897 my $method_name = $method_ref->name;
70 55         901 my $method_pkg = $method_ref->package;
71            
72 55         121 my %hook_arg = $method_ref->get_api_definition_compat();
73            
74 55         87 $hook_arg{method_ref} = $method_ref;
75 55         101 $hook_arg{code} = $method_ref->code;
76              
77             @hook_arg{qw/arg env metadata aux_data/}
78 55         130 = @args{qw/arg env metadata aux_data/};
79              
80             # Result and exception are passed to "after" hook only
81 55 100       949 if ( $self->type eq 'after' ) {
82             @hook_arg{ qw/result exception method_called/ }
83 26         66 = @args{ qw/result exception callee/ }
84             }
85              
86 55         90 for my $type ( $self->HOOK_TYPES ) {
87             my $hook = $args{api}->get_hook(
88 165         349 action => $action_name,
89             method => $method_name,
90             type => $type,
91             );
92            
93 165         278 $hook_arg{ $type.'_ref' } = $hook;
94 165 100       2206 $hook_arg{ $type } = $hook ? $hook->code : undef;
95             }
96            
97 55         62 my $arg = $args{arg};
98              
99             # A drop of sugar
100 55     1   213 $hook_arg{orig} = sub { $method_pkg->$method_name(@$arg) };
  1         3  
101              
102 55         968 my $hook_coderef = $self->code;
103 55         1018 my $hook_sub_name = $self->sub_name;
104 55         914 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     435 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   105 my ($code) = @_;
132              
133 126         122 my $pkg = eval { B::svref_2object($code)->GV->STASH->NAME };
  126         1214  
134              
135 126 50 33     963 return defined $pkg && $pkg ne '' ? $pkg : undef;
136             }
137              
138             1;