File Coverage

blib/lib/Data/SIprefixes.pm
Criterion Covered Total %
statement 18 159 11.3
branch 0 46 0.0
condition 0 24 0.0
subroutine 6 16 37.5
pod 10 10 100.0
total 34 255 13.3


line stmt bran cond sub pod time code
1             package Data::SIprefixes;
2              
3 1     1   20864 use strict;
  1         2  
  1         27  
4 1     1   5 use warnings;
  1         2  
  1         28  
5 1     1   717187 use bignum;
  1         622317  
  1         6  
6 1     1   96796 use base 'Error::Helper';
  1         2  
  1         984  
7 1     1   1515 use Module::List qw(list_modules);
  1         35583  
  1         77  
8 1     1   816 use Data::SImeasures;
  1         1004  
  1         59  
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.1
17              
18             =cut
19              
20             our $VERSION = '0.0.1';
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 0     0 1   my $string=$_[1];
82              
83 0           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 0           bless $self;
95              
96             #loads all the prefixes, this is done dynamically for future expandability
97 0           my @prefixes=keys(list_modules('Data::SIprefixes::', { list_modules => 1 } ));
98 0           my $int=0;
99 0           while ( defined( $prefixes[$int] ) ){
100 0           my $prefix;
101 0           my $toeval='use '.$prefixes[$int].'; $prefix='.$prefixes[$int].'->new;';
102 0           eval( $toeval );
103              
104 0 0         if ( ! defined( $prefix ) ) {
105 0           $self->{perror}=1;
106 0           $self->{error}=1;
107 0           $self->{errorString}='Failed to one of the prefix modules, "'.
108             $prefixes[$int].'". It was returned as undefined';
109 0           $self->warn;
110 0           return $self;
111             }
112              
113 0 0         if ( $prefix->error ){
114 0           $self->{perror}=1;
115 0           $self->{error}=1;
116 0           $self->{errorString}=$prefixes[$int].'->new errored. error="'.$prefix->error.
117             '" errorString="'.$prefix->errorString.'"';
118 0           $self->warn;
119 0           return $self;
120             }
121              
122 0           $prefixes[$int]=~s/^Data\:\:SIprefixes\:\://;
123              
124 0           $self->{prefixes}{$prefixes[$int]}=$prefix;
125            
126 0           $int++;
127             }
128            
129              
130             #remove any beginning or trailling white space
131 0           $string=~s/^[ \t]*//g;
132 0           $string=~s/[ \t]*$//g;
133              
134             #make sure it begins with a number
135 0 0 0       if (
      0        
136             ( $string !~ /^[0123456789]+/ ) &&
137             ( $string !~ /^[012345789]*\.[0123456789]+/ ) &&
138             ( $string !~ /^\.[0123456789]+/ )
139             ){
140 0           $self->{perror}=1;
141 0           $self->{error}=2;
142 0           $self->{errorString}='"'.$string.'" does not appear to start with a number';
143 0           $self->warn;
144            
145 0           return $self;
146             }
147              
148             #no prefix or unit so we can return after setting it
149 0 0 0       if (
      0        
150             ( $string =~ /^[0123456789]+$/ ) ||
151             ( $string =~ /^[012345789]*\.[0123456789]+$/ ) ||
152             ( $string =~ /^\.[0123456789]+$/ )
153             ){
154 0           $self->{number}=$string;
155 0           $self->{prefix}='';
156            
157 0           return $self;
158             }
159            
160             #gets the prefix and unit
161 0           my $notnumeric=$string;
162 0           $notnumeric=~s/^[0123456789.]+//;
163 0           my $notnumericRemove=quotemeta( $notnumeric );
164 0           $self->{number}=$string;
165 0           $self->{number}=~s/^$notnumericRemove//;
166 0           $notnumeric=~s/^[ \t]+//;
167              
168             #matches the long version first
169 0           @prefixes=keys( @{ $self->{prefixes} } );
  0            
