File Coverage

blib/lib/Distribution/Cooker.pm
Criterion Covered Total %
statement 59 101 58.4
branch 10 30 33.3
condition 4 8 50.0
subroutine 18 24 75.0
pod 15 15 100.0
total 106 178 59.5


line stmt bran cond sub pod time code
1             package Distribution::Cooker;
2 7     7   11939 use v5.14;
  7         25  
3              
4 7     7   3894 use subs qw();
  7         169  
  7         198  
5 7     7   37 use vars qw($VERSION);
  7         16  
  7         347  
6              
7 7     7   41 use File::Basename qw(dirname);
  7         15  
  7         522  
8 7     7   52 use File::Path qw(make_path);
  7         13  
  7         464  
9              
10             $VERSION = '1.023';
11              
12             =encoding utf8
13              
14             =head1 NAME
15              
16             Distribution::Cooker - Create a module directory from your own templates
17              
18             =head1 SYNOPSIS
19              
20             use Distribution::Cooker;
21              
22             Distribution::Cooker->run( ... );
23              
24             # most of this should go through the dist_cooker sketch
25              
26             =head1 DESCRIPTION
27              
28             =over 4
29              
30             =cut
31              
32 7     7   44 use Carp qw(croak carp);
  7         16  
  7         383  
33 7     7   46 use Cwd;
  7         13  
  7         440  
34 7     7   5226 use Config::IniFiles;
  7         207612  
  7         305  
35 7     7   3634 use File::Spec::Functions qw(catfile);
  7         5678  
  7         9554  
