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