170 0           $int=0;
171 0           my $notMatched=1;
172 0   0       while(
173             defined( $prefixes[$int] ) &&
174             $notMatched
175             ){
176 0           my $measure=$self->{prefixes}{ $prefixes[$int] }->longMatch( $notnumeric );
177 0 0         if( defined( $measure ) ){
178 0           my $self->{prefix}=$prefixes[$int];
179 0           $self->{long}=1;
180 0           $self->{measure}=$measure;
181 0           $notMatched=0;
182             }
183            
184 0           $int++;
185             };
186              
187             #matches the short version first
188 0           $int=0;
189 0   0       while(
190             defined( $prefixes[$int] ) &&
191             $notMatched
192             ){
193 0           my $measure=$self->{prefixes}{ $prefixes[$int] }->shortMatch( $notnumeric );
194 0 0         if( defined( $measure ) ){
195 0           my $self->{prefix}=$prefixes[$int];
196 0           $self->{measure}=$measure;
197 0           $self->{long}=0;
198 0           $notMatched=0;
199             }
200            
201 0           $int++;
202             };
203            
204            
205 0           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 0     0 1   my $self=$_[0];
222              
223 0 0         if ( ! $self->errorblank ){
224 0           $self->warnString('Failed to blank the previous error');
225             }
226              
227 0           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 0     0 1   my $self=$_[0];
246 0           my $measure=$_[1];
247              
248 0 0         if ( ! $self->errorblank ){
249 0           $self->warnString('Failed to blank the previous error');
250             }
251              
252 0 0         if ( ! defined( $measure ) ){
253 0           $self->{error}=5;
254 0           $self->{errorString}='No measure specified';
255 0           $self->warn;
256 0           return undef;
257             }
258              
259 0           $self->{measure}=$measure;
260              
261 0           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 0     0 1   my $self=$_[0];
276              
277 0 0         if ( ! $self->errorblank ){
278 0           $self->warnString('Failed to blank the previous error');
279             }
280              
281 0           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 0     0 1   my $self=$_[0];
297 0           my $number=$_[1];
298              
299 0 0         if ( ! $self->errorblank ){
300 0           $self->warnString('Failed to blank the previous error');
301             }
302              
303 0 0         if ( ! defined( $number ) ){
304 0           $self->{error}=3;
305 0           $self->{errorString}='No number specified';
306 0           $self->warn;
307 0           return undef;
308             }
309              
310             #make sure it is a number
311 0 0 0       if (
      0        
312             ( $number !~ /^[0123456789]+/ ) &&
313             ( $number !~ /^[012345789]*\.[0123456789]+/ ) &&
314             ( $number !~ /^\.[0123456789]+/ )
315             ){
316 0           $self->{error}=2;
317 0           $self->{errorString}='"'.$number.'" does not appear to be a number';
318 0           $self->warn;
319            
320 0           return $self;
321             }
322              
323 0           $self->{number}=$number;
324              
325 0           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 0     0 1   my $self=$_[0];
344              
345 0 0         if ( ! $self->errorblank ){
346 0           $self->warnString('Failed to blank the previous error');
347             }
348              
349 0           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 0     0 1   my $self=$_[0];
368 0           my $prefix=$_[1];
369              
370 0 0         if ( ! $self->errorblank ){
371 0           $self->warnString('Failed to blank the previous error');
372             }
373              
374             #default to base
375 0 0         if ( ! defined( $prefix ) ){
376 0           $prefix='';
377             }
378              
379             #make sure it is a valid prefix
380 0 0         if ( ! defined( $self->{prefixes}{$prefix} ) ){
381 0           $self->{error}=4;
382 0           $self->{errorString}='"'.$prefix.'" is not a recognized prefix';
383 0           $self->warn;
384 0           return undef;
385             }
386              
387             #sets it back to the base for the current prefix
388 0           my $toBase=$self->{prefixes}{ $self->{prefix} }->toBase;
389 0           $self->{number} = $self->{number} * $toBase;
390              
391             #if we are going to base, exit here
392 0 0         if ( $prefix eq '' ){
393 0           $self->{prefix}=$prefix;
394 0           return 1;
395             }
396              
397             #go from the base for the new prefix
398 0           my $fromBase=$self->{prefixes}{$prefix}->fromBase;
399 0           $self->{number} = $self->{number} * $fromBase;
400              
401 0           $self->{prefix}=$prefix;
402              
403 0           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 0     0 1   my $self=$_[0];
418              
419 0 0         if ( ! $self->errorblank ){
420 0           $self->warnString('Failed to blank the previous error');
421             }
422              
423 0           my $string=$self->{number}.' ';
424              
425 0 0         if ( $self->{long} ){
426 0           $string=$string.$self->{prefix}.$self->{measure};
427             }else{
428 0           $string=$string.$self->symbolGet.$self->{measure};
429             }
430              
431 0           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 0     0 1   my $self=$_[0];
450              
451 0 0         if ( ! $self->errorblank ){
452 0           $self->warnString('Failed to blank the previous error');
453             }
454              
455 0 0         if ( $self->{prefix} eq '' ){
456 0           return '';
457             }
458              
459 0           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 0     0 1   my $self=$_[0];
478              
479 0 0         if ( ! $self->errorblank ){
480 0           $self->warnString('Failed to blank the previous error');
481             }
482              
483 0           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