File Coverage

blib/lib/Smart/Args.pm
Criterion Covered Total %
statement 109 113 96.4
branch 50 58 86.2
condition 8 13 61.5
subroutine 11 11 100.0
pod 2 2 100.0
total 180 197 91.3


line stmt bran cond sub pod time code
1             package Smart::Args;
2 17     17   871377 use strict;
  17         24  
  17         456  
3 17     17   66 use warnings;
  17         37  
  17         389  
4 17     17   411 use 5.008001;
  17         52  
5             our $VERSION = '0.14';
6 17     17   63 use Exporter 'import';
  17         18  
  17         543  
7 17     17   6886 use PadWalker qw/var_name/;
  17         8930  
  17         1052  
8 17     17   94 use Carp ();
  17         21  
  17         245  
9 17     17   7657 use Mouse::Util::TypeConstraints ();
  17         215838  
  17         18068  
10              
11             *_get_isa_type_constraint = \&Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint;
12             *_get_does_type_constraint = \&Mouse::Util::TypeConstraints::find_or_create_does_type_constraint;
13              
14             our @EXPORT = qw/args args_pos/;
15              
16             our $VERBOSE = 1;
17              
18             my %is_invocant = map{ $_ => undef } qw($self $class);
19              
20             sub args {
21             {
22 46     46 1 29718 package DB;
23             # call of caller in DB package sets @DB::args,
24             # which requires list context, but we don't need return values
25 46         332 () = CORE::caller(1);
26             }
27              
28 46 50       150 if(@_) {
29 46   50     357 my $name = var_name(1, \$_[0]) || '';
30 46 100       145 if(exists $is_invocant{ $name }){ # seems method call
31 8         12 $_[0] = shift @DB::args; # set the invocant
32 8 100       18 if(defined $_[1]) { # has rule?
33 2         9 $name =~ s/^\$//;
34             # validate_pos($value, $exists, $name, $basic_rule, $used_ref)
35 2         6 $_[0] = _validate_by_rule($_[0], 1, $name, $_[1]);
36 1         2 shift;
37             }
38 7         7 shift;
39             }
40             }
41              
42 45 100 66     201 my $args = ( @DB::args == 1 && ref($DB::args[0]) )
43             ? $DB::args[0] # must be hash
44             : +{ @DB::args }; # must be key-value list
45              
46             ### $args
47             ### @_
48              
49             # args my $var => RULE
50             # ~~~~ ~~~~
51             # undef defined
52              
53 45         52 my $used = 0;
54 45         116 for(my $i = 0; $i < @_; $i++){
55              
56 59 50       185 (my $name = var_name(1, \$_[$i]))
57             or Carp::croak('usage: args my $var => TYPE, ...');
58 59         231 $name =~ s/^\$//;
59              
60             # with rule (my $foo => $rule, ...)
61 59 100       135 if(defined $_[ $i + 1 ]) {
62             # validate_pos($value, $exists, $name, $basic_rule, $used_ref)
63 52         150 $_[$i] = _validate_by_rule($args->{$name}, exists($args->{$name}), $name, $_[$i + 1], \$used);
64 38         115 $i++;
65             }
66             # without rule (my $foo, my $bar, ...)
67             else {
68 7 100       15 if(!exists $args->{$name}) { # parameters are mandatory by default
69 1         3 @_ = ("missing mandatory parameter named '\$$name'");
70 1         87 goto \&Carp::confess;
71             }
72 6         10 $_[$i] = $args->{$name};
73 6         13 $used++;
74             }
75             }
76              
77 30 100 66     32 if( $used < keys %{$args} && warnings::enabled('void') ) {
  30         412  
78             # hack to get unused argument names
79 4         7 my %vars;
80 4         8 foreach my $slot(@_) {
81 16 100       94 my $name = var_name(1, \$slot) or next;
82 8         27 $name =~ s/^\$//;
83 8         15 $vars{$name} = undef;
84             }
85 4         8 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
86             warnings::warn( void =>
87             'unknown arguments: '
88 4         6 . join ', ', sort grep{ not exists $vars{$_} } keys %{$args} );
  13         743  
  4         9  
89             }
90 28         129 return;
91             }
92              
93             sub args_pos {
94             {
95 11     11 1 15534 package DB;
96             # call of caller in DB package sets @DB::args,
97             # which requires list context, but we don't need return values
98 11         71 () = CORE::caller(1);
99             }
100 11 50       27 if(@_) {
101 11   50     64 my $name = var_name(1, \$_[0]) || '';
102 11 50       27 if(exists $is_invocant{ $name }){ # seems method call
103 11         16 $_[0] = shift @DB::args; # set the invocant
104 11 100       21 if(defined $_[1]) { # has rule?
105 2         7 $name =~ s/^\$//;
106             # validate_pos($value, $exists, $name, $basic_rule, $used_ref)
107 2         6 $_[0] = _validate_by_rule($_[0], 1, $name, $_[1]);
108 1         2 shift;
109             }
110 10         10 shift;
111             }
112             }
113              
114 10         13 my @args = @DB::args;
115              
116             ### $args
117             ### @_
118              
119             # args my $var => RULE
120             # ~~~~ ~~~~
121             # undef defined
122              
123 10         22 for(my $i = 0; $i < @_; $i++){
124 15 50       35 (my $name = var_name(1, \$_[$i]))
125             or Carp::croak('usage: args my $var => TYPE, ...');
126              
127             # with rule (my $foo => $rule, ...)
128 15 100       26 if (defined $_[ $i + 1 ]) {
129 6         15 $_[$i] = _validate_by_rule($args[0], @args>0, $name, $_[$i + 1]);
130 6         18 shift @args;
131 6         11 $i++;
132             }
133             # without rule (my $foo, my $bar, ...)
134             else {
135 9 50       15 if (@args == 0) { # parameters are mandatory by default
136 0         0 @_ = ("missing mandatory parameter named '\$$name'");
137 0         0 goto \&Carp::confess;
138             }
139 9         18 $_[$i] = shift @args;
140             }
141             }
142              
143             # too much arguments
144 10 100       17 if ( scalar(@args) > 0 ) {
145             # hack to get unused argument names
146 4         5 local $Carp::CarpLevel = $Carp::CarpLevel + 1;
147 4         467 Carp::croak( void =>
148             'too much arguments. This function requires only ' . scalar(@_) . ' arguments.' );
149             }
150 6         10 return;
151             }
152              
153             # rule: $type or +{ isa => $type, optional => $bool, default => $default }
154             sub _validate_by_rule {
155 62     62   107 my ($value, $exists, $name, $basic_rule, $used_ref) = @_;
156              
157             # compile the rule
158 62         49 my $rule;
159             my $type;
160 62         60 my $mandatory = 1; # all the arguments are mandatory by default
161 62 100       112 if(ref($basic_rule) eq 'HASH') {
162 17         13 $rule = $basic_rule;
163 17 100       51 if (defined $basic_rule->{isa}) {
    100          
164 9         29 $type = _get_isa_type_constraint($basic_rule->{isa});
165             } elsif (defined $basic_rule->{does}) {
166 2         7 $type = _get_does_type_constraint($basic_rule->{does});
167             }
168 17         136 $mandatory = !$rule->{optional};
169             }
170             else {
171             # $rule is a type constraint name or type constraint object
172 45         112 $type = _get_isa_type_constraint($basic_rule);
173             }
174              
175             # validate the value by the rule
176 62 100       2248 if ($exists){
177 56 100       108 if(defined $type ){
178 52 100       386 if(!$type->check($value)){
179 16         648 $value = _try_coercion_or_die($name, $type, $value);
180             }
181             }
182 42 100       1218 ${$used_ref}++ if defined $used_ref;
  36         56  
183             }
184             else {
185 6 100 66     31 if(defined($rule) and exists $rule->{default}){
    100          
186 2         4 $value = $rule->{default};
187             }
188             elsif($mandatory){
189 2         7 @_ = ("missing mandatory parameter named '\$$name'");
190 2 50       6 if ($VERBOSE) {
191 2         354 goto \&Carp::confess;
192             } else {
193 0         0 goto \&Carp::croak;
194             }
195             }
196             else{
197             # no default, and not mandatory; noop
198             }
199             }
200 46         73 return $value;
201             }
202              
203             sub _try_coercion_or_die {
204 16     16   32 my($name, $tc, $value) = @_;
205 16 100       250 if($tc->has_coercion) {
206 4         23 $value = $tc->coerce($value);
207 4 100       310 $tc->check($value) and return $value;
208             }
209 14         571 @_ = ("'$name': " . $tc->get_message($value));
210 14 50       1614 if ($VERBOSE) {
211 14         2454 goto \&Carp::confess;
212             } else {
213 0           goto \&Carp::croak;
214             }
215             }
216             1;
217             __END__
218              
219             =head1 NAME
220              
221             Smart::Args - argument validation for you
222              
223             =head1 SYNOPSIS
224              
225             use Smart::Args;
226              
227             sub func2 {
228             args my $p => 'Int',
229             my $q => { isa => 'Int', optional => 1 };
230             }
231             func2(p => 3, q => 4); # p => 3, q => 4
232             func2(p => 3); # p => 3, q => undef
233              
234             sub func3 {
235             args my $p => {isa => 'Int', default => 3},
236             }
237             func3(p => 4); # p => 4
238             func3(); # p => 3
239              
240             package F;
241             use Moose;
242             use Smart::Args;
243              
244             sub method {
245             args my $self,
246             my $p => 'Int';
247             }
248             sub class_method {
249             args my $class => 'ClassName',
250             my $p => 'Int';
251             }
252              
253             sub simple_method {
254             args_pos my $self, my $p;
255             }
256              
257             my $f = F->new();
258             $f->method(p => 3);
259              
260             F->class_method(p => 3);
261              
262             F->simple_method(3);
263              
264             =head1 DESCRIPTION
265              
266             Smart::Args is yet another argument validation library.
267              
268             This module makes your module more readable, and writable =)
269              
270             =head1 FUNCTIONS
271              
272             =head2 C<args my $var [, $rule], ...>
273              
274             Checks parameters and fills them into lexical variables. All the parameters
275             are mandatory by default, and unknown parameters (i.e. possibly typos) are
276             reported as C<void> warnings.
277              
278             The arguments of C<args()> consist of lexical <$var>s and optional I<$rule>s.
279              
280             I<$vars> must be a declaration of a lexical variable.
281              
282             I<$rule> can be a type name (e.g. C<Int>), a HASH reference (with
283             C<type>, C<default>, and C<optional>), or a type constraint object.
284              
285             Note that if the first variable is named I<$class> or I<$self>, it
286             is dealt as a method call.
287              
288             See the SYNOPSIS section for examples.
289              
290             =head2 C<args_pos my $var[, $rule, ...>
291              
292             Check parameters and fills them into lexical variables. All the parameters
293             are mandatory by default.
294              
295             The arguments of C<args()> consist of lexical <$var>s and optional I<$rule>s.
296             I<$vars> must be a declaration of a lexical variable.
297              
298             I<$rule> can be a type name (e.g. C<Int>), a HASH reference (with
299             C<type>, C<default>, and C<optional>), or a type constraint object.
300              
301             Note that if the first variable is named I<$class> or I<$self>, it
302             is dealt as a method call.
303              
304             See the SYNOPSIS section for examples.
305              
306             =head1 TYPES
307              
308             The types that C<Smart::Args> uses are type constraints of C<Mouse>.
309             That is, you can define your types in the way Mouse does.
310              
311             In addition, C<Smart::Args> also allows Moose type constraint objects,
312             so you can use any C<MooseX::Types::*> libraries on CPAN.
313              
314             Type coercions are automatically tried if validations fail.
315              
316             See L<Mouse::Util::TypeConstraints> for details.
317              
318             =head1 AUTHOR
319              
320             Tokuhiro Matsuno E<lt>tokuhirom@gmail.comE<gt>
321              
322             =head1 SEE ALSO
323              
324             L<Params::Validate>
325              
326             =head1 LICENSE
327              
328             This library is free software; you can redistribute it and/or modify
329             it under the same terms as Perl itself.
330              
331             =cut