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   974 use strict;
  27         34  
  27         706  
4 27     27   87 use warnings;
  27         28  
  27         574  
5 27     27   89 no warnings 'uninitialized'; ## no critic
  27         24  
  27         605  
6              
7 27     27   76 use Carp;
  27         23  
  27         1207  
8 27     27   100 use JSON;
  27         32  
  27         140  
9              
10 27     27   2806 use RPC::ExtDirect::Config;
  27         31  
  27         442  
11 27     27   78 use RPC::ExtDirect::Util ();
  27         32  
  27         338  
12 27     27   75 use RPC::ExtDirect::Util::Accessor;
  27         25  
  27         45553  
13              
14             ### PUBLIC CLASS METHOD (ACCESSOR) ###
15             #
16             # Return the hook types supported by this Method class
17             #
18              
19 551     551 1 1143 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 40881 my ($class, %arg) = @_;
28            
29 457         441 my $config = $arg{config};
30 457         8687 my $hook_class = $config->api_hook_class;
31            
32 457         456 my $pollHandler = $arg{pollHandler};
33 457         392 my $formHandler = $arg{formHandler};
34            
35             my $is_ordered
36 457   66     1695 = defined $arg{len} && !$pollHandler && !$formHandler;
37            
38 457   100     1523 my $is_named
39             = !$pollHandler && !$formHandler && !$is_ordered;
40            
41 457 100       946 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         2057 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       742 if ( $is_named ) {
58 121   100     379 $arg{params} = $arg{params} || []; # Better safe than sorry
59 121 100       105 $arg{strict} = !1 unless @{ $arg{params} };
  121         384  
60             }
61            
62 457 100       666 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         295 $self->_parse_metadata(\%arg);
66             }
67            
68             # We avoid hard binding on the hook class
69 454         17493 eval "require $hook_class";
70            
71 454         784 my %hooks;
72            
73 454         887 for my $type ( $class->HOOK_TYPES ) {
74 1362         1187 my $hook = delete $arg{ $type };
75            
76 1362 100       2151 $hooks{ $type } = $hook_class->new( type => $type, code => $hook )
77             if $hook;
78             }
79            
80 454         2376 @$self{ keys %arg } = values %arg;
81 454         702 @$self{ keys %hooks } = values %hooks;
82            
83 454         1650 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 165 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       2658 return if $self->pollHandler;
101            
102 149         2548 my $name = $self->name;
103              
104 149         114 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       2490 if ( $self->formHandler ) {
    100          
109 20         44 $def = { name => $name, formHandler => \1 };
110             }
111              
112             # Ordinary method with positioned arguments
113             elsif ( $self->is_ordered ) {
114 90         1541 $def = { name => $name, len => $self->len + 0 }
115             }
116              
117             # Ordinary method with named arguments
118             else {
119 39         660 my $strict = $self->strict;
120              
121 39 50 50     679 $def = {
    100          
122             name => $name,
123             params => $self->params || [],
124             defined $strict ? (strict => ($strict ? \1 : \0)) : (),
125             };
126             }
127              
128 149 100       2644 if ( my $meta = $self->metadata ) {
129 43         54 $def->{metadata} = {};
130              
131 43 100       63 if ( $meta->{len} ) {
132             $def->{metadata} = {
133             len => $meta->{len},
134 32         54 };
135             }
136             else {
137 11         14 my $strict = $meta->{strict};
138              
139             $def->{metadata} = {
140             params => $meta->{params},
141 11 50       47 defined $strict ? (strict => ($strict ? \1 : \0)) : (),
    100          
142             };
143             }
144             }
145              
146 149         274 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 74 my ($self) = @_;
157            
158 70         61 my %attrs;
159            
160 70         1260 $attrs{package} = $self->package;
161 70         1183 $attrs{method} = $self->name;
162 70         1228 $attrs{param_names} = $self->params;
163 70         1169 $attrs{param_no} = $self->len;
164 70   100     1233 $attrs{pollHandler} = $self->pollHandler || 0;
165 70   100     1917 $attrs{formHandler} = $self->formHandler || 0;
166 70 100       152 $attrs{param_no} = undef if $attrs{formHandler};
167            
168 70         106 for my $type ( $self->HOOK_TYPES ) {
169 210         3583 my $hook = $self->$type;
170            
171 210 100       623 $attrs{$type} = $hook->code if $hook;
172             }
173            
174 70         393 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 114 my ($self) = @_;
184            
185 109         1956 my $package = $self->package;
186 109         1828 my $name = $self->name;
187            
188 109         633 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 104 my ($self, %args) = @_;
202            
203 54         60 my $arg = $args{arg};
204 54         939 my $package = $self->package;
205 54         919 my $name = $self->name;
206            
207             # pollHandler methods should always be called in list context
208 54 100       889 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 208 my $self = shift;
231            
232 82         1542 my $checker = $self->argument_checker;
233            
234 82         210 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 18220 my $self = shift;
255            
256 133         2588 my $preparer = $self->argument_preparer;
257            
258 133         350 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 472 my $self = shift;
273            
274 66 100       1172 return 1 unless $self->metadata;
275            
276 29         496 my $checker = $self->metadata_checker;
277            
278 29         68 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 139 my $self = shift;
292            
293 63 50       1156 return unless $self->metadata;
294            
295 63         1152 my $preparer = $self->metadata_preparer;
296            
297 63         158 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         19 my @actual_arg = ();
319            
320             # When called from the client, env_arg should not be defined
321 13 100       239 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       46 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 18 my ($self, $arg, $meta) = @_;
335            
336             # Nothing to check here really except that it's a hashref
337 14 100       49 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         23 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 62 my ($self, %arg) = @_;
354            
355 27         33 my $env = $arg{env};
356 27         23 my $input = $arg{input};
357 27         18 my $upload = $arg{upload};
358            
359             # Data should be a hashref here
360 27         92 my %data = %$input;
361              
362             # Ensure there are no runaway ExtDirect form parameters
363 27         70 delete @data{ @std_params };
364            
365 27         477 my $upload_arg = $self->upload_arg;
366              
367             # Add uploads if there are any
368 27 100       61 $data{ $upload_arg } = $upload if defined $upload;
369            
370 27 100       446 if ( defined (my $env_arg = $self->env_arg) ) {
371 10         17 $data{ $env_arg } = $env;
372             };
373            
374 27         451 my $meta_def = $self->metadata;
375            
376 27 100 66     107 if ( $meta_def && defined (my $meta_arg = $meta_def->{arg}) ) {
377 8         21 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       19 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       40 if ( wantarray ) {
391 20 100       17 for my $param ( @{ $self->decode_params || [] } ) {
  20         347  
392             # This check is necessary because inclusion in decode_params
393             # does not make the field a mandatory argument!
394 22 100       42 if ( exists $data{$param} ) {
395 2         4 my $value = delete $data{$param};
396            
397 2 50       7 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         7  
401            
402 2 50       51 die RPC::ExtDirect::Util::clean_error_message($@)
403             if $@;
404             }
405            
406 2         4 $data{$param} = $value;
407             }
408             }
409            
410 20         97 return %data;
411             }
412             else {
413 7         30 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 33 my ($self, $arg) = @_;
431            
432 28 100       87 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         484  
437            
438 27 100       36 my @missing = map { !exists $arg->{$_} ? $_ : () } @params;
  38         68  
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         42 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 14 my ($self, $meta) = @_;
456            
457 13 100       62 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         177 my $meta_def = $self->metadata;
462 11         10 my @meta_params = @{ $meta_def->{params} };
  11         20  
463            
464 11 100       16 my @missing = map { !exists $meta->{$_} ? $_ : () } @meta_params;
  5         14  
465            
466 11 100       53 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         16 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 63 my ($self, %arg) = @_;
482            
483 27         37 my $env = $arg{env};
484 27         22 my $input = $arg{input};
485              
486 27         24 my %actual_arg;
487            
488 27         469 my $strict = $self->strict;
489 27 100       52 $strict = 1 unless defined $strict;
490            
491 27 100       38 if ( $strict ) {
492 10         12 my @names = @{ $self->params };
  10         185  
493            
494 10         33 @actual_arg{ @names } = @$input{ @names };
495             }
496             else {
497 17         44 %actual_arg = %$input;
498             }
499            
500 27 100       456 if ( defined (my $env_arg = $self->env_arg) ) {
501 6         9 $actual_arg{ $env_arg } = $env;
502             }
503              
504 27         460 my $meta_def = $self->metadata;
505            
506 27 100 66     86 if ( $meta_def && defined (my $meta_arg = $meta_def->{arg}) ) {
507 10         23 my $meta = $self->prepare_method_metadata(%arg);
508 10 50       31 $actual_arg{ $meta_arg } = $meta if defined $meta;
509             }
510              
511 27 100       137 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 36 my ($self, %arg) = @_;
521            
522 19         352 my $meta_def = $self->metadata;
523 19         20 my $meta_input = $arg{metadata};
524            
525 19 100       37 return unless $meta_input;
526            
527 18         15 my %meta;
528            
529 18         19 my $strict = $meta_def->{strict};
530 18 100       35 $strict = 1 unless defined $strict;
531            
532 18 100       24 if ( $strict ) {
533 9         10 my @params = @{ $meta_def->{params} };
  9         18  
534            
535 9         21 @meta{ @params } = @$meta_input{ @params };
536             }
537             else {
538 9         30 %meta = %$meta_input;
539             }
540            
541 18         47 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 41 my ($self, $input) = @_;
551            
552 38         664 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     181 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       62 my $have_len = $want_len > 0 ? @$input : 0;
561            
562 37 100       130 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         64 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 19 my ($self, $meta) = @_;
577            
578 16 100       52 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         258 my $meta_def = $self->metadata;
583 15         19 my $want_len = $meta_def->{len};
584 15         14 my $have_len = @$meta;
585            
586 15 100       62 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         23 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 155 my ($self, %arg) = @_;
601            
602 66         67 my $env = $arg{env};
603 66         62 my $input = $arg{input};
604            
605 66         52 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       1211 if ( my $want_len = $self->len ) {
612             # Input is by reference! Unpack to avoid changing it.
613 43         70 my @data = @$input;
614 43         120 @actual_arg = splice @data, 0, $want_len;
615             }
616            
617 27     27   138 no warnings; ## no critic
  27         33  
  27         12462  
618            
619 66 100       1158 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     79 $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         1092 my $meta_def = $self->metadata;
628            
629 66 100 66     208 if ( $meta_def && defined (my $meta_arg = +$meta_def->{arg}) ) {
630 34         80 my $meta = $self->prepare_method_metadata(%arg);
631            
632 34 50       62 if ( defined $meta ) {
633 34 100 100     87 $meta_arg = 0 if $meta_arg < 0 && -$meta_arg > @actual_arg;
634            
635 34         51 splice @actual_arg, $meta_arg, 0, $meta;
636             }
637             }
638              
639 66 100       244 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 70 my ($self, %arg) = @_;
649            
650 44         734 my $meta_def = $self->metadata;
651 44         43 my $meta_input = $arg{metadata};
652            
653 44 50       64 return unless $meta_input;
654            
655             # Copy array elements to avoid mutating the arrayref
656 44         68 my @meta_data = @$meta_input;
657 44         76 my @meta_output = splice @meta_data, 0, $meta_def->{len};
658            
659 44         96 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   93 my ($self, $arg) = @_;
714            
715 101         124 my $meta = delete $arg->{metadata};
716            
717 101 50       214 if ( 'HASH' eq ref $meta ) {
718 101         91 my $meta_def = {};
719              
720 101 100       165 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       146 ]
728             unless $len > 0;
729            
730 64         70 $meta_def->{len} = $len;
731            
732 64         68 $arg->{metadata_checker} = 'check_ordered_metadata';
733 64         74 $arg->{metadata_preparer} = 'prepare_ordered_metadata';
734             }
735             else {
736 35   100     93 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     1020 if !@$params && (!defined $meta->{strict} || $meta->{strict});
      66        
752              
753 35         562 $meta_def->{strict} = $strict;
754 35         39 $meta_def->{params} = $params;
755            
756 35         42 $arg->{metadata_checker} = 'check_named_metadata';
757 35         42 $arg->{metadata_preparer} = 'prepare_named_metadata';
758             }
759            
760 99         145 $meta_def->{arg} = $self->_get_meta_arg($meta, $arg);
761              
762 98         134 $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   92 my ($self, $meta, $arg) = @_;
778            
779 99         85 my $meta_arg = $meta->{arg};
780            
781 99 100       1950 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       89 ]
791             unless defined $meta_arg;
792             }
793             else {
794 49 100       94 $meta_arg = defined $meta_arg ? $meta_arg : 'metadata';
795             }
796            
797 98         159 return $meta_arg;
798             }
799              
800             1;