File Coverage

blib/lib/Mason/Util.pm
Criterion Covered Total %
statement 51 143 35.6
branch 5 56 8.9
condition 0 6 0.0
subroutine 14 34 41.1
pod 0 19 0.0
total 70 258 27.1


line stmt bran cond sub pod time code
1             package Mason::Util;
2             $Mason::Util::VERSION = '2.22';
3 1     1   36205 use Carp;
  1         2  
  1         61  
4 1     1   837 use Class::Unload;
  1         6094  
  1         29  
5 1     1   314485 use Class::Load;
  1         2001346  
  1         92  
6 1     1   4280 use Data::Dumper;
  1         12358  
  1         116  
7 1     1   11 use Fcntl qw( :DEFAULT :seek );
  1         2  
  1         567  
8 1     1   22 use File::Find;
  1         2  
  1         82  
9 1     1   1014 use File::Spec::Functions ();
  1         1153  
  1         22  
10 1     1   1052 use JSON;
  1         22807  
  1         7  
11 1     1   175 use Try::Tiny;
  1         2  
  1         72  
12 1     1   17 use strict;
  1         2  
  1         41  
13 1     1   6 use warnings;
  1         2  
  1         36  
14 1     1   5 use base qw(Exporter);
  1         2  
  1         1984  
15              
16             our @EXPORT_OK =
17             qw(can_load catdir catfile checksum combine_similar_paths dump_one_line find_wanted first_index is_absolute json_encode json_decode mason_canon_path read_file taint_is_on touch_file trim uniq write_file);
18              
19             my $Fetch_Flags = O_RDONLY | O_BINARY;
20             my $Store_Flags = O_WRONLY | O_CREAT | O_BINARY;
21             my $File_Spec_Using_Unix = $File::Spec::ISA[0] eq 'File::Spec::Unix';
22              
23             # Map null, true and false to real Perl values
24             if ( JSON->VERSION < 2 ) {
25             $JSON::UnMapping = 1;
26             }
27              
28             sub can_load {
29              
30             # Load $class_name if possible. Return 1 if successful, 0 if it could not be
31             # found, and rethrow load error (other than not found).
32             #
33 0     0 0 0 my ($class_name) = @_;
34              
35 0         0 my $result;
36             try {
37 0     0   0 Class::Load::load_class($class_name);
38 0         0 $result = 1;
39             }
40             catch {
41 0 0 0 0   0 if ( /Can\'t locate .* in \@INC/ && !/Compilation failed/ ) {
42 0         0 $result = 0;
43             }
44             else {
45 0         0 die $_;
46             }
47 0         0 };
48 0         0 return $result;
49             }
50              
51             sub catdir {
52 0 0   0 0 0 return $File_Spec_Using_Unix ? join( "/", @_ ) : File::Spec::Functions::catdir(@_);
53             }
54              
55             sub catfile {
56 0 0   0 0 0 return $File_Spec_Using_Unix ? join( "/", @_ ) : File::Spec::Functions::catfile(@_);
57             }
58              
59             sub checksum {
60 0     0 0 0 my ($str) = @_;
61              
62             # Adler32 algorithm
63 0         0 my $s1 = 1;
64 0         0 my $s2 = 1;
65 0         0 for my $c ( unpack( "C*", $str ) ) {
66 0         0 $s1 = ( $s1 + $c ) % 65521;
67 0         0 $s2 = ( $s2 + $s1 ) % 65521;
68             }
69 0         0 return ( $s2 << 16 ) + $s1;
70             }
71              
72             # Convert /foo/bar.m, /foo/bar.pm, /foo.m, /foo.pm to
73             # /foo/bar.{m,pm}, /foo.{m,pm}. I have no idea why this takes
74             # so much code.
75             #
76             sub combine_similar_paths {
77 0     0 0 0 my @paths = @_;
78 0         0 my ( @final, $current_base, @current_exts );
79 0         0 foreach my $path (@paths) {
80 0 0       0 if ( my ( $base, $ext ) = ( $path =~ /^(.*)\.(.*)$/ ) ) {
81 0 0 0     0 if ( defined($current_base) && $current_base ne $base ) {
82 0 0       0 push(
83             @final,
84             "$current_base."
85             . (
86             ( @current_exts == 1 )
87             ? $current_exts[0]
88             : sprintf( '{%s}', join( ',', @current_exts ) )
89             )
90             );
91 0         0 @current_exts = ($ext);
92             }
93             else {
94 0         0 push( @current_exts, $ext );
95             }
96 0         0 $current_base = $base;
97             }
98             else {
99 0         0 push( @final, $path );
100             }
101             }
102 0 0       0 if ( defined($current_base) ) {
103 0 0       0 push(
104             @final,
105             "$current_base."
106             . (
107             ( @current_exts == 1 )
108             ? $current_exts[0]
109             : sprintf( '{%s}', join( ',', @current_exts ) )
110             )
111             );
112             }
113 0         0 return @final;
114             }
115              
116             sub delete_package {
117 0     0 0 0 my $pkg = shift;
118 0         0 Class::Unload->unload($pkg);
119             }
120              
121             sub dump_one_line {
122 0     0 0 0 my ($value) = @_;
123              
124 0         0 return Data::Dumper->new( [$value] )->Indent(0)->Sortkeys(1)->Quotekeys(0)->Terse(1)->Dump();
125             }
126              
127             # From File::Find::Wanted
128             sub find_wanted {
129 0     0 0 0 my $func = shift;
130 0         0 my @files;
131              
132 0         0 local $_;
133 0 0   0   0 find( sub { push @files, $File::Find::name if &$func }, @_ );
  0         0  
134              
135 0         0 return @files;
136             }
137              
138             # From List::MoreUtils
139             sub first_index (&@) {
140 0     0 0 0 my $f = shift;
141 0         0 for my $i ( 0 .. $#_ ) {
142 0         0 local *_ = \$_[$i];
143 0 0       0 return $i if $f->();
144             }
145 0         0 return -1;
146             }
147              
148             sub is_absolute {
149 0     0 0 0 my ($path) = @_;
150              
151 0         0 return substr( $path, 0, 1 ) eq '/';
152             }
153              
154             # Maintain compatibility with both JSON 1 and 2. Borrowed from Data::Serializer::JSON.
155             #
156             sub json_decode {
157 0     0 0 0 my ($text) = @_;
158 0 0       0 return JSON->VERSION < 2 ? JSON->new->jsonToObj($text) : JSON->new->decode($text);
159             }
160              
161             sub json_encode {
162 0     0 0 0 my ($data) = @_;
163 0 0       0 return JSON->VERSION < 2 ? JSON->new->objToJson($data) : JSON->new->utf8->encode($data);
164             }
165              
166             sub mason_canon_path {
167              
168             # Like File::Spec::canonpath but with a few fixes.
169             #
170 0     0 0 0 my $path = shift;
171 0         0 $path =~ s|/+|/|g; # xx////yy -> xx/yy
172 0         0 $path =~ s|(?:/\.)+/|/|g; # xx/././yy -> xx/yy
173             {
174 0 0       0 $path =~ s|^(?:\./)+||s unless $path eq "./"; # ./xx -> xx
  0         0  
175 0         0 $path =~ s|^/(?:\.\./)+|/|s; # /../../xx -> xx
176 0 0       0 $path =~ s|/\Z(?!\n)|| unless $path eq "/"; # xx/ -> xx
177 0 0       0 $path =~ s|/[^/]+/\.\.$|| && redo; # /xx/.. -> /
178 0 0       0 $path =~ s|[^/]+/\.\./|| && redo; # /xx/../yy -> /yy
179             }
180 0         0 return $path;
181             }
182              
183             sub read_file {
184 0     0 0 0 my ($file) = @_;
185              
186             # Fast slurp, adapted from File::Slurp::read, with unnecessary options removed
187             #
188 0         0 my $buf = "";
189 0         0 my $read_fh;
190 0 0       0 unless ( sysopen( $read_fh, $file, $Fetch_Flags ) ) {
191 0         0 croak "read_file '$file' - sysopen: $!";
192             }
193 0         0 my $size_left = -s $read_fh;
194 0         0 while (1) {
195 0         0 my $read_cnt = sysread( $read_fh, $buf, $size_left, length $buf );
196 0 0       0 if ( defined $read_cnt ) {
197 0 0       0 last if $read_cnt == 0;
198 0         0 $size_left -= $read_cnt;
199 0 0       0 last if $size_left <= 0;
200             }
201             else {
202 0         0 croak "read_file '$file' - sysread: $!";
203             }
204             }
205 0         0 return $buf;
206             }
207              
208             sub taint_is_on {
209 1 50   1 0 28 return ${^TAINT} ? 1 : 0;
210             }
211              
212             sub touch_file {
213 0     0 0 0 my ($file) = @_;
214 0 0       0 if ( -f $file ) {
215 0         0 my $time = time;
216 0         0 utime( $time, $time, $file );
217             }
218             else {
219 0         0 write_file( $file, "" );
220             }
221             }
222              
223             sub trim {
224 0     0 0 0 my ($str) = @_;
225 0 0       0 if ( defined($str) ) {
226 0         0 for ($str) { s/^\s+//; s/\s+$// }
  0         0  
  0         0  
227             }
228 0         0 return $str;
229             }
230              
231             # From List::MoreUtils
232             sub uniq (@) {
233 0     0 0 0 my %h;
234 0 0       0 map { $h{$_}++ == 0 ? $_ : () } @_;
  0         0  
235             }
236              
237             sub write_file {
238 1     1 0 121280 my ( $file, $data, $file_create_mode ) = @_;
239              
240 1 50       6 ($file) = $file =~ /^(.*)/s if taint_is_on(); # Untaint blindly
241 1 50       10 $file_create_mode = oct(666) if !defined($file_create_mode);
242              
243             # Fast spew, adapted from File::Slurp::write, with unnecessary options removed
244             #
245             {
246 1         3 my $write_fh;
  1         3  
247 1 50       137 unless ( sysopen( $write_fh, $file, $Store_Flags, $file_create_mode ) ) {
248 0         0 croak "write_file '$file' - sysopen: $!";
249             }
250 1         3 my $size_left = length($data);
251 1         3 my $offset = 0;
252 1         2 do {
253 1         73 my $write_cnt = syswrite( $write_fh, $data, $size_left, $offset );
254 1 50       5 unless ( defined $write_cnt ) {
255 0         0 croak "write_file '$file' - syswrite: $!";
256             }
257 1         3 $size_left -= $write_cnt;
258 1         7 $offset += $write_cnt;
259             } while ( $size_left > 0 );
260 1         65 truncate( $write_fh, sysseek( $write_fh, 0, SEEK_CUR ) )
261             }
262             }
263              
264             1;