File Coverage

lib/Scalar/Classify.pm
Criterion Covered Total %
statement 77 83 92.7
branch 34 50 68.0
condition 16 19 84.2
subroutine 12 12 100.0
pod 2 2 100.0
total 141 166 84.9


line stmt bran cond sub pod time code
1             package Scalar::Classify;
2             # doom@kzsu.stanford.edu
3             # 30 Jun 2015
4              
5             =head1 NAME
6              
7             Scalar::Classify - get type and class information for scalars
8              
9             =head1 SYNOPSIS
10              
11             use Scalar::Classify qw( classify classify_pair );
12              
13             # determine the type (e.g. HASH for a hashref) and the object class (if any)
14             my ( $type, $class ) = classify( $some_scalar );
15              
16              
17             # warn if two args differ, supply default if one is undef
18             my $default_value =
19             classify_pair( $arg1, $arg2 );
20              
21             # Also get type and class; error out if two args differ
22             my ( $default_value, $type, $class ) =
23             classify_pair( $arg1, $arg2, { mismatch_policy => 'error' });
24              
25             # If a given ref was undef, replace it with a default value
26             classify_pair( $arg1, $arg2, { also_qualify => 1 });
27              
28             =head1 DESCRIPTION
29              
30             Scalar::Classify provides a routine named "classify" that can be used
31             to examine a given argument to determine it's type and class (if any).
32              
33             Here "type" means either the return from reftype (, or if it's a scalar,
34             a code indicating whether it's a string or a number, and "class"
35             it the object class, the way a reference has been blessed.
36              
37             This module also provides the routine "classify_pair", which
38             looks at a pair of variables intended to be of the same type, and
39             if at least one of them is defined, uses that to get an
40             appropriate default value for that type.
41              
42             =head2 MOTIVATION
43              
44             Perl contains a built-in "ref" function, and has some useful
45             routines in the standard Scalar::Util library ('ref',
46             'looks_like_number') which can be used to examine the type of an
47             argument. The classify routine provided here internally uses all
48             three of these, returning a two-values that describe the kind of
49             thing you're examining.
50              
51             The immediate goal was to provide support routines for the
52             L project.
53              
54             =head2 EXPORT
55              
56             None by default. Optionally:
57              
58             =over
59              
60             =cut
61              
62 4     4   2457 use 5.008;
  4         11  
63 4     4   17 use strict;
  4         3  
  4         84  
64 4     4   20 use warnings;
  4         9  
  4         140  
65             my $DEBUG = 1;
66 4     4   16 use Carp;
  4         3  
  4         245  
67 4     4   16 use Data::Dumper;
  4         4  
  4         180  
68 4     4   15 use Scalar::Util qw( reftype looks_like_number );
  4         3  
  4         528  
69              
70             our (@ISA, @EXPORT_OK, %EXPORT_TAGS, @EXPORT);
71             BEGIN {
72 4     4   17 require Exporter;
73 4         32 @ISA = qw(Exporter);
74 4         16 %EXPORT_TAGS = ( 'all' => [
75             qw(
76             classify
77             classify_pair
78             ) ] );
79             # The above allows use Scalar::Classify ':all';
80              
81 4         4 @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
  4         10  
82 4         845 @EXPORT = qw( );
83             }
84              
85             our $VERSION = '0.02';
86              
87             =item classify
88              
89             Example usage:
90              
91             my ( $type, $class ) = classify( $some_var );
92              
93             Returns two pieces of information, the underlying "type", and the
94             "class" (if this is a reference blessed into a class).
95              
96             The type is most often (but not limited to) one of the following:
97              
98             ARRAY
99             HASH
100             :NUMBER:
101             :STRING:
102              
103             Other possibilities are the other potential returns from L:
104              
105             CODE
106             GLOB
107             LVALUE
108             FORMAT
109             IO
110             VSTRING
111             Regexp
112              
113             Internally, this uses the built-in function L and the library
114             functions L and L (from L).
115             The type is the return from "reftype" (e.g "ARRAY", "HASH")
116             except that in the case of a simple scalar the type is a code to
117             indicate whether it seems to be a number (":NUMBER:") or a string
118             (":STRING:").
119              
120             Note: if the argument is undefined, the returned type is undef.
121              
122             =cut
123              
124             sub classify {
125 48     48 1 16368 my $arg = shift;
126              
127             # initialize $type to ref()
128 48         53 my $type = ref( $arg ); # '' if undef
129              
130 48         97 my $basetype = reftype( $arg ) ; # undef if undef
131              
132 48         31 my $class; # default undef
133             # it's a blessed ref when ref() not same as reftype()
134 48 100 100     162 if ( defined( $basetype ) && $type ne $basetype ) {
135 14         13 $class = $type;
136 14         12 $type = $basetype;
137             }
138              
139 48 100       65 if( defined $arg ) {
140             # if not reference, we're handling a scalar
141 37 100       56 if ( not( defined( $basetype ) ) ) {
142 10 100       37 if( looks_like_number( $arg ) ) {
143 6         8 $type = ':NUMBER:';
144             } else {
145 4         6 $type = ':STRING:';
146             }
147             }
148             } else {
149 11         13 $type = undef; # more perlish than an empty string
150             }
151              
152 48         73 my @meta = ( $type, $class );
153 48 100       112 return wantarray ? @meta : \@meta;
154             }
155              
156             =item classify_pair
157              
158             Examines a pair of arguments that are intended to be processed in
159             parallel and are expected to be of the same type:
160              
161             If they're both defined, it checks that their types match.
162             If at least one is defined, it generates a default of the
163             same type by using the L method. If both are
164             undef, this default is also undef.
165              
166             In scalar context, it returns just the default value.
167              
168             In list context, it returns the default plus the type and
169             the class (if it's a blessed reference).
170              
171             An options hashref is accepted as a third argument, with
172             allowed options:
173              
174             o mismatch_policy
175              
176             If argument types mismatch, the behavior is determined by
177             the mismatch_policy option, defaulting to 'warn'.
178             The other allowed values are 'error' or 'silent'.
179              
180             o also_qualify
181              
182             If the "also_qualify" option is set to a true value, then
183             the given arguments may be modified in place: if one is
184             undef, it will be assigned the determined default.
185              
186             Examples:
187              
188             my $default_value =
189             classify_pair( $arg1, $arg2, { mismatch_policy => 'error' });
190              
191             my ( $default_value, $type, $class ) =
192             classify_pair( $arg1, $arg2, { mismatch_policy => 'error' });
193              
194             classify_pair( $arg1, $arg2, { also_qualify => 1 });
195              
196             Note the slightly unusual polymorphic behavior: in scalar
197             context returns *just* the default_value, in list context,
198             returns up to three values, the default, the type and the class.
199              
200              
201             =cut
202              
203             sub classify_pair {
204 14     14 1 30884 my $subname = ( caller(0) )[3];
205 14         179 my $opt = $_[2];
206              
207 14   100     74 my $policy = $opt->{ mismatch_policy } || 'warn';
208 14   100     48 my $do_qualify = $opt->{ also_qualify } || 0;
209              
210 14         23 my $meta1 = classify( $_[0] );
211 14         24 my $meta2 = classify( $_[1] );
212              
213             # handle mismatched types
214 14 50       29 if ( $policy ne 'silent' ) {
215 4     4   17 no warnings 'uninitialized';
  4         4  
  4         597  
216 14 100 100     43 if ( defined( $_[0] ) && defined( $_[1] ) ) {
217 4 100       7 unless( $meta1->[0] eq $meta2->[0] ) {
218 3 50       35 croak "mismatched types: $meta1->[0] and $meta2->[0]" if $policy eq 'error';
219 0 0       0 carp "mismatched types: $meta1->[0] and $meta2->[0]" if $policy eq 'warn';
220             }
221 1 50       15 unless( $meta1->[1] eq $meta2->[1] ) {
222 1 50       11 croak "mismatched classes: $meta1->[1] and $meta2->[1]" if $policy eq 'error';
223 0 0       0 carp "mismatched classes: $meta1->[1] and $meta2->[1]" if $policy eq 'warn';
224             }
225             }
226             }
227              
228 10         11 my ( $defval, $class, $type );
229 4     4   92 { no warnings 'uninitialized';
  4         5  
  4         308  
  10         26  
230 10   66     33 $type = $meta1->[0] || $meta2->[0];
231             }
232 10 50       32 unless( defined( $type ) ) {
233 0 0       0 return wantarray ? ( undef, undef, undef ) : undef;
234             }
235              
236 10 100       22 if ( $type eq ':NUMBER:' ) {
    100          
237 3         3 $defval = 0;
238             } elsif ( $type eq ':STRING:' ) {
239 1         3 $defval = '';
240             } else {
241 4     4   19 { no warnings 'uninitialized';
  4         4  
  4         692  
  6         5  
242 6   66     16 $class = $meta1->[1] || $meta2->[1];
243             }
244 6 100       19 if ( $type eq 'ARRAY' ) {
    50          
    0          
245 2         3 $defval = [];
246             } elsif ( $type eq 'HASH' ) {
247 4         6 $defval = {};
248             } elsif ( $type eq 'SCALAR' ) {
249 0         0 my $var;
250 0         0 $defval = \$var;
251             } else { # handle the useless cases: warn and get out of here
252 0         0 carp "$subname can't do anything useful with ref type $type";
253             }
254             }
255              
256 10 100 66     43 if( defined( $defval ) && defined( $class ) ) {
257 2         3 bless( $defval, $class );
258             }
259              
260 10 100       17 if( $do_qualify ) {
261 1 50       3 $_[0] = $defval unless defined( $_[0] );
262 1 50       3 $_[1] = $defval unless defined( $_[1] );
263             }
264              
265 10 100       52 return wantarray ? ( $defval, $type, $class ) : $defval;
266             }
267              
268              
269             1;
270              
271             =back
272              
273             =head1 SEE ALSO
274              
275             L
276              
277             This covers the argument checking case, where you want to verify
278             that something of the correct type was passed. The perl5-porters
279             are interested in adding core support for this module: it's fast
280             and likely to get faster.
281              
282             =head1 AUTHOR
283              
284             Joseph Brenner, Edoom@kzsu.stanford.eduE
285              
286             =head1 COPYRIGHT AND LICENSE
287              
288             Copyright (C) 2016 by Joseph Brenner
289              
290             This library is free software; you can redistribute it and/or modify
291             it under the same terms as Perl itself.
292              
293             See http://dev.perl.org/licenses/ for more information.
294              
295             =cut