File Coverage

blib/lib/File/Tasks.pm
Criterion Covered Total %
statement 77 133 57.8
branch 16 74 21.6
condition 3 6 50.0
subroutine 22 32 68.7
pod 14 15 93.3
total 132 260 50.7


line stmt bran cond sub pod time code
1             package File::Tasks;
2              
3             # See POD at end for docs
4              
5 4     4   59705 use 5.005;
  4         16  
  4         156  
6 4     4   21 use strict;
  4         8  
  4         117  
7 4     4   2958 use Clone ();
  4         27958  
  4         145  
8 4     4   4772 use Params::Util '_INSTANCE';
  4         26481  
  4         713  
9 4     4   4156 use Params::Coerce ();
  4         7150  
  4         78  
10 4     4   2195 use File::Tasks::Provider ();
  4         10  
  4         126  
11 4     4   2371 use File::Tasks::Add ();
  4         13  
  4         93  
12 4     4   2622 use File::Tasks::Edit ();
  4         9  
  4         73  
13 4     4   2097 use File::Tasks::Remove ();
  4         9  
  4         177  
14 4     4   24 use constant 'FFR' => 'File::Find::Rule';
  4         7  
  4         373  
15 4     4   21 use overload 'bool' => sub () { 1 };
  4         6  
  4         48  
16 4     4   238 use overload '+' => '_overlay';
  4         6  
  4         15  
17              
18 4     4   212 use vars qw{$VERSION};
  4         7  
  4         323  
