File Coverage

blib/lib/RPC/ExtDirect/API/Method.pm
Criterion Covered Total %
statement 248 250 99.2
branch 132 146 90.4
condition 33 40 82.5
subroutine 33 34 97.0
pod 18 23 78.2
total 464 493 94.1


line stmt bran cond sub pod time code
1             package RPC::ExtDirect::API::Method;
2              
3 27     27   1099 use strict;
  27         29  
  27         595  
4 27     27   74 use warnings;
  27         33  
  27         532  
5 27     27   80 no warnings 'uninitialized'; ## no critic
  27         24  
  27         653  
6              
7 27     27   86 use Carp;
  27         31  
  27         1192  
8 27     27   97 use JSON;
  27         23  
  27         138  
9              
10 27     27   2680 use RPC::ExtDirect::Config;
  27         28  
  27         439  
11 27     27   79 use RPC::ExtDirect::Util ();
  27         27  
  27         363  
12 27     27   71 use RPC::ExtDirect::Util::Accessor;
  27         33  
  27         45884  
13              
14             ### PUBLIC CLASS METHOD (ACCESSOR) ###
15             #
16             # Return the hook types supported by this Method class
17             #
18              
19 551     551 1 1168 sub HOOK_TYPES { qw/ before instead after / }
20              
21             ### PUBLIC CLASS METHOD (CONSTRUCTOR) ###
22             #
23             # Instantiate a new Method object
24             #
25              
26             sub new {
27 457     457 1 39650 my ($class, %arg) = @_;
28            
29 457         418 my $config = $arg{config};
30 457         8555 my $hook_class = $config->api_hook_class;
31            
32 457         437 my $pollHandler = $arg{pollHandler};
33 457         367 my $formHandler = $arg{formHandler};
34            
35             my $is_ordered
36 457   66     1675 = defined $arg{len} && !$pollHandler && !$formHandler;
37            
38 457   100     1520 my $is_named
39             = !$pollHandler && !$formHandler && !$is_ordered;
40            
41 457 100       1036 my $processor = $pollHandler ? 'pollHandler'
    100          
    100          
42             : $formHandler ? 'formHandler'
43             : $is_ordered ? 'ordered'
44             : 'named'
45             ;
46            
47             # Need $self to call instance methods
48 457         2010 my $self = bless {
49             upload_arg => 'file_uploads',
50             is_named => $is_named,
51             is_ordered => $is_ordered,
52             argument_checker => "check_${processor}_arguments",
53             argument_preparer => "prepare_${processor}_arguments",
54             }, $class;
55            
56             # If the Method is named, and params array is empty, force !strict
57 457 100       714 if ( $is_named ) {
58 121   100     293 $arg{params} = $arg{params} || []; # Better safe than sorry
59 121 100       198 $arg{strict} = !1 unless @{ $arg{params} };
  121         289  
60             }
61            
62 457 100       722 if ( exists $arg{metadata} ) {
63             # This method is coupled too tightly to try untangling it,
64             # so let's pretend that side effects are ok in this case
65 101         174 $self->_parse_metadata(\%arg);
66             }
67            
68             # We avoid hard binding on the hook class
69 454         17177 eval "require $hook_class";
70            
71 454         776 my %hooks;
72            
73 454         841 for my $type ( $class->HOOK_TYPES ) {
74 1362         1123 my $hook = delete $arg{ $type };
75            
76 1362 100       2062 $hooks{ $type } = $hook_class->new( type => $type, code => $hook )
77             if $hook;
78             }
79            
80 454         2229 @$self{ keys %arg } = values %arg;
81 454         695 @$self{ keys %hooks } = values %hooks;
82            
83 454         1628 return $self;
84             }
85              
86             ### PUBLIC INSTANCE METHOD ###
87             #
88             # Return a hashref with the API definition for this Method,
89             # or an empty list
90             #
91              
92             sub get_api_definition {
93 149     149 1 157 my ($self, $env) = @_;
94            
95             # By default we're not using the environment object,
96             # but application developers can override this method
97             # to make permission and/or other kind of checks
98            
99             # Poll handlers are not declared in the remoting API
100 149 50       2443 return if $self->pollHandler;
101            
102 149         2378 my $name = $self->name;
103              
104 149         110 my $def;
105            
106             # Form handlers are defined like this
107             # (\1 means JSON::true and doesn't force us to `use JSON`)
108 149 100       2415 if ( $self->formHandler ) {
    100          
109 20         41 $def = { name => $name, formHandler => \1 };
110             }
111              
112             # Ordinary method with positioned arguments
113             elsif ( $self->is_ordered ) {
114 90         1509 $def = { name => $name, len => $self->len + 0 }
115             }
116              
117             # Ordinary method with named arguments
118             else {
119 39         645 my $strict = $self->strict;
120              
121 39 50 50     652 $def = {
    100          
122             name => $name,
123             params => $self->params || [],
124             defined $strict ? (strict => ($strict ? \1 : \0)) : (),
125             };
126             }
127              
128 149 100       2493 if ( my $meta = $self->metadata ) {
129 43         46 $def->{metadata} = {};
130              
131 43 100       58 if ( $meta->{len} ) {
132             $def->{metadata} = {
133             len => $meta->{len},
134 32         45 };
135             }
136             else {
137 11         14 my $strict = $meta->{strict};
138              
139             $def->{metadata} = {
140             params => $meta->{params},
141 11 50       32 defined $strict ? (strict => ($strict ? \1 : \0)) : (),
    100          
142             };
143             }
144             }
145              
146 149         222 return $def;
147             }
148              
149             ### PUBLIC INSTANCE METHOD ###
150             #
151             # Return a hashref with backwards-compatible API definition
152             # for this Method
153             #
154              
155             sub get_api_definition_compat {
156 70     70 1 70 my ($self) = @_;
157            
158 70         63 my %attrs;
159            
160 70         1305 $attrs{package} = $self->package;
161 70         1211 $attrs{method} = $self->name;
162 70         1238 $attrs{param_names} = $self->params;
163 70         1241 $attrs{param_no} = $self->len;
164 70   100     1256 $attrs{pollHandler} = $self->pollHandler || 0;
165 70   100     1271 $attrs{formHandler} = $self->formHandler || 0;
166 70 100       1006 $attrs{param_no} = undef if $attrs{formHandler};
167            
168 70         119 for my $type ( $self->HOOK_TYPES ) {
169 210         3837 my $hook = $self->$type;
170            
171 210 100       671 $attrs{$type} = $hook->code if $hook;
172             }
173            
174 70         381 return %attrs;
175             }
176              
177             ### PUBLIC INSTANCE METHOD ###
178             #
179             # Return a reference to the actual code for this Method
180             #
181              
182             sub code {
183 109     109 0 107 my ($self) = @_;
184            
185 109         2077 my $package = $self->package;
186 109         1942 my $name = $self->name;
187            
188 109         716 return $package->can($name);
189             }
190              
191             ### PUBLIC INSTANCE METHOD ###
192             #
193             # Run the Method code using the provided Environment object
194             # and input data; return the result or die with exception
195             #
196             # We accept named parameters here to keep the signature compatible
197             # with the corresponding Hook method.
198             #
199              
200             sub run {
201 54     54 1 119 my ($self, %args) = @_;
202            
203 54         65 my $arg = $args{arg};
204 54         1024 my $package = $self->package;
205 54         1019 my $name = $self->name;
206            
207             # pollHandler methods should always be called in list context
208 54 100       994 return $self->pollHandler ? [ $package->$name(@$arg) ]
209             : $package->$name(@$arg)
210             ;
211             }
212              
213             ### PUBLIC INSTANCE METHOD ###
214             #
215             # Check the arguments that were passed in the Ext.Direct request
216             # to make sure they conform to the API declared by this Method.
217             # Arguments should be passed in a reference, either hash- or array-.
218             # This method is expected to die if anything is wrong, or return 1
219             # on success.
220             #
221             # This method is intentionally split into several submethods,
222             # instead of using polymorphic subclasses with method overrides.
223             # Having all these in the same class is easier to maintain and
224             # augment in user subclasses.
225             #
226             # The same applies to `prepare_method_arguments` below.
227             #
228              
229             sub check_method_arguments {
230 82     82 1 201 my $self = shift;
231            
232 82         1585 my $checker = $self->argument_checker;
233            
234 82         225 return $self->$checker(@_);
235             }
236              
237             ### PUBLIC INSTANCE METHOD ###
238             #
239             # Prepare the arguments to be passed to the called Method,
240             # according to the Method's expectations. This works two ways:
241             # on the server side, RPC::ExtDirect::Request will call this method
242             # to prepare the arguments that are to be passed to the actual
243             # Method code that does things; on the client side,
244             # RPC::ExtDirect::Client will call this method to prepare
245             # the arguments that are about to be encoded in JSON and passed
246             # over to the server side.
247             #
248             # The difference is that the server side wants an unfolded list,
249             # and the client side wants a reference, either hash- or array-.
250             # Because of that, prepare_*_arguments are context sensitive.
251             #
252              
253             sub prepare_method_arguments {
254 133     133 1 20099 my $self = shift;
255            
256 133         2674 my $preparer = $self->argument_preparer;
257            
258 133         369 return $self->$preparer(@_);
259             }
260              
261             ### PUBLIC INSTANCE METHOD ###
262             #
263             # Check the metadata that was passed in the Ext.Direct request
264             # to make sure it conforms to the API declared by this Method.
265             #
266             # This method is similar to check_method_arguments and operates
267             # the same way; it is kept separate for easier overriding
268             # in subclasses.
269             #
270              
271             sub check_method_metadata {
272 66     66 0 482 my $self = shift;
273            
274 66 100       1527 return 1 unless $self->metadata;
275            
276 29         524 my $checker = $self->metadata_checker;
277            
278 29         76 return $self->$checker(@_);
279             }
280              
281             ### PUBLIC INSTANCE METHOD ###
282             #
283             # Prepare the metadata to be passed to the called Method,
284             # in accordance with Method's specification.
285             #
286             # This method works similarly to prepare_method_arguments
287             # and is kept separate for easier overriding in subclasses.
288             #
289              
290             sub prepare_method_metadata {
291 63     63 0 126 my $self = shift;
292            
293 63 50       1141 return unless $self->metadata;
294            
295 63         1102 my $preparer = $self->metadata_preparer;
296            
297 63         148 return $self->$preparer(@_);
298             }
299              
300             ### PUBLIC INSTANCE METHOD ###
301             #
302             # Check the arguments for a pollHandler
303             #
304              
305             sub check_pollHandler_arguments {
306             # pollHandlers are not supposed to receive any arguments
307 2     2 1 4 return 1;
308             }
309              
310             ### PUBLIC INSTANCE METHOD ###
311             #
312             # Prepare the arguments for a pollHandler
313             #
314              
315             sub prepare_pollHandler_arguments {
316 13     13 1 29 my ($self, %arg) = @_;
317            
318 13         15 my @actual_arg = ();
319            
320             # When called from the client, env_arg should not be defined
321 13 100       219 if ( defined (my $env_arg = +$self->env_arg) ) {
322 2 50       6 push @actual_arg, $arg{env} if defined $arg{env};
323             }
324            
325 13 100       42 return wantarray ? @actual_arg : [ @actual_arg ];
326             }
327              
328             ### PUBLIC INSTANCE METHOD ###
329             #
330             # Check the arguments for a formHandler
331             #
332              
333             sub check_formHandler_arguments {
334 14     14 1 25 my ($self, $arg, $meta) = @_;
335            
336             # Nothing to check here really except that it's a hashref
337 14 100       58 die sprintf "ExtDirect formHandler Method %s.%s expects named " .
338             "arguments in hashref\n", $self->action, $self->name
339             unless 'HASH' eq ref $arg;
340            
341 13         30 return 1;
342             }
343              
344             ### PUBLIC INSTANCE METHOD ###
345             #
346             # Prepare the arguments for a formHandler
347             #
348              
349             my @std_params = qw/action method extAction extMethod
350             extType extTID extUpload _uploads/;
351              
352             sub prepare_formHandler_arguments {
353 27     27 1 73 my ($self, %arg) = @_;
354            
355 27         39 my $env = $arg{env};
356 27         26 my $input = $arg{input};
357 27         27 my $upload = $arg{upload};
358            
359             # Data should be a hashref here
360 27         97 my %data = %$input;
361              
362             # Ensure there are no runaway ExtDirect form parameters
363 27         83 delete @data{ @std_params };
364            
365 27         569 my $upload_arg = $self->upload_arg;
366              
367             # Add uploads if there are any
368 27 100       74 $data{ $upload_arg } = $upload if defined $upload;
369            
370 27 100       513 if ( defined (my $env_arg = $self->env_arg) ) {
371 10         13 $data{ $env_arg } = $env;
372             };
373            
374 27         491 my $meta_def = $self->metadata;
375            
376 27 100 66     90 if ( $meta_def && defined (my $meta_arg = $meta_def->{arg}) ) {
377 8         26 my $meta = $self->prepare_method_metadata(%arg);
378 8 50       23 $data{ $meta_arg } = $meta if defined $meta;
379            
380             # Form handlers receive the input hash almost unimpeded;
381             # if $meta_arg value is not default 'metadata' the arguments
382             # will include two copies of metadata. We don't want that.
383 8 100       46 delete $data{metadata} unless $meta_arg eq 'metadata';
384             }
385            
386             # Preparers are called in list context on the server side,
387             # where params can be decoded if configured so; the client
388             # will send all form fields JSON encoded anyway so no special
389             # handling required for it.
390 27 100       43 if ( wantarray ) {
391 20 100       17 for my $param ( @{ $self->decode_params || [] } ) {
  20         396  
392             # This check is necessary because inclusion in decode_params
393             # does not make the field a mandatory argument!
394 22 100       53 if ( exists $data{$param} ) {
395 2         4 my $value = delete $data{$param};
396            
397 2 50       8 if ( defined $value ) {
398             # If JSON throws an exception we will rethrow it
399             # after cleaning up
400 2         4 $value = eval { JSON::from_json($value) };
  2         10  
401            
402 2 50       57 die RPC::ExtDirect::Util::clean_error_message($@)
403             if $@;
404             }
405            
406 2         4 $data{$param} = $value;
407             }
408             }
409            
410 20         114 return %data;
411             }
412             else {
413 7         31 return { %data };
414             }
415 0 0       0 return wantarray ? %data : { %data };
416             }
417              
418             ### PUBLIC INSTANCE METHOD ###
419             #
420             # Check the arguments for a Method with named parameters.
421             #
422             # Note that it does not matter if the Method expects its
423             # arguments to be strictly conforming to the declaration
424             # or not; in case of !strict the listed parameters are
425             # still mandatory. In fact !strict only means that
426             # non-declared parameters are not dropped.
427             #
428              
429             sub check_named_arguments {
430 28     28 1 34 my ($self, $arg) = @_;
431            
432 28 100       88 die sprintf "ExtDirect Method %s.%s expects named arguments ".
433             "in hashref\n", $self->action, $self->name
434             unless 'HASH' eq ref $arg;
435            
436 27         27 my @params = @{ $self->params };
  27         501  
437            
438 27 100       40 my @missing = map { !exists $arg->{$_} ? $_ : () } @params;
  38         71  
439            
440 27 100       147 die sprintf "ExtDirect Method %s.%s requires the following ".
441             "parameters: '%s'; these are missing: '%s'\n",
442             $self->action, $self->name,
443             join(', ', @params), join(', ', @missing)
444             if @missing;
445            
446 21         48 return 1;
447             }
448              
449             ### PUBLIC INSTANCE METHOD ###
450             #
451             # Check the metadata for Methods that expect it by-name
452             #
453              
454             sub check_named_metadata {
455 13     13 1 16 my ($self, $meta) = @_;
456            
457 13 100       67 die sprintf "ExtDirect Method %s.%s expects metadata key/value ".
458             "pairs in hashref\n", $self->action, $self->name
459             unless 'HASH' eq ref $meta;
460            
461 11         197 my $meta_def = $self->metadata;
462 11         11 my @meta_params = @{ $meta_def->{params} };
  11         21  
463            
464 11 100       16 my @missing = map { !exists $meta->{$_} ? $_ : () } @meta_params;
  5         14  
465            
466 11 100       55 die sprintf "ExtDirect Method %s.%s requires the following ".
467             "metadata keys: '%s'; these are missing: '%s'\n",
468             $self->action, $self->name,
469             join(', ', @meta_params), join(', ', @missing)
470             if @missing;
471            
472 9         19 return 1;
473             }
474              
475             ### PUBLIC INSTANCE METHOD ###
476             #
477             # Prepare the arguments for a Method with named parameters
478             #
479              
480             sub prepare_named_arguments {
481 27     27 1 65 my ($self, %arg) = @_;
482            
483 27         38 my $env = $arg{env};
484 27         23 my $input = $arg{input};
485              
486 27         23 my %actual_arg;
487            
488 27         486 my $strict = $self->strict;
489 27 100       64 $strict = 1 unless defined $strict;
490            
491 27 100       39 if ( $strict ) {
492 10         13 my @names = @{ $self->params };
  10         171  
493            
494 10         34 @actual_arg{ @names } = @$input{ @names };
495             }
496             else {
497 17         48 %actual_arg = %$input;
498             }
499            
500 27 100       473 if ( defined (my $env_arg = $self->env_arg) ) {
501 6         10 $actual_arg{ $env_arg } = $env;
502             }
503              
504 27         481 my $meta_def = $self->metadata;
505            
506 27 100 66     90 if ( $meta_def && defined (my $meta_arg = $meta_def->{arg}) ) {
507 10         32 my $meta = $self->prepare_method_metadata(%arg);
508 10 50       29 $actual_arg{ $meta_arg } = $meta if defined $meta;
509             }
510              
511 27 100       143 return wantarray ? %actual_arg : { %actual_arg };
512             }
513              
514             ### PUBLIC INSTANCE METHOD ###
515             #
516             # Prepare the metadata for Methods that expect it by-name
517             #
518              
519             sub prepare_named_metadata {
520 19     19 0 32 my ($self, %arg) = @_;
521            
522 19         331 my $meta_def = $self->metadata;
523 19         24 my $meta_input = $arg{metadata};
524            
525 19 100       35 return unless $meta_input;
526            
527 18         19 my %meta;
528            
529 18         20 my $strict = $meta_def->{strict};
530 18 100       29 $strict = 1 unless defined $strict;
531            
532 18 100       26 if ( $strict ) {
533 9         9 my @params = @{ $meta_def->{params} };
  9         17  
534            
535 9         21 @meta{ @params } = @$meta_input{ @params };
536             }
537             else {
538 9         27 %meta = %$meta_input;
539             }
540            
541 18         43 return \%meta;
542             }
543              
544             ### PUBLIC INSTANCE METHOD ###
545             #
546             # Check the arguments for a Method with ordered parameters
547             #
548              
549             sub check_ordered_arguments {
550 38     38 1 40 my ($self, $input) = @_;
551            
552 38         665 my $want_len = $self->len;
553            
554             # Historically Ext.Direct on the JavaScript client side sent null value
555             # instead of empty array for ordered Methods that accept 0 arguments.
556 38 100 100     182 die sprintf "ExtDirect Method %s.%s expects ordered arguments " .
557             "in arrayref\n", $self->action, $self->name
558             if $want_len > 0 && 'ARRAY' ne ref $input;
559              
560 37 100       69 my $have_len = $want_len > 0 ? @$input : 0;
561            
562 37 100       138 die sprintf "ExtDirect Method %s.%s requires %d argument(s) ".
563             "but only %d are provided\n",
564             $self->action, $self->name, $want_len, $have_len
565             unless $have_len >= $want_len;
566            
567 33         65 return 1;
568             }
569              
570             ### PUBLIC INSTANCE METHOD ###
571             #
572             # Check the metadata for Methods that expect it by-position
573             #
574              
575             sub check_ordered_metadata {
576 16     16 1 16 my ($self, $meta) = @_;
577            
578 16 100       56 die sprintf "ExtDirect Method %s.%s expects metadata in arrayref\n",
579             $self->action, $self->name
580             unless 'ARRAY' eq ref $meta;
581            
582 15         267 my $meta_def = $self->metadata;
583 15         20 my $want_len = $meta_def->{len};
584 15         16 my $have_len = @$meta;
585            
586 15 100       61 die sprintf "ExtDirect Method %s.%s requires %d metadata ".
587             "value(s) but only %d are provided\n",
588             $self->action, $self->name, $want_len, $have_len
589             unless $have_len >= $want_len;
590            
591 13         25 return 1;
592             }
593              
594             ### PUBLIC INSTANCE METHOD ###
595             #
596             # Prepare the arguments for a Method with ordered parameters
597             #
598              
599             sub prepare_ordered_arguments {
600 66     66 1 143 my ($self, %arg) = @_;
601            
602 66         66 my $env = $arg{env};
603 66         56 my $input = $arg{input};
604            
605 66         48 my @actual_arg;
606            
607             # For Methods with 0 accepted arguments, input may be either
608             # an empty array from RPC::ExtDirect::Client, or undef from
609             # the JavaScript client. Hysterical raisins are hysterical,
610             # so we have to account for that.
611 66 100       1164 if ( my $want_len = $self->len ) {
612             # Input is by reference! Unpack to avoid changing it.
613 43         76 my @data = @$input;
614 43         81 @actual_arg = splice @data, 0, $want_len;
615             }
616            
617 27     27   141 no warnings; ## no critic
  27         30  
  27         11894  
618            
619 66 100       1189 if ( defined (my $env_arg = +$self->env_arg) ) {
620             # Splicing an empty array at negative subscript will result
621             # in a fatal error; we need to guard against that.
622 30 100 100     84 $env_arg = 0 if $env_arg < 0 && -$env_arg > @actual_arg;
623            
624 30         54 splice @actual_arg, $env_arg, 0, $env;
625             }
626            
627 66         1161 my $meta_def = $self->metadata;
628            
629 66 100 66     206 if ( $meta_def && defined (my $meta_arg = +$meta_def->{arg}) ) {
630 34         88 my $meta = $self->prepare_method_metadata(%arg);
631            
632 34 50       68 if ( defined $meta ) {
633 34 100 100     91 $meta_arg = 0 if $meta_arg < 0 && -$meta_arg > @actual_arg;
634            
635 34         50 splice @actual_arg, $meta_arg, 0, $meta;
636             }
637             }
638              
639 66 100       263 return wantarray ? @actual_arg : [ @actual_arg ];
640             }
641              
642             ### PUBLIC INSTANCE METHOD ###
643             #
644             # Prepare the metadata for Methods that expect it by-position
645             #
646              
647             sub prepare_ordered_metadata {
648 44     44 1 77 my ($self, %arg) = @_;
649            
650 44         753 my $meta_def = $self->metadata;
651 44         41 my $meta_input = $arg{metadata};
652            
653 44 50       68 return unless $meta_input;
654            
655             # Copy array elements to avoid mutating the arrayref
656 44         77 my @meta_data = @$meta_input;
657 44         75 my @meta_output = splice @meta_data, 0, $meta_def->{len};
658            
659 44         97 return \@meta_output;
660             }
661              
662             ### PUBLIC INSTANCE METHOD ###
663             #
664             # Read-only getter for backward compatibility
665             #
666              
667 0     0 0 0 sub is_formhandler { shift->formHandler }
668              
669             ### PUBLIC INSTANCE METHODS ###
670             #
671             # Simple read-write accessors
672             #
673              
674             my $accessors = [qw/
675             config
676             action
677             name
678             params
679             len
680             metadata
681             formHandler
682             pollHandler
683             is_ordered
684             is_named
685             strict
686             package
687             env_arg
688             upload_arg
689             meta_arg
690             argument_checker
691             argument_preparer
692             metadata_checker
693             metadata_preparer
694             decode_params
695             /,
696             __PACKAGE__->HOOK_TYPES,
697             ];
698              
699             RPC::ExtDirect::Util::Accessor::mk_accessors(
700             simple => $accessors,
701             );
702              
703             ############## PRIVATE METHODS BELOW ##############
704              
705             ### PRIVATE INSTANCE METHOD ###
706             #
707             # Parse metadata definition and run sanity checks.
708             #
709             # This method has side effects on $arg!
710             #
711              
712             sub _parse_metadata {
713 101     101   120 my ($self, $arg) = @_;
714            
715 101         112 my $meta = delete $arg->{metadata};
716            
717 101 50       209 if ( 'HASH' eq ref $meta ) {
718 101         93 my $meta_def = {};
719              
720 101 100       166 if ( defined (my $len = $meta->{len}) ) {
721             # Metadata is optional so ordered with 0 arguments
722             # does not make any sense
723             die [
724             sprintf "ExtDirect Method %s.%s cannot accept ".
725             "0 arguments for ordered metadata",
726             $arg->{action}, $arg->{name}
727 66 100       151 ]
728             unless $len > 0;
729            
730 64         97 $meta_def->{len} = $len;
731            
732 64         68 $arg->{metadata_checker} = 'check_ordered_metadata';
733 64         66 $arg->{metadata_preparer} = 'prepare_ordered_metadata';
734             }
735             else {
736 35   100     84 my $params = $meta->{params} || [];
737              
738             # Same as with main arguments; force !strict if named metadata
739             # has empty params
740             my $strict = !@$params ? !1
741             : defined $meta->{strict} ? $meta->{strict}
742             : undef
743 35 100       72 ;
    100          
744            
745             # !strict with no params might be a typo or something;
746             # worth a warning in that case
747             carp sprintf "ExtDirect Method %s.%s implies strict ".
748             "argument checking for named metadata, ".
749             "but no parameter names are specified.",
750             $arg->{action}, $arg->{name}
751 35 100 66     921 if !@$params && (!defined $meta->{strict} || $meta->{strict});
      66        
752              
753 35         473 $meta_def->{strict} = $strict;
754 35         33 $meta_def->{params} = $params;
755            
756 35         36 $arg->{metadata_checker} = 'check_named_metadata';
757 35         40 $arg->{metadata_preparer} = 'prepare_named_metadata';
758             }
759            
760 99         148 $meta_def->{arg} = $self->_get_meta_arg($meta, $arg);
761              
762 98         146 $arg->{metadata} = $meta_def;
763             }
764             }
765              
766             ### PRIVATE INSTANCE METHOD ###
767             #
768             # Check that the metadata has valid argument name or position
769             # to be applied to the called Method.
770             #
771             # This code is split from the method above so that we could
772             # override it in the Client which doesn't need to run the same
773             # checks as the server side.
774             #
775              
776             sub _get_meta_arg {
777 99     99   93 my ($self, $meta, $arg) = @_;
778            
779 99         76 my $meta_arg = $meta->{arg};
780            
781 99 100       1902 if ( $self->is_ordered ) {
782             # There is no way to splice new elements at the end of array
783             # without knowing array length. Splicing at negative subscripts
784             # will not do what is expected, and I don't see a sane default
785             # otherwise. So insist on having the arg defined.
786             die [
787             sprintf "ExtDirect Method %s.%s cannot accept ".
788             "ordered metadata with no arg position specified",
789             $arg->{action}, $arg->{name}
790 50 100       100 ]
791             unless defined $meta_arg;
792             }
793             else {
794 49 100       74 $meta_arg = defined $meta_arg ? $meta_arg : 'metadata';
795             }
796            
797 98         157 return $meta_arg;
798             }
799              
800             1;