File Coverage

blib/lib/Module/Generate.pm
Criterion Covered Total %
statement 354 377 93.9
branch 120 124 96.7
condition 37 63 58.7
subroutine 57 60 95.0
pod 32 35 91.4
total 600 659 91.0


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