File Coverage

blib/lib/Module/Generate.pm
Criterion Covered Total %
statement 352 377 93.3
branch 120 124 96.7
condition 36 63 57.1
subroutine 57 60 95.0
pod 32 35 91.4
total 597 659 90.5


line stmt bran cond sub pod time code
1             package Module::Generate;
2              
3 7     7   868983 use 5.006;
  7         30  
4 7     7   60 use strict;
  7         57  
  7         289  
5 7     7   66 use warnings;
  7         37  
  7         465  
6              
7 7     7   57 use Cwd qw/abs_path/;
  7         21  
  7         483  
8 7     7   10099 use Perl::Tidy;
  7         3937631  
  7         1173  
9 7     7   75 use Data::Dumper;
  7         14  
  7         436  
10 7     7   3439 use Module::Starter;
  7         19578  
  7         50  
11             $Data::Dumper::Deparse = 1;
12             our $VERSION = '1.03';
13             our %CLASS;
14             our $SUB_INDEX = 1;
15              
16             sub start {
17 10 100   10 1 315514 return ref $_[0] ? $_[0] : bless {}, $_[0];
18             }
19              
20             sub dist {
21 5     5 1 8951 $CLASS{DIST} = $_[1];
22 5 100       33 return ref $_[0] ? $_[0] : bless {}, $_[0];
23             }
24              
25             sub class {
26 36     36 1 93965 my ($self, $class) = @_;
27 36         246 $CLASS{CURRENT} = $CLASS{$class} = {
28             NAME => $class
29             };
30 36 100       234 return ref $self ? $self : bless {}, $self;
31             }
32              
33             sub lib {
34 9     9 1 14699 $CLASS{LIB} = $_[1];
35 9 100       55 return ref $_[0] ? $_[0] : bless {}, $_[0];
36             }
37              
38             sub tlib {
39 4     4 1 195667 $CLASS{TLIB} = $_[1];
40 4 100       26 return ref $_[0] ? $_[0] : bless {}, $_[0];
41             }
42              
43             sub author {
44 7     7 1 5673 $CLASS{AUTHOR} = $_[1];
45 7 100       43 return ref $_[0] ? $_[0] : bless {}, $_[0];
46             }
47              
48             sub email {
49 7     7 1 4868 $CLASS{EMAIL} = $_[1];
50 7 100       39 return ref $_[0] ? $_[0] : bless {}, $_[0];
51             }
52              
53             sub version {
54 7     7 1 4908 $CLASS{VERSION} = $_[1];
55 7 100       50 return ref $_[0] ? $_[0] : bless {}, $_[0];
56             }
57              
58             sub synopsis {
59 1     1 1 5 $CLASS{CURRENT}{SYNOPSIS} = $_[1];
60 1         5 return $_[0];
61             }
62              
63             sub abstract {
64 5     5 1 14 $CLASS{CURRENT}{ABSTRACT} = $_[1];
65 5         20 return $_[0];
66             }
67              
68             sub no_warnings {
69 0     0 1 0 my $self = shift;
70 0   0     0 $CLASS{CURRENT}{NO_WARNINGS} ||= [];
71 0         0 push @{ $CLASS{CURRENT}{NO_WARNINGS} }, @_;
  0         0  
72 0         0 return $self;
73             }
74              
75             sub no_strict {
76 0     0 1 0 my $self = shift;
77 0   0     0 $CLASS{CURRENT}{NO_STRICT} ||= [];
78 0         0 push @{ $CLASS{CURRENT}{NO_STRICT} }, @_;
  0         0  
79 0         0 return $self;
80             }
81              
82             sub use {
83 6     6 1 13 my $self = shift;
84 6   100     39 $CLASS{CURRENT}{USE} ||= [];
85 6         10 push @{ $CLASS{CURRENT}{USE} }, @_;
  6         20  
86 6         23 return $self;
87             }
88              
89             sub base {
90 3     3 1 9 my $self = shift;
91 3   100     20 $CLASS{CURRENT}{BASE} ||= [];
92 3         6 push @{ $CLASS{CURRENT}{BASE} }, @_;
  3         11  
93 3         23 return $self;
94             }
95              
96             sub parent {
97 3     3 1 9 my $self = shift;
98 3   100     23 $CLASS{CURRENT}{PARENT} ||= [];
99 3         7 push @{ $CLASS{CURRENT}{PARENT} }, @_;
  3         13  
100 3         15 return $self;
101             }
102              
103             sub require {
104 3     3 1 10 my $self = shift;
105 3   100     23 $CLASS{CURRENT}{REQUIRE} ||= [];
106 3         6 push @{ $CLASS{CURRENT}{REQUIRE} }, @_;
  3         13  
107 3         14 return $self;
108             }
109              
110             sub our {
111 5     5 1 9 my $self = shift;
112 5   100     40 $CLASS{CURRENT}{GLOBAL} ||= [];
113 5         8 push @{ $CLASS{CURRENT}{GLOBAL} }, @_;
  5         18  
114 5         29 return $self;
115             }
116              
117             sub begin {
118 4     4 1 14 $CLASS{CURRENT}{BEGIN} = $_[1];
119 4         14 return $_[0];
120             }
121              
122             sub unitcheck {
123 1     1 1 6 $CLASS{CURRENT}{UNITCHECK} = $_[1];
124 1         5 return $_[0];
125             }
126              
127             sub check {
128 1     1 1 6 $CLASS{CURRENT}{CHECK} = $_[1];
129 1         6 return $_[0];
130             }
131              
132             sub init {
133 1     1 1 5 $CLASS{CURRENT}{INIT} = $_[1];
134 1         6 return $_[0];
135             }
136              
137             sub end {
138 1     1 1 4 $CLASS{CURRENT}{END} = $_[1];
139 1         6 return $_[0];
140             }
141              
142             sub new {
143 6     6 1 34 my ($self, $sub) = @_;
144             $CLASS{CURRENT}{SUBS}{CURRENT} = $CLASS{CURRENT}{SUBS}{new} = {
145 6         69 INDEX => $SUB_INDEX++,
146             POD => "Instantiate a new $CLASS{CURRENT}{NAME} object.",
147             EXAMPLE => "$CLASS{CURRENT}{NAME}\-\>new"
148             };
149 6 100       804 $CLASS{CURRENT}{SUBS}{CURRENT}{CODE} = $sub ? $sub : eval "sub {
150             my (\$cls, \%args) = (shift, scalar \@_ == 1 ? \%{\$_[0]} : \@_);
151             bless \\%args, \$cls;
152             }";
153             $CLASS{CURRENT}{SUBS}{CURRENT}{TEST} = [
154 6         47 ['ok', sprintf 'my $obj = %s->new', $CLASS{CURRENT}{NAME}],
155             ['isa_ok', '$obj', qq|'$CLASS{CURRENT}{NAME}'|],
156             ];
157 6         28 return $self;
158             }
159              
160             sub accessor {
161 6     6 1 16 my ($self, $sub, $code) = @_;
162 6         55 $CLASS{CURRENT}{SUBS}{CURRENT} = $CLASS{CURRENT}{SUBS}{$sub} = {
163             INDEX => $SUB_INDEX++,
164             ACCESSOR => 1,
165             POD => "get or set ${sub}.",
166             EXAMPLE => "\$obj->${sub}\;\n\n\t\$obj->${sub}(\$value)\;"
167             };
168 6 100       569 $CLASS{CURRENT}{SUBS}{CURRENT}{CODE} = $code ? $code : eval "sub {
169             my (\$self, \$value) = \@_;
170             if (defined \$value) {
171             \$self->{$sub} = \$value;
172             }
173             return \$self->{$sub}
174             }";
175             $CLASS{CURRENT}{SUBS}{CURRENT}{TEST} = [
176 6         46 ['can_ok', qq|\$obj|, qq|'$sub'|],
177             ['is', qq|\$obj->$sub|, 'undef'],
178             ['is', qq|\$obj->$sub('test')|, qq|'test'|],
179             ['deep',qq|\$obj->$sub({ a => 'b' })|, qq|{ a => 'b' }|],
180             ['deep',qq|\$obj->$sub|, qq|{ a => 'b' }|]
181             ];
182 6         25 return $self;
183             }
184              
185             sub sub {
186 9     9 1 21 my ($self, $sub) = @_;
187 9         33 $CLASS{CURRENT}{SUBS}{CURRENT} = $CLASS{CURRENT}{SUBS}{$sub} = {
188             INDEX => $SUB_INDEX++
189             };
190             $CLASS{CURRENT}{SUBS}{CURRENT}{TEST} = [
191 9         32 ['can_ok', qq|\$obj|, qq|'$sub'|],
192             ];
193 9         35 return $self;
194             }
195              
196             sub macro {
197 6     6 1 29 my ($self, $name, $code) = @_;
198 6 100       44 $code = ref $code ? Dumper $code : $code;
199 6         7658 $code =~ s/\$VAR1 = //;
200 6         31 $code =~ s/sub\s*//;
201 6         122 $code =~ s/{\s*\n*|\s*\n*};$//g;
202 6         26 $CLASS{MACRO}{$name} = $code;
203 6         29 return $self;
204             }
205              
206             sub keyword {
207 6 100 100 6 1 58 my ($self, $name, %keyword) = (shift, shift, (! ref $_[0] ? @_ : ref $_[0] eq 'HASH' ? %{$_[0]} : (
  1 100       8  
    100          
208             CODE => $_[0],
209             KEYWORDS => $_[1] || [],
210             ($_[2] ? ( POD_TITLE => $_[2] ) : ())
211             )));
212 6         13 push @{$keyword{KEYWORDS}}, $name;
  6         16  
213 6         19 $CLASS{KEYWORD}{$name} = \%keyword;
214 6         36 my $MACROS = join '|', map { quotemeta($_) } keys %{$CLASS{MACRO}};
  0         0  
  6         23  
215             {
216 7     7   86089 no strict 'refs';
  7         42  
  7         25523  
  6         10  
217 6         11 my $cls = ref $self;
218 6         52 *{"${cls}::$name"} = sub {
219 5     5   14 my ($self, $value) = (shift, _stringify_struct($MACROS, @_));
220 5         24 $CLASS{CURRENT}{SUBS}{CURRENT} = $CLASS{CURRENT}{SUBS}{$SUB_INDEX} = {
221             INDEX => $SUB_INDEX++,
222             KEYWORD => $name,
223             $name => $value
224             };
225 5         8 for (qw/POD EXAMPLE/) {
226 10 100       24 if ($CLASS{KEYWORD}{$name}{"POD_$_"}) {
227 4         7 $CLASS{CURRENT}{SUBS}{CURRENT}{$_} = $CLASS{KEYWORD}{$name}{"POD_$_"};
228 4         11 $CLASS{CURRENT}{SUBS}{CURRENT}{$_} =~ s/\$keyword/$value/g;
229             }
230             }
231 5         18 return $self;
232 6         26 };
233 6         12 for my $add (@{$keyword{KEYWORDS}}) {
  6         12  
234 12 100       31 next if $add eq $name;
235 6         27 *{"${cls}::$add"} = sub {
236 2     2   6 my ($self, $code) = (shift, _stringify_struct($MACROS, @_));
237 2         4 $CLASS{CURRENT}{SUBS}{CURRENT}{$add} = $code;
238 2         11 return $self;
239 6         23 };
240             }
241             }
242 6         38 return $self;
243             }
244              
245             sub code {
246 10     10 1 19 my ($self, $code) = @_;
247 10         23 $CLASS{CURRENT}{SUBS}{CURRENT}{CODE} = $code;
248 10         31 return $self;
249             }
250              
251             sub no_code {
252 2     2 0 4 my ($self, $code) = @_;
253 2         4 $CLASS{CURRENT}{SUBS}{CURRENT}{NO_CODE} = $code;
254 2         7 return $self;
255             }
256              
257             sub pod {
258 5     5 1 10 my ($self, $pod) = @_;
259 5         13 $CLASS{CURRENT}{SUBS}{CURRENT}{POD} = $pod;
260 5         16 return $self;
261             }
262              
263             sub example {
264 4     4 1 28 my ($self, $pod) = @_;
265 4         17 $CLASS{CURRENT}{SUBS}{CURRENT}{EXAMPLE} = $pod;
266 4         12 return $self;
267             }
268              
269             sub class_tests {
270 1     1 0 2 my ($self, @tests) = @_;
271 1         1 push @{$CLASS{CURRENT}{CLASS_TESTS}}, @tests;
  1         3  
272 1         4 return $self;
273             }
274              
275             sub test {
276 4     4 1 6 my ($self, @tests) = @_;
277 4         5 push @{$CLASS{CURRENT}{SUBS}{CURRENT}{TEST}}, @tests;
  4         24  
278 4         13 return $self;
279             }
280              
281             sub clear_tests {
282 0     0 0 0 my ($self) = @_;
283 0         0 $CLASS{CURRENT}{SUBS}{CURRENT}{TEST} = [];
284 0         0 return $self;
285             }
286              
287             sub generate {
288 8     8 1 2263 my ($self, %args) = @_;
289              
290 8         34 my @classes = sort grep { $_ !~ m/^(LIB|TLIB|AUTHOR|EMAIL|VERSION|DIST|CURRENT|MACRO|KEYWORD)$/ } keys %CLASS;
  46         131  
291              
292 8   100     37 my $lib = $CLASS{LIB} || ".";
293 8         25 my $tlib = $CLASS{TLIB};
294 8 100       26 if ($CLASS{DIST}) {
295 2         4 my $distro = delete $CLASS{DIST};
296             Module::Starter->create_distro(
297             dir => $lib . "/$distro",
298             distro => $distro,
299             builder => 'ExtUtils::MakeMaker',
300             modules => [@classes],
301             author => 'LNATION',
302             email => 'email@lnation.org',
303 2         9 %{$args{DIST}}
  2         36  
304             );
305 0         0 $tlib = "$lib/$distro/t";
306 0         0 $lib = "$lib/$distro/lib";
307             }
308              
309 6         18 for my $class (@classes) {
310             my $cls = _perl_tidy(
311             sprintf(
312             qq{package %s; use strict; use warnings;%s%s%s\n%s\n%s\n%s\n\n1;\n\n__END__%s },
313             $class,
314             _build_no_strict($CLASS{$class}{NO_STRICT}),
315             _build_no_warnings($CLASS{$class}{NO_WARNINGS}),
316             _build_use($CLASS{$class}),
317             _build_global($CLASS{$class}{GLOBAL}),
318             _build_phase($CLASS{$class}),
319             _build_subs($CLASS{$class}),
320 7         48 _build_pod($class, $CLASS{$class})
321             )
322             );
323              
324 7         83 (my $path = $class) =~ s/\:\:/\//g;
325 7         27 my $file = sprintf "%s/%s.pm", $lib, $path;
326 7         38 _make_path($file);
327 7 100       1596 open(my $fh, '>', $file) or die "Cannot open file to write $!";
328 6         122 print $fh $cls;
329 6         456 close $fh;
330 6 100       78 _generate_tlib($class, $tlib) if ($tlib);
331             }
332             }
333              
334             sub _generate_tlib {
335 4     4   4943 my ($class, $tlib) = @_;
336             my $test_file = _perl_tidy(
337             sprintf(
338             qq{use Test::More; use strict; use warnings;%sdone_testing();},
339 4         32 _build_tests($CLASS{$class})
340             )
341             );
342 4         44 $class =~ s/\:\:/-/g;
343 4         17 my $file = sprintf "%s/%s.t", $tlib, $class;
344 4         22 _make_path($file);
345 4 100       710 open(my $fh, '>', $file) or die "Cannot open file to write $!";
346 3         100 print $fh $test_file;
347 3         239 close $fh;
348             }
349              
350              
351             sub _make_path {
352 13     13   164170 my $path = abs_path();
353 13         84 for (split '/', $_[0]) {
354 56 100       243 next if $_ =~ m/\.pm|\.t/;
355 45         111 $path .= "/$_";
356 45         159 $path =~ m/(.*)/;
357 45 100       1630 if (! -d $1) {
358 5 100       617 mkdir $1 or die "Cannot open file for writing $!";
359             }
360             }
361 12         49 return $path;
362             }
363              
364             sub _build_no_strict {
365 7 0 50 7   37 if ($_[0] && scalar @{$_[0]}) {
  0         0  
366 0         0 return sprintf "\nno strict qw/%s/;\n", join " ", @{$_[0]};
  0         0  
367             }
368 7         36 return '';
369             }
370              
371             sub _build_no_warnings {
372 7 0 50 7   26 if ($_[0] && scalar @{$_[0]}) {
  0         0  
373 0         0 return sprintf "\nno warnings qw/%s/;\n", join " ", @{$_[0]};
  0         0  
374             }
375 7         60 return '';
376             }
377              
378             sub _build_use {
379 8     8   21 my @codes;
380 8 100       31 if ($_[0]->{USE}) {
381 4         11 my @use = @{$_[0]->{USE}};
  4         14  
382 4         17 while (@use) {
383 4         10 my $mod = shift @use;
384 4 100 66     32 $mod .= ' ' . shift @use if ($use[0] && $use[0] =~ s/^\[(.*)\]$/$1/sg);
385 4         24 push @codes, "use $mod;";
386             }
387             }
388 8 100       32 push @codes, sprintf("use base qw/%s/;", join " ", @{$_[0]->{BASE}}) if $_[0]->{BASE};
  1         6  
389 8 100       30 push @codes, sprintf("use base qw/%s/;", join " ", @{$_[0]->{PARENT}}) if $_[0]->{PARENT};
  1         5  
390 8 100       25 push @codes, map { "use $_;" } @{$_[0]->{REQUIRE}} if $_[0]->{REQUIRE};
  1         4  
  1         3  
391 8         54 return join "\n", @codes;
392             }
393              
394             sub _build_global {
395 7     7   13 my @codes = map { "our $_;" } @{$_[0]};
  2         9  
  7         49  
396 7   100     37 $CLASS{VERSION} ||= 0.01;
397 7         31 unshift @codes, "our \$VERSION = $CLASS{VERSION};";
398 7         34 return join "\n", @codes;
399             }
400              
401             sub _build_phase {
402 8     8   1933 my $phases = shift;
403 8         14 my @codes;
404 8         21 for (qw/BEGIN UNITCHECK CHECK INIT END/) {
405 40 100       95 if ($phases->{$_}) {
406 3 100       16 my $code = ref $phases->{$_} ? Dumper $phases->{$_} : $phases->{$_};
407 3         1496 $code =~ s/\$VAR1 = //;
408 3         17 $code =~ s/^\s*sub\s*//;
409 3         63 $code =~ s/\s*\n*\s*package Module\:\:Generate\;|use warnings\;|use strict\;//g;
410 3         8 $code =~ s/};$/}/;
411 3         7 $code = sprintf "%s %s;", 'BEGIN', $code;
412 3         8 push @codes, $code;
413             }
414             }
415 8         39 return join "\n", @codes;
416             }
417              
418             sub _stringify_struct {
419 15     15   35 my ($MACROS, @struct) = @_;
420 15 100       35 if ($#struct > 0) {
421 2         6 return '(' . (join ", ", map { _stringify_struct($MACROS, $_) } @struct) . ')';
  4         10  
422             }
423 13 100       44 $struct[0] = ref $struct[0] ? Dumper $struct[0] : $struct[0];
424 13 100       371 return unless defined $struct[0];
425 12         86 $struct[0] =~ s/\$VAR1 = //;
426 12         334 $struct[0] =~ s/\s*\n*\s*package Module\:\:Generate\;|use warnings\;|use strict\;//g;
427 12         50 $struct[0] =~ s/{\s*\n*/{/;
428 12         24 $struct[0] =~ s/};$/}/;
429 12         69 $struct[0] =~ s/\&($MACROS)/$CLASS{MACRO}{$1}/g;
430 12         41 return $struct[0];
431             }
432              
433             sub _build_subs {
434 12     12   2596 my ($class) = @_;
435 12         23 my @codes;
436 12         91 delete $class->{SUBS}{CURRENT};
437 12         24 my $MACROS = join '|', map { quotemeta($_) } keys %{$CLASS{MACRO}};
  4         16  
  12         64  
438 12         65 for my $sub (sort {
439             $class->{SUBS}{$a}{INDEX} <=> $class->{SUBS}{$b}{INDEX}
440 17         35 } keys %{$class->{SUBS}}) {
  12         56  
441 21 100       73 next if $class->{SUBS}{$sub}{NO_CODE};
442 20         31 my $code;
443 20 100       79 if ($class->{SUBS}{$sub}{KEYWORD}) {
    100          
444 5         10 my $meta = $class->{SUBS}{$sub};
445 5         14 my $keyword = $CLASS{KEYWORD}{$class->{SUBS}{$sub}{KEYWORD}};
446             $meta->{CODE} = _stringify_struct(
447             $MACROS,
448 1         5 ((ref($meta->{CODE}) || "") eq "ARRAY" ? @{$meta->{CODE}} : $meta->{CODE})
449 5 100 50     24 ) if defined $meta->{CODE};
    100          
450 5 100       25 $code = $keyword->{CODE} ? $keyword->{CODE}->($meta, $keyword->{KEYWORDS}) : $meta->{CODE};
451             } elsif ($class->{SUBS}{$sub}{CODE}) {
452 14 100       86 $code = ref $class->{SUBS}{$sub}{CODE} ? Dumper $class->{SUBS}{$sub}{CODE} : $class->{SUBS}{$sub}{CODE};
453 14         25043 $code =~ s/\$VAR1 = //;
454 14         75 $code =~ s/^\s*sub\s*//;
455 14         1968 $code =~ s/\s*\n*\s*package Module\:\:Generate\;|use warnings\;|use strict\;//g;
456 14         63 $code =~ s/{\s*\n*/{/;
457 14         48 $code =~ s/};$/}/;
458 14 100       226 $code =~ s/\&($MACROS)/$CLASS{MACRO}{$1}/g if $MACROS;
459 14         36 $code = sprintf "sub %s %s", $sub, $code;
460             } else {
461 1         2 $code = sprintf "sub %s {\n\n\n}", $sub;
462             }
463 20         141 push @codes, $code;
464             }
465 12         97 return join "\n", @codes;
466             }
467              
468             sub _build_pod {
469 8     8   2634 my ($class, $definition) = @_;
470 7     7   67 my $d = do { no strict 'refs'; \*{"Module::Generate::DATA"} };
  7         13  
  7         12539  
  8         29  
  8         12  
  8         52  
471 8         96 seek $d, 0, 0;
472 8         6459 my $content = join '', <$d>;
473 8         1640 $content =~ s/^.*\n__DATA__\n/\n/s;
474 8         76 $content =~ s/\n__END__\n.*$/\n/s;
475              
476 8         48 my %sections = (
477             subs => [],
478             accessor => []
479             );
480              
481 8         18 for my $sub (sort {
482             $definition->{SUBS}{$a}{INDEX} <=> $definition->{SUBS}{$b}{INDEX}
483 17         51 } keys %{$definition->{SUBS}}) {
  8         53  
484 17 100       63 my $spod = $definition->{SUBS}{$sub}{POD} ? $definition->{SUBS}{$sub}{POD} : "";
485 17 100       54 if ($definition->{SUBS}{$sub}{KEYWORD}) {
    100          
486 3         7 my $name = $definition->{SUBS}{$sub}{$definition->{SUBS}{$sub}{KEYWORD}};
487 3         18 push @{$sections{$definition->{SUBS}{$sub}{KEYWORD}}}, $definition->{SUBS}{$sub}{EXAMPLE}
488             ? sprintf("=head2 %s\n\n%s\n\n\t%s",
489             $name, $spod, $definition->{SUBS}{$sub}{EXAMPLE})
490 3 100       5 : sprintf("=head2 %s\n\n%s", $name, $spod);
491             } elsif ($definition->{SUBS}{$sub}{ACCESSOR}) {
492 4         20 push @{$sections{accessor}}, $definition->{SUBS}{$sub}{EXAMPLE}
493             ? sprintf("=head2 %s\n\n%s\n\n\t%s",
494             $sub, $spod, $definition->{SUBS}{$sub}{EXAMPLE})
495 4 100       7 : sprintf("=head2 %s\n\n%s", $sub, $spod);
496             } else {
497 10         51 push @{$sections{subs}}, $definition->{SUBS}{$sub}{EXAMPLE}
498             ? sprintf("=head2 %s\n\n%s\n\n\t%s",
499             $sub, $spod, $definition->{SUBS}{$sub}{EXAMPLE})
500 10 100       18 : sprintf("=head2 %s\n\n%s", $sub, $spod);
501             }
502             }
503              
504 8 100       19 if (scalar @{$sections{accessor}}) {
  8         37  
505 2         5 unshift @{$sections{accessor}}, "=head1 ACCESSORS";
  2         7  
506             }
507              
508 8 100       15 if (scalar @{$sections{subs}}) {
  8         46  
509 5         8 unshift @{$sections{subs}}, "=head1 SUBROUTINES/METHODS";
  5         16  
510             }
511              
512 8         17 for (keys %{$CLASS{KEYWORD}}) {
  8         49  
513 4   66     5 unshift @{$sections{$_}}, sprintf "=head1 %s", $CLASS{KEYWORD}{$_}{POD_TITLE} || uc($_);
  4         31  
514             }
515              
516 8         21 my @subs = map { @{ $sections{$_} }} 'subs', 'accessor', sort keys %{$CLASS{KEYWORD}};
  20         39  
  20         57  
  8         28  
517              
518 8         25 my $lcname = lc($class);
519 8         30 (my $safename = $class) =~ s/\:\:/-/g;
520 8 100       42 $CLASS{EMAIL} =~ s/\@/ at / if $CLASS{EMAIL};
521             my %params = (
522             lcname => $lcname,
523             safename => $safename,
524             name => $class,
525             abstract => ($definition->{ABSTRACT} ? $definition->{ABSTRACT} : sprintf('The great new %s!', $class)),
526             version => $CLASS{VERSION} || '0.01',
527             subs => join("\n\n", @subs),
528             synopsis => ($definition->{SYNOPSIS}
529             ? $definition->{SYNOPSIS}
530             : sprintf("Quick summary of what the module does.\n\tuse %s;\n\n\tmy \$foo = %s->new();\n\n\t...", $class, $class)
531             ),
532             author => $CLASS{AUTHOR} || "AUTHOR",
533 8 100 50     176 email => $CLASS{EMAIL} || "EMAIL"
    100 100        
      100        
534             );
535              
536 8         38 my $reg = join "|", keys %params;
537              
538 8         673 $content =~ s/\{\{($reg)\}\}/$params{$1}/g;
539              
540 8         128 return $content;
541             }
542              
543             sub _perl_tidy {
544 12     12   2332 my $source = shift;
545 12         40 my $dest_string;
546             my $stderr_string;
547 12         0 my $errorfile_string;
548 12         39 my $argv = "-npro -pbp -nst -se -nola -t";
549            
550 12         122 my $error = Perl::Tidy::perltidy(
551             argv => $argv,
552             source => \$source,
553             destination => \$dest_string,
554             stderr => \$stderr_string,
555             errorfile => \$errorfile_string,
556             );
557              
558 12 100       3012075 if ($stderr_string) {
559             # serious error in input parameters, no tidied output
560 1         30 print "<<STDERR>>\n$stderr_string\n";
561 1         8 die "Exiting because of serious errors\n";
562             }
563              
564 11         119 return $dest_string;
565             }
566              
567             sub _build_tests {
568 5     5   19 my ($class, $obj_ok) = @_;
569 5         27 my $tests = sprintf("our (\$sub, \$globref); BEGIN { use_ok('%s'); \$sub = sub {}; \$globref = \\*globref; }", $class->{NAME});
570              
571 5 100       31 if ($class->{CLASS_TESTS}) {
572 1         3 my $c = 1;
573 1         2 for my $subset (@{$class->{CLASS_TESTS}}) {
  1         6  
574             $tests .= sprintf "subtest 'class_tests$c' => sub { plan tests => %s; %s };",
575 2         6 scalar @{$subset},
576 2         6 join( '', map{ _build_test($_) } @{ $subset });
  6         17  
  2         5  
577 2         7 $c++;
578             }
579             }
580 5 100       32 if ($class->{SUBS}->{new}->{TEST}) {
581             $tests .= sprintf "subtest 'new' => sub { plan tests => %s; %s };",
582 2         10 scalar @{$class->{SUBS}->{new}->{TEST}},
583 2         4 join '', map{ _build_test($_) } @{ $class->{SUBS}->{new}->{TEST} };
  4         13  
  2         8  
584 2         8 $obj_ok = $class->{SUBS}->{new}->{TEST}->[0];
585             }
586              
587 5         15 for my $sub (sort {
588 11   100     81 ($class->{SUBS}{$a}{INDEX} || 0) <=> ($class->{SUBS}{$b}{INDEX} ||0)
      100        
589 5         45 } keys %{$class->{SUBS}}) {
590 13 100       45 next if $sub eq 'new';
591 8 100       23 unshift @{$class->{SUBS}->{$sub}->{TEST}}, $obj_ok if $obj_ok;
  3         12  
592             $tests .= sprintf "subtest '%s' => sub { plan tests => %s; %s };",
593             ($class->{SUBS}->{$sub}->{KEYWORD} ? ( $class->{SUBS}->{$sub}->{KEYWORD} . ' ' . quotemeta($class->{SUBS}->{$sub}->{$class->{SUBS}->{$sub}->{KEYWORD}}) ) : $sub),
594 6         19 scalar @{$class->{SUBS}->{$sub}->{TEST}},
595 25         58 join '', map{ _build_test($_) } @{ $class->{SUBS}->{$sub}->{TEST} }
  6         18  
596 8 100       53 if $class->{SUBS}->{$sub}->{TEST};
    100          
597             }
598              
599 5         45 return $tests;
600             }
601              
602             our %TESTS;
603             BEGIN {
604             %TESTS = (
605             ok => sub {
606 11   33     120 return sprintf q|ok(%s, q{%s});|, $_[1], $_[2] || $_[1];
607             },
608             can_ok => sub {
609 3         11 return sprintf q|can_ok(%s, %s);|, $_[1], $_[2];
610             },
611             isa_ok => sub {
612 2         17 return sprintf q|isa_ok(%s, %s);|, $_[1], $_[2];
613             },
614             is => sub {
615 14   33     116 return sprintf q|is(%s, %s, q{%s});|, $_[1], $_[2], $_[3] || $_[1];
616             },
617             isnt => sub {
618 0   0     0 return sprintf q|isnt(%s, %s, q{%s});|, $_[1], $_[2], $_[3] || $_[1];
619             },
620             like => sub {
621 0   0     0 return sprintf q|like(%s, %s, q{%s});|, $_[1], $_[2], $_[3] || $_[1];
622             },
623             unlike => sub {
624 0   0     0 return sprintf q|unlike(%s, %s, q{%s});|, $_[1], $_[2], $_[3] || $_[1];
625             },
626             deep => sub {
627 4   33     38 return sprintf q|is_deeply(%s, %s, q{%s});|, $_[1], $_[2], $_[3] || $_[1];
628             },
629             eval => sub {
630 1   33     21 return sprintf q|eval {%s}; like($@, qr/%s/i, q{%s});|, $_[1], $_[2], $_[3] || $_[1];
631             }
632 7     7   856 );
633             }
634              
635             sub _build_test {
636 36     36   60 my $test = shift;
637 36 100       81 return ref $test ? $TESTS{$test->[0]}->(@{$test}) : $test;
  35         109  
638             }
639              
640             1;
641              
642             __DATA__
643              
644             =head1 NAME
645              
646             {{name}} - {{abstract}}
647              
648             =head1 VERSION
649              
650             Version {{version}}
651              
652             =cut
653              
654             =head1 SYNOPSIS
655              
656             {{synopsis}}
657              
658             {{subs}}
659              
660             =head1 AUTHOR
661              
662             {{author}}, C<< <{{email}}> >>
663              
664             =head1 BUGS
665              
666             Please report any bugs or feature requests to C<bug-{{lcname}} at rt.cpan.org>, or through
667             the web interface at L<https://rt.cpan.org/NoAuth/ReportBug.html?Queue={{safename}}>. I will be notified, and then you'll
668             automatically be notified of progress on your bug as I make changes.
669              
670             =head1 SUPPORT
671              
672             You can find documentation for this module with the perldoc command.
673              
674             perldoc {{name}}
675              
676             You can also look for information at:
677              
678             =over 2
679              
680             =item * RT: CPAN's request tracker (report bugs here)
681              
682             L<https://rt.cpan.org/NoAuth/Bugs.html?Dist={{safename}}>
683              
684             =item * Search CPAN
685              
686             L<https://metacpan.org/release/{{safename}}>
687              
688             =back
689              
690             =head1 ACKNOWLEDGEMENTS
691              
692             =head1 LICENSE AND COPYRIGHT
693              
694             This software is Copyright (c) 2020 by {{author}}.
695              
696             This is free software, licensed under:
697              
698             The Artistic License 2.0 (GPL Compatible)
699              
700             =cut
701              
702             __END__
703              
704             =head1 NAME
705              
706             Module::Generate - Assisting with module generation.
707              
708             =head1 VERSION
709              
710             Version 1.03
711              
712             =cut
713              
714             =head1 SYNOPSIS
715              
716             use Module::Generate;
717              
718             Module::Generate->dist('Plane')
719             ->author('LNATION')
720             ->email('email@lnation.org')
721             ->version('0.01')
722             ->class('Plane')
723             ->abstract('Plane')
724             ->our('$type')
725             ->begin(sub {
726             $type = 'boeing';
727             })
728             ->new
729             ->pod('Instantiate a new plane.')
730             ->example('my $plane = Plane->new')
731             ->accessor('airline')
732             ->sub('type')
733             ->code(sub { $type })
734             ->pod('Returns the type of plane.')
735             ->example('$plane->type')
736             ->sub('altitude')
737             ->code(sub {
738             $_[1] / $_[2];
739             ...
740             })
741             ->pod('Discover the altitude of the plane.')
742             ->example('$plane->altitude(100, 100)')
743             ->generate;
744              
745             ...
746              
747             Module::Generate->dist('Holiday')
748             ->author('LNATION')
749             ->email('email@lnation.org')
750             ->version('0.01')
751             ->class('Feed::Data')
752             ->use('Data::LnArray')
753             ->our('$holiday')
754             ->begin(sub {
755             $holiday = Data::LnArray->new;
756             })
757             ->sub('parse')
758             ->sub('write')
759             ->sub('render')
760             ->sub('generate')
761             ->sub('_raw')
762             ->sub('_text')
763             ->sub('_json')
764             ->generate;
765              
766             =head1 SUBROUTINES/METHODS
767              
768             =head2 start
769              
770             Instantiate a new Module::Generate object.
771              
772             my $mg = Module::Generate->start;
773              
774             =head2 dist
775              
776             Provide a name for the distribution.
777              
778             my $dist = Module::Generate->dist('Plane');
779              
780             =cut
781              
782             =head2 lib
783              
784             Provide a path where the generated files will be compiled.
785              
786             my $module = Module::Generate->lib('./path/to/lib');
787              
788             =cut
789              
790             =head2 tlib
791              
792             Provide a path where the generated test will be compiled.
793              
794             my $module = Module::Generate->tlib('./path/to/t');
795              
796             =cut
797              
798             =head2 author
799              
800             The author of the distribution/module.
801              
802             my $module = Module::Generate->author('LNATION');
803              
804             =cut
805              
806             =head2 email
807              
808             The authors email of the distribution/module.
809              
810             my $module = Module::Generate->email('email@lnation.org');
811              
812             =cut
813              
814             =head2 version
815              
816             The version number of the distribution/module.
817              
818             my $version = Module::Generate->version('0.01');
819              
820             =cut
821              
822             =head2 class
823              
824             Start a new class/package/module..
825              
826             my $class = Module::Generate->class('Plane');
827              
828             =cut
829              
830             =head2 abstract
831              
832             Provide abstract text for the class.
833              
834             $class->abstract('Over my head.');
835              
836             =head2 synopsis
837              
838             Provide a synopsis for the class.
839              
840             $class->synopsis('...');
841              
842             =cut
843              
844             =head2 use
845              
846             Declare modules that should be included in the class.
847              
848             $class->use(qw/Moo MooX::LazierAttributes/);
849              
850             =cut
851              
852             =head2 base
853              
854             Establish an ISA relationship with base classes at compile time.
855              
856             Unless you are using the fields pragma, consider this discouraged in favor of the lighter-weight parent.
857              
858             $class->base(qw/Foo Bar/);
859              
860             =cut
861              
862             =head2 parent
863              
864             Establish an ISA relationship with base classes at compile time.
865              
866             $class->parent(qw/Foo Bar/);
867              
868             =cut
869              
870             =head2 require
871              
872             Require library files to be included if they have not already been included.
873              
874             $class->require(qw/Foo Bar/);
875              
876             =cut
877              
878             =head2 our
879              
880             Declare variable of the same name in the current package for use within the lexical scope.
881              
882             $class->our(qw/$one $two/);
883              
884             =cut
885              
886             =head2 begin
887              
888             Define a code block is executed as soon as possible.
889              
890             $class->begin(sub {
891             ...
892             });
893              
894             =cut
895              
896             =head2 unitcheck
897              
898             Define a code block that is executed just after the unit which defined them has been compiled.
899              
900             $class->unitcheck(sub {
901             ...
902             });
903              
904             =cut
905              
906             =head2 check
907              
908             Define a code block that is executed just after the initial Perl compile phase ends and before the run time begins.
909              
910             $class->check(sub {
911             ...
912             });
913              
914             =cut
915              
916             =head2 init
917              
918             Define a code block that is executed just before the Perl runtime begins execution.
919              
920             $class->init(sub {
921             ...
922             });
923              
924             =cut
925              
926             =head2 end
927              
928             Define a code block is executed as late as possible.
929              
930             $class->end(sub {
931             ...
932             });
933              
934             =cut
935              
936             =head2 new
937              
938             Define an object constructor.
939              
940             $class->new;
941              
942             equivalent to:
943              
944             sub new {
945             my ($cls, %args) = (shift, scalar @_ == 1 ? %{$_[0]} : @_);
946             bless \%args, $cls;
947             }
948              
949             optionally you can pass your own sub routine.
950              
951             $class->new(sub { ... });
952              
953             =head2 accessor
954              
955             Define a accessor.
956              
957             $class->accessor('test');
958              
959             equivalent to:
960              
961             sub test {
962             my ($self, $value) = @_;
963             if ($value) {
964             $self->{$sub} = $value;
965             }
966             return $self->{$sub}
967             }";
968              
969             =head2 sub
970              
971             Define a sub routine/method.
972              
973             my $sub = $class->sub('name');
974              
975             =cut
976              
977             =head2 code
978              
979             Define the code that will be run for the sub.
980              
981             $sub->code(sub {
982             return 'Robert';
983             });
984              
985             =cut
986              
987             =head2 pod
988              
989             Provide pod text that describes the sub.
990              
991             $sub->pod('What is my name?');
992              
993             =cut
994              
995             =head2 example
996              
997             Provide a code example which will be suffixed to the pod definition.
998              
999             $sub->example('$foo->name');
1000              
1001             =cut
1002              
1003             =head2 test
1004              
1005             Provide tests for the sub.
1006              
1007             $sub->test(['is', '$obj->name', q|'test'|], [ ... ], ...)
1008              
1009             =cut
1010              
1011             =head2 macro
1012              
1013             Implement a macro that can be inserted across classes.
1014              
1015             my $mg = Module::Generate->author('LNATION')
1016             ->email('email@lnation.org')
1017             ->version('0.01');
1018             $mg->macro('self', sub {
1019             my ($self, $value) = @_;
1020             });
1021             my $class = $mg->class('Foo');
1022             $class->sub('bar')
1023             ->code(sub { &self; $value; });
1024             $class->generate;
1025              
1026             ###
1027              
1028             package Foo;
1029             use strict;
1030             use warnings;
1031             our $VERSION = 0.01;
1032              
1033             sub bar {
1034             my ( $self, $value ) = @_;
1035              
1036             $value;
1037             }
1038              
1039             1;
1040              
1041             __END__
1042              
1043             =head2 keyword
1044              
1045             Implement a keyword that can be used accross classes.
1046              
1047              
1048             my $mg = Module::Generate
1049             ->author('LNATION')
1050             ->email('email@lnation.org');
1051             $mg->keyword('with', sub {
1052             my ($meta) = @_;
1053             return qq|with $meta->{with};|;
1054             });
1055              
1056             $mg->keyword('has',
1057             CODE => sub {
1058             my ($meta) = @_;
1059             $meta->{is} ||= q|'ro'|;
1060             my $attributes = join ', ', map {
1061             ($meta->{$_} ? (sprintf "%s => %s", $_, $meta->{$_}) : ())
1062             } qw/is required/;
1063             my $code = qq|
1064             has $meta->{has} => ( $attributes );|;
1065             return $code;
1066             },
1067             KEYWORDS => [qw/is required/],
1068             POD_TITLE => 'ATTRIBUTES',
1069             POD_POD => 'get or set $keyword',
1070             POD_EXAMPLE => "\$obj->\$keyword;\n\n\t\$obj->\$keyword(\$value);"
1071             );
1072              
1073             $mg->class('Keyword')
1074             ->use('Moo')
1075             ->with(qw/'Keyword::Role'/)
1076             ->test(
1077             ['ok', q|my $obj = Keyword->new( thing => 'abc', test => 'def' )|],
1078             ['is', q|$obj->test|, q|'def'|]
1079             )
1080             ->has('thing')->required(1)
1081             ->test(
1082             ['ok', q|my $obj = Keyword->new( thing => 'abc' )|],
1083             ['is', q|$obj->thing|, q|'abc'|],
1084             ['eval', q|$obj = Keyword->new()|, 'required']
1085             );
1086              
1087             $mg->class('Keyword::Role')
1088             ->use('Moo::Role')
1089             ->has('test')->is(q|'rw'|)
1090             ->test(
1091             ['ok', q|my $obj = do { eval q{
1092             package FooBar;
1093             use Moo;
1094             with 'Keyword::Role';
1095             1;
1096             }; 1; } && FooBar->new| ],
1097             ['is', q|$obj->test|, q|undef|],
1098             ['ok', q|$obj->test('abc')|],
1099             ['is', q|$obj->test|, q|'abc'|]
1100             );
1101              
1102             =head2 class_tests
1103              
1104             Define additional subtests for a class.
1105              
1106             $mg->class_tests([
1107             ['ok', q|my $obj = do { eval q{
1108             package FooBar;
1109             use Moo;
1110             with 'Keyword::Role';
1111             1;
1112             }; 1; } && FooBar->new| ],
1113             ['is', q|$obj->test|, q|undef|],
1114             ['ok', q|$obj->test('abc')|],
1115             ['is', q|$obj->test|, q|'abc'|]
1116             ], [
1117             ['ok', q|my $obj = do { eval q{
1118             package BarFoo;
1119             use Moo;
1120             with 'Keyword::Role';
1121             1;
1122             }; 1; } && BarFoo->new| ],
1123             ['is', q|$obj->test|, q|undef|],
1124             ['ok', q|$obj->test('abc')|],
1125             ['is', q|$obj->test|, q|'abc'|]
1126             ]);
1127              
1128              
1129             =head2 generate
1130              
1131             Compile the code.
1132              
1133             $sub->generate(%args);
1134              
1135             =cut
1136              
1137             =head1 AUTHOR
1138              
1139             LNATION, C<< <email at lnation.org> >>
1140              
1141             =head1 BUGS
1142              
1143             Please report any bugs or feature requests to C<bug-module-generate at rt.cpan.org>, or through
1144             the web interface at L<https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Module-Generate>. I will be notified, and then you'll
1145             automatically be notified of progress on your bug as I make changes.
1146              
1147             =head1 SUPPORT
1148              
1149             You can find documentation for this module with the perldoc command.
1150              
1151             perldoc Module::Generate
1152              
1153             You can also look for information at:
1154              
1155             =over 2
1156              
1157             =item * RT: CPAN's request tracker (report bugs here)
1158              
1159             L<https://rt.cpan.org/NoAuth/Bugs.html?Dist=Module-Generate>
1160              
1161             =item * Search CPAN
1162              
1163             L<https://metacpan.org/release/Module-Generate>
1164              
1165             =back
1166              
1167             =head1 ACKNOWLEDGEMENTS
1168              
1169             =head1 LICENSE AND COPYRIGHT
1170              
1171             This software is Copyright (c) 2020 by LNATION.
1172              
1173             This is free software, licensed under:
1174              
1175             The Artistic License 2.0 (GPL Compatible)
1176              
1177             =cut
1178              
1179             1; # End of Module::Generate