File Coverage

blib/lib/File/LinkTree/Builder.pm
Criterion Covered Total %
statement 65 74 87.8
branch 12 28 42.8
condition 6 15 40.0
subroutine 17 20 85.0
pod 10 11 90.9
total 110 148 74.3


line stmt bran cond sub pod time code
1 1     1   886 use strict;
  1         2  
  1         35  
2 1     1   5 use warnings;
  1         2  
  1         57  
3             package File::LinkTree::Builder;
4             {
5             $File::LinkTree::Builder::VERSION = '0.006';
6             }
7             # ABSTRACT: builds a tree of symlinks based on file metadata
8              
9 1     1   6 use Carp ();
  1         3  
  1         12  
10 1     1   5 use Cwd ();
  1         1  
  1         12  
11 1     1   5 use File::Basename ();
  1         2  
  1         13  
12 1     1   761 use File::Next;
  1         2215  
  1         37  
13 1     1   7 use File::Path ();
  1         2  
  1         17  
14 1     1   6 use File::Spec;
  1         1  
  1         955  
15              
16              
17              
18             sub build_tree {
19 1     1 1 1733 my ($self, $arg) = @_;
20 1         4 $self->new($arg)->run;
21             }
22              
23              
24             sub new {
25 1     1 1 3 my ($class, $arg) = @_;
26 1   50     6 $arg ||= {};
27              
28 1   50     9 my $on_existing = $arg->{on_existing} || 'die';
29 1 50 33     9 die "invalid 'on_existing' argument"
30             unless $on_existing eq 'die' or $on_existing eq 'skip';
31              
32 1 50 33     7 die "only give storage_root or storage_roots, not both"
33             if $arg->{storage_root} and $arg->{storage_roots};
34              
35 1 50       8 $arg->{storage_root} = $arg->{storage_roots} if $arg->{storage_roots};
36              
37 0         0 my @storage_roots = ref $arg->{storage_root}
38 1 50       6 ? @{$arg->{storage_root}}
39             : $arg->{storage_root};
40              
41 1         6 my $iterator = File::Next::files(
42             {
43             file_filter => $arg->{file_filter},
44             },
45             @storage_roots,
46             );
47              
48 1 50       108 Carp::croak "no file storage_root" unless $iterator;
49              
50 1   50     10 my $self = bless {
51             iterator => $iterator,
52             link_paths => $arg->{link_paths},
53             storage_root => \@storage_roots,
54             link_root => $arg->{link_root} || '.',
55             hardlink => ! ! $arg->{hardlink},
56             on_existing => $on_existing,
57             } => $class;
58              
59             # It's set this way so that in a subclass that has one fixed method to get
60             # metadata, it can croak! -- rjbs, 2007-06-12
61 1 50       7 $self->set_metadata_getter($arg->{metadata_getter})
62             if exists $arg->{metadata_getter};
63              
64 1         5 return $self;
65             }
66              
67              
68             sub metadata_for_file {
69 2     2 1 4 my ($self, $filename) = @_;
70              
71 2 50       12 return $self->{metadata_getter}->($filename) if $self->{metadata_getter};
72 0         0 Carp::croak "no metadata getter supplied";
73             }
74              
75              
76 0     0 0 0 sub storage_root { @{ $_[0]->{storage_roots} } };
  0         0  
77 0     0 1 0 sub storage_roots { @{ $_[0]->{storage_roots} } };
  0         0  
78              
79              
80 4     4 1 59 sub link_root { $_[0]->{link_root} }
81              
82              
83 3     3 1 15 sub iterator { $_[0]->{iterator} };
84              
85              
86             sub link_paths {
87 2     2 1 4 my ($self) = @_;
88 2         2 return @{ $self->{link_paths} };
  2         9  
89             }
90              
91              
92 4     4 1 26 sub hardlink { $_[0]->{hardlink} }
93              
94              
95             # XXX: Refactor me plzkthx! -- rjbs, 2007-06-13
96             sub run {
97 1     1 1 3 my ($self) = @_;
98              
99 1         5 FILE: while (my $filename = $self->iterator->()) {
100 2         362 my $abs_file = File::Spec->rel2abs($filename, Cwd::getcwd);
101 2         7 my $meta = $self->metadata_for_file($abs_file);
102 2         477 my $basename = File::Basename::basename($filename);
103              
104 2         8 for my $datapath ($self->link_paths) {
105 8 50       50 my @path = map {
    50          
106 4         9 defined $meta->{$_} and length $meta->{$_} ? $meta->{$_} : '-'
107             } @$datapath;
108              
109 4         9 for my $path (@path) {
110 8         32 $path =~ s{/}{-}g;
111 8         16 $path =~ s{^\.}{_};
112             }
113              
114 4         12 my $path = File::Spec->catfile($self->link_root, @path);
115 4         1323 File::Path::mkpath($path);
116              
117 4         46 my $link = File::Spec->catfile($path, $basename);
118              
119 4 50 33     124 next FILE if -e $link and $self->_skip_existing_links;
120              
121 4 50       10 if ($self->hardlink) {
122 0 0       0 link $abs_file => $link
123             or die "couldn't create link <$link> to <$abs_file>: $!";
124             } else {
125 4 50       386 symlink $abs_file => $link
126             or die "couldn't create link <$link> to <$abs_file>: $!";
127             }
128             }
129             }
130             }
131              
132             sub _skip_existing_links {
133 0     0   0 my ($self) = @_;
134 0 0       0 return 1 if $self->{on_existing} eq 'skip';
135             }
136              
137              
138             sub set_metadata_getter {
139 1     1 1 2 my ($self, $coderef) = @_;
140 1         6 $self->{metadata_getter} = $coderef;
141             }
142              
143              
144             1;
145              
146             __END__