File Coverage

blib/lib/Outthentic/Story.pm
Criterion Covered Total %
statement 21 268 7.8
branch 0 112 0.0
condition 0 28 0.0
subroutine 7 51 13.7
pod 0 31 0.0
total 28 490 5.7


line stmt bran cond sub pod time code
1              
2             package Outthentic::Story;
3              
4 1     1   6 use strict;
  1         1  
  1         25  
5 1     1   3 use base 'Exporter';
  1         2  
  1         91  
6 1     1   540 use Outthentic::DSL;
  1         6480  
  1         28  
7 1     1   326 use Outthentic::Story::Stat;
  1         2  
  1         24  
8 1     1   418 use File::ShareDir;
  1         5996  
  1         52  
9 1     1   8 use JSON;
  1         18  
  1         8  
10 1     1   122 use Carp;
  1         3  
  1         5260  
11              
12              
13             our @EXPORT = qw{
14              
15             new_story end_of_story set_story story_cache_dir
16              
17             get_prop set_prop
18              
19             debug_mod1 debug_mod2 debug_mod12
20              
21             set_stdout get_stdout stdout_file
22              
23             dsl captures capture stream match_lines
24              
25             run_story apply_story_vars story_var story_vars_pretty
26              
27             do_perl_hook
28              
29             do_ruby_hook
30              
31             do_python_hook
32              
33             do_bash_hook
34              
35             ignore_story_err
36              
37             project_root_dir
38              
39             test_root_dir
40              
41             cache_root_dir
42              
43             host
44              
45             dump_os
46              
47             };
48              
49             our @stories = ();
50             our $OS;
51              
52             sub new_story {
53            
54              
55 0     0 0   my $self = {
56             ID => scalar(@stories),
57             props => {
58             ignore_story_err => 0 ,
59             dsl => Outthentic::DSL->new() ,
60             story_vars => {} },
61             };
62              
63 0           push @stories, $self;
64              
65 0           1;
66              
67             }
68              
69             sub end_of_story {
70              
71 0 0   0 0   if (debug_mod12()){
72 0           main::note("end of story: ".(get_prop('story')));
73             }
74              
75 0           delete $stories[-1];
76              
77             }
78              
79             sub set_story {
80              
81 0     0 0   my $dist_lib_dir = File::ShareDir::dist_dir('Outthentic');
82              
83 0           my $ruby_run_cmd;
84              
85 0 0         if (-f project_root_dir()."/Gemfile" ){
86 0           $ruby_run_cmd = "cd ".project_root_dir()." && bundle exec ruby -I $dist_lib_dir -r outthentic -I ".story_cache_dir()
87             } else {
88 0           $ruby_run_cmd = "ruby -I $dist_lib_dir -r outthentic -I ".story_cache_dir();
89             }
90              
91 0           my $python_run_cmd = "PYTHONPATH=\$PYTHONPATH:".(story_cache_dir()).":$dist_lib_dir python";
92              
93 0           get_prop('dsl')->{languages}->{ruby} = $ruby_run_cmd;
94              
95 0           get_prop('dsl')->{languages}->{python} = $python_run_cmd;
96              
97 0           get_prop('dsl')->{cache_dir} = story_cache_dir();
98              
99 0           my $bash_run_opts = "source "._bash_glue_file()." && source $dist_lib_dir/outthentic.bash";
100              
101 0           get_prop('dsl')->{languages}->{ruby} = $ruby_run_cmd;
102              
103 0           get_prop('dsl')->{languages}->{bash} = $bash_run_opts;
104              
105 0           _make_cache_dir();
106              
107 0           _mk_perl_glue_file();
108              
109 0           _mk_ruby_glue_file();
110              
111 0           _mk_python_glue_file();
112              
113 0           _mk_bash_glue_file();
114              
115             }
116              
117             sub _story {
118 0     0     @stories[-1];
119             }
120              
121             sub _story_id {
122 0     0     _story()->{ID};
123             }
124              
125             sub get_prop {
126              
127 0     0 0   my $name = shift;
128              
129 0           _story()->{props}->{$name};
130            
131             }
132              
133             sub set_prop {
134              
135 0     0 0   my $name = shift;
136 0           my $value = shift;
137 0           _story()->{props}->{$name} = $value;
138            
139             }
140              
141              
142             sub project_root_dir {
143 0     0 0   get_prop('project_root_dir');
144             }
145              
146              
147             sub test_root_dir { # this one is deprected and exists for back compatibilty, use cache_root_dir instead
148 0     0 0   get_prop('cache_root_dir');
149             }
150              
151             sub cache_root_dir {
152 0     0 0   get_prop('cache_root_dir');
153             }
154              
155             sub host {
156 0     0 0   get_prop('host');
157             }
158              
159             sub ignore_story_err {
160              
161 0     0 0   my $val = shift;
162 0           my $rv;
163              
164 0 0         if (defined $val){
165 0           set_prop('ignore_story_err',$val);
166             } else {
167 0           $rv = get_prop('ignore_story_err');
168             }
169 0           $rv;
170             }
171              
172              
173             sub debug_mod1 {
174              
175 0     0 0   get_prop('debug') == 1
176             }
177              
178             sub debug_mod2 {
179              
180 0     0 0   get_prop('debug') == 2
181             }
182              
183             sub debug_mod12 {
184              
185 0 0   0 0   debug_mod1() or debug_mod2()
186             }
187              
188              
189             sub set_stdout {
190              
191 0     0 0   my $line = shift;
192 0 0         open FSTDOUT, ">>", stdout_file() or die $!;
193 0           print FSTDOUT $line, "\n";
194 0           close FSTDOUT;
195              
196             }
197              
198             sub get_stdout {
199              
200 0 0   0 0   return unless -f stdout_file();
201              
202 0           my $data;
203              
204 0 0         open FSTDOUT, stdout_file() or die $!;
205 0           my $data = join "", ;
206 0           close FSTDOUT;
207 0           $data;
208             }
209              
210             sub stdout_file {
211              
212 0     0 0   story_cache_dir()."/std.out"
213              
214             }
215              
216             sub _make_cache_dir {
217              
218 0     0     my $cache_dir = cache_root_dir()."/story-"._story_id();
219              
220 0 0         if (debug_mod12()){
221 0           main::note("make cache dir: $cache_dir");
222             }
223 0           system("rm -rf $cache_dir");
224 0           system("mkdir -p $cache_dir");
225             }
226              
227             sub story_cache_dir {
228 0     0 0   cache_root_dir()."/story-"._story_id();
229             }
230              
231             sub _perl_glue_file {
232 0     0     story_cache_dir()."/glue.pm";
233             }
234              
235             sub _ruby_glue_file {
236 0     0     story_cache_dir()."/glue.rb";
237             }
238              
239             sub _python_glue_file {
240 0     0     story_cache_dir()."/glue.py";
241             }
242              
243             sub _bash_glue_file {
244 0     0     story_cache_dir()."/glue.bash";
245             }
246              
247             sub dsl {
248 0     0 0   get_prop('dsl')
249             }
250              
251             sub stream {
252 0     0 0   dsl()->stream
253             }
254              
255             sub captures {
256              
257             dsl()->{captures}
258 0     0 0   }
259              
260             sub capture {
261 0     0 0   dsl()->{captures}->[0]
262             }
263              
264             sub match_lines {
265              
266             dsl()->{match_lines}
267 0     0 0   }
268              
269             sub run_story {
270              
271 0     0 0   my $path = shift;
272              
273 0   0       my $story_vars = shift || {};
274              
275 0           Outthentic::Story::Stat->new_story({
276             vars => $story_vars,
277             path => $path
278             });
279              
280 0           my $cache_root_dir = get_prop('cache_root_dir');
281              
282 0           my $project_root_dir = get_prop('project_root_dir');
283              
284 0           my $story_module = "$cache_root_dir/$project_root_dir/modules/$path/sparrow.pl";
285              
286 0 0         die "story module file $story_module does not exist" unless -e $story_module;
287              
288 0 0         if (debug_mod12()){
289 0           main::note("run downstream story: $path");
290 0           for my $k (keys %{$story_vars}){
  0            
291 0           my $v = $story_vars->{$k};
292 0           main::note("downstream story var: $k => $v");
293             }
294             }
295              
296             {
297 0           package main;
298 0 0         unless (do $story_module) {
299 0 0         die "couldn't parse story module file $story_module: $@" if $@;
300             }
301             }
302              
303             # return statistic for downstream story just executed
304 0           return Outthentic::Story::Stat->current;
305             }
306              
307             sub do_perl_hook {
308              
309 0     0 0   my $hook_file = shift;
310              
311             {
312 0           package main;
313 0 0         unless (do $hook_file) {
314 0 0         die "couldn't parse perl hook file $hook_file: $@" if $@;
315             }
316             }
317              
318 0           return 1;
319             }
320              
321              
322             sub _mk_perl_glue_file {
323              
324 0 0   0     open PERL_GLUE, ">", _perl_glue_file() or confess "can't create perl glue file ".(_perl_glue_file())." : $!";
325              
326 0           my $cache_root_dir = cache_root_dir();
327 0           my $story_dir = get_prop('story_dir');
328 0           my $project_root_dir = project_root_dir();
329 0           my $debug_mod12 = debug_mod12();
330 0           my $cache_dir = story_cache_dir;
331              
332 0           my $os = _resolve_os();
333              
334 0           print PERL_GLUE <<"CODE";
335              
336             package glue;
337             1;
338              
339             package main;
340             use strict;
341            
342             sub debug_mod12 {
343             $debug_mod12
344             }
345              
346             sub cach_root_dir {
347             '$cache_root_dir'
348             }
349              
350             sub test_root_dir {
351             '$cache_root_dir'
352             }
353              
354             sub project_root_dir {
355             '$project_root_dir'
356             }
357              
358             sub cache_dir {
359             '$cache_dir'
360             }
361              
362             sub story_dir {
363             '$story_dir'
364             }
365              
366             sub os { '$os' }
367              
368              
369             1;
370              
371             CODE
372              
373 0           close PERL_GLUE;
374              
375             }
376              
377             sub _mk_ruby_glue_file {
378              
379 0 0   0     open RUBY_GLUE, ">", _ruby_glue_file() or die $!;
380              
381 0           my $stdout_file = stdout_file();
382 0           my $cache_root_dir = cache_root_dir();
383 0           my $story_dir = get_prop('story_dir');
384 0           my $project_root_dir = project_root_dir();
385 0           my $debug_mod12 = debug_mod12();
386              
387 0           my $cache_dir = story_cache_dir;
388              
389 0           print RUBY_GLUE <<"CODE";
390              
391             def debug_mod12
392             '$debug_mod12'
393             end
394              
395             def cache_root_dir
396             '$cache_root_dir'
397             end
398              
399             def test_root_dir
400             '$cache_root_dir'
401             end
402              
403             def project_root_dir
404             '$project_root_dir'
405             end
406              
407             def cache_dir
408             '$cache_dir'
409             end
410              
411             def story_dir
412             '$story_dir'
413             end
414              
415             def stdout_file
416             '$stdout_file'
417             end
418              
419             CODE
420              
421 0           close RUBY_GLUE;
422              
423             }
424              
425             sub _mk_python_glue_file {
426              
427 0 0   0     open PYTHON_GLUE, ">", _python_glue_file() or die $!;
428              
429 0           my $stdout_file = stdout_file();
430 0           my $cache_root_dir = cache_root_dir();
431 0           my $story_dir = get_prop('story_dir');
432 0           my $project_root_dir = project_root_dir();
433 0           my $debug_mod12 = debug_mod12();
434              
435 0           my $cache_dir = story_cache_dir;
436              
437 0           print PYTHON_GLUE <<"CODE";
438              
439             def debug_mod12():
440             return $debug_mod12
441              
442             def cache_root_dir():
443             return '$cache_root_dir'
444              
445             def test_root_dir():
446             return '$cache_root_dir'
447              
448             def project_root_dir():
449             return '$project_root_dir'
450              
451             def cache_dir():
452             return '$cache_dir'
453              
454             def story_dir():
455             return '$story_dir'
456              
457             def stdout_file():
458             return '$stdout_file'
459              
460             CODE
461              
462 0           close PYTHON_GLUE;
463              
464             }
465              
466             sub _mk_bash_glue_file {
467              
468              
469 0     0     my $story_dir = get_prop('story_dir');
470              
471 0 0         open BASH_GLUE, ">", _bash_glue_file() or die $!;
472              
473 0           my $stdout_file = stdout_file();
474 0           my $cache_root_dir = cache_root_dir();
475 0           my $project_root_dir = project_root_dir();
476 0           my $debug_mod12 = debug_mod12();
477              
478 0           my $cache_dir = story_cache_dir;
479              
480 0           my $os = _resolve_os();
481              
482 0           print BASH_GLUE <<"CODE";
483              
484             debug_mod=debug_mod12
485              
486             test_root_dir=$cache_root_dir
487              
488             cache_root_dir=$cache_root_dir
489              
490             project_root_dir=$project_root_dir
491              
492             cache_dir=$cache_dir
493              
494             story_dir=$story_dir
495              
496             stdout_file=$stdout_file
497              
498             os=$os
499              
500             CODE
501              
502 0           close BASH_GLUE;
503              
504             }
505              
506             sub do_ruby_hook {
507              
508 0     0 0   my $file = shift;
509              
510 0           my $ruby_lib_dir = File::ShareDir::dist_dir('Outthentic');
511              
512 0           my $cmd;
513              
514 0 0         if (-f project_root_dir()."/Gemfile" ){
515 0           $cmd = "cd ".project_root_dir()." && bundle exec ruby -I $ruby_lib_dir -r outthentic -I ".story_cache_dir()." $file"
516             } else {
517 0           $cmd = "ruby -I $ruby_lib_dir -r outthentic -I ".story_cache_dir()." $file"
518             }
519              
520 0 0         if (debug_mod12()){
521 0           main::note("do_ruby_hook: $cmd");
522             }
523              
524              
525 0           my $rand = int(rand(1000));
526              
527 0           my $st = system("$cmd 2>".story_cache_dir()."/$rand.err 1>".story_cache_dir()."/$rand.out");
528              
529 0 0         if($st != 0){
530 0           die "do_ruby_hook failed. \n see ".story_cache_dir()."/$rand.err for details";
531             }
532              
533 0           my $out_file = story_cache_dir()."/$rand.out";
534              
535 0 0         open RUBY_HOOK_OUT, $out_file or die "can't open RUBY_HOOK_OUT file $out_file to read!";
536              
537 0           my @out = ;
538              
539 0           close RUBY_HOOK_OUT;
540              
541 0           my $story_vars_json;
542              
543 0           for my $l (@out) {
544              
545 0 0         next if $l=~/#/;
546              
547 0 0         ignore_story_err($1) if $l=~/ignore_story_err:\s+(\d)/;
548            
549 0 0         if ($l=~s/story_var_json_begin.*// .. $l=~s/story_var_json_end.*//){
550 0           $story_vars_json.=$l;
551 0           next;
552             }
553              
554              
555 0 0         if ($l=~/story:\s+(\S+)/){
556              
557 0           my $path = $1;
558              
559 0 0         if (debug_mod12()){
560 0           main::note("run downstream story from ruby hook");
561             }
562              
563 0   0       run_story($path, decode_json($story_vars_json||{}));
564 0           $story_vars_json = undef;
565              
566             }
567             }
568              
569 0           return 1;
570             }
571              
572             sub do_python_hook {
573              
574 0     0 0   my $file = shift;
575              
576 0           my $python_lib_dir = File::ShareDir::dist_dir('Outthentic');
577              
578 0           my $cmd = "PYTHONPATH=\$PYTHONPATH:".(story_cache_dir()).":$python_lib_dir python $file";
579            
580 0 0         if (debug_mod12()){
581 0           main::note("do_python_hook: $cmd");
582             }
583              
584              
585 0           my $rand = int(rand(1000));
586              
587 0           my $st = system("$cmd 2>".story_cache_dir()."/$rand.err 1>".story_cache_dir()."/$rand.out");
588              
589 0 0         if($st != 0){
590 0           die "do_python_hook failed. \n see ".story_cache_dir()."/$rand.err for details";
591             }
592              
593 0           my $out_file = story_cache_dir()."/$rand.out";
594              
595 0 0         open PYTHON_HOOK_OUT, $out_file or die "can't open PYTHON_HOOK_OUT file $out_file to read!";
596              
597 0           my @out = ;
598              
599 0           close PYTHON_HOOK_OUT;
600              
601 0           my $story_vars_json;
602              
603 0           for my $l (@out) {
604              
605 0 0         next if $l=~/#/;
606              
607 0 0         ignore_story_err($1) if $l=~/ignore_story_err:\s+(\d)/;
608            
609 0 0         if ($l=~s/story_var_json_begin.*// .. $l=~s/story_var_json_end.*//){
610 0           $story_vars_json.=$l;
611 0           next;
612             }
613              
614              
615 0 0         if ($l=~/story:\s+(\S+)/){
616              
617 0           my $path = $1;
618              
619 0 0         if (debug_mod12()){
620 0           main::note("run downstream story from python hook");
621             }
622              
623 0   0       run_story($path, decode_json($story_vars_json||{}));
624              
625 0           $story_vars_json = undef;
626              
627             }
628             }
629              
630 0           return 1;
631             }
632              
633             sub do_bash_hook {
634              
635 0     0 0   my $file = shift;
636              
637 0           my $bash_lib_dir = File::ShareDir::dist_dir('Outthentic');
638              
639 0           my $cmd = "source "._bash_glue_file()." && source $bash_lib_dir/outthentic.bash";
640              
641 0           $cmd.=" && source $file";
642              
643 0           $cmd="bash -c '$cmd'";
644              
645 0 0         if (debug_mod12()){
646 0           main::note("do_bash_hook: $cmd");
647             }
648              
649              
650 0           my $rand = int(rand(1000));
651              
652 0           my $st = system("$cmd 2>".story_cache_dir()."/$rand.err 1>".story_cache_dir()."/$rand.out");
653              
654 0 0         if($st != 0){
655 0           die "do_bash_hook failed. \n see ".story_cache_dir()."/$rand.err for details";
656             }
657              
658 0           my $out_file = story_cache_dir()."/$rand.out";
659              
660 0 0         open HOOK_OUT, $out_file or die "can't open HOOK_OUT file $out_file to read!";
661              
662 0           my @out = ;
663              
664 0           close HOOK_OUT;
665              
666 0           my %story_vars_bash = ();
667              
668 0           for my $l (@out) {
669              
670 0 0         next if $l=~/#/;
671              
672 0 0         ignore_story_err($1) if $l=~/ignore_story_err:\s+(\d)/;
673            
674 0 0         if ($l=~/story_var_bash:\s+(\S+)\s+(.*)/){
675 0           $story_vars_bash{$1}=$2;
676             #warn %story_vars_bash;
677 0           next;
678             }
679              
680 0 0         if ($l=~/story:\s+(\S+)/){
681 0           my $path = $1;
682 0 0         if (debug_mod12()){
683 0           main::note("run downstream story from bash hook");
684             }
685 0           run_story($path, {%story_vars_bash});
686 0           %story_vars_bash = ();
687             }
688             }
689              
690 0           return 1;
691              
692             }
693              
694              
695             sub apply_story_vars {
696              
697 0     0 0   my $story_vars = Outthentic::Story::Stat->current->{vars};
698              
699 0           set_prop( story_vars => $story_vars );
700              
701 0 0         open STORY_VARS, ">", (story_cache_dir())."/variables.json"
702             or die "can't open ".(story_cache_dir())."/variables.json write: $!";
703              
704 0           print STORY_VARS encode_json($story_vars);
705              
706 0           close STORY_VARS;
707              
708 0 0         open STORY_VARS, ">", (story_cache_dir())."/variables.bash"
709             or die "can't open ".(story_cache_dir())."/variables.bash write: $!";
710              
711 0           for my $name (keys %{$story_vars} ){
  0            
712 0           print STORY_VARS "$name=".$story_vars->{$name}."\n";
713             }
714              
715 0           close STORY_VARS;
716              
717             }
718              
719             sub story_var {
720              
721 0     0 0   my $name = shift;
722              
723 0           get_prop( 'story_vars' )->{$name};
724              
725             }
726              
727             sub story_vars_pretty {
728              
729 0     0 0   join " ", map { "$_:".(story_var($_)) } sort keys %{get_prop( 'story_vars' ) };
  0            
  0            
730              
731             }
732              
733             sub dump_os {
734              
735 0     0 0   my $cmd = <<'HERE';
736             #! /usr/bin/env sh
737              
738             # Find out the target OS
739             if [ -s /etc/os-release ]; then
740             # freedesktop.org and systemd
741             . /etc/os-release
742             OS=$NAME
743             VER=$VERSION_ID
744             elif lsb_release -h >/dev/null 2>&1; then
745             # linuxbase.org
746             OS=$(lsb_release -si)
747             VER=$(lsb_release -sr)
748             elif [ -s /etc/lsb-release ]; then
749             # For some versions of Debian/Ubuntu without lsb_release command
750             . /etc/lsb-release
751             OS=$DISTRIB_ID
752             VER=$DISTRIB_RELEASE
753             elif [ -s /etc/debian_version ]; then
754             # Older Debian/Ubuntu/etc.
755             OS=Debian
756             VER=$(cat /etc/debian_version)
757             elif [ -s /etc/SuSe-release ]; then
758             # Older SuSE/etc.
759             printf "TODO\n"
760             elif [ -s /etc/redhat-release ]; then
761             # Older Red Hat, CentOS, etc.
762             OS=$(cat /etc/redhat-release| head -n 1)
763             else
764             RELEASE_INFO=$(cat /etc/*-release 2>/dev/null | head -n 1)
765              
766             if [ ! -z "$RELEASE_INFO" ]; then
767             OS=$(printf -- "$RELEASE_INFO" | awk '{ print $1 }')
768             VER=$(printf -- "$RELEASE_INFO" | awk '{ print $NF }')
769             else
770             # Fall back to uname, e.g. "Linux ", also works for BSD, etc.
771             OS=$(uname -s)
772             VER=$(uname -r)
773             fi
774             fi
775              
776             echo $OS$VER
777              
778             HERE
779              
780 0           `$cmd`
781              
782             }
783              
784             sub _resolve_os {
785              
786            
787 0 0   0     if (!$OS){
788              
789 0           DONE: while (1) {
790 0           my $data = dump_os();
791 0 0 0       $data=~/alpine/i and $OS = 'alpine' and last DONE;
792 0 0 0       $data=~/minoca/i and $OS = "minoca" and last DONE;
793 0 0 0       $data=~/centos linux(\d+)/i and $OS = "centos$1" and last DONE;
794 0 0 0       $data=~/Red Hat.*release\s+(\d)/i and $OS = "centos$1" and last DONE;
795 0 0 0       $data=~/arch/i and $OS = 'archlinux' and last DONE;
796 0 0 0       $data=~/funtoo/i and $OS = 'funtoo' and last DONE;
797 0 0 0       $data=~/fedora/i and $OS = 'fedora' and last DONE;
798 0 0 0       $data=~/amazon/i and $OS = 'amazon' and last DONE;
799 0 0 0       $data=~/ubuntu/i and $OS = 'ubuntu' and last DONE;
800 0 0 0       $data=~/debian/i and $OS = 'debian' and last DONE;
801 0           last DONE;
802             }
803             }
804 0           return $OS;
805             }
806              
807             package main;
808              
809 0     0     sub os { Outthentic::Story::_resolve_os }
810              
811              
812              
813             1;
814              
815             __END__