line
stmt
bran
cond
sub
pod
time
code
1
#!/usr/bin/perl
2
#-------------------------------------------------------------------------------
3
# Data::Edit::Conversion - Perform a restartable series of steps in parallel.
4
# Philip R Brenan at gmail dot com, Appa Apps Ltd Inc., 2018
5
#-------------------------------------------------------------------------------
6
package Data::Edit::Conversion;
7
our $VERSION = "20180610";
8
require v5.16;
9
49
49
25823
use warnings FATAL => qw(all);
49
343
49
1421
10
49
49
196
use strict;
49
98
49
1225
11
49
49
196
use Carp qw(confess cluck);
49
98
49
3920
12
49
49
22883
use Data::Dump qw(dump);
49
307083
49
2646
13
49
49
42630
use Data::Table::Text qw(:all);
49
2637719
49
22638
14
49
49
29204
use Storable;
49
131369
49
2401
15
49
49
343
use Time::HiRes qw(time);
49
98
49
294
16
49
49
3920
use utf8;
49
98
49
294
17
18
#1 Methods # Specify and run the restartable conversion of zero or more files in parallel
19
20
sub defaultMaximumNumberOfProcesses #P Default maximum number of processes to use during the conversion
21
0
0
1
0
{8}
22
23
sub defaultOutFileLimit #P Default maximum number of files to clear art a time.
24
0
0
1
0
{32}
25
26
sub new(@) #S Create a conversion specification for zero or more files represented by projects.
27
49
49
1
686
{my (@attributes) = @_; # L describing the launch
28
49
931
my $launch = bless {@attributes};
29
49
50
33
1666
ref($launch->projects) &&
30
ref($launch->projects) =~ m(\AData::Edit::Conversion::Projects\Z) or # Convert zero or more projects
31
confess "projects=>Data::Edit::Conversion::Projects required";
32
33
49
50
4165
ref($launch->convert) or confess "convert=>specification required";
34
49
50
1127
$launch->save //= q(save); # Default save folder
35
36
49
50
1666
if (my $r = ref $launch->convert) # Conversion specification
37
49
50
637
{if ($r =~ m(ARRAY))
38
49
147
{my @convert = @{$launch->convert};
49
1372
39
49
441
for my $stage(@convert)
40
196
50
33
1176
{if (ref($stage) and ref($stage) =~ m(ARRAY))
41
196
50
441
{@$stage >= 2 or
42
confess "name=>sub required for stage, got: ".dump($stage);
43
196
441
my ($name, $sub) = @$stage;
44
196
50
441
ref($name) and
45
confess "Scalar required for stage name, got: ". dump($name);
46
196
50
33
1470
ref($sub) && ref($sub) =~ m(\ACODE\Z) or
47
confess "Code required for stage $name, got: ". dump($sub);
48
}
49
else
50
0
0
{confess "[conversion stage name => conversion sub] required, got: ".
51
dump($stage);
52
}
53
}
54
55
49
343
for my $convert(@convert) # Each conversion step
56
196
1519
{my ($stepName, $sub) = @$convert; # Step name, processing sub
57
58
196
50
3479
$launch->stepNumberByName->{$stepName} and # Check each step has a unique name
59
confess "Duplicate step name: $stepName";
60
196
1372
$launch->stepNumberByName->{$stepName} = @{$launch->stepsByNumber}; # Step number from name
196
2793
61
62
196
2793
push @{$launch->stepsByNumber}, sub # Conversion steps
63
130
130
1075
{my ($launch, $projectName) = @_;
64
130
50
881
ref($launch) =~ m(Data::Edit::Conversion)s or confess;
65
130
50
332
!ref($projectName) or confess;
66
130
1193
my $S = time(); # Start time
67
130
2757
my $R = $sub->($launch->projects->{$projectName}, $stepName);
68
130
375526
$launch->projects->{$projectName}->stepTimes->{$stepName} = # Step processed in float seconds
69
time() - $S;
70
130
7955
$launch->saveProject($projectName, $stepName);
71
130
124121
$R # Return result of convertion
72
196
4606
};
73
}
74
}
75
else # Syntax
76
0
0
{confess "Convert=>[[conversion stage name => conversion sub], ...]".
77
" required, got: ".dump($launch->convert);
78
}
79
}
80
81
49
1568
return $launch; # Launch specification
82
}
83
84
sub launch($;$$) # Launch the conversion of several files, each represented by a project, in parallel processes, saving the project state after each step of the conversion so that subsequent conversions can be restarted at later steps to speed up development by bypassing initial processing steps unless they are really needed. The L and L are transferred back from each project's sub process to the main calling process so that the main process can further process their results.
85
174
174
1
811
{my ($launch, $title, $restart) = @_; # Launch specification, optional title, optional name of latest step to restart at.
86
174
2849
my $projects = $launch->projects;
87
174
33
4196
my $mp = $launch->maximumNumberOfProcesses // # Maximum number of simultaneous processes
88
defaultMaximumNumberOfProcesses;
89
90
174
50
4490
if (my $out = $launch->out) # Clear the output area if present
91
0
0
0
{my $limit = $launch->outFileLimit // defaultOutFileLimit;
92
0
0
clearFolder($out, $limit);
93
}
94
95
174
4255
$_->stepTimes = {} for values %$projects; # Reset step times
96
97
174
50
18029
if ($mp == 1) # Process sequentially
98
0
0
{for my $projectName(sort keys %$projects) # Convert matching projects
99
0
0
{$launch->launchProject($projectName, $restart);
100
}
101
}
102
else # Process in parallel
103
174
199
{my $count = 0;
104
174
992
for my $projectName(sort keys %$projects) # Convert matching projects
105
1224
50
4596
{wait if ++$count > $mp; # Wait for processes to finish
106
1224
100
882621
$launch->launchProject($projectName, $restart), exit unless fork; # Convert in parallel
107
}
108
126
100
5940
for(;;) {last if wait == -1} # Wait for conversions to complete
1134
48995019
109
110
126
539
if (1) # Reload last good step to retrieve fully processed data
111
126
806
{my @steps = reverse @{$launch->convert}; # Steps
126
17529
112
126
4286
for my $projectName(sort keys %$projects) # Projects
113
1008
2081
{for my $step(@steps) # Find latest save file
114
1008
2075
{my ($stepName) = @$step;
115
1008
2279
my $file = $launch->stepSaveFile($projectName, $stepName);
116
1008
50
59817
if (-e $file) # Retrive file
117
1008
4349
{my $d = retrieve $file;
118
1008
91753
$projects->{$projectName}->data = $d->data; # Transfer data back so a good idea to put something meaningful here especially in the last step
119
1008
17639
$projects->{$projectName}->stepTimes = $d->stepTimes; # Transfer list of steps processed
120
1008
14729
last; # Ignore earlier steps
121
}
122
}
123
}
124
}
125
}
126
}
127
128
sub restart($$;$) # Launch the conversion of several files represented by projects in parallel, starting at the specified step: the L from the previous step will be restored unless it does not exist in which case the conversion will be run from the latest step available prior to this step or right from the start.
129
125
125
1
885
{my ($launch, $restart, $title) = @_; # Launch specification, step to restart at, optional title
130
131
defined($restart) or confess "No such step: $restart, choose from: ". # No such restart step
132
125
50
467
dump([sort keys %{$launch->stepNumberByName}]);
0
0
133
134
125
100
1565
$launch->launch($title // "Restart", $restart); # Launch with restart
135
}
136
137
#1 Launch Attributes # Use these attributes to configure a launch.
138
139
genLValueScalarMethods(q(convert)); #I [[step name => sub]...] A list of steps and their associated subs to process that step. At the end of each step the data stored on L is saved to allow for a later restart at the next step.
140
genLValueScalarMethods(q(maximumNumberOfProcesses)); #I Maximum number of processes to run in parallel
141
genLValueScalarMethods(q(out)); #I Optional file output area. This area will be cleared at the start of each launch.
142
genLValueScalarMethods(q(outFileLimit)); #I Limit on the number of files to be cleared from the L folder at the start of each launch.
143
genLValueScalarMethods(q(projects)); #I A reference to a hash of Data::Edit::Conversion::Project definitions. This can be most easily created by using L.
144
genLValueScalarMethods(q(save)); #I Temporary files will be stored in this folder
145
genLValueScalarMethods(q(stepNumberByName)); #O Get the number of a step from its name
146
genLValueArrayMethods (q(stepsByNumber)); #O Array of steps to be performed. The subs in this array call the user supplied subs after approriate set up and then do the required set down after the execution of each step.
147
148
sub stepSaveFile($$$) #P Save file for a project and a step
149
1362
1362
1
2702
{my ($launch, $projectName, $step) = @_; # Launch specification, project, step name
150
1362
50
5002
ref($launch) =~ m(Data::Edit::Conversion)s or confess;
151
1362
50
2236
!ref($projectName) or confess;
152
1362
21311
fpe($launch->save, $projectName, $step, q(data))
153
}
154
155
sub deleteProject($$$) #P Delete results before executing a particular step
156
130
130
1
294
{my ($launch, $projectName, $step) = @_; # Launch specification, project, step
157
130
50
970
ref($launch) =~ m(Data::Edit::Conversion)s or confess;
158
130
50
559
!ref($projectName) or confess;
159
130
1615
my $file = $launch->stepSaveFile($projectName, $step);
160
130
20906
unlink $file;
161
}
162
163
sub saveProject($$$) #P Save project at a particular step
164
130
130
1
377
{my ($launch, $projectName, $step) = @_; # Launch specification, project, step
165
130
50
875
ref($launch) =~ m(Data::Edit::Conversion)s or confess;
166
130
50
451
!ref($projectName) or confess;
167
130
696
my $file = $launch->stepSaveFile($projectName, $step);
168
130
32475
makePath $file;
169
130
45881
my $project = $launch->projects->{$projectName};
170
130
1686
store $project, $file;
171
}
172
173
sub loadProject($$$) #P Load a project at a particular step
174
31
31
1
207
{my ($launch, $projectName, $stepNumber) = @_; # Launch specification, project, step to reload
175
31
50
407
ref($launch) =~ m(Data::Edit::Conversion)s or confess;
176
31
50
108
!ref($projectName) or confess;
177
31
881
my $step = $launch->convert->[$stepNumber][0]; # Step anme from step number
178
31
278
my $file = $launch->stepSaveFile($projectName, $step);
179
31
50
2320
-e $file or confess "No such file:\n$file";
180
31
753
my $p = retrieve $file;
181
31
50
7922
$p or confess "Unable to retrieve project save file:\n$file";
182
31
732
$launch->projects->{$projectName} = $p;
183
}
184
185
sub launchProject($$;$) #P Convert a single project in a seperate process
186
48
48
1
3208
{my ($launch, $projectName, $restart) = @_; # Launch specification, project to be processed, optional latest step to restart at
187
48
50
3429
ref($launch) =~ m(Data::Edit::Conversion)s or confess;
188
48
50
755
!ref($projectName) or confess;
189
48
11964
my $projects = $launch->projects; # Projects
190
48
1383
my $project = $projects->{$projectName}; # Project
191
48
50
702
$project or confess "No such project: $projectName";
192
48
2753
my $source = $project->source;
193
48
671
my $count = 0;
194
48
2030
my $steps = $launch->convert; # Conversion steps
195
48
712
my @steps = @$steps; # All the steps for a complete project
196
197
48
100
700
if ($restart) # Remove steps we can skip in a restart because we have a restart file for that step
198
40
1333
{my $requested = $launch->stepNumberByName->{$restart}; # Requested restart step number
199
defined($requested) or confess "No such step: $restart, choose from: ". # Missing step
200
40
50
826
dump([sort keys %{$launch->stepNumberByName}]);
0
0
201
40
95
my $actual; # Actual restart step number
202
40
721
for my $step(1..$requested) # Each possible restart step
203
63
1547
{my $stepName = $launch->convert->[$step-1][0]; # Step name from step number
204
63
981
my $save = $launch->stepSaveFile($projectName, $stepName); # Save file
205
63
50
9142
last unless -e $save; # Have to start at the preceeding step if the required start file is missing for this step
206
63
100
1
1726
last if fileOutOfDate sub{1}, $save, $source; # Have to start at the preceeding step if the source file is newer than the start file
1
140
207
62
5563
$actual = $step-1; # Step we are going to restart at
208
62
175
shift @steps; # Consider step as done
209
}
210
40
100
561
if (defined $actual) # Overlay saved project
211
31
425
{$launch->loadProject($projectName, $actual);
212
31
694
$launch->projects->{$projectName}->stepTimes = {}; # Steps processed
213
}
214
}
215
216
48
1232
for my $step(@steps) # Remove subsequent save files
217
130
1111
{my ($stepName) = @$step;
218
130
744
$launch->deleteProject($projectName, $stepName);
219
}
220
221
48
299
for my $step(@steps) # Each step in the conversion
222
130
633
{my ($stepName, $code) = @$step;
223
130
3455
my $stepNumber = $launch->stepNumberByName->{$stepName};
224
130
3477
$launch->stepsByNumber->[$stepNumber]->($launch, $projectName);
225
}
226
} # launchProject
227
228
sub loadProjectsFromFolder($@) #S Create a project for file in and below the specified folder and return the projects created
229
49
49
1
196
{my ($dir, @extensions) = @_; # Folder to search, list of file extensions to search for
230
49
294
my @f = searchDirectoryTreesForMatchingFiles($dir, @extensions);
231
49
190316
my $p = {}; # Project hash
232
49
539
for my $file(@f)
233
392
1911
{my (undef, $name, undef) = parseFileName($file); # Project name from file name
234
392
7693
$p->{$name} = # Create project definition
235
Data::Edit::Conversion::Project::new(name=>$name, source=>$file);
236
}
237
49
3724
bless $p, "Data::Edit::Conversion::Projects"; # Return hash of projects
238
}
239
240
sub projectData($$) # Get L for a project after a launch has completed
241
1008
1008
1
254906
{my ($launch, $projectName) = @_; # Launch specification, project
242
1008
13028
$launch->projects->{$projectName}->data;
243
}
244
245
sub projectSteps($$) # Get the L showing the executed time in seconds for each step in a project after a launch has completed. If a step name is not present in this hash then the step was not run.
246
1002
1002
1
2021
{my ($launch, $projectName) = @_; # Launch specification, project
247
1002
13507
$launch->projects->{$projectName}->stepTimes;
248
}
249
250
package Data::Edit::Conversion::Project;
251
49
49
89719
use Carp qw(confess cluck);
49
147
49
3087
252
49
49
343
use Data::Dump qw(dump);
49
98
49
2009
253
49
49
294
use Data::Table::Text qw(:all);
49
49
49
18424
254
49
49
294
use Storable;
49
147
49
1568
255
49
49
196
use utf8;
49
98
49
196
256
257
my %projects; # Prevent duplicate namea and number projects
258
259
#1 Project # A project is one input file to be converted in one more restartable steps.
260
261
#2 Construction # Methods used to construct projects
262
263
sub new #S Create a project to describe the conversion of a source file containing xml representing documentation into one or more L topics.
264
392
392
1862
{my $p = bless{@_};
265
392
10731
$p->number = keys %projects;
266
392
8428
my $n = $p->name;
267
392
6615
my $s = $p->source;
268
269
392
50
2352
confess "No name for project\n" unless $n;
270
392
50
1029
confess "Duplicate project: $n\n" if $projects{$n};
271
392
50
735
confess "No source file for project: $n\n" unless $s;
272
392
50
4704
confess "Source file does not exist: $s\n" unless -e $s;
273
274
392
2597
$projects{$n} = $p;
275
}
276
277
#2 Attributes # Attributes of a project
278
genLValueScalarMethods(q(name)); # Name of project.
279
genLValueScalarMethods(q(number)); # Number of the project.
280
genLValueScalarMethods(q(source)); # Input file containing the source xml.
281
genLValueScalarMethods(q(data)); # Per project data being converted
282
genLValueScalarMethods(q(stepTimes)); # Hash of steps processed during a launch
283
genLValueScalarMethods(q(test)) ; # Whether this is a test project or not
284
genLValueScalarMethods(q(title)); # Title of the project.
285
genLValueScalarMethods(q(numberOfFileNamesRequested)); # Number of files names requested by this project
286
287
#2 Methods # Methods applicable to a project
288
289
my $stringToFileName; # Number of files created
290
291
sub stringToFileName($$$) # Create a unique file name from a specified string.
292
4
4
48
{my ($project, $title, $ext) = @_; # Project, string, desired extension - defaults to .dita
293
4
75
my $N = $project->number; # Project number
294
4
64
my $n = $project->numberOfFileNamesRequested++; # File number
295
4
26
my $t = lc $title; # Edit out constructs that would produce annoying file names
296
4
7
$t =~ s/\s+//gs;
297
4
11
$t =~ s/<.+?>//gs;
298
4
7
$t =~ s/<.+?>//gs;
299
4
6
$t =~ s/[^a-zA-Z]//gs; # Readable ascii
300
4
20
fpe(qq($t$N).qq(_$n), $ext); # Project numnber makes each file unique despite use of separate processes to run conversions
301
}
302
303
package Data::Edit::Conversion;
304
305
#-------------------------------------------------------------------------------
306
# Export
307
#-------------------------------------------------------------------------------
308
309
49
49
16709
use Exporter qw(import);
49
98
49
1568
310
311
49
49
294
use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
49
98
49
11417
312
313
@ISA = qw(Exporter);
314
@EXPORT_OK = qw(
315
);
316
%EXPORT_TAGS = (all=>[@EXPORT, @EXPORT_OK]);
317
318
# podDocumentation
319
320
=pod
321
322
=encoding utf-8
323
324
=head1 Name
325
326
Data::Edit::Conversion - Perform a restartable series of steps in parallel.
327
328
=head1 Synopsis
329
330
Launch the conversion of several files, each represented by a project, in
331
parallel processes, saving the project state after each step of the conversion
332
so that subsequent conversions can be restarted at later steps to speed up
333
development by bypassing initial processing steps unless they are really
334
needed. The L and L are transferred back
335
from each project's sub process to the main calling process so that the main
336
process can further process their results.
337
338
use warnings FATAL=>qw(all);
339
use strict;
340
use Test::More tests=>90;
341
use File::Touch;
342
use Data::Edit::Conversion;
343
344
my $N = 8; # Number of test files == projects per launch
345
346
makePath(my $inDir = q(in)); clearFolder($inDir, 20); # Create and clear folders
347
348
my $tAge = File::Touch->new(mtime=>int time - 100); # Age file
349
$tAge->touch(writeFile(fpe($inDir, $_, q(xml)), <
350
$_
351
END
352
353
my $convert = sub {my ($p) = @_; $p->data = $p->data =~ s(\s) ()gsr x 2}; # Convert one project
354
355
my $l = Data::Edit::Conversion::new # Convert $N projects in parallel
356
(projects => Data::Edit::Conversion::loadProjectsFromFolder($inDir,qw(xml)),
357
convert =>
358
[[load => sub {my ($p) = @_; $p->data = readFile($p->source)}], # Load a project
359
[c1 => $convert],
360
[c2 => $convert],
361
[c3 => $convert],
362
],
363
maximumNumberOfProcesses => $N,
364
);
365
366
my $verify = sub # Verify launch results
367
{my (@stepsExecuted) = @_; # Steps that should have been executed
368
ok $l->projectData($_) eq $_ x 8 for 1..$N; # Check result of each conversion
369
is_deeply [sort keys %{$l->projectSteps($_)}], [@stepsExecuted] for 1..$N; # Check expected steps have been executed
370
};
371
372
$l->launch; &$verify(qw(c1 c2 c3 load)); # Full run
373
$l->restart(q(load)); &$verify(qw(c1 c2 c3 load)); # Restart the launch at various points
374
$l->restart(q(c1)); &$verify(qw(c1 c2 c3));
375
$l->restart(q(c2)); &$verify(qw(c2 c3));
376
$l->restart(q(c3)); &$verify(qw(c3));
377
378
File::Touch->new(mtime=>int time + 100)->touch(qq($inDir/1.xml)); # Renew source file to force all the steps to be redone despite requesting a restart
379
$l->restart(q(c2), "After touch");
380
ok $l->projectData($_) eq $_ x 8 for 1..$N;
381
is_deeply [sort keys %{$l->projectSteps(1)}], [qw(c1 c2 c3 load)];
382
is_deeply [sort keys %{$l->projectSteps(2)}], [qw(c2 c3)];
383
384
=head1 Description
385
386
387
The following sections describe the methods in each functional area of this
388
module. For an alphabetic listing of all methods by name see L.
389
390
391
392
=head1 Methods
393
394
Specify and run the restartable conversion of zero or more files in parallel
395
396
=head2 new(@)
397
398
Create a conversion specification for zero or more files represented by projects.
399
400
Parameter Description
401
1 @attributes L describing the launch
402
403
This is a static method and so should be invoked as:
404
405
Data::Edit::Conversion::new
406
407
408
=head2 launch($$$)
409
410
Launch the conversion of several files represented by projects in parallel
411
412
Parameter Description
413
1 $launch Launch specification
414
2 $title Optional title
415
3 $restart Optional name of latest step to restart at.
416
417
=head2 restart($$$)
418
419
Launch the conversion of several files represented by projects in parallel, starting at the specified step: the L from the previous step will be restored unless it does not exist in which case the conversion will be run from the latest step available prior to this step or right from the start.
420
421
Parameter Description
422
1 $launch Launch specification
423
2 $restart Step to restart at
424
3 $title Optional title
425
426
=head1 Launch Attributes
427
428
Use these attributes to configure a launch.
429
430
=head2 convert :lvalue
431
432
I [[step name => sub]...] A list of steps and their associated subs to process that step. At the end of each step the data stored on L is saved to allow for a later restart at the next step.
433
434
435
=head2 maximumNumberOfProcesses :lvalue
436
437
I Maximum number of processes to run in parallel
438
439
440
=head2 out :lvalue
441
442
I Optional file output area. This area will be cleared at the start of each launch.
443
444
445
=head2 outFileLimit :lvalue
446
447
I Limit on the number of files to be cleared from the L folder at the start of each launch.
448
449
450
=head2 projects :lvalue
451
452
I A reference to a hash of Data::Edit::Conversion::Project definitions. This can be most easily created by using L.
453
454
455
=head2 save :lvalue
456
457
I Temporary files will be stored in this folder
458
459
460
=head2 stepNumberByName :lvalue
461
462
O Get the number of a step from its name
463
464
465
=head2 stepsByNumber :lvalue
466
467
O Array of steps to be performed. The subs in this array call the user supplied subs after approriate set up and then do the required set down after the execution of each step.
468
469
470
=head2 loadProjectsFromFolder($@)
471
472
Create a project for file in and below the specified folder and return the projects created
473
474
Parameter Description
475
1 $dir Folder to search
476
2 @extensions List of file extensions to search for
477
478
This is a static method and so should be invoked as:
479
480
Data::Edit::Conversion::loadProjectsFromFolder
481
482
483
=head2 projectData($$)
484
485
Get L for a project after a launch has completed
486
487
Parameter Description
488
1 $launch Launch specification
489
2 $projectName Project
490
491
=head2 projectSteps($$)
492
493
Get the L showing the executed time in seconds for each step in a project after a launch has completed. If a step name is not present in this hash then the step was not run.
494
495
Parameter Description
496
1 $launch Launch specification
497
2 $projectName Project
498
499
=head1 Project
500
501
A project is one input file to be converted in one more restartable steps.
502
503
=head2 new()
504
505
Create a project to describe the conversion of a source file containing xml representing documentation into one or more L topics.
506
507
508
This is a static method and so should be invoked as:
509
510
Data::Edit::Conversion::new
511
512
513
=head2 name :lvalue
514
515
I Name of project.
516
517
518
=head2 number :lvalue
519
520
I Number of the project.
521
522
523
=head2 source :lvalue
524
525
I Input file containing the source xml.
526
527
528
=head2 data :lvalue
529
530
O Per project data being converted
531
532
533
=head2 stepTimes :lvalue
534
535
O Hash of steps processed during a launch
536
537
538
=head2 title :lvalue
539
540
I Title of the project.
541
542
543
544
=head1 Private Methods
545
546
=head2 defaultMaximumNumberOfProcesses()
547
548
Default maximum number of processes to use during the conversion
549
550
551
=head2 defaultOutFileLimit()
552
553
Default maximum number of files to clear art a time.
554
555
556
=head2 stepSaveFile($$$)
557
558
Save file for a project and a step
559
560
Parameter Description
561
1 $launch Launch specification
562
2 $projectName Project
563
3 $step Step name
564
565
=head2 deleteProject($$$)
566
567
Delete results before executing a particular step
568
569
Parameter Description
570
1 $launch Launch specification
571
2 $projectName Project
572
3 $step Step
573
574
=head2 saveProject($$$)
575
576
Save project at a particular step
577
578
Parameter Description
579
1 $launch Launch specification
580
2 $projectName Project
581
3 $step Step
582
583
=head2 loadProject($$$)
584
585
Load a project at a particular step
586
587
Parameter Description
588
1 $launch Launch specification
589
2 $projectName Project
590
3 $stepNumber Step to reload
591
592
=head2 launchProject($$$)
593
594
Convert a single project in a seperate process
595
596
Parameter Description
597
1 $launch Launch specification
598
2 $projectName Project to be processed
599
3 $restart Optional latest step to restart at
600
601
602
=head1 Index
603
604
605
1 L
606
607
2 L
608
609
3 L
610
611
4 L
612
613
5 L
614
615
6 L
616
617
7 L
618
619
8 L
620
621
9 L
622
623
10 L
624
625
11 L
626
627
12 L
628
629
13 L
630
631
14 L
632
633
15 L
634
635
16 L
636
637
17 L
638
639
18 L
640
641
19 L
642
643
20 L
644
645
21 L
646
647
22 L
648
649
23 L
650
651
24 L
652
653
25 L
654
655
26 L
656
657
27 L
658
659
=head1 Installation
660
661
This module is written in 100% Pure Perl and, thus, it is easy to read,
662
comprehend, use, modify and install via B:
663
664
sudo cpan install Data::Edit::Conversion
665
666
=head1 Author
667
668
L
669
670
L
671
672
=head1 Copyright
673
674
Copyright (c) 2016-2018 Philip R Brenan.
675
676
This module is free software. It may be used, redistributed and/or modified
677
under the same terms as Perl itself.
678
679
=cut
680
681
682
683
# Tests and documentation
684
685
sub test
686
49
49
0
490
{my $p = __PACKAGE__;
687
49
441
binmode($_, ":utf8") for *STDOUT, *STDERR;
688
49
50
2548
return if eval "eof(${p}::DATA)";
689
49
2842
my $s = eval "join('', <${p}::DATA>)";
690
49
50
294
$@ and die $@;
691
49
49
245
eval $s;
49
49
98
49
49
1519
49
49
196
49
98
49
1960
49
27195
49
2982434
49
833
49
135044
49
748818
49
43855
49
3381
692
1
50
626
$@ and die $@;
693
}
694
695
test unless caller;
696
697
1;
698
# podDocumentation
699
__DATA__