File Coverage

blib/lib/App/Fasops/Command/join.pm
Criterion Covered Total %
statement 82 87 94.2
branch 17 22 77.2
condition 7 9 77.7
subroutine 12 12 100.0
pod 6 6 100.0
total 124 136 91.1


line stmt bran cond sub pod time code
1             package App::Fasops::Command::join;
2 21     21   14689 use strict;
  21         50  
  21         655  
3 21     21   106 use warnings;
  21         41  
  21         514  
4 21     21   99 use autodie;
  21         39  
  21         117  
5              
6 21     21   106923 use App::Fasops -command;
  21         58  
  21         200  
7 21     21   7299 use App::RL::Common;
  21         42  
  21         578  
8 21     21   119 use App::Fasops::Common;
  21         41  
  21         21070  
9              
10             sub abstract {
11 2     2 1 56 return 'join multiple blocked fasta files by common target';
12             }
13              
14             sub opt_spec {
15             return (
16 4     4 1 35 [ "outfile|o=s", "Output filename. [stdout] for screen" ],
17             [ "name|n=s", "According to this species. Default is the first one" ],
18             { show_defaults => 1, }
19             );
20             }
21              
22             sub usage_desc {
23 4     4 1 46188 return "fasops join [options] [more infiles]";
24             }
25              
26             sub description {
27 1     1 1 782 my $desc;
28 1         5 $desc .= ucfirst(abstract) . ".\n";
29 1         3 $desc .= <<'MARKDOWN';
30              
31             * are paths to axt files, .axt.gz is supported
32             * infile == stdin means reading from STDIN
33              
34             MARKDOWN
35              
36 1         4 return $desc;
37             }
38              
39             sub validate_args {
40 3     3 1 2825 my ( $self, $opt, $args ) = @_;
41              
42 3 100       6 if ( !@{$args} ) {
  3         13  
43 1         3 my $message = "This command need one or more input files.\n\tIt found";
44 1         3 $message .= sprintf " [%s]", $_ for @{$args};
  1         4  
45 1         3 $message .= ".\n";
46 1         11 $self->usage_error($message);
47             }
48 2         4 for ( @{$args} ) {
  2         6  
49 4 50       122 next if lc $_ eq "stdin";
50 4 100       17 if ( !Path::Tiny::path($_)->is_file ) {
51 1         119 $self->usage_error("The input file [$_] doesn't exist.");
52             }
53             }
54              
55 1 50       53 if ( !exists $opt->{outfile} ) {
56             $opt->{outfile}
57 0         0 = Path::Tiny::path( $args->[0] )->absolute . ".join.fas";
58             }
59             }
60              
61             sub execute {
62 1     1 1 8 my ( $self, $opt, $args ) = @_;
63              
64 1         3 my $out_fh;
65 1 50       6 if ( lc( $opt->{outfile} ) eq "stdout" ) {
66 1         4 $out_fh = *STDOUT{IO};
67             }
68             else {
69 0         0 open $out_fh, ">", $opt->{outfile};
70             }
71              
72 1         13 tie my %block_of, "Tie::IxHash";
73 1         20 for my $infile ( @{$args} ) {
  1         4  
74 3         355 my $in_fh;
75 3 50       11 if ( lc $infile eq "stdin" ) {
76 0         0 $in_fh = *STDIN{IO};
77             }
78             else {
79 3         18 $in_fh = IO::Zlib->new( $infile, "rb" );
80             }
81              
82 3         5024 my $content = ''; # content of one block
83 3         6 while (1) {
84 18 100 100     177 last if $in_fh->eof and $content eq '';
85 15         1074 my $line = '';
86 15 100       54 if ( !$in_fh->eof ) {
87 12         856 $line = $in_fh->getline;
88             }
89 15 100 66     2796 if ( ( $line eq '' or $line =~ /^\s+$/ ) and $content ne '' ) {
      66        
90 3         14 my $info_of = App::Fasops::Common::parse_block($content);
91 3         8 $content = '';
92              
93             # set $opt->{name} to the first one of the first block
94 3 50       20 if ( !defined $opt->{name} ) {
95 0         0 ( $opt->{name} ) = keys %{$info_of};
  0         0  
96             }
97              
98             # target name
99 3         17 my $header = App::RL::Common::encode_header( $info_of->{ $opt->{name} } );
100              
101 3 100       482 if ( exists $block_of{$header} ) {
102             my @other_names
103 2         12 = grep { $_ ne $opt->{name} } keys %{$info_of};
  4         42  
  2         7  
104 2         7 for my $name (@other_names) {
105 2         7 $block_of{$header}->{$name} = $info_of->{$name};
106             }
107             }
108             else {
109 1         10 $block_of{$header} = $info_of;
110             }
111             }
112             else {
113 12         91 $content .= $line;
114             }
115             }
116 3         498 $in_fh->close;
117             }
118              
119 1         143 for my $header ( keys %block_of ) {
120 1         19 my $info_of = $block_of{$header};
121              
122 1         8 my @names = keys %{$info_of};
  1         3  
123              
124 1         26 for my $name (@names) {
125 4         129 my $info = $info_of->{$name};
126 4         31 printf {$out_fh} ">%s\n", App::RL::Common::encode_header($info);
  4         16  
127 4         788 printf {$out_fh} "%s\n", $info->{seq};
  4         21  
128             }
129 1         37 print {$out_fh} "\n";
  1         6  
130             }
131              
132 1         20 close $out_fh;
133             }
134              
135             1;