| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package RPC::ExtDirect::Request; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 8 |  |  | 8 |  | 2977 | use strict; | 
|  | 8 |  |  |  |  | 9 |  | 
|  | 8 |  |  |  |  | 189 |  | 
| 4 | 8 |  |  | 8 |  | 25 | use warnings; | 
|  | 8 |  |  |  |  | 8 |  | 
|  | 8 |  |  |  |  | 180 |  | 
| 5 | 8 |  |  | 8 |  | 32 | no  warnings 'uninitialized';           ## no critic | 
|  | 8 |  |  |  |  | 10 |  | 
|  | 8 |  |  |  |  | 208 |  | 
| 6 |  |  |  |  |  |  |  | 
| 7 | 8 |  |  | 8 |  | 25 | use Carp; | 
|  | 8 |  |  |  |  | 7 |  | 
|  | 8 |  |  |  |  | 398 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 8 |  |  | 8 |  | 671 | use RPC::ExtDirect::Config; | 
|  | 8 |  |  |  |  | 8 |  | 
|  | 8 |  |  |  |  | 153 |  | 
| 10 | 8 |  |  | 8 |  | 22 | use RPC::ExtDirect::Util::Accessor; | 
|  | 8 |  |  |  |  | 8 |  | 
|  | 8 |  |  |  |  | 145 |  | 
| 11 | 8 |  |  | 8 |  | 22 | use RPC::ExtDirect::Util qw/ clean_error_message /; | 
|  | 8 |  |  |  |  | 6 |  | 
|  | 8 |  |  |  |  | 10929 |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | ### PACKAGE GLOBAL VARIABLE ### | 
| 14 |  |  |  |  |  |  | # | 
| 15 |  |  |  |  |  |  | # Turn on for debugging | 
| 16 |  |  |  |  |  |  | # | 
| 17 |  |  |  |  |  |  | # DEPRECATED. Use `debug_request` or `debug` Config options instead. | 
| 18 |  |  |  |  |  |  | # | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | our $DEBUG; | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | ### PACKAGE GLOBAL VARIABLE ### | 
| 23 |  |  |  |  |  |  | # | 
| 24 |  |  |  |  |  |  | # Set Exception class name so it could be configured | 
| 25 |  |  |  |  |  |  | # | 
| 26 |  |  |  |  |  |  | # DEPRECATED. Use `exception_class_request` or | 
| 27 |  |  |  |  |  |  | # `exception_class` Config options instead. | 
| 28 |  |  |  |  |  |  | # | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | our $EXCEPTION_CLASS; | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | ### PUBLIC CLASS METHOD (ACCESSOR) ### | 
| 33 |  |  |  |  |  |  | # | 
| 34 |  |  |  |  |  |  | # Return the list of supported hook types | 
| 35 |  |  |  |  |  |  | # | 
| 36 |  |  |  |  |  |  |  | 
| 37 | 68 |  |  | 68 | 0 | 142 | sub HOOK_TYPES { qw/ before instead after / } | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | ### PUBLIC CLASS METHOD (CONSTRUCTOR) ### | 
| 40 |  |  |  |  |  |  | # | 
| 41 |  |  |  |  |  |  | # Initializes new instance of RPC::ExtDirect::Request | 
| 42 |  |  |  |  |  |  | # | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | sub new { | 
| 45 | 71 |  |  | 71 | 0 | 153 | my ($class, $arg) = @_; | 
| 46 |  |  |  |  |  |  |  | 
| 47 | 71 |  | 33 |  |  | 199 | my $api    = delete $arg->{api}    || RPC::ExtDirect->get_api(); | 
| 48 | 71 |  | 33 |  |  | 153 | my $config = delete $arg->{config} || RPC::ExtDirect::Config->new(); | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | my $debug = exists $arg->{debug} ? !!(delete $arg->{debug}) | 
| 51 | 71 | 100 |  |  |  | 865 | :                        $config->debug_request | 
| 52 |  |  |  |  |  |  | ; | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | # Need blessed object to call private methods | 
| 55 | 71 |  |  |  |  | 187 | my $self = bless { | 
| 56 |  |  |  |  |  |  | api    => $api, | 
| 57 |  |  |  |  |  |  | config => $config, | 
| 58 |  |  |  |  |  |  | debug  => $debug, | 
| 59 |  |  |  |  |  |  | }, $class; | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | # Unpack and validate arguments | 
| 62 |  |  |  |  |  |  | my ($action_name, $method_name, $tid, $data, $type, $upload, $meta, $aux) | 
| 63 | 71 |  |  |  |  | 78 | = eval { $self->_unpack_arguments($arg) }; | 
|  | 71 |  |  |  |  | 138 |  | 
| 64 |  |  |  |  |  |  |  | 
| 65 | 71 | 100 |  |  |  | 167 | return $self->_exception({ | 
| 66 |  |  |  |  |  |  | action  => $action_name, | 
| 67 |  |  |  |  |  |  | method  => $method_name, | 
| 68 |  |  |  |  |  |  | tid     => $tid, | 
| 69 |  |  |  |  |  |  | message => $@->[0], | 
| 70 |  |  |  |  |  |  | }) if $@; | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | # Look up the Method | 
| 73 | 69 |  |  |  |  | 194 | my $method_ref = $api->get_method_by_name($action_name, $method_name); | 
| 74 |  |  |  |  |  |  |  | 
| 75 | 69 | 100 |  |  |  | 156 | return $self->_exception({ | 
| 76 |  |  |  |  |  |  | action  => $action_name, | 
| 77 |  |  |  |  |  |  | method  => $method_name, | 
| 78 |  |  |  |  |  |  | tid     => $tid, | 
| 79 |  |  |  |  |  |  | message => 'ExtDirect action or method not found' | 
| 80 |  |  |  |  |  |  | }) unless $method_ref; | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | # Check if arguments passed in $data are of right kind | 
| 83 | 65 |  |  |  |  | 132 | my $exception = $self->check_arguments( | 
| 84 |  |  |  |  |  |  | action_name => $action_name, | 
| 85 |  |  |  |  |  |  | method_name => $method_name, | 
| 86 |  |  |  |  |  |  | method_ref  => $method_ref, | 
| 87 |  |  |  |  |  |  | tid         => $tid, | 
| 88 |  |  |  |  |  |  | data        => $data, | 
| 89 |  |  |  |  |  |  | metadata    => $meta, | 
| 90 |  |  |  |  |  |  | ); | 
| 91 |  |  |  |  |  |  |  | 
| 92 | 65 | 100 |  |  |  | 158 | return $exception if defined $exception; | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | # Bulk assignment for brevity | 
| 95 | 60 |  |  |  |  | 285 | @$self{ qw/ tid type data metadata upload method_ref run_count aux / } | 
| 96 |  |  |  |  |  |  | = ($tid, $type, $data, $meta, $upload, $method_ref, 0, $aux); | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | # Finally, resolve the hooks; it's easier to do that upfront | 
| 99 |  |  |  |  |  |  | # since it involves API lookup | 
| 100 | 60 |  |  |  |  | 165 | for my $hook_type ( $class->HOOK_TYPES ) { | 
| 101 | 180 |  |  |  |  | 371 | my $hook = $api->get_hook( | 
| 102 |  |  |  |  |  |  | action => $action_name, | 
| 103 |  |  |  |  |  |  | method => $method_name, | 
| 104 |  |  |  |  |  |  | type   => $hook_type, | 
| 105 |  |  |  |  |  |  | ); | 
| 106 |  |  |  |  |  |  |  | 
| 107 | 180 | 100 |  |  |  | 1292 | $self->$hook_type($hook) if $hook; | 
| 108 |  |  |  |  |  |  | } | 
| 109 |  |  |  |  |  |  |  | 
| 110 | 60 |  |  |  |  | 280 | return $self; | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | ### PUBLIC INSTANCE METHOD ### | 
| 114 |  |  |  |  |  |  | # | 
| 115 |  |  |  |  |  |  | # Checks if method arguments are in order | 
| 116 |  |  |  |  |  |  | # | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | my @checkers = qw/ check_method_arguments check_method_metadata /; | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | my %checker_property = ( | 
| 121 |  |  |  |  |  |  | check_method_arguments => 'data', | 
| 122 |  |  |  |  |  |  | check_method_metadata  => 'metadata', | 
| 123 |  |  |  |  |  |  | ); | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | sub check_arguments { | 
| 126 | 56 |  |  | 56 | 0 | 171 | my ($self, %arg) = @_; | 
| 127 |  |  |  |  |  |  |  | 
| 128 | 56 |  |  |  |  | 68 | my $action_name = $arg{action_name}; | 
| 129 | 56 |  |  |  |  | 48 | my $method_name = $arg{method_name}; | 
| 130 | 56 |  |  |  |  | 53 | my $method_ref  = $arg{method_ref}; | 
| 131 | 56 |  |  |  |  | 96 | my $tid         = $arg{tid}; | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | # Event poll handlers return Event objects instead of plain data; | 
| 134 |  |  |  |  |  |  | # there is no sense in calling them directly | 
| 135 | 56 | 100 |  |  |  | 1312 | if ( $method_ref->pollHandler ) { | 
| 136 | 1 |  |  |  |  | 8 | return $self->_exception({ | 
| 137 |  |  |  |  |  |  | action  => $action_name, | 
| 138 |  |  |  |  |  |  | method  => $method_name, | 
| 139 |  |  |  |  |  |  | tid     => $tid, | 
| 140 |  |  |  |  |  |  | message => "ExtDirect pollHandler method ". | 
| 141 |  |  |  |  |  |  | "$action_name.$method_name should not ". | 
| 142 |  |  |  |  |  |  | "be called directly" | 
| 143 |  |  |  |  |  |  | }); | 
| 144 |  |  |  |  |  |  | } | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | else { | 
| 147 |  |  |  |  |  |  | # One extra check for formHandlers | 
| 148 | 55 | 100 |  |  |  | 1039 | if ( $method_ref->formHandler ) { | 
| 149 | 14 |  |  |  |  | 23 | my $data = $arg{data}; | 
| 150 |  |  |  |  |  |  |  | 
| 151 | 14 | 100 | 66 |  |  | 130 | if ( 'HASH' ne ref($data) || !exists $data->{extAction} || | 
|  |  |  | 66 |  |  |  |  | 
| 152 |  |  |  |  |  |  | !exists $data->{extMethod} ) | 
| 153 |  |  |  |  |  |  | { | 
| 154 | 2 |  |  |  |  | 22 | return $self->_exception({ | 
| 155 |  |  |  |  |  |  | action  => $action_name, | 
| 156 |  |  |  |  |  |  | method  => $method_name, | 
| 157 |  |  |  |  |  |  | tid     => $tid, | 
| 158 |  |  |  |  |  |  | message => "ExtDirect formHandler method ". | 
| 159 |  |  |  |  |  |  | "$action_name.$method_name should only ". | 
| 160 |  |  |  |  |  |  | "be called with form submits" | 
| 161 |  |  |  |  |  |  | }) | 
| 162 |  |  |  |  |  |  | } | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | # The actual heavy lifting happens in the Method itself | 
| 166 | 53 |  |  |  |  | 86 | for my $checker ( @checkers ) { | 
| 167 | 105 |  |  |  |  | 129 | my $what = $checker_property{ $checker }; | 
| 168 | 105 |  |  |  |  | 99 | my $have = $arg{ $what }; | 
| 169 |  |  |  |  |  |  |  | 
| 170 | 105 |  |  |  |  | 102 | local $@; | 
| 171 |  |  |  |  |  |  |  | 
| 172 | 105 |  |  |  |  | 105 | eval { $method_ref->$checker($have) }; | 
|  | 105 |  |  |  |  | 279 |  | 
| 173 |  |  |  |  |  |  |  | 
| 174 | 105 | 100 |  |  |  | 246 | if ( my $error = $@ ) { | 
| 175 | 2 |  |  |  |  | 7 | $error =~ s/\n$//; | 
| 176 |  |  |  |  |  |  |  | 
| 177 | 2 |  |  |  |  | 15 | return $self->_exception({ | 
| 178 |  |  |  |  |  |  | action  => $action_name, | 
| 179 |  |  |  |  |  |  | method  => $method_name, | 
| 180 |  |  |  |  |  |  | tid     => $tid, | 
| 181 |  |  |  |  |  |  | message => $error, | 
| 182 |  |  |  |  |  |  | where   => ref($method_ref) ."->${checker}", | 
| 183 |  |  |  |  |  |  | }); | 
| 184 |  |  |  |  |  |  | } | 
| 185 |  |  |  |  |  |  | } | 
| 186 |  |  |  |  |  |  | } | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | # undef means no exception | 
| 189 | 51 |  |  |  |  | 129 | return undef;               ## no critic | 
| 190 |  |  |  |  |  |  | } | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | ### PUBLIC INSTANCE METHOD ### | 
| 193 |  |  |  |  |  |  | # | 
| 194 |  |  |  |  |  |  | # Runs the request; returns false value if method died on us, | 
| 195 |  |  |  |  |  |  | # true otherwise | 
| 196 |  |  |  |  |  |  | # | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | sub run { | 
| 199 | 61 |  |  | 61 | 0 | 8683 | my ($self, $env) = @_; | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | # Ensure run() is not called twice | 
| 202 | 61 | 100 |  |  |  | 1279 | return $self->_set_error("ExtDirect request can't run more than once per batch") | 
| 203 |  |  |  |  |  |  | if $self->run_count > 0; | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | # Set the flag | 
| 206 | 60 |  |  |  |  | 1109 | $self->run_count(1); | 
| 207 |  |  |  |  |  |  |  | 
| 208 | 60 |  |  |  |  | 1072 | my $method_ref = $self->method_ref; | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | # Prepare the arguments | 
| 211 |  |  |  |  |  |  | my @method_arg = $method_ref->prepare_method_arguments( | 
| 212 |  |  |  |  |  |  | env      => $env, | 
| 213 |  |  |  |  |  |  | input    => $self->{data}, | 
| 214 | 60 |  |  |  |  | 1094 | upload   => $self->upload, | 
| 215 |  |  |  |  |  |  | metadata => $self->metadata, | 
| 216 |  |  |  |  |  |  | ); | 
| 217 |  |  |  |  |  |  |  | 
| 218 | 60 |  |  |  |  | 1170 | my %params = ( | 
| 219 |  |  |  |  |  |  | api        => $self->api, | 
| 220 |  |  |  |  |  |  | method_ref => $method_ref, | 
| 221 |  |  |  |  |  |  | env        => $env, | 
| 222 |  |  |  |  |  |  | arg        => \@method_arg, | 
| 223 |  |  |  |  |  |  | metadata   => $self->metadata, | 
| 224 |  |  |  |  |  |  | aux_data   => $self->aux, | 
| 225 |  |  |  |  |  |  | ); | 
| 226 |  |  |  |  |  |  |  | 
| 227 | 60 |  |  |  |  | 82 | my ($run_method, $callee, $result, $exception) = (1); | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | # Run "before" hook if we got one | 
| 230 | 60 | 100 | 100 |  |  | 1073 | ($result, $exception, $run_method) = $self->_run_before_hook(%params) | 
| 231 |  |  |  |  |  |  | if $self->before && $self->before->runnable; | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | # If there is "instead" hook, call it instead of the method | 
| 234 | 60 | 100 |  |  |  | 248 | ($result, $exception, $callee) = $self->_run_method(%params) | 
| 235 |  |  |  |  |  |  | if $run_method; | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | # Finally, run "after" hook if we got one | 
| 238 | 60 | 100 | 66 |  |  | 1149 | $self->_run_after_hook( | 
| 239 |  |  |  |  |  |  | %params, | 
| 240 |  |  |  |  |  |  | result    => $result, | 
| 241 |  |  |  |  |  |  | exception => $exception, | 
| 242 |  |  |  |  |  |  | callee    => $callee | 
| 243 |  |  |  |  |  |  | ) if $self->after && $self->after->runnable; | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | # Fail gracefully if method call was unsuccessful | 
| 246 | 60 | 100 |  |  |  | 265 | return $self->_process_exception($env, $exception) | 
| 247 |  |  |  |  |  |  | if $exception; | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | # Else stash the results | 
| 250 | 55 |  |  |  |  | 79 | $self->{result} = $result; | 
| 251 |  |  |  |  |  |  |  | 
| 252 | 55 |  |  |  |  | 168 | return 1; | 
| 253 |  |  |  |  |  |  | } | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | ### PUBLIC INSTANCE METHOD ### | 
| 256 |  |  |  |  |  |  | # | 
| 257 |  |  |  |  |  |  | # If method call was successful, returns result hashref. | 
| 258 |  |  |  |  |  |  | # If an error occured, returns exception hashref. It will contain | 
| 259 |  |  |  |  |  |  | # error-specific message only if we're debugging. This is somewhat weird | 
| 260 |  |  |  |  |  |  | # requirement in ExtDirect specification. If the debug config option | 
| 261 |  |  |  |  |  |  | # is not set, the exception hashref will contain generic error message. | 
| 262 |  |  |  |  |  |  | # | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | sub result { | 
| 265 | 47 |  |  | 47 | 0 | 10043 | my ($self) = @_; | 
| 266 |  |  |  |  |  |  |  | 
| 267 | 47 |  |  |  |  | 84 | return $self->_get_result_hashref(); | 
| 268 |  |  |  |  |  |  | } | 
| 269 |  |  |  |  |  |  |  | 
| 270 |  |  |  |  |  |  | ### PUBLIC INSTANCE METHOD ### | 
| 271 |  |  |  |  |  |  | # | 
| 272 |  |  |  |  |  |  | # Return the data represented as a list | 
| 273 |  |  |  |  |  |  | # | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | sub data { | 
| 276 | 0 |  |  | 0 | 0 | 0 | my ($self) = @_; | 
| 277 |  |  |  |  |  |  |  | 
| 278 | 0 |  |  |  |  | 0 | return 'HASH'  eq ref $self->{data} ? %{ $self->{data} } | 
| 279 | 0 | 0 |  |  |  | 0 | : 'ARRAY' eq ref $self->{data} ? @{ $self->{data} } | 
|  | 0 | 0 |  |  |  | 0 |  | 
| 280 |  |  |  |  |  |  | :                                () | 
| 281 |  |  |  |  |  |  | ; | 
| 282 |  |  |  |  |  |  | } | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | ### PUBLIC INSTANCE METHODS ### | 
| 285 |  |  |  |  |  |  | # | 
| 286 |  |  |  |  |  |  | # Simple read-write accessors. | 
| 287 |  |  |  |  |  |  | # | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | my $accessors = [qw/ | 
| 290 |  |  |  |  |  |  | config | 
| 291 |  |  |  |  |  |  | api | 
| 292 |  |  |  |  |  |  | debug | 
| 293 |  |  |  |  |  |  | method_ref | 
| 294 |  |  |  |  |  |  | type | 
| 295 |  |  |  |  |  |  | tid | 
| 296 |  |  |  |  |  |  | state | 
| 297 |  |  |  |  |  |  | where | 
| 298 |  |  |  |  |  |  | message | 
| 299 |  |  |  |  |  |  | upload | 
| 300 |  |  |  |  |  |  | run_count | 
| 301 |  |  |  |  |  |  | metadata | 
| 302 |  |  |  |  |  |  | aux | 
| 303 |  |  |  |  |  |  | /, | 
| 304 |  |  |  |  |  |  | __PACKAGE__->HOOK_TYPES, | 
| 305 |  |  |  |  |  |  | ]; | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | RPC::ExtDirect::Util::Accessor::mk_accessors( | 
| 308 |  |  |  |  |  |  | simple => $accessors, | 
| 309 |  |  |  |  |  |  | ); | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | ############## PRIVATE METHODS BELOW ############## | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | ### PRIVATE INSTANCE METHOD ### | 
| 314 |  |  |  |  |  |  | # | 
| 315 |  |  |  |  |  |  | # Return new Exception object | 
| 316 |  |  |  |  |  |  | # | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | sub _exception { | 
| 319 | 15 |  |  | 15 |  | 23 | my ($self, $arg) = @_; | 
| 320 |  |  |  |  |  |  |  | 
| 321 | 15 |  |  |  |  | 323 | my $config   = $self->config; | 
| 322 | 15 |  |  |  |  | 283 | my $ex_class = $config->exception_class_request; | 
| 323 |  |  |  |  |  |  |  | 
| 324 | 15 |  |  |  |  | 699 | eval "require $ex_class"; | 
| 325 |  |  |  |  |  |  |  | 
| 326 | 15 |  |  |  |  | 45 | my $where = $arg->{where}; | 
| 327 |  |  |  |  |  |  |  | 
| 328 | 15 | 100 |  |  |  | 38 | if ( !$where ) { | 
| 329 | 9 |  |  |  |  | 91 | my ($package, $sub) | 
| 330 |  |  |  |  |  |  | = (caller 1)[3] =~ / \A (.*) :: (.*?) \z /xms; | 
| 331 | 9 |  |  |  |  | 30 | $arg->{where} = $package . '->' . $sub; | 
| 332 |  |  |  |  |  |  | }; | 
| 333 |  |  |  |  |  |  |  | 
| 334 | 15 |  |  |  |  | 315 | return $ex_class->new({ | 
| 335 |  |  |  |  |  |  | config  => $config, | 
| 336 |  |  |  |  |  |  | debug   => $self->debug, | 
| 337 |  |  |  |  |  |  | verbose => $config->verbose_exceptions, | 
| 338 |  |  |  |  |  |  | %$arg | 
| 339 |  |  |  |  |  |  | }); | 
| 340 |  |  |  |  |  |  | } | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | ### PRIVATE INSTANCE METHOD ### | 
| 343 |  |  |  |  |  |  | # | 
| 344 |  |  |  |  |  |  | # Replaces Request object with Exception object | 
| 345 |  |  |  |  |  |  | # | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | sub _set_error { | 
| 348 | 4 |  |  | 4 |  | 6 | my ($self, $msg, $where) = @_; | 
| 349 |  |  |  |  |  |  |  | 
| 350 |  |  |  |  |  |  | # Munge $where to avoid it being '_set_error' all the time | 
| 351 | 4 | 100 |  |  |  | 10 | if ( !defined $where ) { | 
| 352 | 1 |  |  |  |  | 8 | my ($package, $sub) = (caller 1)[3] =~ / \A (.*) :: (.*?) \z /xms; | 
| 353 | 1 |  |  |  |  | 4 | $where = $package . '->' . $sub; | 
| 354 |  |  |  |  |  |  | }; | 
| 355 |  |  |  |  |  |  |  | 
| 356 | 4 |  |  |  |  | 67 | my $method_ref = $self->method_ref; | 
| 357 |  |  |  |  |  |  |  | 
| 358 |  |  |  |  |  |  | # We need newborn Exception object to tear its guts out | 
| 359 | 4 |  |  |  |  | 70 | my $ex = $self->_exception({ | 
| 360 |  |  |  |  |  |  | action  => $method_ref->action, | 
| 361 |  |  |  |  |  |  | method  => $method_ref->name, | 
| 362 |  |  |  |  |  |  | tid     => $self->tid, | 
| 363 |  |  |  |  |  |  | message => $msg, | 
| 364 |  |  |  |  |  |  | where   => $where, | 
| 365 |  |  |  |  |  |  | debug   => $self->debug, | 
| 366 |  |  |  |  |  |  | }); | 
| 367 |  |  |  |  |  |  |  | 
| 368 |  |  |  |  |  |  | # Now the black voodoo magiKC part, live on stage | 
| 369 | 4 |  |  |  |  | 40 | delete @$self{ keys %$self }; | 
| 370 | 4 |  |  |  |  | 21 | @$self{ keys %$ex } = values %$ex; | 
| 371 |  |  |  |  |  |  |  | 
| 372 |  |  |  |  |  |  | # Finally, cover our sins with a blessing and we've been born again! | 
| 373 | 4 |  |  |  |  | 10 | bless $self, ref $ex; | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  | # Humbly return failure to be propagated upwards | 
| 376 | 4 |  |  |  |  | 28 | return !1; | 
| 377 |  |  |  |  |  |  | } | 
| 378 |  |  |  |  |  |  |  | 
| 379 |  |  |  |  |  |  | ### PRIVATE INSTANCE METHOD ### | 
| 380 |  |  |  |  |  |  | # | 
| 381 |  |  |  |  |  |  | # Unpacks arguments into a list and validates them | 
| 382 |  |  |  |  |  |  | # | 
| 383 |  |  |  |  |  |  |  | 
| 384 |  |  |  |  |  |  | my @std_keys = qw/ | 
| 385 |  |  |  |  |  |  | extAction action extMethod method extTID tid data metadata | 
| 386 |  |  |  |  |  |  | extType type extUpload _uploads | 
| 387 |  |  |  |  |  |  | /; | 
| 388 |  |  |  |  |  |  |  | 
| 389 |  |  |  |  |  |  | sub _unpack_arguments { | 
| 390 | 71 |  |  | 71 |  | 81 | my ($self, $arg) = @_; | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | # Unpack and normalize arguments | 
| 393 | 71 |  | 66 |  |  | 199 | my $action = $arg->{extAction} || $arg->{action}; | 
| 394 | 71 |  | 66 |  |  | 425 | my $method = $arg->{extMethod} || $arg->{method}; | 
| 395 | 71 |  | 66 |  |  | 147 | my $tid    = $arg->{extTID}    || $arg->{tid}; # can't be 0 | 
| 396 | 71 |  | 100 |  |  | 164 | my $type   = $arg->{type}      || 'rpc'; | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | # For a formHandler, the "data" field is the form itself; | 
| 399 |  |  |  |  |  |  | # the arguments are fields in the form-encoded POST body | 
| 400 | 71 |  | 66 |  |  | 154 | my $data   = $arg->{data} || $arg; | 
| 401 | 71 |  |  |  |  | 71 | my $meta   = $arg->{metadata}; | 
| 402 |  |  |  |  |  |  | my $upload = $arg->{extUpload} eq 'true' ? $arg->{_uploads} | 
| 403 |  |  |  |  |  |  | :                               undef | 
| 404 | 71 | 100 |  |  |  | 138 | ; | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | # Throwing arrayref so that die() wouldn't add file/line to the string | 
| 407 | 71 | 100 | 66 |  |  | 290 | die [ "ExtDirect action (class name) required" ] | 
| 408 |  |  |  |  |  |  | unless defined $action && length $action > 0; | 
| 409 |  |  |  |  |  |  |  | 
| 410 | 70 | 100 | 66 |  |  | 242 | die [ "ExtDirect method name required" ] | 
| 411 |  |  |  |  |  |  | unless defined $method && length $method > 0; | 
| 412 |  |  |  |  |  |  |  | 
| 413 | 69 |  |  |  |  | 183 | my %arg_keys = map { $_ => 1, } keys %$arg; | 
|  | 372 |  |  |  |  | 504 |  | 
| 414 | 69 |  |  |  |  | 270 | delete @arg_keys{ @std_keys }; | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | # Collect ancillary data that might be passed in the packet | 
| 417 |  |  |  |  |  |  | # and make it available to the Hooks. This might be used e.g. | 
| 418 |  |  |  |  |  |  | # for passing CSRF protection tokens, etc. | 
| 419 | 69 |  |  |  |  | 98 | my %aux = map { $_ => $arg->{$_} } keys %arg_keys; | 
|  | 24 |  |  |  |  | 52 |  | 
| 420 |  |  |  |  |  |  |  | 
| 421 | 69 | 100 |  |  |  | 154 | my $aux_ref = %aux ? { %aux } : undef; | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | return ( | 
| 424 | 69 |  |  |  |  | 241 | $action, $method, $tid, $data, $type, $upload, $meta, $aux_ref | 
| 425 |  |  |  |  |  |  | ); | 
| 426 |  |  |  |  |  |  | } | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | ### PRIVATE INSTANCE METHOD ### | 
| 429 |  |  |  |  |  |  | # | 
| 430 |  |  |  |  |  |  | # Run "before" hook | 
| 431 |  |  |  |  |  |  | # | 
| 432 |  |  |  |  |  |  |  | 
| 433 |  |  |  |  |  |  | sub _run_before_hook { | 
| 434 | 26 |  |  | 26 |  | 77 | my ($self, %arg) = @_; | 
| 435 |  |  |  |  |  |  |  | 
| 436 | 26 |  |  |  |  | 35 | my ($run_method, $result, $exception) = (1); | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | # This hook may die() with an Exception | 
| 439 | 26 |  |  |  |  | 34 | local $@; | 
| 440 | 26 |  |  |  |  | 34 | my $hook_result = eval { $self->before->run(%arg) }; | 
|  | 26 |  |  |  |  | 516 |  | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | # If "before" hook died, cancel Method call | 
| 443 | 26 | 100 |  |  |  | 324 | if ( $@ ) { | 
| 444 | 3 |  |  |  |  | 3 | $exception  = $@; | 
| 445 | 3 |  |  |  |  | 5 | $run_method = !1; | 
| 446 |  |  |  |  |  |  | }; | 
| 447 |  |  |  |  |  |  |  | 
| 448 |  |  |  |  |  |  | # If "before" hook returns anything but number 1, | 
| 449 |  |  |  |  |  |  | # treat it as an Ext.Direct response and do not call | 
| 450 |  |  |  |  |  |  | # the actual method | 
| 451 | 26 | 100 |  |  |  | 56 | if ( $hook_result ne '1' ) { | 
| 452 | 5 |  |  |  |  | 6 | $result     = $hook_result; | 
| 453 | 5 |  |  |  |  | 5 | $run_method = !1; | 
| 454 |  |  |  |  |  |  | }; | 
| 455 |  |  |  |  |  |  |  | 
| 456 | 26 |  |  |  |  | 79 | return ($result, $exception, $run_method); | 
| 457 |  |  |  |  |  |  | } | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | ### PRIVATE INSTANCE METHOD ### | 
| 460 |  |  |  |  |  |  | # | 
| 461 |  |  |  |  |  |  | # Runs "instead" hook if it exists, or the method itself | 
| 462 |  |  |  |  |  |  | # | 
| 463 |  |  |  |  |  |  |  | 
| 464 |  |  |  |  |  |  | sub _run_method { | 
| 465 | 55 |  |  | 55 |  | 127 | my ($self, %arg) = @_; | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | # We call methods by code reference | 
| 468 | 55 |  |  |  |  | 1100 | my $hook      = $self->instead; | 
| 469 | 55 |  | 66 |  |  | 128 | my $run_hook  = $hook && $hook->runnable; | 
| 470 | 55 | 100 |  |  |  | 1038 | my $callee    = $run_hook ? $hook : $self->method_ref; | 
| 471 |  |  |  |  |  |  |  | 
| 472 | 55 |  |  |  |  | 63 | local $@; | 
| 473 | 55 |  |  |  |  | 67 | my $result    = eval { $callee->run(%arg) }; | 
|  | 55 |  |  |  |  | 181 |  | 
| 474 | 55 |  |  |  |  | 84 | my $exception = $@; | 
| 475 |  |  |  |  |  |  |  | 
| 476 | 55 |  |  |  |  | 138 | return ($result, $exception, $callee->code); | 
| 477 |  |  |  |  |  |  | } | 
| 478 |  |  |  |  |  |  |  | 
| 479 |  |  |  |  |  |  | ### PRIVATE INSTANCE METHOD ### | 
| 480 |  |  |  |  |  |  | # | 
| 481 |  |  |  |  |  |  | # Run "after" hook | 
| 482 |  |  |  |  |  |  | # | 
| 483 |  |  |  |  |  |  |  | 
| 484 |  |  |  |  |  |  | sub _run_after_hook { | 
| 485 | 24 |  |  | 24 |  | 91 | my ($self, %arg) = @_; | 
| 486 |  |  |  |  |  |  |  | 
| 487 |  |  |  |  |  |  | # Localize so that we don't clobber the $@ | 
| 488 | 24 |  |  |  |  | 26 | local $@; | 
| 489 |  |  |  |  |  |  |  | 
| 490 |  |  |  |  |  |  | # Return value and exceptions are ignored | 
| 491 | 24 |  |  |  |  | 27 | eval { $self->after->run(%arg) }; | 
|  | 24 |  |  |  |  | 452 |  | 
| 492 |  |  |  |  |  |  | } | 
| 493 |  |  |  |  |  |  |  | 
| 494 |  |  |  |  |  |  | ### PRIVATE INSTANCE METHOD ### | 
| 495 |  |  |  |  |  |  | # | 
| 496 |  |  |  |  |  |  | # Return result hashref | 
| 497 |  |  |  |  |  |  | # | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | sub _get_result_hashref { | 
| 500 | 47 |  |  | 47 |  | 43 | my ($self) = @_; | 
| 501 |  |  |  |  |  |  |  | 
| 502 | 47 |  |  |  |  | 993 | my $method_ref = $self->method_ref; | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  | my $result_ref = { | 
| 505 |  |  |  |  |  |  | type   => 'rpc', | 
| 506 |  |  |  |  |  |  | tid    => $self->tid, | 
| 507 |  |  |  |  |  |  | action => $method_ref->action, | 
| 508 |  |  |  |  |  |  | method => $method_ref->name, | 
| 509 |  |  |  |  |  |  | result => $self->{result},  # To avoid collisions | 
| 510 | 47 |  |  |  |  | 1522 | }; | 
| 511 |  |  |  |  |  |  |  | 
| 512 | 47 |  |  |  |  | 138 | return $result_ref; | 
| 513 |  |  |  |  |  |  | } | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | ### PRIVATE INSTANCE METHOD ### | 
| 516 |  |  |  |  |  |  | # | 
| 517 |  |  |  |  |  |  | # Process exception message returned by die() in method or hooks | 
| 518 |  |  |  |  |  |  | # | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | sub _process_exception { | 
| 521 | 5 |  |  | 5 |  | 11 | my ($self, $env, $exception) = @_; | 
| 522 |  |  |  |  |  |  |  | 
| 523 |  |  |  |  |  |  | # Stringify exception and treat it as error message | 
| 524 | 5 |  |  |  |  | 19 | my $msg = clean_error_message("$exception"); | 
| 525 |  |  |  |  |  |  |  | 
| 526 |  |  |  |  |  |  | # Report actual package and method in case we're debugging | 
| 527 | 5 |  |  |  |  | 109 | my $method_ref = $self->method_ref; | 
| 528 | 5 |  |  |  |  | 89 | my $where      = $method_ref->package .'->'. $method_ref->name; | 
| 529 |  |  |  |  |  |  |  | 
| 530 | 5 |  |  |  |  | 17 | return $self->_set_error($msg, $where); | 
| 531 |  |  |  |  |  |  | } | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | 1; |