File Coverage

blib/lib/Constant/Generator.pm
Criterion Covered Total %
statement 114 115 99.1
branch 44 52 84.6
condition 6 11 54.5
subroutine 26 26 100.0
pod 1 1 100.0
total 191 205 93.1


line stmt bran cond sub pod time code
1             package Constant::Generator;
2              
3             require 5.006001;
4 6     6   112422 use Carp;
  6         9  
  6         414  
5 6     6   28 use strict;
  6         8  
  6         137  
6 6     6   40 use warnings 'all';
  6         11  
  6         256  
7 6     6   21 no warnings 'uninitialized';
  6         7  
  6         5135  
8              
9             our $VERSION = '1.013';
10             our %GEN;
11              
12             my ($idx, $fl_set_ldr, $c);
13              
14             my $ldr = sub{
15             my $mod = pop;
16             $GEN{$mod} ? (
17             ($c->{$mod} = 0),
18             return sub{$c->{$mod} ? (($_=''),(keys(%$c) > 1 ? delete($c->{$mod}) : undef %$c),return undef) : (($_=$GEN{$mod}),return ++$c->{$mod})}
19             ) : (
20             return undef
21             );
22             };
23              
24             sub gen{
25 28     28 1 18968 my ($pkg, $list, $h) = @_;
26              
27             @_ > 1 or (
28             $h = $pkg,
29 28 50       138 ($pkg, $list) = @$h{qw/pkg list/}
30             );
31              
32 28 100 50     87 $h and (ref $h eq 'HASH' or croak "wrong usage: $h isn't hash");
33              
34 28 50       39 $pkg || croak 'no package name';
35 28 50 33     123 ref $list eq 'ARRAY' and @$list > 0 or croak 'no constant names';
36              
37 28 100       56 $h->{fl_exp_ok} and $h->{fl_exp}=1;
38 28 100   57   37 my @cnst_decls = map { &{$h->{sub_dcrtr} || sub{"\U$h->{prfx}$_[0]"}}($_) } @$list;
  59         40  
  59         288  
  57         216  
39 28         48 undef $list;
40              
41 28 100       56 my ($i, $t, $WW) = defined($h->{int0}) ? $h->{int0} : 1;
42 28         84 my $qr = qr/Bareword/oi;
43 28         31 my %dcl = map{$_ => do{
  59         46  
44 59 100   41   51 $t = &{$h->{sub} || sub{$_[0]}}($i++);
  59         246  
  41         40  
45 59 100   16   286 local $SIG{__WARN__}=sub{($WW=join '',@_)=~$qr && die "bareword\n";};
  16         227  
46 59         2021 eval $t;
47 59 100       435 $@=~$qr ? "\"$t\"" : $t
48             }
49             } @cnst_decls;
50 28         42 undef @cnst_decls;
51              
52             my $s =
53             'package '.$pkg.';'
54             .($h->{fl_exp} ?
55             'require Exporter;our @ISA=qw/Exporter/;'
56             :
57             ''
58             ).(
59             $constant::VERSION < 1.03 ? (
60 0         0 'use constant '.(join 'use constant ', map{"$_=>$dcl{$_};"} keys %dcl)
61             ) : (
62 59         231 'use constant {'.(join ',', map{"$_=>$dcl{$_}"} keys %dcl).'};'
63             )
64             ).($h->{fl_exp} ?
65             'our @EXPORT'.($h->{fl_exp_ok} ? '_OK' : '').'=qw/'.(join ' ', keys %dcl).'/;'
66             :
67             ''
68             ).($h->{fl_decl} ?
69 22         61 'our %CONSTS=('.(join ',', map{"$_=>$dcl{$_}"} keys %dcl).');'
70             :
71             ''
72             ).($h->{fl_rev} ?
73 28 100       161 'our %STSNOC=('.(join ',', map{"$dcl{$_}=>\"$_\""} keys %dcl).');'
  22 50       55  
    100          
    100          
    100          
    100          
74             :
75             ''
76             ).('1');
77 28         58 undef %dcl;
78              
79 28 100       57 unless($h->{fl_no_load}){
80 4     4   20 eval $s;
  4     3   5  
  4     1   370  
  3     1   13  
  3     1   3  
  3     1   214  
  1     1   4  
  1     1   1  
  1     1   54  
  1     1   4  
  1     1   1  
  1     1   90  
  1     1   4  
  1     1   0  
  1     1   55  
  1     1   3  
  1     1   1  
  1     1   54  
  1         4  
  1         1  
  1         44  
  1         4  
  1         1  
  1         55  
  1         3  
  1         1  
  1         779  
  1         3  
  1         1  
  1         46  
  1         3  
  1         1  
  1         45  
  1         4  
  1         1  
  1         48  
  1         3  
  1         1  
  1         48  
  1         4  
  1         1  
  1         56  
  1         3  
  1         1  
  1         62  
  1         4  
  1         1  
  1         56  
  1         4  
  1         1  
  1         57  
  1         4  
  1         1  
  1         70  
  23         1626  
81 23 50       56 $@ && die("Constant generation error: $@");
82             }
83              
84 28         34 my $fn = $pkg; $fn=~s/::/\//og; $fn .= '.pm';
  28         41  
  28         30  
85 28 100 66     89 if(!$h->{fl_no_load} and $h->{fl_no_ldr}){
86 5 100       10 $h->{fl_no_inc_stub} or $INC{$fn} = ''
87             }
88              
89 28         45 $GEN{$fn} = $s;
90             $fl_set_ldr || $h->{fl_no_ldr} || (
91 28 100 66     63 $fl_set_ldr++,
92             $idx = unshift(@INC, $ldr),
93             );
94              
95 28 100       47 if($h->{fl_exp2file}){
96 1 50       2 defined($h->{root_dir}) or ($h->{root_dir} = '.');
97             -d ($h->{root_dir}) || (
98             warn("WARNING: export directory $h->{root_dir} isn't usable; force working directory to .\n"),
99 1 50       15 ($h->{root_dir} = '.'),
100             );
101              
102 1 50       60 open my $fh, "> $h->{root_dir}/${fn}" or die "Can't create file $h->{root_dir}/$fn, error: $!\n";
103 1         9 print $fh $s;
104 1         32 close $fh;
105             }
106              
107 28 100       38 $h->{sub_post_src} && &{$h->{sub_post_src}}($s);
  3         8  
108              
109 28         87 undef $_ for ($WW, $t, $h, $qr);
110              
111 28         1458 1;
112             }
113              
114             *generate = \&gen;
115              
116             1;
117              
118             =pod
119              
120             =head1 NAME
121              
122             Constant::Generator - this module bring flexible (I hope) constant generator to You
123              
124             =head1 VERSION
125              
126             version 1.013
127              
128             =head1 DESCRIPTION
129              
130             This module has only one short `workhorse' that implement constant generation logic.
131             This workhorse do perl-source code generation and come to you with extra power via options (logic modificators).
132             Let me save Your time in constant generation :).
133              
134             =head1 SYNOPSYS
135              
136             use Constant::Generator;
137              
138             # eval use constant {ERR_SUCCESS => 1, ERR_PERMS => 2} and put constant names to @EXPORT_OK
139             Constant::Generator::gen('Sys::Errors', [qw/success perms/], {fl_exp_ok => 1, prfx => 'ERR_',});
140              
141             # eval use constant {EV_SYNC => 1, EV_TIMEOUT => 2} and put EV_* constant name to @EXPORT
142             Constant::Generator::gen('Sys::Events', [qw/sync timeout/], {fl_exp => 1, prfx => 'EV_',});
143              
144             # generate source code and save pm-file in specified path
145             # if You're not ready to read `on-line' source files, perltidy can help you; enjoy :)
146             Constant::Generator::gen('Sys::Flags', [qw/HTTP_REDIRECT SERVICE_NOT_AVAIL/], {
147             fl_exp => 1, # generate source with exportable constants
148             prfx => 'FL_', # all constants has FL_ prefix
149             fl_decl => 0, # don't fill Sys::Flags::CONSTS hash defined `key-value' pairs
150             fl_rev => 1, # set Sys::Flags::STSNOC (reversed for CONSTS) hash with `value-key' pairs
151             fl_no_load => 1, # don't load code
152             fl_no_ldr => 1, # don't set loader at @INC
153             fl_exp2file => 1, # export source code to pm-file
154             root_dir => '/mnt/remote/sshfs/hypnotoad_controller', # yep, I'm mojolicious fun..so what? :)
155             });
156              
157             =head1 USE CASE
158              
159             I think that this module is good solution to generate application constants at bootstrap time using predefined
160             lists and rules. It provide easy way to synchronize constants over network for linked services.
161              
162             =head1 INTERFACE
163              
164             =head2 Functions
165              
166             =over 4
167              
168             =item gen
169              
170             gen($pkg_name, $list_array, $options_hash);
171              
172             This sub implement full logic. Support two call forms:
173             1) full form: Constant::Generator::gen('Sys::Event', [qw/alert warn/], {fl_exp => 1});
174             2) all-in-options: Constant::Generator::gen({fl_exp => 1, pkg => 'Sys::Event', list => [qw/alert warn/]});
175              
176             =back
177              
178             =head2 Options
179              
180             =over 4
181              
182             =item pkg
183              
184             (`package') usable only in second call form; package name for constants
185              
186             =item list
187              
188             (`list') usable only in second call form; array of constant names
189              
190             =item fl_exp
191              
192             (`flag EXPORT') make constants auto-exportable (fill @EXPORT, man Exporter); so use Sys::Event will export
193             constants
194              
195             =item fl_exp_ok
196              
197             (`flag EXPORT_OK') make constants exportable, but no autoexport (fill @EXPORT_OK, man Exporter);
198             so use Sys::Event qw'ALERT' will export ALERT constant
199              
200             =item prfx
201              
202             (`prefix') prepend constant names with static prefix
203              
204             =item sub_dcrtr
205              
206             (`sub decorator') by default, generator uppercase all constant names, but you can set custom constant name
207             decorator to evaluate constant names at runtime; this options also override prfx set:
208              
209             # generate constants using `rot13-decorator` to define constant names
210             Constant::Generator::gen('TestPkg16', [qw/const33 const34/], {
211             fl_exp => 1,
212             fl_decl => 1,
213             fl_rev => 1,
214             prfx => 'CONST_',
215             sub_dcrtr => sub{ # `rot13-decorator'
216             my $a = $_[0]=~tr/a-zA-Z/n-za-mN-ZA-M/r;
217             },
218             });
219              
220             =item int0
221              
222             by default is 1 - value for first constant; autoincrement for next constants;
223              
224             =item sub
225              
226             (`substitute') set function to disable default constant evaluation (int0 option) that will
227             generate constant values, ex:
228              
229             # customized constant values
230             Constant::Generator::gen('TestPkg5', [qw/const11 const12/], {
231             fl_exp => 1,
232             sub => sub{($_[0]<<2)}
233             });
234              
235              
236             =item fl_decl
237              
238             (`flag declaration') is set, after definition all constants and values will be available at
239             %{__PACKAGE__::CONSTS} hash
240              
241             =item fl_rev
242              
243             (`flag reverse declaration') same as above but reversed pair (values => keys) will be available at
244             %{__PACKAGE__::STSNOC} hash
245              
246             =item fl_no_load
247              
248             (`flag no loading') don't load constants, i.e. generator don't call eval for generated source code
249              
250             =item fl_no_ldr
251              
252             (`flag no loader') don't put loader sub into @INC
253              
254             =item fl_no_inc_stub
255              
256             (`flag no %INC stub') this options usable only if fl_no_load isn't set and fl_no_ldr is set;
257             if flag is not set then generator set $INC{__PACKAGE__} to '' (this stub allow to load module
258             later even without loader in @INC). Set flag to disable it (use will throw an error).
259              
260             Constant::Generator::gen('TestPkg18', [qw/const37 const38/], {
261             fl_exp => 1,
262             fl_no_load => 0,
263             fl_no_ldr => 1,
264             fl_no_inc_stub => 1, # default is 0
265             });
266             # here $INC{TestPkg18} is set to ''
267             ...
268             use TestPkg18;
269             # here we have two exported constants: CONST37 and CONST38
270              
271             =item fl_exp2file
272              
273             (`flag export to file') if flag is set than constant generator will export source to pm-file
274              
275             =item root_dir
276              
277             (`root directory') directory for generated sources; '.' by default
278              
279             =item sub_post_src
280              
281             (`sub post source') provide custom function to accept generated source code in first argument
282              
283             sub src_print{
284             print $_[0];
285             }
286              
287             Constant::Generator::gen('TestPkg23', [qw/const47 const48/], {
288             ...
289             sub_post_src => \&src_print
290             });
291              
292             =back
293              
294             =head1 AUTHOR
295              
296             Tvori Dobro
297              
298             =head1 COPYRIGHT AND LICENSE
299              
300             This library is free software; you can redistribute it and/or modify
301             it under the same terms as Perl itself.
302              
303             =cut