File Coverage

blib/lib/Data/SIprefixes.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Data::SIprefixes;
2              
3 1     1   24470 use strict;
  1         2  
  1         41  
4 1     1   4 use warnings;
  1         3  
  1         28  
5 1     1   2420 use bignum;
  1         10085  
  1         5  
6 1     1   183373 use base 'Error::Helper';
  1         3  
  1         997  
7 1     1   14306 use Module::List qw(list_modules);
  0            
  0            
8             use Data::SImeasures;
9              
10             =head1 NAME
11              
12             Data::SIprefixes - This helps with working with numbers with SI prefixed measures.
13              
14             =head1 VERSION
15              
16             Version 0.0.0
17              
18             =cut
19              
20             our $VERSION = '0.0.0';
21              
22              
23             =head1 SYNOPSIS
24              
25             use Data::SIprefixes;
26            
27             my $sim=Data::SIprefixes->new('3 grams');
28              
29             print 'Number: '.$sip->numberGet.
30             "\nprefix: ".$sip->prefixGet.
31             "\nmeasure: ".$sip->measureGet.
32             "\nsymbol: ".$sip->symbolGet.
33             "\nuse symbol: ".$sip->symbolUse.
34             "\nstring: ".$sip->string."\n";
35              
36             While new can properly parse some prefix/measure combos,
37             the ones below are set statically as they can cause confusion.
38              
39             amp
40             ampere
41             coulomb
42             farad
43             Gy
44             henry
45             kat
46             katal
47             kelvin
48             m
49             metre
50             meter
51             mol
52             mole
53             newton
54             Pa
55             pascal
56             T
57              
58             Because of this, while it is possible of matching other symbols
59             with out a measure, the ones listed below can't be.
60              
61             m
62             T
63              
64             =head1 METHODS
65              
66             =head2 new
67              
68             This initiates the object.
69              
70             One argument is required and that is
71             the string.
72              
73             my $sip->new('3 kilometer');
74             if ( $sip->error ){
75             warn('error:'.$sip->error.': '.$sip->errorString);
76             }
77              
78             =cut
79              
80             sub new{
81             my $string=$_[1];
82              
83             my $self={
84             perror=>undef,
85             error=>undef,
86             errorString=>'',
87             long=>undef,
88             measure=>'',
89             number=>undef,
90             original=>$string,
91             prefixes=>{},
92             prefix=>'',
93             };
94             bless $self;
95              
96             #loads all the prefixes, this is done dynamically for future expandability
97             my @prefixes=keys(list_modules('Data::SIprefixes::', { list_modules => 1 } ));
98             my $int=0;
99             while ( defined( $prefixes[$int] ) ){
100             my $prefix;
101             my $toeval='use '.$prefixes[$int].'; $prefix='.$prefixes[$int].'->new;';
102             eval( $toeval );
103              
104             if ( ! defined( $prefix ) ) {
105             $self->{perror}=1;
106             $self->{error}=1;
107             $self->{errorString}='Failed to one of the prefix modules, "'.
108             $prefixes[$int].'". It was returned as undefined';
109             $self->warn;
110             return $self;
111             }
112              
113             if ( $prefix->error ){
114             $self->{perror}=1;
115             $self->{error}=1;
116             $self->{errorString}=$prefixes[$int].'->new errored. error="'.$prefix->error.
117             '" errorString="'.$prefix->errorString.'"';
118             $self->warn;
119             return $self;
120             }
121              
122             $prefixes[$int]=~s/^Data\:\:SIprefixes\:\://;
123              
124             $self->{prefixes}{$prefixes[$int]}=$prefix;
125            
126             $int++;
127             }
128            
129              
130             #remove any beginning or trailling white space
131             $string=~s/^[ \t]*//g;
132             $string=~s/[ \t]*$//g;
133              
134             #make sure it begins with a number
135             if (
136             ( $string !~ /^[0123456789]+/ ) &&
137             ( $string !~ /^[012345789]*\.[0123456789]+/ ) &&
138             ( $string !~ /^\.[0123456789]+/ )
139             ){
140             $self->{perror}=1;
141             $self->{error}=2;
142             $self->{errorString}='"'.$string.'" does not appear to start with a number';
143             $self->warn;
144            
145             return $self;
146             }
147              
148             #no prefix or unit so we can return after setting it
149             if (
150             ( $string =~ /^[0123456789]+$/ ) ||
151             ( $string =~ /^[012345789]*\.[0123456789]+$/ ) ||
152             ( $string =~ /^\.[0123456789]+$/ )
153             ){
154             $self->{number}=$string;
155             $self->{prefix}='';
156            
157             return $self;
158             }
159            
160             #gets the prefix and unit
161             my $notnumeric=$string;
162             $notnumeric=~s/^[0123456789.]+//;
163             my $notnumericRemove=quotemeta( $notnumeric );
164             $self->{number}=$string;
165             $self->{number}=~s/^$notnumericRemove//;
166             $notnumeric=~s/^[ \t]+//;
167              
168             #matches the long version first
169             @prefixes=keys( @{ $self->{prefixes} } );
170             $int=0;
171             my $notMatched=1;
172             while(
173             defined( $prefixes[$int] ) &&
174             $notMatched
175             ){
176             my $measure=$self->{prefixes}{ $prefixes[$int] }->longMatch( $notnumeric );
177             if( defined( $measure ) ){
178             my $self->{prefix}=$prefixes[$int];
179             $self->{long}=1;
180             $self->{measure}=$measure;
181             $notMatched=0;
182             }
183            
184             $int++;
185             };
186              
187             #matches the short version first
188             $int=0;
189             while(
190             defined( $prefixes[$int] ) &&
191             $notMatched
192             ){
193             my $measure=$self->{prefixes}{ $prefixes[$int] }->shortMatch( $notnumeric );
194             if( defined( $measure ) ){
195             my $self->{prefix}=$prefixes[$int];
196             $self->{measure}=$measure;
197             $self->{long}=0;
198             $notMatched=0;
199             }
200            
201             $int++;
202             };
203            
204            
205             return $self;
206             }
207              
208             =head2 measureGet
209              
210             This returns the measure part.
211              
212             This won't error as long as new did not.
213              
214             If no measure was found, '' is returned.
215              
216             my $measure=$sip->measureSet('gram');
217              
218             =cut
219              
220             sub measureGet{
221             my $self=$_[0];
222              
223             if ( ! $self->errorblank ){
224             $self->warnString('Failed to blank the previous error');
225             }
226              
227             return $self->{measure};
228             }
229              
230             =head2 measureSet
231              
232             This returns the measure part.
233              
234             This sets the measure. This will accept ''
235             as a valid measure.
236              
237             $sip->measureSet( $measure );
238             if ( $sip->error ){
239             warn('error:'.$sip->error.': '.$sip->errorString);
240             }
241              
242             =cut
243              
244             sub measureSet{
245             my $self=$_[0];
246             my $measure=$_[1];
247              
248             if ( ! $self->errorblank ){
249             $self->warnString('Failed to blank the previous error');
250             }
251              
252             if ( ! defined( $measure ) ){
253             $self->{error}=5;
254             $self->{errorString}='No measure specified';
255             $self->warn;
256             return undef;
257             }
258              
259             $self->{measure}=$measure;
260              
261             return 1;
262             }
263              
264             =head2 numberGet
265              
266             This returns the numeric part.
267              
268             This won't error as long as new did not.
269              
270             my $error=$sip->numberGet;
271              
272             =cut
273              
274             sub numberGet{
275             my $self=$_[0];
276              
277             if ( ! $self->errorblank ){
278             $self->warnString('Failed to blank the previous error');
279             }
280              
281             return $self->{number};
282             }
283              
284             =head2 numberSet
285              
286             This sets the numeric part.
287              
288             This won't error as long as what you provide
289             a number.
290              
291             my $error=$sip->numberGet('4');
292              
293             =cut
294              
295             sub numberSet{
296             my $self=$_[0];
297             my $number=$_[1];
298              
299             if ( ! $self->errorblank ){
300             $self->warnString('Failed to blank the previous error');
301             }
302              
303             if ( ! defined( $number ) ){
304             $self->{error}=3;
305             $self->{errorString}='No number specified';
306             $self->warn;
307             return undef;
308             }
309              
310             #make sure it is a number
311             if (
312             ( $number !~ /^[0123456789]+/ ) &&
313             ( $number !~ /^[012345789]*\.[0123456789]+/ ) &&
314             ( $number !~ /^\.[0123456789]+/ )
315             ){
316             $self->{error}=2;
317             $self->{errorString}='"'.$number.'" does not appear to be a number';
318             $self->warn;
319            
320             return $self;
321             }
322              
323             $self->{number}=$number;
324              
325             return '1';
326             }
327              
328             =head2 prefixGet
329              
330             This returns the current metric prefix.
331              
332             As long as it new worked with out error,
333             then this will not error.
334              
335             A return of '', means there is no current
336             prefix.
337              
338             my $prefix=$sip->prefixGet;
339              
340             =cut
341              
342             sub prefixGet{
343             my $self=$_[0];
344              
345             if ( ! $self->errorblank ){
346             $self->warnString('Failed to blank the previous error');
347             }
348              
349             return $self->{prefix};
350             }
351              
352             =head2 prefixSet
353              
354             This sets the metric prefix and update
355             the number to reflect that change.
356              
357             One argument is accepted and it is the new
358             prefix. A value of '' set it to no prefix.
359              
360             If no prefix is specified, '' is used.
361              
362             $sip->prefixSet('kilo');
363              
364             =cut
365              
366             sub prefixSet{
367             my $self=$_[0];
368             my $prefix=$_[1];
369              
370             if ( ! $self->errorblank ){
371             $self->warnString('Failed to blank the previous error');
372             }
373              
374             #default to base
375             if ( ! defined( $prefix ) ){
376             $prefix='';
377             }
378              
379             #make sure it is a valid prefix
380             if ( ! defined( $self->{prefixes}{$prefix} ) ){
381             $self->{error}=4;
382             $self->{errorString}='"'.$prefix.'" is not a recognized prefix';
383             $self->warn;
384             return undef;
385             }
386              
387             #sets it back to the base for the current prefix
388             my $toBase=$self->{prefixes}{ $self->{prefix} }->toBase;
389             $self->{number} = $self->{number} * $toBase;
390              
391             #if we are going to base, exit here
392             if ( $prefix eq '' ){
393             $self->{prefix}=$prefix;
394             return 1;
395             }
396              
397             #go from the base for the new prefix
398             my $fromBase=$self->{prefixes}{$prefix}->fromBase;
399             $self->{number} = $self->{number} * $fromBase;
400              
401             $self->{prefix}=$prefix;
402              
403             return 1;
404             }
405              
406             =head2 string
407              
408             This returns a formatted string of the number, prefix/symbol, and measure.
409              
410             This will not error as long as the module initialized with out error.
411              
412             my $string=$sip->string;
413              
414             =cut
415              
416             sub string{
417             my $self=$_[0];
418              
419             if ( ! $self->errorblank ){
420             $self->warnString('Failed to blank the previous error');
421             }
422              
423             my $string=$self->{number}.' ';
424              
425             if ( $self->{long} ){
426             $string=$string.$self->{prefix}.$self->{measure};
427             }else{
428             $string=$string.$self->symbolGet.$self->{measure};
429             }
430              
431             return $string;
432             }
433              
434             =head2 symbolGet
435              
436             This returns the symbol for the prefix.
437              
438             As long as it new worked with out error,
439             then this will not error.
440              
441             A return of '', means there is no current
442             prefix and thus no symbol.
443              
444             my $symbol=$sip->getSymbol;
445              
446             =cut
447              
448             sub symbolGet{
449             my $self=$_[0];
450              
451             if ( ! $self->errorblank ){
452             $self->warnString('Failed to blank the previous error');
453             }
454              
455             if ( $self->{prefix} eq '' ){
456             return '';
457             }
458              
459             return $self->{prefixes}{$self->{prefix}}->symbol;
460             }
461              
462             =head2 symbolUse
463              
464             Returns if the symbol should be used or not.
465              
466             The returned value is boolean and this is based off
467             of how it is matched.
468              
469             This will not error as long as the module has
470             initialized with out error.
471              
472             my $symbolUse=$sim->symbolUse;
473              
474             =cut
475              
476             sub symbolUse{
477             my $self=$_[0];
478              
479             if ( ! $self->errorblank ){
480             $self->warnString('Failed to blank the previous error');
481             }
482              
483             return $self->{long};
484             }
485              
486             =head1 ERROR CODES
487              
488             =head2 1
489              
490             Failed to load one of the prefixes.
491              
492             =head2 2
493              
494             The string does not begin with a number.
495              
496             =head2 3
497              
498             No number specified.
499              
500             =head2 4
501              
502             No recognized prefix specified.
503              
504             =head2 5
505              
506             No measure specified.
507              
508             =head1 AUTHOR
509              
510             Zane C. Bowers-Hadley, C<< >>
511              
512             =head1 BUGS
513              
514             Please report any bugs or feature requests to C, or through
515             the web interface at L. I will be notified, and then you'll
516             automatically be notified of progress on your bug as I make changes.
517              
518              
519              
520              
521             =head1 SUPPORT
522              
523             You can find documentation for this module with the perldoc command.
524              
525             perldoc Data::SIprefixes
526              
527              
528             You can also look for information at:
529              
530             =over 4
531              
532             =item * RT: CPAN's request tracker (report bugs here)
533              
534             L
535              
536             =item * AnnoCPAN: Annotated CPAN documentation
537              
538             L
539              
540             =item * CPAN Ratings
541              
542             L
543              
544             =item * Search CPAN
545              
546             L
547              
548             =back
549              
550              
551             =head1 ACKNOWLEDGEMENTS
552              
553              
554             =head1 LICENSE AND COPYRIGHT
555              
556             Copyright 2012 Zane C. Bowers-Hadley.
557              
558             This program is free software; you can redistribute it and/or modify it
559             under the terms of either: the GNU General Public License as published
560             by the Free Software Foundation; or the Artistic License.
561              
562             See http://dev.perl.org/licenses/ for more information.
563              
564              
565             =cut
566              
567             1; # End of Data::SIprefixes