19             BEGIN {
20 4     4   6037 $VERSION = '0.07';
21             }
22              
23              
24              
25              
26              
27             #####################################################################
28             # Constructor and Accessors
29              
30             sub new {
31 2 50   2 1 1617 my $class = ref $_[0] ? ref shift : shift;
32 2 50       11 my %params = (ref $_[0] eq 'HASH') ? %{shift()} : @_;
  0         0  
33              
34             # Create the basic object
35 2         11 my $self = bless {
36             provider => 'File::Tasks::Provider',
37             tasks => {},
38             ignore => undef,
39             }, $class;
40              
41             # Accept an alternate provider
42 2 50       16 if ( _INSTANCE($params{provider}, 'File::Tasks::Provider') ) {
43 0         0 $self->{provider} = $params{provider};
44             }
45              
46             # Set the auto-ignore
47 2 100       15 if ( _INSTANCE($params{ignore}, FFR) ) {
48 1         6 $self->{ignore} = $params{ignore}->prune->discard;
49             }
50              
51 2         27 $self;
52             }
53              
54 0     0 1 0 sub provider { $_[0]->{provider} }
55              
56 1     1 1 314 sub ignore { $_[0]->{ignore} }
57              
58             # We need to do this ourself, as sort in scalar context returns undef
59             sub paths {
60             wantarray
61 2         13 ? sort keys %{$_[0]->{tasks}}
  2         12  
62 4 100   4 1 13 : scalar(keys %{$_[0]->{tasks}});
63             }
64              
65             sub tasks {
66 2     2 1 5 my $tasks = $_[0]->{tasks};
67 2         6 map { $tasks->{$_} } $_[0]->paths;
  5         16  
68             }
69              
70             sub task {
71 0     0 1 0 my $self = shift;
72 0 0       0 my $path = defined $_[0] ? shift : return undef;
73 0         0 $self->{tasks}->{$path};
74             }
75              
76              
77              
78              
79              
80             #####################################################################
81             # Building the File::Tasks
82              
83             sub add {
84 0     0 1 0 $_[0]->set(File::Tasks::Add->new(@_));
85             }
86              
87             sub edit {
88 0     0 1 0 $_[0]->set(File::Tasks::Edit->new(@_));
89             }
90              
91             sub remove {
92 5     5 1 35 $_[0]->set(File::Tasks::Remove->new(@_));
93             }
94              
95             sub remove_dir {
96 2     2 1 339 my $self = shift;
97 2 50       46 my $dir = -d $_[0] ? shift : return undef;
98 2         16 require File::Find::Rule; # Only load as needed
99 2   33     25 my $Rule = _INSTANCE(shift, 'File::Find::Rule') || FFR->new;
100 2   66     105 $Rule = FFR->or( $self->{ignore} || (), $Rule )->relative->file;
101              
102             # Execute the file and add all resulting files as Remove entries
103 2         268 my @files = $Rule->in($dir);
104 2         2132 foreach my $file ( @files ) {
105 5 50       16 $self->remove( $file ) or return undef;
106             }
107              
108 2         13 scalar @files;
109             }
110              
111             sub set {
112 5     5 1 9 my $self = shift;
113 5 50       111 my $Task = _INSTANCE(shift, 'File::Tasks::Task') or return undef;
114 5 50       14 $self->clashes($Task->path) and return undef;
115 5         18 $self->{tasks}->{$Task} = $Task;
116             }
117              
118             sub clashes {
119 5     5 1 8 my $self = shift;
120 5 50       11 my $path = defined $_[0] ? shift : return undef;
121 5 50       16 return '' if $self->{tasks}->{$path};
122 5         7 foreach ( sort keys %{$self->{tasks}} ) {
  5         22  
123 4 50       12 return 1 if $path eq $_;
124 4 50       36 return 1 if $_ =~ m!^$path/!;
125 4 50       39 return 1 if $path =~ m!$_/!;
126             }
127 5         18 '';
128             }
129              
130              
131              
132              
133              
134             #####################################################################
135             # Actions for the File::Tasks
136              
137             sub test {
138 0     0 1   my $self = shift;
139 0           foreach my $path ( sort keys %{$self->{tasks}} ) {
  0            
140 0 0         my $Task = $self->{tasks}->{$path} or return undef;
141 0 0         $Task->test or return undef;
142             }
143 0           1;
144             }
145              
146             sub execute {
147 0     0 0   my $self = shift;
148 0           foreach my $path ( sort keys %{$self->{tasks}} ) {
  0            
149 0 0         my $Task = $self->{tasks}->{$path} or return undef;
150 0 0         $Task->execute or return undef;
151             }
152 0           1;
153             }
154              
155              
156              
157              
158              
159             #####################################################################
160             # Higher Order Methods
161              
162             sub overlay {
163 0     0 1   my $self = Clone::clone shift;
164 0 0         my $other = Params::Coerce::coerce('File::Tasks', shift) or return undef;
165 0           foreach my $Task ( $other->tasks ) {
166 0           my $Current = $self->task($Task->path);
167 0 0         unless ( $Current ) {
168 0 0         $self->set($Current) or return undef;
169 0           next;
170             }
171 0 0         if ( $Task->type eq 'add' ) {
    0          
172 0 0         if ( $Current->type eq 'add' ) {
173             # Add over Add - Replace existing object
174 0           $self->{tasks}->{$Task} = $Task;
175             } else {
176             # Add over Edit - Convert Add to Edit and replace
177             # Add over Delete - Convert Add to Edit and replace
178 0 0         my $Edit = File::Tasks::Edit->new(
179             $self, $Task->path, $Task->source,
180             ) or return undef;
181 0           $self->{tasks}->{$Edit} = $Edit;
182             }
183             } elsif ( $Task->type eq 'edit' ) {
184 0 0         if ( $Current->type eq 'add' ) {
185             # Edit over Add - Convert Edit to Add and replace
186 0 0         my $Add = File::Tasks::Add->new(
187             $self, $Task->path, $Task->source,
188             ) or return undef;
189 0           $self->{tasks}->{$Add} = $Add;
190             } else {
191             # Edit over Edit - Replace existing object
192             # Edit over Delete - Replace existing object
193 0           $self->{tasks}->{$Task} = $Task;
194             }
195             } else {
196 0 0         if ( $Current->type eq 'add' ) {
    0          
197             # Delete over Add - Tasks cancel each other out
198 0           delete $self->{tasks}->{$Task};
199             } elsif ( $Current->type eq 'edit' ) {
200             # Delete over Edit - Replace existing object
201 0           $self->{tasks}->{$Task} = $Task;
202             } else {
203             # Nothing to do
204             }
205             }
206             }
207 0           $self;
208             }
209              
210             # A thin wrapper to handle the way overloaded arguments are provided
211             sub _overlay {
212 0 0   0     my $left = _INSTANCE(shift, 'File::Tasks') ? shift : return undef;
213 0 0         my $right = Params::Coerce::coerce('File::Tasks', shift) or return undef;
214 0 0         ($left, $right) = ($right, $left) if $_[0];
215 0           $left->overlay($right);
216             }
217              
218              
219              
220              
221              
222             #####################################################################
223             # Coercion Support
224              
225             # From an entire builder
226             sub __from_Archive_Builder {
227 0     0     my $self = shift->new;
228 0 0         my $Builder = _INSTANCE(shift, 'Archive::Builder') or return undef;
229 0           my $files = $Builder->files;
230 0           foreach my $path ( keys %$files ) {
231 0 0         $self->add( $path, $files->{$path} ) or return undef;
232             }
233 0           $self;
234             }
235              
236             # From a single Section
237             sub __from_Archive_Builder_Section {
238 0     0     my $self = shift->new;
239 0 0         my $Section = _INSTANCE(shift, 'Archive::Builder::Section') or return undef;
240 0           my $files = $Section->files;
241 0           foreach my $path ( keys %$files ) {
242 0 0         $self->add( $path, $files->{$path} ) or return undef;
243             }
244 0           $self;
245             }
246            
247             1;
248              
249             __END__