File Coverage

lib/Su/Process.pm
Criterion Covered Total %
statement 128 140 91.4
branch 46 68 67.6
condition 9 12 75.0
subroutine 19 21 90.4
pod 6 6 100.0
total 208 247 84.2


line stmt bran cond sub pod time code
1             package Su::Process;
2              
3 22     22   263014 use strict;
  22         49  
  22         812  
4 22     22   114 use warnings;
  22         40  
  22         761  
5 22     22   126 use Exporter;
  22         41  
  22         973  
6 22     22   115 use File::Path;
  22         51  
  22         1264  
7 22     22   10155 use Data::Dumper;
  22         89921  
  22         1255  
8 22     22   146 use Test::More;
  22         41  
  22         195  
9 22     22   6575 use Carp;
  22         42  
  22         1705  
10 22     22   23128 use Fatal qw(open close);
  22         389848  
  22         174  
11              
12 22     22   39058 use Su::Template;
  22         72  
  22         160  
13 22     22   148 use Su::Log;
  22         42  
  22         44441  
14              
15             our @ISA = qw(Exporter);
16              
17             our @EXPORT = qw(comp gen generate_proc generate_process);
18              
19             our $DEBUG = 0;
20              
21             # not used
22             my $MODULE_PATH = __FILE__;
23              
24             our $PROCESS_BASE_DIR = "./";
25              
26             our $PROCESS_DIR = "Procs";
27              
28             =pod
29              
30             =head1 NAME
31              
32             Su::Process - A module to generate and execute user process.
33              
34             =head1 SYNOPSIS
35              
36             use Su::Process;
37              
38             # Generate the Process file.
39              
40             generate_proc('pkg/SomeProc');
41              
42             # Execute the Process and get it's result.
43              
44             $ret = gen("pkg/SomeProc");
45             $ret = comp("pkg/SomeProc");
46              
47             =head1 DESCRIPTION
48              
49             Su::Process has a method to generate the template of the Process
50             module to describe user process. These Processes are called from the
51             method Su::resolve. The user Processes are also called directry by
52             the method L. The method
53             L is an alias of the method
54             L for embed to the template like a component.
55              
56             =head1 ATTRIBUTES
57              
58             =head2 C<$SUPRESS_LOAD_ERROR>
59              
60             For suppress the load error because of the specified module file is not found.
61              
62             $Su::Process::SUPPRESS_LOAD_ERROR = 1;
63              
64             =cut
65              
66             our $SUPPRESS_LOAD_ERROR = 0;
67              
68             =head1 FUNCTIONS
69              
70             =over
71              
72             =cut
73              
74             =item new()
75              
76             A Constructor.
77              
78             =cut
79              
80             sub new {
81 68     68 1 131844 my $self = shift;
82              
83 68 100       370 my %h = @_ if @_;
84 68         422 my $log = Su::Log->new;
85 68         254 $h{logger} = $log;
86 68         334 return bless \%h, $self;
87             } ## end sub new
88              
89             sub import {
90 21     21   176 my $self = shift;
91              
92             # Save import list and remove from hash.
93 21         59 my %tmp_h = @_;
94 21         46 my $imports_aref = $tmp_h{import};
95 21         48 delete $tmp_h{import};
96 21         44 my $base = $tmp_h{base};
97 21         46 my $dir = $tmp_h{dir};
98 21         93 Su::Log->trace( "base:" . Dumper($base) );
99 21         207 Su::Log->trace( "dir:" . Dumper($dir) );
100              
101             # print "base:" . Dumper($base) . "\n";
102             # print "dir:" . Dumper($dir) . "\n";
103              
104 21 100       187 $PROCESS_BASE_DIR = $base if $base;
105 21 100       86 $PROCESS_DIR = $dir if $dir;
106              
107 21 100 100     136 if ( $base || $dir ) {
108 4         6 $self->export_to_level( 1, $self, @{$imports_aref} );
  4         4136  
109             } else {
110              
111             # If 'base' or 'dir' is not passed, then all of the parameters are required method names.
112 17         4237 $self->export_to_level( 1, $self, @_ );
113             }
114              
115             } ## end sub import
116              
117             =item generate_process()
118              
119             This function is just a synonym of the method L.
120              
121             =cut
122              
123             sub generate_process {
124              
125 0 0   0 1 0 my $self = shift if ( ref $_[0] eq __PACKAGE__ );
126 0 0       0 if ($self) {
127 0         0 $self->generate_proc(@_);
128             } else {
129 0         0 generate_proc(@_);
130             }
131             } ## end sub generate_process
132              
133             =item generate_proc()
134              
135             Generate the Process file to describe your own code in the method 'process'.
136             This method can be used from the command line like the following.
137              
138             perl -MSu::Process -e '{generate_proc("MainProc")}'
139              
140             If generation is success, this subroutine return the generated file
141             name, else should die or return undef.
142              
143             =cut
144              
145             sub generate_proc {
146 5 100   5 1 1009 my $self = shift if ( ref $_[0] eq __PACKAGE__ );
147 5 100       23 my $PROCESS_BASE_DIR = $self->{base} ? $self->{base} : $PROCESS_BASE_DIR;
148 5 50       24 my $PROCESS_DIR = $self->{dir} ? $self->{dir} : $PROCESS_DIR;
149              
150 5         13 my ( $comp_id, $gen_type ) = @_;
151              
152             # Make directory path.
153 5         47 my @arr = split( '/|::', $comp_id );
154 5         12 my $comp_base_name = '';
155 5 100       27 if ( scalar @arr > 1 ) {
156 3         14 $comp_base_name = join( '/', @arr[ 0 .. scalar @arr - 2 ] );
157             }
158              
159 5         11 my $dir;
160              
161             # If class name is specified with package, then not use $PROCESS_DIR as a part of output path.
162 5 100       22 if ( $comp_id =~ /::/ ) {
163 3         9 $dir = $PROCESS_BASE_DIR . "/" . $comp_base_name;
164             } else {
165 2         8 $dir = $PROCESS_BASE_DIR . "/" . $PROCESS_DIR . "/" . $comp_base_name;
166             }
167              
168             # Prepare directory for generate file.
169 5 100       314 mkpath $dir unless ( -d $dir );
170              
171             # '$!' can't judge error correctly.
172             # $! and die "$!:" . $dir;
173 5 50       70 if ( !-d $dir ) {
174 0         0 die "Can't make dir:" . $!;
175             }
176              
177             # Generate file.
178 5         11 my $comp_id_filepath = $comp_id;
179 5         19 $comp_id_filepath =~ s!::!/!g;
180 5         11 my $fpath;
181              
182             # If package name is specified with class name, then not use $PROCESS_DIR as output file path.
183 5 100       19 if ( $comp_id =~ /::/ ) {
184 3         21 $fpath = $PROCESS_BASE_DIR . "/" . $comp_id_filepath . ".pm";
185             } else {
186 2         9 $fpath =
187             $PROCESS_BASE_DIR . "/" . $PROCESS_DIR . "/" . $comp_id_filepath . ".pm";
188             }
189 5         193 open( my $file, '>', $fpath );
190              
191             # Get the function contents.
192 5         608 $comp_id =~ s/\//::/g;
193 5 50       26 my $fun = '_template_' . ( $gen_type ? $gen_type : 'default' );
194 5         12 my $contents = '';
195 5         419 $contents = eval( "return " . $fun . "(\"$comp_id\");" );
196 5 50       40 $@ and die $@;
197              
198 5         95 my $ret = print $file $contents;
199 5 50       19 if ( $ret == 1 ) {
200 5         9646 print "generated:$fpath\n";
201 5         632 return $fpath;
202             } else {
203 0         0 print "output fail:$fpath\n";
204 0         0 return undef;
205             }
206              
207             } ## end sub generate_proc
208              
209             =begin comment
210              
211             Return the contents of a new Process which uses Su template.
212             This method is called by gen_proc via dinamic method call.
213              
214             =end comment
215              
216             =cut
217              
218             sub _template_default {
219 5 50   5   22 my $self = shift if ( ref $_[0] eq __PACKAGE__ );
220              
221 5         12 my $comp_id = shift;
222 5         25 my $ret = expand( << '__HERE__', $comp_id );
223             % my $comp_id = shift;
224             package <%=$comp_id%>;
225             use strict;
226             use warnings;
227             use Su::Template;
228              
229             my $model={};
230              
231             sub new {
232             return bless { model => $model }, shift;
233             }
234              
235             # The main method for this process class.
236             sub process{
237             my $self = shift if ($_[0] && ref $_[0] eq __PACKAGE__);
238             my $self_module_name = shift if ($_[0] && $_[0] eq __PACKAGE__);
239             my $model = keys %{ $self->{model} } ? $self->{model} : $model;
240              
241             my $param = shift;
242             #$Su::Template::DEBUG=1;
243             my $ret = expand(<<'__TMPL__');
244              
245             __TMPL__
246             #$Su::Template::DEBUG=0;
247             return $ret;
248             }
249              
250             # This method is called if specified as a map filter class.
251             sub map_filter{
252             my $self = shift if ref $_[0] eq __PACKAGE__;
253             my @results = @_;
254              
255             for ( @results ){
256            
257             }
258              
259             return @results;
260             }
261              
262             # This method is called if specified as a reduce filter class.
263             sub reduce_filter{
264             my $self = shift if ref $_[0] eq __PACKAGE__;
265             my @results = @_;
266             my $result;
267             for ( @results ){
268            
269             }
270              
271             return $result;
272             }
273              
274             # This method is called if specified as a scalar filter class.
275             sub scalar_filter{
276             my $self = shift if ref $_[0] eq __PACKAGE__;
277             my $result = shift;
278              
279              
280             return $result;
281             }
282              
283             sub model{
284             my $self = shift if ref $_[0] eq __PACKAGE__;
285             my $self_module_name = shift if $_[0] eq __PACKAGE__;
286             my $arg = shift;
287             if ($arg) {
288             if ($self) { $self->{model} = $arg; }
289             else {
290             $model = $arg;
291             }
292             } else {
293             if ($self) {
294             return $self->{model};
295             } else {
296             return $model;
297             }
298             } ## end else [ if ($arg) ]
299             }
300              
301             1;
302             __HERE__
303              
304 5         66 return $ret;
305             } ## end sub _template_default
306              
307             =begin comment
308              
309             Return the contents of a new Process which uses Mojo template.
310             This method is called by gen_proc via dinamic method call.
311              
312             =end comment
313              
314             =cut
315              
316             sub _template_mojo {
317 0 0   0   0 my $self = shift if ( ref $_[0] eq __PACKAGE__ );
318              
319 0         0 my $comp_id = shift;
320 0         0 my $header = "package $comp_id;";
321 0         0 my $ret = << '__HERE__';
322             use Mojo::Template;
323             use strict;
324             use warnings;
325              
326             my $model = {};
327              
328             sub process{
329             if($_[0] eq __PACKAGE__){
330             shift;
331             }
332              
333             my $ctx_hash_ref = shift;
334             my $mt = Mojo::Template->new;
335             my $ret = $mt->render(<<'__TMPL__',$ctx_hash_ref);
336             % my $ctx_href = shift;
337              
338              
339             __TMPL__
340              
341             return $ret;
342             }
343              
344             sub model{
345             if($_[0] eq __PACKAGE__){
346             shift;
347             }
348             my $arg = shift;
349             if ($arg){
350             $model = $arg;
351             }else{
352             return $model;
353             }
354             }
355              
356             1;
357             __HERE__
358              
359 0         0 return $header . "\n" . $ret;
360              
361             } ## end sub _template_mojo
362              
363             =item comp()
364              
365             This method is just a alias of L metnod.
366              
367             =cut
368              
369             sub comp {
370 1     1 1 374 return gen(@_);
371             }
372              
373             =item gen()
374              
375             my $ret = gen('process_id');
376             my $ret = gen('dir/process_id');
377             my $ret = gen('dir::process_id');
378              
379             Return the result of the process which coressponds to the passed
380             process id.
381             The process id is a qualified module name.
382             Note that the specified process is simply called it's C
383             method and can't access to it's model field.
384              
385             =cut
386              
387             sub gen {
388 7 50   7 1 2051 my $self = shift if ( ref $_[0] eq __PACKAGE__ );
389 7 50       37 my $PROCESS_BASE_DIR = $self->{base} ? $self->{base} : $PROCESS_BASE_DIR;
390 7 50       24 my $PROCESS_DIR = $self->{dir} ? $self->{dir} : $PROCESS_DIR;
391              
392 7         16 my $comp_id = shift;
393 7         17 my @ctx = @_;
394              
395 7         25 my $f = $PROCESS_BASE_DIR . "/" . $PROCESS_DIR . "/" . $comp_id;
396 7         23 my $suffix = _has_suffix($f);
397              
398             # If passed file has suffix, return file contents itself.
399 7 50 66     196 if ( -f $f && $suffix and $suffix ne '.pm' ) {
      66        
400 1         5 return _read_contents($f);
401             }
402              
403 6         40 my $proc = Su::Process->new;
404 6         23 my $proc_module = $proc->load_module($comp_id);
405              
406 6         29 return $proc_module->process(@ctx);
407              
408             } ## end sub gen
409              
410             =item load_module()
411              
412             my $su_proc = Su::Process->new;
413             my $proc_module = $su_proc->load_module($module_name);
414              
415             =cut
416              
417             sub load_module {
418 73 50   73 1 278 my $self = shift if ( ref $_[0] eq __PACKAGE__ );
419 73 100       387 my $PROCESS_BASE_DIR = $self->{base} ? $self->{base} : $PROCESS_BASE_DIR;
420 73 100       209 my $PROCESS_DIR = $self->{dir} ? $self->{dir} : $PROCESS_DIR;
421 73         111 my $comp_id = shift;
422 73 50       179 my @ctx = @_ if @_;
423              
424 73         98 my $f = $comp_id;
425 73         352 $f =~ s!::!/!g;
426 73         123 $f .= ".pm";
427              
428             # Trim the head of dot slash(./) of the file path.
429 73         134 $f =~ s!^\./(.+)!$1!;
430              
431             # Replace directory separator to package separator.
432 73         128 $comp_id =~ s/\//::/g;
433              
434             # If $comp_id is a package which described in some module file whose
435             # filename is not match $comp_id, then we can't load package
436             # '$comp_id' from filename using require. In such case, we hope
437             # package may be already loaded, so we don't load and just return the
438             # package id.
439 73         113 eval { require $f; };
  73         18429  
440              
441             # Note if $SUPRESS_LOAD_ERROR is set, don't throw error.
442 73 50 66     2530 croak $@ if $@ and !$SUPPRESS_LOAD_ERROR;
443              
444 73         98 my $ret;
445              
446             # TODO: Add mode to re-use instance.
447 73 100       223 if ( exists &{ ( $comp_id . "::new" ) } ) {
  73         296  
448 18         75 $ret = $comp_id->new;
449             } else {
450 55         100 $ret = $comp_id;
451             }
452              
453             # require $comp_id if $@;
454 73         264 return $ret;
455              
456             } ## end sub load_module
457              
458             =begin comment
459              
460             Read the contents of the passed file.
461              
462             =end comment
463              
464             =cut
465              
466             sub _read_contents {
467 1 50   1   185 my $self = shift if ( ref $_[0] eq __PACKAGE__ );
468              
469 1         4 my $path = shift;
470 1         14 my $fh = undef;
471 1 50       46 open $fh, '<', $path or die "Can't open file:$!";
472 1         277 my $ret = join '', <$fh>;
473 1         40 close $fh;
474 1         28 return $ret;
475             } ## end sub _read_contents
476              
477             =begin comment
478              
479             Return the suffix string if the passed string has some suffix.
480             If the passed string has not any suffix, then return undef.
481              
482             =end comment
483              
484             =cut
485              
486             sub _has_suffix {
487 17 50   17   78 my $self = shift if ( ref $_[0] eq __PACKAGE__ );
488              
489 17         28 my $path = shift;
490 17         72 my @pass_elem = split( '/', $path );
491 17 100       73 $path = @pass_elem[ scalar @pass_elem - 1 ] if scalar @pass_elem > 1;
492 17         43 my $ridx = rindex( $path, '.' );
493 17 100       95 return ( $ridx == -1 ? undef : substr( $path, $ridx ) );
494             } ## end sub _has_suffix
495              
496             =pod
497              
498             =back
499              
500             =cut
501              
502             1;
503