File Coverage

blib/lib/Constant/Generate.pm
Criterion Covered Total %
statement 147 156 94.2
branch 43 54 79.6
condition 22 25 88.0
subroutine 28 29 96.5
pod 0 1 0.0
total 240 265 90.5


line stmt bran cond sub pod time code
1             package Constant::Generate;
2 6     6   212385 use strict;
  6         15  
  6         287  
3 6     6   32 use warnings;
  6         11  
  6         375  
4             our $VERSION = '0.16';
5              
6 6     6   11346 use Data::Dumper;
  6         108002  
  6         639  
7 6     6   65 use Carp qw(confess);
  6         13  
  6         298  
8 6     6   10569 use Constant::Generate::Dualvar;
  6         19  
  6         43  
9 6     6   39 use Scalar::Util qw(looks_like_number);
  6         11  
  6         1179  
10              
11             #these two functions produce reverse mapping, one for simple constants, and
12             #one for bitfields
13              
14             use constant {
15 6         558 CONST_BITFLAG => 1,
16             CONST_SIMPLE => 2,
17             CONST_STRING => 3
18 6     6   34 };
  6         13  
19              
20             sub _gen_bitfield_fn {
21 6     6   31 no strict "refs";
  6         10  
  6         968  
22 5     5   14 my ($name,$rhash) = @_;
23 5         29 *{$name} = sub($) {
24 8     8   6319 my $flag = $_[0];
25 8         40 join("|",
26 8         48 @{$rhash}{(
27             grep($flag & $_, keys %$rhash)
28             )}
29             );
30 5         23 };
31             }
32              
33             sub _gen_int_fn {
34 6     6   34 no strict 'refs';
  6         11  
  6         695  
35 3     3   8 my ($name,$rhash) = @_;
36 3 50   6   13 *{$name} = sub ($) { $rhash->{$_[0] + 0} || "" };
  3         19  
  6         5626  
37             }
38              
39             sub _gen_str_fn {
40 6     6   32 no strict 'refs';
  6         8  
  6         1911  
41 1     1   2 my ($name,$rhash) = @_;
42 1 50   1   3 *{$name} = sub ($) { $rhash->{ $_[0] } || "" };
  1         4  
  1         1004  
43             }
44              
45              
46             sub _gen_integer_syms {
47 10     10   91 my ($uarr, $symhash, $start) = @_;
48 10         23 foreach my $sym (@$uarr) {
49 20         316 $symhash->{$sym} = $start;
50 20         152 $start++;
51             }
52             }
53              
54             sub _gen_bitflag_syms {
55 4     4   8 my ($uarr,$symhash,$start) = @_;
56 4         9 foreach my $sym (@$uarr) {
57 11         25 $symhash->{$sym} = 1 << $start;
58 11         17 $start++;
59             }
60             }
61              
62             sub _gen_string_syms {
63 1     1   14 my ($uarr,$symhash,$prefix) = @_;
64 1         3 foreach my $sym (@$uarr) {
65 3         9 $symhash->{$sym} = $sym;
66             }
67             }
68              
69             sub _gen_constant {
70 47     47   95 my ($pkg,$name,@values) = @_;
71 6     6   111 no strict 'refs';
  6         17  
  6         3184  
72 47         166 my $fqname = $pkg . "::$name";
73 47 100       225 if(@values == 1) {
74 45         326 my $value = $values[0];
75 45     0   483 *{$fqname} = sub () { $value };
  45         423  
  0         0  
76             } else {
77 2     2   5 *{$fqname} = sub () { @values };
  2         10  
  2         29  
78             }
79             }
80              
81             sub _gen_map_rhash {
82 9     9   18 my ($symhash, $prefix, $display_prefix) = @_;
83 9         11 my (%maphash,%rhash);
84 9 50 66     39 if($prefix && $display_prefix) {
85 0         0 while (my ($sym,$val) = each %$symhash) {
86 0         0 $maphash{$prefix.$sym} = $val;
87             }
88             } else {
89 9         34 %maphash = %$symhash;
90             }
91            
92             #Check for duplicate constants pointing to the same value
93 9         503 while (my ($sym,$val) = each %maphash) {
94 25         27 push @{$rhash{$val}}, $sym;
  25         114  
95             }
96 9         31 while (my ($val,$syms) = each %rhash) {
97 25 50       48 if(@$syms > 1) {
98 0         0 $rhash{$val} = sprintf("(%s)", join(",", @$syms));
99             } else {
100 25         101 $rhash{$val} = $syms->[0];
101             }
102             }
103 9         27 return \%rhash;
104             }
105              
106             sub _mangle_exporter {
107 19     19   186 my ($pkg, $symlist, $tag,
108             $uspec_export, $uspec_export_ok, $uspec_export_tags) = @_;
109            
110 19         138 my @emap = (
111             [$uspec_export, \my $my_export, 'EXPORT', 'ARRAY'],
112             [$uspec_export_ok, \my $my_export_ok, 'EXPORT_OK', 'ARRAY'],
113             [$uspec_export_tags, \my $my_export_tags, 'EXPORT_TAGS', 'HASH', \$tag]
114             );
115            
116 19         44 foreach (@emap) {
117 57         102 my ($uspec,$myspec,$pvar,$vtype,$depvar) = @$_;
118 57 100       178 if(!$uspec) {
119 51         99 next;
120             }
121 6 50 66     91 if (defined $depvar && !$$depvar) {
122 0         0 next;
123             }
124 6 50       12 if(ref $uspec) {
125 0         0 $$myspec = $uspec;
126             } else {
127 6     6   34 no strict 'refs';
  6         12  
  6         26188  
128 6 50       6 if(!defined ($$myspec = *{$pkg."::$pvar"}{$vtype})) {
  6         55  
129 0         0 confess "Requested mangling of $pvar, but $pvar not yet declared!";
130             }
131             }
132             }
133            
134 19 100       48 if($uspec_export_ok) {
135 2         6 push @$my_export_ok, @$symlist;
136             }
137 19 100       47 if($uspec_export) {
138 1         9 push @$my_export, @$symlist;
139             }
140 19 100       83 if($uspec_export_tags) {
141 3         180 $my_export_tags->{$tag} = [ @$symlist ];
142             }
143             #Verify the required variables
144             }
145              
146             my $FN_CONST_TBL = {
147             CONST_BITFLAG, \&_gen_bitflag_syms,
148             CONST_SIMPLE, \&_gen_integer_syms,
149             CONST_STRING, \&_gen_string_syms
150             };
151              
152             my $FN_RMAP_TBL = {
153             CONST_BITFLAG, \&_gen_bitfield_fn,
154             CONST_SIMPLE, \&_gen_int_fn,
155             CONST_STRING, \&_gen_str_fn,
156             };
157              
158             sub utype2const {
159 19     19 0 35 my $utype = shift;
160 19 100 100     171 if(!$utype || $utype =~ /int/i) {
    100          
    50          
161 12         33 return CONST_SIMPLE;
162             } elsif ($utype =~ /bit/i) {
163 6         173 return CONST_BITFLAG;
164             } elsif ($utype =~ /str/i) {
165 1         3 return CONST_STRING;
166             } else {
167 0         0 die "Unrecognized type '$utype'";
168             }
169             }
170              
171             sub _getopt(\%$) {
172 246     246   767 my ($h,$opt) = @_;
173 246 100       413 foreach ($opt,"-$opt") { return delete $h->{$_} if exists $h->{$_} }
  476         2900  
174             }
175              
176             sub import {
177 19     19   55840 my ($cls,$symspecs,%opts) = @_;
178 19 50       66 return 1 unless $symspecs;
179            
180 19         222 my $reqpkg = caller();
181 19         53 my $type = utype2const(_getopt(%opts, "type"));
182            
183             #Determine our tag for %EXPORT_TAGS and reverse mapping
184            
185 19         77 my $mapname = _getopt(%opts, "mapname");
186 19         61 my $export_tag = _getopt(%opts, "tag");
187 19   100     42 my $prefix = _getopt(%opts, "prefix") || "";
188 19         122 my $display_prefix = _getopt(%opts, "show_prefix");
189 19   100     42 my $start = _getopt(%opts, "start_at") || 0;
190 19   100     45 my $stringy = _getopt(%opts, "stringy_vars")
191             || _getopt(%opts, "dualvar");
192            
193 19         45 my $listname = _getopt(%opts, "allvalues");
194 19         45 my $symsname = _getopt(%opts, "allsyms");
195            
196 19 100 100     543 if((!$mapname) && $export_tag) {
197 3         7 $mapname = $export_tag . "_to_str";
198             }
199            
200             #Generate the values.
201 19         27 my %symhash;
202             #Initial value
203            
204 19 100       235 ref $symspecs eq 'HASH' ? %symhash = %$symspecs :
205             $FN_CONST_TBL->{$type}->($symspecs, \%symhash, $start);
206            
207             #tie it all together
208            
209 19         78 while (my ($symname,$symval) = each %symhash) {
210 45 100 66     294 if($stringy && looks_like_number($symval)) {
211            
212 13 50       23 my $dv_name = $display_prefix ? $prefix . $symname : $symname;
213            
214 13         114 $symval = Constant::Generate::Dualvar::CG_dualvar(
215             $symval, $dv_name);
216             }
217 45         211 _gen_constant($reqpkg, $prefix.$symname, $symval);
218             }
219            
220             #After we have determined values for all the symbols, we can establish our
221             #reverse mappings, if so requested
222 19 100       50 if($mapname) {
223 9         25 my $rhash = _gen_map_rhash(\%symhash, $prefix, $display_prefix);
224 9         475 $FN_RMAP_TBL->{$type}->($reqpkg."::$mapname", $rhash);
225             }
226            
227 19 100       54 if($prefix) {
228 5         84 foreach my $usym (keys %symhash) {
229 11         14 my $v = delete $symhash{$usym};
230 11         31 $symhash{$prefix.$usym} = $v;
231             }
232             }
233            
234 19         44 my $auto_export = _getopt(%opts, "export");
235 19         44 my $auto_export_ok = _getopt(%opts, "export_ok");
236 19         41 my $h_etags = _getopt(%opts, "export_tags");
237            
238 19         65 my @symlist = keys %symhash;
239            
240 19 100       50 if($listname) {
241 1         5 my %tmp = reverse %symhash;
242 1         4 _gen_constant($reqpkg, $listname, keys %tmp);
243 1         3 push @symlist, $listname;
244             }
245 19 100       45 if($symsname) {
246 1         3 _gen_constant($reqpkg, $symsname, keys %symhash);
247 1         2 push @symlist, $symsname;
248             }
249            
250 19 100       53 push @symlist, $mapname if $mapname;
251 19   100     105 _mangle_exporter($reqpkg, \@symlist,
252             $export_tag,
253             $auto_export, $auto_export_ok, $h_etags || $export_tag);
254              
255 19 50       11104 if(%opts) {
256 0           die "Unknown keys " . join(",", keys %opts);
257             }
258             }
259              
260             __END__
261              
262             =head1 NAME
263              
264             Constant::Generate - Common tasks for symbolic constants
265              
266             =head2 SYNOPSIS
267              
268             Simplest use
269              
270             use Constant::Generate [ qw(CONST_FOO CONST_BAR) ];
271             printf( "FOO=%d, BAR=%d\n", CONST_FOO, CONST_BAR );
272            
273             Bitflags:
274              
275             use Constant::Generate [qw(ANNOYING STRONG LAZY)], type => 'bits';
276             my $state = (ANNOYING|LAZY);
277             $state & STRONG == 0;
278              
279             With reverse mapping:
280              
281             use Constant::Generate
282             [qw(CLIENT_IRSSI CLIENT_XCHAT CLIENT_PURPLE)],
283             type => "bits",
284             mapname => "client_type_to_str";
285            
286             my $client_type = CLIENT_IRSSI | CLIENT_PURPLE;
287            
288             print client_type_to_str($client_type); #prints 'CLIENT_IRSSI|CLIENT_PURPLE';
289              
290             Generate reverse maps, but do not generate values. also, push to exporter
291              
292             #Must define @EXPORT_OK and tags beforehand
293            
294             our @EXPORT_OK;
295             our %EXPORT_TAGS;
296            
297             use Constant::Generate {
298             O_RDONLY => 00,
299             O_WRONLY => 01,
300             O_RDWR => 02,
301             O_CREAT => 0100
302             }, tag => "openflags", type => 'bits';
303            
304             my $oflags = O_RDWR|O_CREAT;
305             print openflags_to_str($oflags); #prints 'O_RDWR|O_CREAT';
306              
307             DWIM Constants
308              
309             use Constant::Generate {
310             RDONLY => 00,
311             WRONLY => 01,
312             RDWR => 02,
313             CREAT => 0100
314             }, prefix => 'O_', dualvar => 1;
315            
316             my $oflags = O_RDWR|O_CREAT;
317             O_RDWR eq 'RDWR';
318              
319             Export to other packages
320              
321             package My::Constants
322             BEGIN { $INC{'My/Constants.pm} = 1; }
323            
324             use base qw(Exporter);
325             our (@EXPORT_OK,@EXPORT,%EXPORT_TAGS);
326            
327             use Constant::Generate [qw(FOO BAR BAZ)],
328             tag => "my_constants",
329             export_ok => 1;
330            
331             package My::User;
332             use My::Constants qw(:my_constants);
333             FOO == 0 && BAR == 1 && BAZ == 2 &&
334             my_constants_to_str(FOO eq 'FOO') && my_constants_to_str(BAR eq 'BAR') &&
335             my_constants_to_str(BAZ eq 'BAZ');
336              
337             =head2 DESCRIPTION
338              
339             C<Constant::Generate> provides useful utilities for handling, debugging, and
340             generating opaque, 'magic-cookie' type constants as well as value-significant
341             constants.
342              
343             Using its simplest interface,
344             it will generate a simple enumeration of names passed to it on import.
345              
346             Read import options to use.
347              
348             =head2 USAGE
349              
350             All options and configuration for this module are specified at import time.
351              
352             The canonical usage of this module is
353            
354             use Constant::Generate $symspec, %options;
355              
356             =head3 Symbol Specifications
357              
358             This is passed as the first argument to C<import> and can exist as a reference
359             to either a hash or an array. In the case of an array reference, the array will
360             just contain symbol names whose values will be automatically assigned in order,
361             with the first symbol being C<0> and each subsequent symbol incrementing on
362             the value of the previous. The default starting value can be modified using the
363             C<start_at> option (see L</Options>).
364              
365             If the symbol specification is a hashref, then keys are symbol names and values
366             are the symbol values, similar to what L<constant> uses.
367              
368             By default, symbols are assumed to correlate to a single independent integer value,
369             and any reverse mapping performed will only ever map a symbol value to a single
370             symbol name.
371              
372             For bitflags, it is possible to specify C<type =E<gt> 'bits'> in the L</Options>
373             which will modify the auto-generation of the constants as well as provide
374             suitable output for reverse mapping functions.
375              
376             =head3 Basic Options
377              
378             The second argument to the import function is a hash of options.
379              
380             All options may be prefixed by a dash (C<-option>) or in their 'bare' form
381             (C<option>).
382              
383             =over
384              
385             =item C<type>
386              
387             This specifies the type of constant used in the enumeration for the first
388             argument as well as the generation of reverse mapping functions.
389             Valid values are ones matching the regular expression C</bit/i> for
390             bitfield values, and ones matching C</int/i> for simple integer values.
391              
392             You can also specify C</str/i> for string constants. When the symbol specification
393             is an array, the value for the string constants will be the strings themselves.
394              
395             If C<type> is not specified, it defaults to integer values.
396              
397             =item C<start_at>
398              
399             Only valid for auto-generated numeric values.
400             This specifies the starting value for the first constant of the enumeration.
401             If the enumeration is a bitfield, then the
402             value is a factor by which to left-shift 1, thus
403            
404             use Constant::Generate [qw(OPT_FOO OPT_BAR)], type => "bits";
405            
406             OPT_FOO == 1 << 0;
407             OPT_BAR == 1 << 1;
408             #are true
409              
410             and so on.
411              
412             For non-bitfield values, this is simply a counter:
413              
414             use Constant::Generate [qw(CONST_FOO CONST_BAR)], start_at => 42;
415            
416             CONST_FOO == 42;
417             CONST_BAR == 43;
418              
419             =item C<tag>
420              
421             Specify a tag to use for the enumeration.
422              
423             This tag is used to generate the reverse mapping function, and is also the key
424             under which symbols will be exported via C<%EXPORT_TAGS>.
425              
426             =item C<mapname>
427              
428             Specify the name of the reverse mapping function for the enumeration. If this is
429             omitted, it will default to the form
430              
431             $tag . "_to_str";
432              
433             where C<$tag> is the L</tag> option passed. If neither are specified, then a
434             reverse mapping function will not be generated.
435              
436             =item C<export>, C<export_ok>, C<export_tags>
437              
438             This group of options specifies the usage and modification of
439             C<@EXPORT>, C<@EXPORT_OK> and C<%EXPORT_TAGS> respectively,
440             which are used by L<Exporter>.
441              
442             Values for these options should either be simple scalar booleans,
443             or reference objects corresponding to the appropriate variables.
444              
445             If references are not used as values for these options, C<Constant::Generate>
446             will expect you to have defined these modules already, and otherwise die.
447              
448             =item C<prefix>
449              
450             Set this to a string to be prefixed to all constant names declared in the symbol
451             specification; thus the following are equivalent:
452              
453             use Constant::Generate [qw( MY_FOO MY_BAR MY_BAZ )];
454              
455             With auto-prefixing:
456              
457             use Constant::Generate [qw( FOO BAR BAZ )], prefix => "MY_";
458              
459             =item C<show_prefix>
460              
461             When prefixes are specified, the default is that reverse mapping functions will
462             display only the 'bare', user-specified name. Thus:
463              
464             use Constant::Generate [qw( FOO )], prefix => "MY_", mapname => "const_str";
465             const_str(MY_FOO) eq 'FOO';
466              
467             Setting C<show_prefix> to a true value will display the full name.
468              
469             =back
470              
471             =head3 Dual-Var Constants
472              
473             Use of dual variable constants (which return an integer or string value depending
474             on the context) can be enabled by passing C<stringy_vars> to C<Constant::Generate>,
475             or using C<Constant::Generate::Dualvar>:
476              
477             =over
478              
479             =item C<stringy_vars>
480              
481             =item C<dualvar>
482              
483             This will apply some trickery to the values returned by the constant symbols.
484              
485             Normally, constant symbols will return only their numeric value, and a reverse
486             mapping function is needed to retrieve the original symbolic name.
487              
488             When C<dualvar> is set to a true value the values returned by the constant
489             subroutine will do the right thing in string and numeric contexts; thus:
490              
491             use Constant::Generate::Dualvar [qw(FOO BAR)];
492            
493             FOO eq 'FOO';
494             FOO == 0;
495              
496             The L</show_prefix> option controls whether the prefix is part of the stringified
497             form.
498              
499             Do not rely too much on C<dualvar> to magically convert any number into
500             some meaningful string form. In particular, it will only work on scalars which
501             are directly descended from the constant symbols. Paritcularly, this means that
502             unpack()ing or receiving data from a different process will not result in these
503             special stringy variables.
504              
505             The C<stringy_vars> option is an alias for C<dualvar>,
506             which is supported for backwards compatibility.
507              
508             =back
509              
510             =head3 Listings
511              
512             The following options enable constant subroutines which return lists of the
513             symbols or their values:
514              
515             use Constant::Generate [qw(
516             FOO BAR BAZ
517             )],
518             allvalues => "VALS",
519             allsyms => "SYMS";
520            
521             printf "VALUES: %s\n", join(", ", VALUES);
522             # => 0, 1, 2 (in no particular order)
523            
524             printf "SYMBOLS: %s\n", join(", ", SYMS);
525             # => FOO, BAR, BAZ (in no particular order)
526              
527             Or something potentially more useful:
528              
529             use Constant::Generate [qw(
530             COUGH
531             SNEEZE
532             HICCUP
533             ZOMBIES
534             )],
535             type => 'bits',
536             allvalues => 'symptoms',
537             mapname => "symptom_str";
538            
539             my $remedies = {
540             COUGH, "Take some honey",
541             SNEEZE, "Buy some tissues",
542             HICCUP, "Drink some water"
543             };
544            
545             my $patient = SNEEZE | COUGH | ZOMBIES;
546            
547             foreach my $symptom (symptoms()) {
548             next unless $patient & $symptom;
549             my $remedy = $remedies->{$symptom};
550             if(!$remedy) {
551             printf "Uh-Oh, we don't have a remedy for %s. Go to a hospital!\n",
552             symptom_str($symptom);
553             } else {
554             printf "You should: %s\n", $remedy;
555             }
556             }
557              
558             =over
559              
560             =item C<allvalues>
561              
562             Sometimes it is convenient to have a list of all the constants defined in the
563             enumeration. Setting C<allvalues> will make C<Constant::Generate> create a like-named
564             constant subroutine which will return a list of all the I<values> created.
565              
566             =item C<allsyms>
567              
568             Like L</allvalues>, but will return a list of strings for the constants in
569             the enumeration.
570              
571             =back
572              
573             =head3 EXPORTING
574              
575             This module also allows you to define a 'constants' module of your own,
576             from which you can export constants to other files in your package.
577             Figuring out the right exporter parameters is quite hairy,
578             and the export options can natually be a bit tricky.
579              
580             In order to succesfully export symbols made by this module, you must specify
581             either C<export_ok> or C<export> as hash options to C<import>. These correspond
582             to the like-named variables documented by L<Exporter>.
583              
584             Additionally, export tags can be specified only if one of the C<export> flags is
585             set to true (again, following the behavior of C<Exporter>). The auto-export
586             feature is merely one of syntactical convenience, but these three forms are
587             effectively equivalent:
588              
589             Nicest way:
590              
591             use base qw(Exporter);
592             our (@EXPORT, %EXPORT_TAGS);
593             use Constant::Generate
594             [qw(FOO BAR BAZ)],
595             export => 1,
596             tag => "some_constants"
597             ;
598              
599             A bit more explicit:
600              
601             use base qw(Exporter);
602             use Constant::Generate
603             [qw(FOO BAR BAZ)],
604             export => \our @EXPORT,
605             export_tags => \our %EXPORT_TAGS,
606             tag => "some_constants",
607             mapname => "some_constants_to_str",
608             ;
609              
610             Or DIY:
611              
612             use base qw(Exporter);
613             our @EXPORT;
614             my @SYMS;
615             BEGIN {
616             @SYMS = qw(FOO BAR BAZ);
617             }
618            
619             use Constant::Generate \@SYMS, mapname => "some_constants_to_str";
620            
621             push @EXPORT, @SYMS, "some_constants_to_str";
622             $EXPORT_TAGS{'some_constants'} = [@SYMS, "some_constants_to_str"];
623              
624             Also note that any L</allvalues>, L</allsyms>, or L</mapname>
625             subroutines will be exported according
626             to whatever specifications were configured for the constants themselves.
627              
628             =head2 NOTES
629              
630             The C<dualvar> or C<stringy_var> option can be short-handed by doing the following:
631              
632             use Constant::Generate::Dualvar [qw(
633             FOO
634             BAR
635             BAZ
636             )], prefix => 'MY_';
637             MY_FOO eq 'FOO';
638              
639             etc.
640              
641             =head1 BUGS & TODO
642              
643             It's somewhat ironic that a module which aims to promote the use of symbolic
644             constants has all of its configuration options determined by hashes and strings.
645              
646             =head1 AUTHOR & COPYRIGHT
647              
648             Copyright (c) 2011 by M. Nunberg
649              
650             You may use and distribute this software under the same terms and conditions as
651             Perl itself, OR under the terms and conditions of the GNU GPL, version 2 or greater.
652