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   899 use strict;
  27         29  
  27         602  
4 27     27   81 use warnings;
  27         25  
  27         540  
5 27     27   86 no warnings 'uninitialized'; ## no critic
  27         26  
  27         638  
6              
7 27     27   79 use Carp;
  27         26  
  27         1247  
8 27     27   96 use JSON;
  27         24  
  27         137  
9              
10 27     27   2686 use RPC::ExtDirect::Config;
  27         28  
  27         432  
11 27     27   79 use RPC::ExtDirect::Util ();
  27         32  
  27         341  
12 27     27   76 use RPC::ExtDirect::Util::Accessor;
  27         27  
  27         45488  
13              
14             ### PUBLIC CLASS METHOD (ACCESSOR) ###
15             #
16             # Return the hook types supported by this Method class
17             #
18              
19 551     551 1 1172 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 41448 my ($class, %arg) = @_;
28            
29 457         460 my $config = $arg{config};
30 457         9272 my $hook_class = $config->api_hook_class;
31            
32 457         455 my $pollHandler = $arg{pollHandler};
33 457         411 my $formHandler = $arg{formHandler};
34            
35             my $is_ordered
36 457   66     1765 = defined $arg{len} && !$pollHandler && !$formHandler;
37            
38 457   100     1547 my $is_named
39             = !$pollHandler && !$formHandler && !$is_ordered;
40            
41 457 100       991 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         2078 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       748 if ( $is_named ) {
58 121   100     380 $arg{params} = $arg{params} || []; # Better safe than sorry
59 121 100       117 $arg{strict} = !1 unless @{ $arg{params} };
  121         397  
60             }
61            
62 457 100       700 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         270 $self->_parse_metadata(\%arg);
66             }
67            
68             # We avoid hard binding on the hook class
69 454         17970 eval "require $hook_class";
70            
71 454         831 my %hooks;
72            
73 454         911 for my $type ( $class->HOOK_TYPES ) {
74 1362         1192 my $hook = delete $arg{ $type };
75            
76 1362 100       2120 $hooks{ $type } = $hook_class->new( type => $type, code => $hook )
77             if $hook;
78             }
79            
80 454         2287 @$self{ keys %arg } = values %arg;
81 454         726 @$self{ keys %hooks } = values %hooks;
82            
83 454         1663 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 156 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       2605 return if $self->pollHandler;
101            
102 149         2577 my $name = $self->name;
103              
104 149         103 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       2561 if ( $self->formHandler ) {
    100          
109 20         51 $def = { name => $name, formHandler => \1 };
110             }
111              
112             # Ordinary method with positioned arguments
113             elsif ( $self->is_ordered ) {
114 90         1532 $def = { name => $name, len => $self->len + 0 }
115             }
116              
117             # Ordinary method with named arguments
118             else {
119 39         653 my $strict = $self->strict;
120              
121 39 50 50     675 $def = {
    100          
122             name => $name,
123             params => $self->params || [],
124             defined $strict ? (strict => ($strict ? \1 : \0)) : (),
125             };
126             }
127              
128 149 100       2642 if ( my $meta = $self->metadata ) {
129 43         49 $def->{metadata} = {};
130              
131 43 100       59 if ( $meta->{len} ) {
132             $def->{metadata} = {
133             len => $meta->{len},
134 32         56 };
135             }
136             else {
137 11         14 my $strict = $meta->{strict};
138              
139             $def->{metadata} = {
140             params => $meta->{params},
141 11 50       31 defined $strict ? (strict => ($strict ? \1 : \0)) : (),
    100          
142             };
143             }
144             }
145              
146 149         270 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 69 my ($self) = @_;
157            
158 70         51 my %attrs;
159            
160 70         1249 $attrs{package} = $self->package;
161 70         1221 $attrs{method} = $self->name;
162 70         1250 $attrs{param_names} = $self->params;
163 70         1191 $attrs{param_no} = $self->len;
164 70   100     1182 $attrs{pollHandler} = $self->pollHandler || 0;
165 70   100     1186 $attrs{formHandler} = $self->formHandler || 0;
166 70 100       833 $attrs{param_no} = undef if $attrs{formHandler};
167            
168 70         117 for my $type ( $self->HOOK_TYPES ) {
169 210         3664 my $hook = $self->$type;
170            
171 210 100       621 $attrs{$type} = $hook->code if $hook;
172             }
173            
174 70         372 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 105 my ($self) = @_;
184            
185 109         1936 my $package = $self->package;
186 109         1834 my $name = $self->name;
187            
188 109         651 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 109 my ($self, %args) = @_;
202            
203 54         56 my $arg = $args{arg};
204 54         958 my $package = $self->package;
205 54         931 my $name = $self->name;
206            
207             # pollHandler methods should always be called in list context
208 54 100       949 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 210 my $self = shift;
231            
232 82         1508 my $checker = $self->argument_checker;
233            
234 82         215 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 18897 my $self = shift;
255            
256 133         2546 my $preparer = $self->argument_preparer;
257            
258 133         335 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 475 my $self = shift;
273            
274 66 100       1247 return 1 unless $self->metadata;
275            
276 29         511 my $checker = $self->metadata_checker;
277            
278 29         71 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       1882 return unless $self->metadata;
294            
295 63         1051 my $preparer = $self->metadata_preparer;
296            
297 63         146 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 30 my ($self, %arg) = @_;
317            
318 13         14 my @actual_arg = ();
319            
320             # When called from the client, env_arg should not be defined
321 13 100       241 if ( defined (my $env_arg = +$self->env_arg) ) {
322 2 50       7 push @actual_arg, $arg{env} if defined $arg{env};
323             }
324            
325 13 100       45 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       54 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         25 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 65 my ($self, %arg) = @_;
354            
355 27         29 my $env = $arg{env};
356 27         27 my $input = $arg{input};
357 27         22 my $upload = $arg{upload};
358            
359             # Data should be a hashref here
360 27         89 my %data = %$input;
361              
362             # Ensure there are no runaway ExtDirect form parameters
363 27         68 delete @data{ @std_params };
364            
365 27         509 my $upload_arg = $self->upload_arg;
366              
367             # Add uploads if there are any
368 27 100       63 $data{ $upload_arg } = $upload if defined $upload;
369            
370 27 100       461 if ( defined (my $env_arg = $self->env_arg) ) {
371 10         14 $data{ $env_arg } = $env;
372             };
373            
374 27         470 my $meta_def = $self->metadata;
375            
376 27 100 66     80 if ( $meta_def && defined (my $meta_arg = $meta_def->{arg}) ) {
377 8         18 my $meta = $self->prepare_method_metadata(%arg);
378 8 50       20 $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       44 if ( wantarray ) {
391 20 100       15 for my $param ( @{ $self->decode_params || [] } ) {
  20         354  
392             # This check is necessary because inclusion in decode_params
393             # does not make the field a mandatory argument!
394 22 100       43 if ( exists $data{$param} ) {
395 2         5 my $value = delete $data{$param};
396            
397 2 50       6 if ( defined $value ) {
398             # If JSON throws an exception we will rethrow it
399             # after cleaning up
400 2         3 $value = eval { JSON::from_json($value) };
  2         6  
401            
402 2 50       54 die RPC::ExtDirect::Util::clean_error_message($@)
403             if $@;
404             }
405            
406 2         4 $data{$param} = $value;
407             }
408             }
409            
410 20         98 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 30 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         24 my @params = @{ $self->params };
  27         484  
437            
438 27 100       36 my @missing = map { !exists $arg->{$_} ? $_ : () } @params;
  38         72  
439            
440 27 100       151 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         46 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 15 my ($self, $meta) = @_;
456            
457 13 100       64 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         186 my $meta_def = $self->metadata;
462 11         11 my @meta_params = @{ $meta_def->{params} };
  11         23  
463            
464 11 100       15 my @missing = map { !exists $meta->{$_} ? $_ : () } @meta_params;
  5         127  
465            
466 11 100       60 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         20 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 64 my ($self, %arg) = @_;
482            
483 27         38 my $env = $arg{env};
484 27         30 my $input = $arg{input};
485              
486 27         27 my %actual_arg;
487            
488 27         502 my $strict = $self->strict;
489 27 100       55 $strict = 1 unless defined $strict;
490            
491 27 100       38 if ( $strict ) {
492 10         13 my @names = @{ $self->params };
  10         180  
493            
494 10         37 @actual_arg{ @names } = @$input{ @names };
495             }
496             else {
497 17         48 %actual_arg = %$input;
498             }
499            
500 27 100       470 if ( defined (my $env_arg = $self->env_arg) ) {
501 6         8 $actual_arg{ $env_arg } = $env;
502             }
503              
504 27         487 my $meta_def = $self->metadata;
505            
506 27 100 66     105 if ( $meta_def && defined (my $meta_arg = $meta_def->{arg}) ) {
507 10         25 my $meta = $self->prepare_method_metadata(%arg);
508 10 50       26 $actual_arg{ $meta_arg } = $meta if defined $meta;
509             }
510              
511 27 100       164 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 34 my ($self, %arg) = @_;
521            
522 19         324 my $meta_def = $self->metadata;
523 19         32 my $meta_input = $arg{metadata};
524            
525 19 100       33 return unless $meta_input;
526            
527 18         15 my %meta;
528            
529 18         20 my $strict = $meta_def->{strict};
530 18 100       32 $strict = 1 unless defined $strict;
531            
532 18 100       21 if ( $strict ) {
533 9         8 my @params = @{ $meta_def->{params} };
  9         18  
534            
535 9         23 @meta{ @params } = @$meta_input{ @params };
536             }
537             else {
538 9         25 %meta = %$meta_input;
539             }
540            
541 18         39 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 45 my ($self, $input) = @_;
551            
552 38         681 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     191 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       67 my $have_len = $want_len > 0 ? @$input : 0;
561            
562 37 100       151 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         66 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 21 my ($self, $meta) = @_;
577            
578 16 100       48 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         248 my $meta_def = $self->metadata;
583 15         20 my $want_len = $meta_def->{len};
584 15         14 my $have_len = @$meta;
585            
586 15 100       63 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 153 my ($self, %arg) = @_;
601            
602 66         74 my $env = $arg{env};
603 66         54 my $input = $arg{input};
604            
605 66         56 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       1137 if ( my $want_len = $self->len ) {
612             # Input is by reference! Unpack to avoid changing it.
613 43         76 my @data = @$input;
614 43         89 @actual_arg = splice @data, 0, $want_len;
615             }
616            
617 27     27   143 no warnings; ## no critic
  27         36  
  27         12690  
618            
619 66 100       1147 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         47 splice @actual_arg, $env_arg, 0, $env;
625             }
626            
627 66         1110 my $meta_def = $self->metadata;
628            
629 66 100 66     228 if ( $meta_def && defined (my $meta_arg = +$meta_def->{arg}) ) {
630 34         71 my $meta = $self->prepare_method_metadata(%arg);
631            
632 34 50       57 if ( defined $meta ) {
633 34 100 100     86 $meta_arg = 0 if $meta_arg < 0 && -$meta_arg > @actual_arg;
634            
635 34         40 splice @actual_arg, $meta_arg, 0, $meta;
636             }
637             }
638              
639 66 100       258 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 73 my ($self, %arg) = @_;
649            
650 44         706 my $meta_def = $self->metadata;
651 44         46 my $meta_input = $arg{metadata};
652            
653 44 50       60 return unless $meta_input;
654            
655             # Copy array elements to avoid mutating the arrayref
656 44         68 my @meta_data = @$meta_input;
657 44         66 my @meta_output = splice @meta_data, 0, $meta_def->{len};
658            
659 44         91 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   102 my ($self, $arg) = @_;
714            
715 101         112 my $meta = delete $arg->{metadata};
716            
717 101 50       210 if ( 'HASH' eq ref $meta ) {
718 101         99 my $meta_def = {};
719              
720 101 100       134 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       150 ]
728             unless $len > 0;
729            
730 64         76 $meta_def->{len} = $len;
731            
732 64         67 $arg->{metadata_checker} = 'check_ordered_metadata';
733 64         71 $arg->{metadata_preparer} = 'prepare_ordered_metadata';
734             }
735             else {
736 35   100     83 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       81 ;
    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     877 if !@$params && (!defined $meta->{strict} || $meta->{strict});
      66        
752              
753 35         496 $meta_def->{strict} = $strict;
754 35         33 $meta_def->{params} = $params;
755            
756 35         36 $arg->{metadata_checker} = 'check_named_metadata';
757 35         41 $arg->{metadata_preparer} = 'prepare_named_metadata';
758             }
759            
760 99         146 $meta_def->{arg} = $self->_get_meta_arg($meta, $arg);
761              
762 98         154 $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   85 my ($self, $meta, $arg) = @_;
778            
779 99         85 my $meta_arg = $meta->{arg};
780            
781 99 100       1956 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       91 ]
791             unless defined $meta_arg;
792             }
793             else {
794 49 100       84 $meta_arg = defined $meta_arg ? $meta_arg : 'metadata';
795             }
796            
797 98         149 return $meta_arg;
798             }
799              
800             1;