File Coverage

blib/lib/Constant/Generator.pm
Criterion Covered Total %
statement 113 113 100.0
branch 42 48 87.5
condition 9 13 69.2
subroutine 26 26 100.0
pod 1 1 100.0
total 191 201 95.0


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