36              
37             __PACKAGE__->run( $ARGV[0] ) unless caller;
38              
39             =item run( [ MODULE_NAME, [ DESCRIPTION ] ] )
40              
41             Calls pre-run, collects information about the module you want to
42             create, cooks the templates, and calls post-run.
43              
44             If you don't specify the module name, it prompts you. If you don't
45             specify a description, it prompts you.
46              
47             =cut
48              
49             sub run {
50 3     3 1 9306 my( $class, $module, $description ) = @_;
51              
52 3         9 my $self = $class->new;
53 3         11 $self->init;
54              
55 3         6 $self->pre_run;
56              
57 3   66     13 $self->module(
58             $module || prompt( "Module name" )
59             );
60 3 100       6 croak( "No module specified!\n" ) unless $self->module;
61 2 50       5 croak( "Illegal module name [$module]\n" )
62             unless $self->module =~ m/ \A [A-Za-z0-9_]+ ( :: [A-Za-z0-9_]+ )* \z /x;
63 2   66     11 $self->description(
64             $description || prompt( "Description" )
65             );
66              
67 2         4 $self->dist(
68             $self->module_to_distname( $self->module )
69             );
70              
71 2         7 $self->cook;
72              
73 2         7 $self->post_run;
74              
75 2         9 $self;
76             }
77              
78             =item new
79              
80             Create the bare object. There's nothing fancy here, but if you need
81             something more powerful you can create a subclass.
82              
83             =cut
84              
85             # There's got to be a better way to deal with the config
86             sub new {
87 9     9 1 17503 my $file = catfile( $ENV{HOME}, '.dist_cookerrc' );
88 9         25 my $config;
89 9         22 my( $name, $email ) = ( 'Frank Serpico', 'serpico@example.com' );
90              
91 9 50       183 if( -e $file ) {
92 0         0 require Config::IniFiles;
93 0         0 $config = Config::IniFiles->new( -file => $file );
94 0         0 $name = $config->val( 'user', 'name' );
95 0         0 $email = $config->val( 'user', 'email' );
96             }
97              
98             bless {
99 9         94 name => $name,
100             email => $email,
101             }, $_[0]
102             }
103              
104             =item init
105              
106             Initialize the object. There's nothing fancy here, but if you need
107             something more powerful you can create a subclass.
108              
109             =cut
110              
111 4     4 1 466 sub init { 1 }
112              
113             =item pre_run
114              
115             Method to call before run() starts its work. run() will call this for
116             you. By default this is a no-op, but you can redefine it or override
117             it in a subclass.
118              
119             run() calls this method immediately after it creates the object but
120             before it initializes it.
121              
122             =cut
123              
124 4     4 1 898 sub pre_run { 1 }
125              
126             =item post_run
127              
128             Method to call after C ends its work. C calls this for
129             you. By default this is a no-op, but you can redefine it or override
130             it in a subclass.
131              
132             =cut
133              
134 3     3 1 28 sub post_run { 1 }
135              
136             =item cook
137              
138             Take the templates and cook them. This version uses Template
139             Toolkit, but you can make a subclass to override it.
140              
141             I assume my own favorite values, and haven't made these
142             customizable yet.
143              
144             =over 4
145              
146             =item ttree (from Template) is in C
147              
148             =item Your distribution template directory is F<~/.templates/dist_cooker>
149              
150             =item Your module template name is F
151              
152             =back
153              
154             When C processes the templates, it provides definitions for
155             these template variables:
156              
157             =over 4
158              
159             =item description => the module description
160              
161             =item module => the package name (Foo::Bar)
162              
163             =item module_dist => the distribution name (Foo-Bar)
164              
165             =item module_file => module file name (Bar.pm)
166              
167             =item module_path => module path under lib/ (Foo/Bar.pm)
168              
169             =item repo_name => lowercase module with hyphens (foo-bar)
170              
171             =item year => the current year
172              
173             =back
174              
175             While processing the templates, C ignores F<.git>, F<.svn>, and
176             F directories.
177              
178             =cut
179              
180             sub cook {
181 0     0 1 0 my $self = shift;
182              
183             my( $module, $dist, $path ) =
184 0         0 map { $self->$_() } qw( module dist module_path );
  0         0  
185              
186 0 0       0 mkdir $dist, 0755 or croak "mkdir $dist: $!";
187 0 0       0 chdir $dist or croak "chdir $dist: $!";
188              
189 0         0 my $cwd = cwd();
190 0         0 my $year = ( localtime )[5] + 1900;
191 0         0 my $repo_name = lc( $module =~ s/::/-/gr );
192              
193 0         0 my $email = $self->{email};
194 0         0 my $name = $self->{name};
195 0         0 my $description = $self->description;
196              
197             # this is a terrible way to do this. I'll get right on that.
198 0         0 my @command = ( $self->ttree_command ,
199             "-s", $self->distribution_template_dir ,
200             "-d", cwd(), ,
201             "-define", qq|module='$module'| ,
202             "-define", qq|module_dist='$dist'| ,
203             "-define", qq|year='$year'| ,
204             "-define", qq|module_path='$path'| ,
205             "-define", qq|repo_name='$repo_name'| ,
206             "-define", qq|description='$description'| ,
207             "-define", qq|email='$email'| ,
208             "-define", qq|name='$name'| ,
209             q{--ignore='^.*'} ,
210             );
211              
212 0         0 system { $command[0] } @command;
  0         0  
213              
214 0         0 my $dir = catfile( 'lib', dirname( $path ) );
215 0         0 print "dir is [$dir]\n";
216 0         0 make_path( $dir );
217 0 0       0 croak( "Directory [$dir] does not exist" ) unless -d $dir;
218              
219 0         0 my $old = catfile( 'lib', $self->module_template_basename );
220 0         0 my $new = catfile( 'lib', $path );
221              
222 0 0       0 rename $old => $new
223             or croak "Could not rename [$old] to [$new]: $!";
224             }
225              
226             =item ttree_command
227              
228             Returns the name for the ttree command from template, and croaks if
229             that path does not exist or is not executable.
230              
231             The default path is F. Change this with the TTREE
232             environment variable or you can override this in a subclass.
233              
234             =cut
235              
236             sub ttree_command {
237 0   0 0 1 0 my $path = $ENV{'TTREE'} // "/usr/local/bin/ttree";
238              
239 0 0       0 croak "Didn't find ttree at $path!\n" unless -e $path;
240 0 0       0 croak "$path is not executable!\n" unless -x $path;
241              
242 0         0 $path;
243             }
244              
245             =item distribution_template_dir
246              
247             Returns the name of the directory that contains the distribution
248             templates.
249              
250             The default path is F<~/.templates/modules>. You can override this in
251             a subclass.
252              
253             =cut
254              
255             sub distribution_template_dir {
256 0     0 1 0 my $path = catfile( $ENV{HOME}, '.templates', 'modules' );
257              
258 0 0       0 croak "Couldn't find templates at $path!\n" unless -d $path;
259              
260 0         0 $path;
261             }
262              
263             =item description
264              
265             Returns the description of the module.
266              
267             The default name is C. You can override
268             this in a subclass.
269              
270             =cut
271              
272             sub description {
273 2 50   2 1 8 $_[0]->{description} = $_[1] if defined $_[1];
274 2 50       6 $_[0]->{description} || 'TODO: describe this module'
275             }
276              
277             =item module_template_basename
278              
279             Returns the name of the file that is the module.
280              
281             The default name is F. You can override this in a subclass.
282              
283             =cut
284              
285             sub module_template_basename {
286 0     0 1 0 "Foo.pm";
287             }
288              
289             =item module( [ MODULE_NAME ] )
290              
291             Return the module name. With an argument, set the module name.
292              
293             =cut
294              
295             sub module {
296 15 100   15 1 832 $_[0]->{module} = $_[1] if defined $_[1];
297 15         273 $_[0]->{module};
298             }
299              
300             =item module_path()
301              
302             Return the module path under F. You must have set C
303             already.
304              
305             =cut
306              
307             sub module_path {
308 0     0 1 0 my @parts = split /::/, $_[0]->{module};
309 0 0       0 return unless @parts;
310 0         0 $parts[-1] .= '.pm';
311 0         0 my $path = catfile( @parts );
312             }
313              
314             =item dist( [ DIST_NAME ] )
315              
316             Return the dist name. With an argument, set the module name.
317              
318             =cut
319              
320             sub dist {
321 7 100   7 1 466 $_[0]->{dist} = $_[1] if defined $_[1];
322 7         33 $_[0]->{dist};
323             }
324              
325             =item module_to_distname( MODULE_NAME )
326              
327             Take a module name, such as C, and turn it into a
328             distribution name, such as C.
329              
330             =cut
331              
332             sub module_to_distname {
333 3     3 1 834 my( $self, $module ) = @_;
334              
335 3         7 my $dist = $module; $dist =~ s/::/-/g;
  3         30  
336 3         6 my $file = $module; $file =~ s/.*:://; $file .= ".pm";
  3         14  
  3         7  
337              
338 3         14 return $dist;
339             }
340              
341             =item prompt( MESSAGE )
342              
343             Show the user MESSAGE, grap a line from STDIN, and return it.
344              
345             =cut
346              
347             sub prompt {
348 0     0 1   print join "\n", @_;
349 0           print "> ";
350              
351 0           my $line = ;
352 0           chomp $line;
353 0           $line;
354             }
355              
356             =back
357              
358             =head1 TO DO
359              
360             Right now, C uses the defaults that I like, but
361             that should come from a configuration file.
362              
363             =head1 SEE ALSO
364              
365             Other modules, such as C, do a similar job but don't
366             give you as much flexibility with your templates.
367              
368             =head1 SOURCE AVAILABILITY
369              
370             This module is in Github:
371              
372             http://github.com/briandfoy/distribution-cooker/
373              
374             =head1 AUTHOR
375              
376             brian d foy, C<< >>
377              
378             =head1 COPYRIGHT AND LICENSE
379              
380             Copyright © 2008-2018, brian d foy . All rights reserved.
381              
382             You may redistribute this under the same terms as Perl itself.
383              
384             =cut
385              
386             1;