File Coverage

blib/lib/File/Slurp/Tree.pm
Criterion Covered Total %
statement 42 42 100.0
branch 8 10 80.0
condition 4 8 50.0
subroutine 8 8 100.0
pod 2 2 100.0
total 64 70 91.4


line stmt bran cond sub pod time code
1             package File::Slurp::Tree;
2 2     2   1813 use strict;
  2         4  
  2         67  
3 2     2   1636 use File::Find::Rule;
  2         15324  
  2         19  
4 2     2   604 use File::Path qw( mkpath );
  2         4  
  2         116  
5 2     2   1848 use File::Slurp qw( read_file write_file );
  2         24625  
  2         135  
6             require Exporter;
7 2     2   18 use base 'Exporter';
  2         5  
  2         156  
8 2     2   9 use vars qw(@EXPORT $VERSION);
  2         3  
  2         764  
9              
10             @EXPORT = qw( slurp_tree spew_tree );
11             $VERSION = 1.24;
12              
13             =head1 NAME
14              
15             File::Slurp::Tree - slurp and emit file trees as nested hashes
16              
17             =head1 SYNOPSIS
18              
19             # (inefficiently) duplicate a file tree from path a to b
20             use File::Slurp::Tree;
21             my $tree = slurp_tree( "path_a" );
22             spew_tree( "path_b" => $tree );
23              
24             =head1 DESCRIPTION
25              
26             File::Slurp::Tree provides functions for slurping and emitting trees
27             of files and directories.
28              
29             # an example of use in a test suite
30             use Test::More tests => 1;
31             use File::Slurp::Tree;
32             is_deeply( slurp_tree( "t/some_path" ), { foo => {}, bar => "sample\n" },
33             "some_path contains a directory called foo, and a file bar" );
34              
35             The tree datastructure is a hash of hashes. The keys of each hash are
36             names of directories or files. Directories have hash references as
37             their value, files have a scalar which holds the contents of the file.
38              
39             =head1 EXPORTED ROUTINES
40              
41             =head2 slurp_tree( $path, %options )
42              
43             return a nested hash reference containing everything within $path
44              
45             %options may include the following keys:
46              
47             =over
48              
49             =item rule
50              
51             a L object that will match the files and directories
52             in the path. defaults to an empty rule (matches everything)
53              
54             =back
55              
56             =cut
57              
58             sub slurp_tree {
59 2     2 1 7 my $in = shift;
60 2         8 my %args = @_;
61              
62             # top must not have a trailing slash, in may. this fixes Greg's bug
63             # and allows in to be "/"
64 2         13 (my $top = $in) =~ s{/$}{};
65              
66 2   33     39 my $rule = $args{rule} || File::Find::Rule->new;
67 2         61 my $tree = {};
68 2         16 for my $file ( $rule->in( $in ) ) {
69 6 100       3155 next if $file eq $top;
70 4         50 (my $rel = $file) =~ s{^\Q$top\E/}{};
71 4 50       16 next unless $rel; # it's /
72             #print "top:$top file:$file rel:$rel\n";
73              
74 4         20 my @elems = split m{/}, $rel;
75              
76             # go to the top of the tree
77 4         10 my $node = $tree;
78             # and walk along the path
79 4         18 while (my $elem = shift @elems) {
80             # on the path || a dir
81 4 100 66     126 if (@elems || -d $file) {
82 2   50     31 $node = $node->{ $elem } ||= {};
83             }
84             else {
85             # a file, slurp it
86 2         24 $node->{ $elem } = read_file "$file", binmode => ':raw';
87             }
88             }
89             }
90 2         28 return $tree;
91             }
92              
93             =head2 spew_tree( $path => $tree )
94              
95             Creates a file tree as described by C<$tree> at C<$path>
96              
97             =cut
98              
99             sub spew_tree {
100 2     2 1 160970 my ($top, $tree) = @_;
101 2         3 eval { mkpath( $top ) };
  2         379  
102 2         12 for my $stem (keys %$tree) {
103 2 100       280 if (ref $tree->{$stem}) { # directory
104 1         8 spew_tree( "$top/$stem", $tree->{ $stem } );
105             }
106             else { # file
107 1 50       16 write_file( "$top/$stem", { binmode => ':raw' }, $tree->{ $stem } )
108             if defined $tree->{ $stem }; # avoid an undef warning
109             }
110             }
111 2         9 return 1;
112             }
113              
114             1;
115             __END__